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