]> git.lizzy.rs Git - plan9front.git/blob - sys/src/9/pc/devrtc.c
kernel: cleanup makefile for $CONF.$O target
[plan9front.git] / sys / src / 9 / pc / devrtc.c
1 #include        "u.h"
2 #include        "../port/lib.h"
3 #include        "mem.h"
4 #include        "dat.h"
5 #include        "fns.h"
6 #include        "../port/error.h"
7
8 /*
9  *  real time clock and non-volatile ram
10  */
11
12 enum {
13         Paddr=          0x70,   /* address port */
14         Pdata=          0x71,   /* data port */
15
16         Seconds=        0x00,
17         Minutes=        0x02,
18         Hours=          0x04, 
19         Mday=           0x07,
20         Month=          0x08,
21         Year=           0x09,
22         Status=         0x0A,
23
24         Nvoff=          128,    /* where usable nvram lives */
25         Nvsize=         256,
26
27         Nbcd=           6,
28 };
29
30 typedef struct Rtc      Rtc;
31 struct Rtc
32 {
33         int     sec;
34         int     min;
35         int     hour;
36         int     mday;
37         int     mon;
38         int     year;
39 };
40
41
42 enum{
43         Qdir = 0,
44         Qrtc,
45         Qnvram,
46 };
47
48 Dirtab rtcdir[]={
49         ".",    {Qdir, 0, QTDIR},       0,      0555,
50         "nvram",        {Qnvram, 0},    Nvsize, 0664,
51         "rtc",          {Qrtc, 0},      0,      0664,
52 };
53
54 static ulong rtc2sec(Rtc*);
55 static void sec2rtc(ulong, Rtc*);
56
57 void
58 rtcinit(void)
59 {
60         if(ioalloc(Paddr, 2, 0, "rtc/nvr") < 0)
61                 panic("rtcinit: ioalloc failed");
62 }
63
64 static Chan*
65 rtcattach(char* spec)
66 {
67         return devattach('r', spec);
68 }
69
70 static Walkqid*  
71 rtcwalk(Chan* c, Chan *nc, char** name, int nname)
72 {
73         return devwalk(c, nc, name, nname, rtcdir, nelem(rtcdir), devgen);
74 }
75
76 static int       
77 rtcstat(Chan* c, uchar* dp, int n)
78 {
79         return devstat(c, dp, n, rtcdir, nelem(rtcdir), devgen);
80 }
81
82 static Chan*
83 rtcopen(Chan* c, int omode)
84 {
85         omode = openmode(omode);
86         switch((ulong)c->qid.path){
87         case Qrtc:
88                 if(strcmp(up->user, eve)!=0 && omode!=OREAD)
89                         error(Eperm);
90                 break;
91         case Qnvram:
92                 if(strcmp(up->user, eve)!=0)
93                         error(Eperm);
94         }
95         return devopen(c, omode, rtcdir, nelem(rtcdir), devgen);
96 }
97
98 static void      
99 rtcclose(Chan*)
100 {
101 }
102
103 #define GETBCD(o) ((bcdclock[o]&0xf) + 10*(bcdclock[o]>>4))
104
105 static long      
106 _rtctime(void)
107 {
108         uchar bcdclock[Nbcd];
109         Rtc rtc;
110         int i;
111
112         /* don't do the read until the clock is no longer busy */
113         for(i = 0; i < 10000; i++){
114                 outb(Paddr, Status);
115                 if(inb(Pdata) & 0x80)
116                         continue;
117
118                 /* read clock values */
119                 outb(Paddr, Seconds);   bcdclock[0] = inb(Pdata);
120                 outb(Paddr, Minutes);   bcdclock[1] = inb(Pdata);
121                 outb(Paddr, Hours);     bcdclock[2] = inb(Pdata);
122                 outb(Paddr, Mday);      bcdclock[3] = inb(Pdata);
123                 outb(Paddr, Month);     bcdclock[4] = inb(Pdata);
124                 outb(Paddr, Year);      bcdclock[5] = inb(Pdata);
125
126                 outb(Paddr, Status);
127                 if((inb(Pdata) & 0x80) == 0)
128                         break;
129         }
130
131         /*
132          *  convert from BCD
133          */
134         rtc.sec = GETBCD(0);
135         rtc.min = GETBCD(1);
136         rtc.hour = GETBCD(2);
137         rtc.mday = GETBCD(3);
138         rtc.mon = GETBCD(4);
139         rtc.year = GETBCD(5);
140
141         /*
142          *  the world starts jan 1 1970
143          */
144         if(rtc.year < 70)
145                 rtc.year += 2000;
146         else
147                 rtc.year += 1900;
148         return rtc2sec(&rtc);
149 }
150
151 static Lock nvrtlock;
152
153 long
154 rtctime(void)
155 {
156         int i;
157         long t, ot;
158
159         ilock(&nvrtlock);
160
161         /* loop till we get two reads in a row the same */
162         t = _rtctime();
163         for(i = 0; i < 100; i++){
164                 ot = t;
165                 t = _rtctime();
166                 if(ot == t)
167                         break;
168         }
169         if(i == 100) print("we are boofheads\n");
170
171         iunlock(&nvrtlock);
172
173         return t;
174 }
175
176 static long      
177 rtcread(Chan* c, void* buf, long n, vlong off)
178 {
179         ulong t;
180         char *a, *start;
181         ulong offset = off;
182
183         if(c->qid.type & QTDIR)
184                 return devdirread(c, buf, n, rtcdir, nelem(rtcdir), devgen);
185
186         switch((ulong)c->qid.path){
187         case Qrtc:
188                 t = rtctime();
189                 n = readnum(offset, buf, n, t, 12);
190                 return n;
191         case Qnvram:
192                 if(n == 0)
193                         return 0;
194                 if(n > Nvsize)
195                         n = Nvsize;
196                 a = start = smalloc(n);
197
198                 ilock(&nvrtlock);
199                 for(t = offset; t < offset + n; t++){
200                         if(t >= Nvsize)
201                                 break;
202                         outb(Paddr, Nvoff+t);
203                         *a++ = inb(Pdata);
204                 }
205                 iunlock(&nvrtlock);
206
207                 if(waserror()){
208                         free(start);
209                         nexterror();
210                 }
211                 memmove(buf, start, t - offset);
212                 poperror();
213
214                 free(start);
215                 return t - offset;
216         }
217         error(Ebadarg);
218         return 0;
219 }
220
221 #define PUTBCD(n,o) bcdclock[o] = (n % 10) | (((n / 10) % 10)<<4)
222
223 static long      
224 rtcwrite(Chan* c, void* buf, long n, vlong off)
225 {
226         int t;
227         char *a, *start;
228         Rtc rtc;
229         ulong secs;
230         uchar bcdclock[Nbcd];
231         char *cp, *ep;
232         ulong offset = off;
233
234         if(offset!=0)
235                 error(Ebadarg);
236
237
238         switch((ulong)c->qid.path){
239         case Qrtc:
240                 /*
241                  *  read the time
242                  */
243                 cp = ep = buf;
244                 ep += n;
245                 while(cp < ep){
246                         if(*cp>='0' && *cp<='9')
247                                 break;
248                         cp++;
249                 }
250                 secs = strtoul(cp, 0, 0);
251         
252                 /*
253                  *  convert to bcd
254                  */
255                 sec2rtc(secs, &rtc);
256                 PUTBCD(rtc.sec, 0);
257                 PUTBCD(rtc.min, 1);
258                 PUTBCD(rtc.hour, 2);
259                 PUTBCD(rtc.mday, 3);
260                 PUTBCD(rtc.mon, 4);
261                 PUTBCD(rtc.year, 5);
262
263                 /*
264                  *  write the clock
265                  */
266                 ilock(&nvrtlock);
267                 outb(Paddr, Seconds);   outb(Pdata, bcdclock[0]);
268                 outb(Paddr, Minutes);   outb(Pdata, bcdclock[1]);
269                 outb(Paddr, Hours);     outb(Pdata, bcdclock[2]);
270                 outb(Paddr, Mday);      outb(Pdata, bcdclock[3]);
271                 outb(Paddr, Month);     outb(Pdata, bcdclock[4]);
272                 outb(Paddr, Year);      outb(Pdata, bcdclock[5]);
273                 iunlock(&nvrtlock);
274                 return n;
275         case Qnvram:
276                 if(n == 0)
277                         return 0;
278                 if(n > Nvsize)
279                         n = Nvsize;
280         
281                 start = a = smalloc(n);
282                 if(waserror()){
283                         free(start);
284                         nexterror();
285                 }
286                 memmove(a, buf, n);
287                 poperror();
288
289                 ilock(&nvrtlock);
290                 for(t = offset; t < offset + n; t++){
291                         if(t >= Nvsize)
292                                 break;
293                         outb(Paddr, Nvoff+t);
294                         outb(Pdata, *a++);
295                 }
296                 iunlock(&nvrtlock);
297
298                 free(start);
299                 return t - offset;
300         }
301         error(Ebadarg);
302         return 0;
303 }
304
305 Dev rtcdevtab = {
306         'r',
307         "rtc",
308
309         devreset,
310         rtcinit,
311         devshutdown,
312         rtcattach,
313         rtcwalk,
314         rtcstat,
315         rtcopen,
316         devcreate,
317         rtcclose,
318         rtcread,
319         devbread,
320         rtcwrite,
321         devbwrite,
322         devremove,
323         devwstat,
324 };
325
326 #define SEC2MIN 60L
327 #define SEC2HOUR (60L*SEC2MIN)
328 #define SEC2DAY (24L*SEC2HOUR)
329
330 /*
331  *  days per month plus days/year
332  */
333 static  int     dmsize[] =
334 {
335         365, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
336 };
337 static  int     ldmsize[] =
338 {
339         366, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
340 };
341
342 /*
343  *  return the days/month for the given year
344  */
345 static int*
346 yrsize(int y)
347 {
348         if((y%4) == 0 && ((y%100) != 0 || (y%400) == 0))
349                 return ldmsize;
350         else
351                 return dmsize;
352 }
353
354 /*
355  *  compute seconds since Jan 1 1970
356  */
357 static ulong
358 rtc2sec(Rtc *rtc)
359 {
360         ulong secs;
361         int i;
362         int *d2m;
363
364         secs = 0;
365
366         /*
367          *  seconds per year
368          */
369         for(i = 1970; i < rtc->year; i++){
370                 d2m = yrsize(i);
371                 secs += d2m[0] * SEC2DAY;
372         }
373
374         /*
375          *  seconds per month
376          */
377         d2m = yrsize(rtc->year);
378         for(i = 1; i < rtc->mon; i++)
379                 secs += d2m[i] * SEC2DAY;
380
381         secs += (rtc->mday-1) * SEC2DAY;
382         secs += rtc->hour * SEC2HOUR;
383         secs += rtc->min * SEC2MIN;
384         secs += rtc->sec;
385
386         return secs;
387 }
388
389 /*
390  *  compute rtc from seconds since Jan 1 1970
391  */
392 static void
393 sec2rtc(ulong secs, Rtc *rtc)
394 {
395         int d;
396         long hms, day;
397         int *d2m;
398
399         /*
400          * break initial number into days
401          */
402         hms = secs % SEC2DAY;
403         day = secs / SEC2DAY;
404         if(hms < 0) {
405                 hms += SEC2DAY;
406                 day -= 1;
407         }
408
409         /*
410          * generate hours:minutes:seconds
411          */
412         rtc->sec = hms % 60;
413         d = hms / 60;
414         rtc->min = d % 60;
415         d /= 60;
416         rtc->hour = d;
417
418         /*
419          * year number
420          */
421         if(day >= 0)
422                 for(d = 1970; day >= *yrsize(d); d++)
423                         day -= *yrsize(d);
424         else
425                 for (d = 1970; day < 0; d--)
426                         day += *yrsize(d-1);
427         rtc->year = d;
428
429         /*
430          * generate month
431          */
432         d2m = yrsize(rtc->year);
433         for(d = 1; day >= d2m[d]; d++)
434                 day -= d2m[d];
435         rtc->mday = day + 1;
436         rtc->mon = d;
437
438         return;
439 }
440
441 uchar
442 nvramread(int addr)
443 {
444         uchar data;
445
446         ilock(&nvrtlock);
447         outb(Paddr, addr);
448         data = inb(Pdata);
449         iunlock(&nvrtlock);
450
451         return data;
452 }
453
454 void
455 nvramwrite(int addr, uchar data)
456 {
457         ilock(&nvrtlock);
458         outb(Paddr, addr);
459         outb(Pdata, data);
460         iunlock(&nvrtlock);
461 }