]> git.lizzy.rs Git - plan9front.git/blob - sys/src/9/kw/devrtc.c
Import sources from 2011-03-30 iso image - lib
[plan9front.git] / sys / src / 9 / kw / devrtc.c
1 /*
2  * devrtc - real-time clock, for kirkwood
3  */
4 #include "u.h"
5 #include "../port/lib.h"
6 #include "mem.h"
7 #include "dat.h"
8 #include "fns.h"
9 #include "../port/error.h"
10 #include "io.h"
11
12 typedef struct  RtcReg  RtcReg;
13 typedef struct  Rtc     Rtc;
14
15 struct RtcReg
16 {
17         ulong   time;
18         ulong   date;
19         ulong   alarmtm;
20         ulong   alarmdt;
21         ulong   intrmask;
22         ulong   intrcause;
23 };
24
25 struct Rtc
26 {
27         int     sec;
28         int     min;
29         int     hour;
30         int     wday;
31         int     mday;
32         int     mon;
33         int     year;
34 };
35
36 enum {
37         Qdir,
38         Qrtc,
39 };
40
41 static Dirtab rtcdir[] = {
42         ".",    {Qdir, 0, QTDIR},       0,              0555,
43         "rtc",  {Qrtc},                 NUMSIZE,        0664,
44 };
45 static  RtcReg  *rtcreg;                /* filled in by attach */
46 static  Lock    rtclock;
47
48 #define SEC2MIN 60
49 #define SEC2HOUR (60*SEC2MIN)
50 #define SEC2DAY (24L*SEC2HOUR)
51
52 /*
53  * days per month plus days/year
54  */
55 static  int     dmsize[] =
56 {
57         365, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
58 };
59 static  int     ldmsize[] =
60 {
61         366, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
62 };
63
64 /*
65  *  return the days/month for the given year
66  */
67 static int *
68 yrsize(int yr)
69 {
70         if((yr % 4) == 0)
71                 return ldmsize;
72         else
73                 return dmsize;
74 }
75
76 /*
77  *  compute seconds since Jan 1 1970
78  */
79 static ulong
80 rtc2sec(Rtc *rtc)
81 {
82         ulong secs;
83         int i;
84         int *d2m;
85
86         /*
87          *  seconds per year
88          */
89         secs = 0;
90         for(i = 1970; i < rtc->year; i++){
91                 d2m = yrsize(i);
92                 secs += d2m[0] * SEC2DAY;
93         }
94
95         /*
96          *  seconds per month
97          */
98         d2m = yrsize(rtc->year);
99         for(i = 1; i < rtc->mon; i++)
100                 secs += d2m[i] * SEC2DAY;
101
102         secs += (rtc->mday-1) * SEC2DAY;
103         secs += rtc->hour * SEC2HOUR;
104         secs += rtc->min * SEC2MIN;
105         secs += rtc->sec;
106
107         return secs;
108 }
109
110 /*
111  *  compute rtc from seconds since Jan 1 1970
112  */
113 static void
114 sec2rtc(ulong secs, Rtc *rtc)
115 {
116         int d;
117         long hms, day;
118         int *d2m;
119
120         /*
121          * break initial number into days
122          */
123         hms = secs % SEC2DAY;
124         day = secs / SEC2DAY;
125         if(hms < 0) {
126                 hms += SEC2DAY;
127                 day -= 1;
128         }
129
130         /*
131          * 19700101 was thursday
132          */
133         rtc->wday = (day + 7340036L) % 7;
134
135         /*
136          * generate hours:minutes:seconds
137          */
138         rtc->sec = hms % 60;
139         d = hms / 60;
140         rtc->min = d % 60;
141         d /= 60;
142         rtc->hour = d;
143
144         /*
145          * year number
146          */
147         if(day >= 0)
148                 for(d = 1970; day >= *yrsize(d); d++)
149                         day -= *yrsize(d);
150         else
151                 for (d = 1970; day < 0; d--)
152                         day += *yrsize(d-1);
153         rtc->year = d;
154
155         /*
156          * generate month
157          */
158         d2m = yrsize(rtc->year);
159         for(d = 1; day >= d2m[d]; d++)
160                 day -= d2m[d];
161         rtc->mday = day + 1;
162         rtc->mon = d;
163 }
164
165 enum {
166         Rtcsec  = 0x00007f,
167         Rtcmin  = 0x007f00,
168         Rtcms   = 8,
169         Rtchr12 = 0x1f0000,
170         Rtchr24 = 0x3f0000,
171         Rtchrs  = 16,
172
173         Rdmday  = 0x00003f,
174         Rdmon   = 0x001f00,
175         Rdms    = 8,
176         Rdyear  = 0x7f0000,
177         Rdys    = 16,
178
179         Rtcpm   = 1<<21,                /* pm bit */
180         Rtc12   = 1<<22,                /* 12 hr clock */
181 };
182
183 static ulong
184 bcd2dec(ulong bcd)
185 {
186         ulong d, m, i;
187
188         d = 0;
189         m = 1;
190         for(i = 0; i < 2 * sizeof d; i++){
191                 d += ((bcd >> (4*i)) & 0xf) * m;
192                 m *= 10;
193         }
194         return d;
195 }
196
197 static ulong
198 dec2bcd(ulong d)
199 {
200         ulong bcd, i;
201
202         bcd = 0;
203         for(i = 0; d != 0; i++){
204                 bcd |= (d%10) << (4*i);
205                 d /= 10;
206         }
207         return bcd;
208 }
209
210 static long
211 _rtctime(void)
212 {
213         ulong t, d;
214         Rtc rtc;
215
216         t = rtcreg->time;
217         d = rtcreg->date;
218
219         rtc.sec = bcd2dec(t & Rtcsec);
220         rtc.min = bcd2dec((t & Rtcmin) >> Rtcms);
221
222         if(t & Rtc12){
223                 rtc.hour = bcd2dec((t & Rtchr12) >> Rtchrs) - 1; /* 1—12 */
224                 if(t & Rtcpm)
225                         rtc.hour += 12;
226         }else
227                 rtc.hour = bcd2dec((t & Rtchr24) >> Rtchrs);    /* 0—23 */
228
229         rtc.mday = bcd2dec(d & Rdmday);                         /* 1—31 */
230         rtc.mon = bcd2dec((d & Rdmon) >> Rdms);                 /* 1—12 */
231         rtc.year = bcd2dec((d & Rdyear) >> Rdys) + 2000;        /* year%100 */
232
233 //      print("%0.2d:%0.2d:%.02d %0.2d/%0.2d/%0.2d\n", /* HH:MM:SS YY/MM/DD */
234 //              rtc.hour, rtc.min, rtc.sec, rtc.year, rtc.mon, rtc.mday);
235         return rtc2sec(&rtc);
236 }
237
238 long
239 rtctime(void)
240 {
241         int i;
242         long t, ot;
243
244         ilock(&rtclock);
245
246         /* loop until we get two reads in a row the same */
247         t = _rtctime();
248         ot = ~t;
249         for(i = 0; i < 100 && ot != t; i++){
250                 ot = t;
251                 t = _rtctime();
252         }
253         if(ot != t)
254                 print("rtctime: we are boofheads\n");
255
256         iunlock(&rtclock);
257         return t;
258 }
259
260 static void
261 setrtc(Rtc *rtc)
262 {
263         ilock(&rtclock);
264         rtcreg->time = dec2bcd(rtc->wday) << 24 | dec2bcd(rtc->hour) << 16 |
265                 dec2bcd(rtc->min) << 8 | dec2bcd(rtc->sec);
266         rtcreg->date = dec2bcd(rtc->year - 2000) << 16 |
267                 dec2bcd(rtc->mon) << 8 | dec2bcd(rtc->mday);
268         iunlock(&rtclock);
269 }
270
271 static Chan*
272 rtcattach(char *spec)
273 {
274         rtcreg = (RtcReg*)soc.rtc;
275         return devattach(L'r', spec);
276 }
277
278 static Walkqid*
279 rtcwalk(Chan *c, Chan *nc, char **name, int nname)
280 {
281         return devwalk(c, nc, name, nname, rtcdir, nelem(rtcdir), devgen);
282 }
283
284 static int
285 rtcstat(Chan *c, uchar *dp, int n)
286 {
287         return devstat(c, dp, n, rtcdir, nelem(rtcdir), devgen);
288 }
289
290 static Chan*
291 rtcopen(Chan *c, int omode)
292 {
293         return devopen(c, omode, rtcdir, nelem(rtcdir), devgen);
294 }
295
296 static void
297 rtcclose(Chan*)
298 {
299 }
300
301 static long
302 rtcread(Chan *c, void *buf, long n, vlong off)
303 {
304         if(c->qid.type & QTDIR)
305                 return devdirread(c, buf, n, rtcdir, nelem(rtcdir), devgen);
306
307         switch((ulong)c->qid.path){
308         default:
309                 error(Egreg);
310         case Qrtc:
311                 return readnum(off, buf, n, rtctime(), NUMSIZE);
312         }
313 }
314
315 static long
316 rtcwrite(Chan *c, void *buf, long n, vlong off)
317 {
318         ulong offset = off;
319         char *cp, sbuf[32];
320         Rtc rtc;
321
322         switch((ulong)c->qid.path){
323         default:
324                 error(Egreg);
325         case Qrtc:
326                 if(offset != 0 || n >= sizeof(sbuf)-1)
327                         error(Ebadarg);
328                 memmove(sbuf, buf, n);
329                 sbuf[n] = '\0';
330                 for(cp = sbuf; *cp != '\0'; cp++)
331                         if(*cp >= '0' && *cp <= '9')
332                                 break;
333                 sec2rtc(strtoul(cp, 0, 0), &rtc);
334                 setrtc(&rtc);
335                 return n;
336         }
337 }
338
339 Dev rtcdevtab = {
340         L'r',
341         "rtc",
342
343         devreset,
344         devinit,
345         devshutdown,
346         rtcattach,
347         rtcwalk,
348         rtcstat,
349         rtcopen,
350         devcreate,
351         rtcclose,
352         rtcread,
353         devbread,
354         rtcwrite,
355         devbwrite,
356         devremove,
357         devwstat,
358         devpower,
359 };