FreeBSD/Linux Kernel Cross Reference
sys/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>='' && *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 }
Cache object: 76872331e1db13f294d0f36423f5a7ab
|