]> git.lizzy.rs Git - plan9front.git/blob - sys/src/9/mtx/devrtc.c
mtx: fix pci access routines (see changeset 8012:78891f472fbf)
[plan9front.git] / sys / src / 9 / mtx / devrtc.c
1 /*
2  *      M48T59/559 Timekeeper
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
11 #include        "io.h"
12
13 enum{
14         STB0 = 0x74,
15         STB1 = 0x75,
16         Data = 0x77,
17
18         NVOFF=  0,
19         NVLEN=  0x1ff0,         /* length in bytes of NV RAM */
20
21         /*
22          *  register offsets into time of day clock
23          */
24         NVflags=                0x1ff0,
25         NVwatchdog=     0x1ff7,
26         NVctl=          0x1ff8,
27         NVsec,
28         NVmin,
29         NVhour, 
30         NVday,          /* (1 = Sun) */
31         NVmday,         /* (1-31) */
32         NVmon,          /* (1-12) */
33         NVyear,         /* (0-99) */
34
35         /* NVctl */
36         RTwrite = (1<<7),
37         RTread = (1<<6),
38         RTsign = (1<<5),
39         RTcal = 0x1f,
40
41         /* NVwatchdog */
42         WDsteer = (1<<7),               /* 0 -> intr, 1 -> reset */
43         WDmult = (1<<2),                /* 5 bits of multiplier */
44         WDres0 = (0<<0),                /* 1/16 sec resolution */
45         WDres1 = (1<<0),                /* 1/4 sec resolution */
46         WDres2 = (2<<0),                /* 1 sec resolution */
47         WDres3 = (3<<0),                /* 4 sec resolution */
48
49         Qdir = 0,
50         Qrtc,
51         Qnvram,
52 };
53
54 /*
55  *  broken down time
56  */
57 typedef struct
58 {
59         int     sec;
60         int     min;
61         int     hour;
62         int     mday;
63         int     mon;
64         int     year;
65 } Rtc;
66
67 QLock   rtclock;                /* mutex on nvram operations */
68
69 static Dirtab rtcdir[]={
70         ".",            {Qdir, 0, QTDIR},       0,      DMDIR|0555,
71         "rtc",          {Qrtc, 0},      0,      0644,
72         "nvram",        {Qnvram, 0},    0,      0600,
73 };
74
75 static ulong    rtc2sec(Rtc*);
76 static void     sec2rtc(ulong, Rtc*);
77 static void     setrtc(Rtc*);
78 static void     nvcksum(void);
79 static void     nvput(int, uchar);
80 static uchar    nvget(int);
81
82 static Chan*
83 rtcattach(char *spec)
84 {
85         return devattach('r', spec);
86 }
87
88 static Walkqid*
89 rtcwalk(Chan *c, Chan *nc, char **name, int nname)
90 {
91         return devwalk(c, nc, name, nname, rtcdir, nelem(rtcdir), devgen);
92 }
93
94 static int       
95 rtcstat(Chan *c, uchar *dp, int n)
96 {
97         return devstat(c, dp, n, rtcdir, nelem(rtcdir), devgen);
98 }
99
100 static Chan*
101 rtcopen(Chan *c, int omode)
102 {
103         omode = openmode(omode);
104         switch((ulong)c->qid.path){
105         case Qrtc:
106                 if(strcmp(up->user, eve)!=0 && omode!=OREAD)
107                         error(Eperm);
108                 break;
109         case Qnvram:
110                 if(strcmp(up->user, eve)!=0 || !cpuserver)
111                         error(Eperm);
112         }
113         return devopen(c, omode, rtcdir, nelem(rtcdir), devgen);
114 }
115
116 static void      
117 rtcclose(Chan*)
118 {
119 }
120
121 static long      
122 rtcread(Chan *c, void *buf, long n, vlong off)
123 {
124         char *p;
125         ulong t;
126         int i;
127         ulong offset = off;
128
129         if(c->qid.type & QTDIR)
130                 return devdirread(c, buf, n, rtcdir, nelem(rtcdir), devgen);
131
132         switch((ulong)c->qid.path){
133         case Qrtc:
134                 qlock(&rtclock);
135                 t = rtctime();
136                 qunlock(&rtclock);
137                 n = readnum(offset, buf, n, t, 12);
138                 return n;
139         case Qnvram:
140                 offset += NVOFF;
141                 if(offset > NVLEN)
142                         return 0;
143                 if(n > NVLEN - offset)
144                         n = NVLEN - offset;
145                 p = buf;
146                 qlock(&rtclock);
147                 for(i = 0; i < n; i++)
148                         p[i] = nvget(i+offset);
149                 qunlock(&rtclock);
150                 return n;
151         }
152         error(Egreg);
153         return -1;              /* never reached */
154 }
155
156 static long      
157 rtcwrite(Chan *c, void *buf, long n, vlong off)
158 {
159         Rtc rtc;
160         ulong secs;
161         char *cp, *ep;
162         int i;
163         ulong offset = off;
164
165         switch((ulong)c->qid.path){
166         case Qrtc:
167                 if(offset!=0)
168                         error(Ebadarg);
169                 /*
170                  *  read the time
171                  */
172                 cp = ep = buf;
173                 ep += n;
174                 while(cp < ep){
175                         if(*cp>='0' && *cp<='9')
176                                 break;
177                         cp++;
178                 }
179                 secs = strtoul(cp, 0, 0);
180                 /*
181                  *  convert to bcd
182                  */
183                 sec2rtc(secs, &rtc);
184                 /*
185                  * write it
186                  */
187                 qlock(&rtclock);
188                 setrtc(&rtc);
189                 qunlock(&rtclock);
190                 return n;
191         case Qnvram:
192                 offset += NVOFF;
193                 if(offset > NVLEN)
194                         return 0;
195                 if(n > NVLEN - offset)
196                         n = NVLEN - offset;
197                 qlock(&rtclock);
198                 for(i = 0; i < n; i++)
199                         nvput(i+offset, ((uchar*)buf)[i]);
200                 nvcksum();
201                 qunlock(&rtclock);
202                 return n;
203         }
204         error(Egreg);
205         return -1;              /* never reached */
206 }
207
208 long
209 rtcbwrite(Chan *c, Block *bp, ulong offset)
210 {
211         return devbwrite(c, bp, offset);
212 }
213
214 Dev rtcdevtab = {
215         'r',
216         "rtc",
217
218         devreset,
219         devinit,
220         devshutdown,
221         rtcattach,
222         rtcwalk,
223         rtcstat,
224         rtcopen,
225         devcreate,
226         rtcclose,
227         rtcread,
228         devbread,
229         rtcwrite,
230         devbwrite,
231         devremove,
232         devwstat,
233 };
234
235 static void
236 nvput(int offset, uchar val)
237 {
238         outb(STB0, offset);
239         outb(STB1, offset>>8);
240         outb(Data, val);
241 }
242
243 static uchar
244 nvget(int offset)
245 {
246         outb(STB0, offset);
247         outb(STB1, offset>>8);
248         return inb(Data);
249 }
250
251 static void
252 nvcksum(void)
253 {
254 }
255
256 void
257 watchreset(void)
258 {
259         splhi();
260         nvput(NVwatchdog, WDsteer|(1*WDmult)|WDres0);
261         for(;;);
262 }
263
264 static int
265 getbcd(int bcd)
266 {
267         return (bcd&0x0f) + 10 * (bcd>>4);
268 }
269
270 static int
271 putbcd(int val)
272 {
273         return (val % 10) | (((val/10) % 10) << 4);
274 }
275
276 long     
277 rtctime(void)
278 {
279         int ctl;
280         Rtc rtc;
281
282         /*
283          *  convert from BCD
284          */
285         ctl = nvget(NVctl);
286         ctl &= RTsign|RTcal;
287         nvput(NVctl, ctl|RTread);
288
289         rtc.sec = getbcd(nvget(NVsec) & 0x7f);
290         rtc.min = getbcd(nvget(NVmin));
291         rtc.hour = getbcd(nvget(NVhour));
292         rtc.mday = getbcd(nvget(NVmday));
293         rtc.mon = getbcd(nvget(NVmon));
294         rtc.year = getbcd(nvget(NVyear));
295         if(rtc.year < 70)
296                 rtc.year += 2000;
297         else
298                 rtc.year += 1900;
299
300         nvput(NVctl, ctl);
301
302         return rtc2sec(&rtc);
303 }
304
305 static void
306 setrtc(Rtc *rtc)
307 {
308         int ctl;
309
310         ctl = nvget(NVctl);
311         ctl &= RTsign|RTcal;
312         nvput(NVctl, ctl|RTwrite);
313
314         nvput(NVsec, putbcd(rtc->sec));
315         nvput(NVmin, putbcd(rtc->min));
316         nvput(NVhour, putbcd(rtc->hour));
317         nvput(NVmday, putbcd(rtc->mday));
318         nvput(NVmon, putbcd(rtc->mon));
319         nvput(NVyear, putbcd(rtc->year % 100));
320
321         nvput(NVctl, ctl);
322 }
323
324 #define SEC2MIN 60L
325 #define SEC2HOUR (60L*SEC2MIN)
326 #define SEC2DAY (24L*SEC2HOUR)
327
328 /*
329  *  days per month plus days/year
330  */
331 static  int     dmsize[] =
332 {
333         365, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
334 };
335 static  int     ldmsize[] =
336 {
337         366, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
338 };
339
340 /*
341  *  return the days/month for the given year
342  */
343 static int *
344 yrsize(int y)
345 {
346
347         if((y%4) == 0 && ((y%100) != 0 || (y%400) == 0))
348                 return ldmsize;
349         else
350                 return dmsize;
351 }
352
353 /*
354  *  compute seconds since Jan 1 1970
355  */
356 static ulong
357 rtc2sec(Rtc *rtc)
358 {
359         ulong secs;
360         int i;
361         int *d2m;
362
363         secs = 0;
364
365         /*
366          *  seconds per year
367          */
368         for(i = 1970; i < rtc->year; i++){
369                 d2m = yrsize(i);
370                 secs += d2m[0] * SEC2DAY;
371         }
372
373         /*
374          *  seconds per month
375          */
376         d2m = yrsize(rtc->year);
377         for(i = 1; i < rtc->mon; i++)
378                 secs += d2m[i] * SEC2DAY;
379
380         secs += (rtc->mday-1) * SEC2DAY;
381         secs += rtc->hour * SEC2HOUR;
382         secs += rtc->min * SEC2MIN;
383         secs += rtc->sec;
384
385         return secs;
386 }
387
388 /*
389  *  compute rtc from seconds since Jan 1 1970
390  */
391 static void
392 sec2rtc(ulong secs, Rtc *rtc)
393 {
394         int d;
395         long hms, day;
396         int *d2m;
397
398         /*
399          * break initial number into days
400          */
401         hms = secs % SEC2DAY;
402         day = secs / SEC2DAY;
403         if(hms < 0) {
404                 hms += SEC2DAY;
405                 day -= 1;
406         }
407
408         /*
409          * generate hours:minutes:seconds
410          */
411         rtc->sec = hms % 60;
412         d = hms / 60;
413         rtc->min = d % 60;
414         d /= 60;
415         rtc->hour = d;
416
417         /*
418          * year number
419          */
420         if(day >= 0)
421                 for(d = 1970; day >= *yrsize(d); d++)
422                         day -= *yrsize(d);
423         else
424                 for (d = 1970; day < 0; d--)
425                         day += *yrsize(d-1);
426         rtc->year = d;
427
428         /*
429          * generate month
430          */
431         d2m = yrsize(rtc->year);
432         for(d = 1; day >= d2m[d]; d++)
433                 day -= d2m[d];
434         rtc->mday = day + 1;
435         rtc->mon = d;
436
437         return;
438 }