xref: /inferno-os/os/js/devrtc.c (revision b43c1ca5eb5fc65b93ae935a568432712797b049)
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 #include	"io.h"
9 
10 /*
11  * Mostek MK48T12-15 Zeropower/Timekeeper
12  * This driver is actually portable.
13  */
14 typedef struct Rtc	Rtc;
15 struct Rtc
16 {
17 	int	sec;
18 	int	min;
19 	int	hour;
20 	int	wday;
21 	int	mday;
22 	int	mon;
23 	int	year;
24 };
25 
26 static uchar	rtcgencksum(void);
27 static void	setrtc(Rtc *rtc);
28 static long	rtctime(void);
29 static int	*yrsize(int yr);
30 static int	*yrsize(int yr);
31 static ulong	rtc2sec(Rtc *rtc);
32 static void	sec2rtc(ulong secs, Rtc *rtc);
33 
34 static struct
35 {
36 	uchar	*cksum;
37 	uchar	*ram;
38 	RTCdev	*rtc;
39 }nvr;
40 
41 enum{
42 	Qdir,
43 	Qrtc,
44 	Qnvram,
45 };
46 
47 QLock	rtclock;		/* mutex on clock operations */
48 
49 static Dirtab rtcdir[]={
50 	".",		{Qdir, 0, QTDIR},	0,	0555,
51 	"rtc",		{Qrtc, 0},	0,		0666,
52 	"nvram",	{Qnvram, 0},	NVWRITE,	0666,
53 };
54 #define	NRTC	(sizeof(rtcdir)/sizeof(rtcdir[0]))
55 
56 static void
57 rtcinit(void)
58 {
59 	KMap *k;
60 
61 	k = kmappa(NVR_CKSUM_PHYS, PTENOCACHE|PTEIO);
62 	nvr.cksum = (uchar*)VA(k);
63 
64 	k = kmappa(NVR_PHYS, PTENOCACHE|PTEIO);
65 	nvr.ram = (uchar*)VA(k);
66 	nvr.rtc = (RTCdev*)(VA(k)+RTCOFF);
67 
68 	rtcgencksum();
69 }
70 
71 static Chan*
72 rtcattach(char *spec)
73 {
74 	return devattach('r',spec);
75 }
76 
77 static Walkqid*
78 rtcwalk(Chan *c, Chan *nc, char **name, int nname)
79 {
80 	return devwalk(c, nc, name, nname, rtcdir, NRTC, devgen);
81 }
82 
83 static int
84 rtcstat(Chan *c, uchar *dp, int n)
85 {
86 	return devstat(c, dp, n, rtcdir, NRTC, devgen);
87 }
88 
89 static Chan*
90 rtcopen(Chan *c, int omode)
91 {
92 	omode = openmode(omode);
93 	switch((ulong)c->qid.path){
94 	case Qrtc:
95 		if(strcmp(up->env->user, eve)!=0 && omode!=OREAD)
96 			error(Eperm);
97 		break;
98 	case Qnvram:
99 		if(strcmp(up->env->user, eve)!=0)
100 			error(Eperm);
101 	}
102 	return devopen(c, omode, rtcdir, NRTC, devgen);
103 }
104 
105 static void
106 rtccreate(Chan *c, char *name, int omode, ulong perm)
107 {
108 	USED(c, name, omode, perm);
109 	error(Eperm);
110 }
111 
112 static void
113 rtcclose(Chan *c)
114 {
115 	USED(c);
116 }
117 
118 static long
119 rtcread(Chan *c, void *buf, long n, vlong offset)
120 {
121 	ulong t, ot;
122 
123 	if(c->qid.type & QTDIR)
124 		return devdirread(c, buf, n, rtcdir, NRTC, devgen);
125 
126 	switch((ulong)c->qid.path){
127 	case Qrtc:
128 		qlock(&rtclock);
129 		t = rtctime();
130 		do{
131 			ot = t;
132 			t = rtctime();	/* make sure there's no skew */
133 		}while(t != ot);
134 		qunlock(&rtclock);
135 		n = readnum(offset, buf, n, t, 12);
136 		return n;
137 	case Qnvram:
138 		if(offset > NVREAD)
139 			return 0;
140 		if(n > NVREAD - offset)
141 			n = NVREAD - offset;
142 		qlock(&rtclock);
143 		memmove(buf, nvr.ram+offset, n);
144 		qunlock(&rtclock);
145 		return n;
146 	}
147 	error(Egreg);
148 	return 0;		/* not reached */
149 }
150 
151 /*
152  * XXX - Tad: fixme to generate the correct checksum
153  */
154 static uchar
155 rtcgencksum(void)
156 {
157 	uchar cksum;
158 	int i;
159 	static uchar p1cksum = 0;
160 	static uchar p1cksumvalid=0;
161 
162 	if(!p1cksumvalid) {
163 		for(i=1; i < 0x1000 ; i++)
164 			p1cksum ^= nvr.cksum[i];
165 		p1cksumvalid = 1;
166 	}
167 
168 	cksum = p1cksum;
169 
170 	for(i=0; i < 0xfdf ; i++) {
171 		cksum ^= nvr.ram[i];
172 	}
173 
174 	return cksum;
175 }
176 
177 static long
178 rtcwrite(Chan *c, void *buf, long n, vlong offset)
179 {
180 	Rtc rtc;
181 	ulong secs;
182 	char *cp, sbuf[32];
183 
184 	switch((ulong)c->qid.path){
185 	case Qrtc:
186 		/*
187 		 *  read the time
188 		 */
189 		if(offset != 0 || n >= sizeof(sbuf)-1)
190 			error(Ebadarg);
191 		memmove(sbuf, buf, n);
192 		sbuf[n] = '\0';
193 		cp = sbuf;
194 		while(*cp){
195 			if(*cp>='0' && *cp<='9')
196 				break;
197 			cp++;
198 		}
199 		secs = strtoul(cp, 0, 0);
200 		/*
201 		 *  convert to bcd
202 		 */
203 		sec2rtc(secs, &rtc);
204 		/*
205 		 * write it
206 		 */
207 		qlock(&rtclock);
208 		setrtc(&rtc);
209 		qunlock(&rtclock);
210 		return n;
211 	case Qnvram:
212 		if(offset > NVWRITE)
213 			return 0;
214 		if(n > NVWRITE - offset)
215 			n = NVWRITE - offset;
216 		qlock(&rtclock);
217 		memmove(nvr.ram+offset, buf, n);
218 		*nvr.cksum = rtcgencksum();
219 		qunlock(&rtclock);
220 		return n;
221 	}
222 	error(Egreg);
223 	return 0;		/* not reached */
224 }
225 
226 #define bcd2dec(bcd)	(((((bcd)>>4) & 0x0F) * 10) + ((bcd) & 0x0F))
227 #define dec2bcd(dec)	((((dec)/10)<<4)|((dec)%10))
228 
229 static void
230 setrtc(Rtc *rtc)
231 {
232 	struct RTCdev *dev;
233 
234 	dev = nvr.rtc;
235 	dev->control |= RTCWRITE;
236 	wbflush();
237 	dev->year = dec2bcd(rtc->year % 100);
238 	dev->mon = dec2bcd(rtc->mon);
239 	dev->mday = dec2bcd(rtc->mday);
240 	dev->hour = dec2bcd(rtc->hour);
241 	dev->min = dec2bcd(rtc->min);
242 	dev->sec = dec2bcd(rtc->sec);
243 	wbflush();
244 	dev->control &= ~RTCWRITE;
245 	wbflush();
246 }
247 
248 static long
249 rtctime(void)
250 {
251 	struct RTCdev *dev;
252 	Rtc rtc;
253 
254 	dev = nvr.rtc;
255 	dev->control |= RTCREAD;
256 	wbflush();
257 	rtc.sec = bcd2dec(dev->sec) & 0x7F;
258 	rtc.min = bcd2dec(dev->min & 0x7F);
259 	rtc.hour = bcd2dec(dev->hour & 0x3F);
260 	rtc.mday = bcd2dec(dev->mday & 0x3F);
261 	rtc.mon = bcd2dec(dev->mon & 0x3F);
262 	rtc.year = bcd2dec(dev->year);
263 	dev->control &= ~RTCREAD;
264 	wbflush();
265 
266 	if (rtc.mon < 1 || rtc.mon > 12)
267 		return 0;
268 	/*
269 	 *  the world starts Jan 1 1970
270 	 */
271 	if(rtc.year < 70)
272 		rtc.year += 2000;
273 	else
274 		rtc.year += 1900;
275 
276 	return rtc2sec(&rtc);
277 }
278 
279 #define SEC2MIN 60L
280 #define SEC2HOUR (60L*SEC2MIN)
281 #define SEC2DAY (24L*SEC2HOUR)
282 
283 /*
284  *  days per month plus days/year
285  */
286 static	int	dmsize[] =
287 {
288 	365, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
289 };
290 static	int	ldmsize[] =
291 {
292 	366, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
293 };
294 
295 /*
296  *  return the days/month for the given year
297  */
298 static int *
299 yrsize(int yr)
300 {
301 	if((yr % 4) == 0)
302 		return ldmsize;
303 	else
304 		return dmsize;
305 }
306 
307 /*
308  *  compute seconds since Jan 1 1970
309  */
310 static ulong
311 rtc2sec(Rtc *rtc)
312 {
313 	ulong secs;
314 	int i;
315 	int *d2m;
316 
317 	secs = 0;
318 
319 	/*
320 	 *  seconds per year
321 	 */
322 	for(i = 1970; i < rtc->year; i++){
323 		d2m = yrsize(i);
324 		secs += d2m[0] * SEC2DAY;
325 	}
326 
327 	/*
328 	 *  seconds per month
329 	 */
330 	d2m = yrsize(rtc->year);
331 	for(i = 1; i < rtc->mon; i++)
332 		secs += d2m[i] * SEC2DAY;
333 
334 	secs += (rtc->mday-1) * SEC2DAY;
335 	secs += rtc->hour * SEC2HOUR;
336 	secs += rtc->min * SEC2MIN;
337 	secs += rtc->sec;
338 
339 	return secs;
340 }
341 
342 /*
343  *  compute rtc from seconds since Jan 1 1970
344  */
345 static void
346 sec2rtc(ulong secs, Rtc *rtc)
347 {
348 	int d;
349 	long hms, day;
350 	int *d2m;
351 
352 	/*
353 	 * break initial number into days
354 	 */
355 	hms = secs % SEC2DAY;
356 	day = secs / SEC2DAY;
357 	if(hms < 0) {
358 		hms += SEC2DAY;
359 		day -= 1;
360 	}
361 
362 	/*
363 	 * generate hours:minutes:seconds
364 	 */
365 	rtc->sec = hms % 60;
366 	d = hms / 60;
367 	rtc->min = d % 60;
368 	d /= 60;
369 	rtc->hour = d;
370 
371 	/*
372 	 * year number
373 	 */
374 	if(day >= 0)
375 		for(d = 1970; day >= *yrsize(d); d++)
376 			day -= *yrsize(d);
377 	else
378 		for (d = 1970; day < 0; d--)
379 			day += *yrsize(d-1);
380 	rtc->year = d;
381 
382 	/*
383 	 * generate month
384 	 */
385 	d2m = yrsize(rtc->year);
386 	for(d = 1; day >= d2m[d]; d++)
387 		day -= d2m[d];
388 	rtc->mday = day + 1;
389 	rtc->mon = d;
390 
391 	return;
392 }
393 
394 Dev rtcdevtab = {
395 	'r',
396 	"rtc",
397 
398 	devreset,
399 	rtcinit,
400 	devshutdown,
401 	rtcattach,
402 	rtcwalk,
403 	rtcstat,
404 	rtcopen,
405 	rtccreate,
406 	rtcclose,
407 	rtcread,
408 	devbread,
409 	rtcwrite,
410 	devbwrite,
411 	devremove,
412 	devwstat,
413 };
414