xref: /inferno-os/os/js/devrtc.c (revision c094a1409b780cc543c077e8469fdb28b4c90afb)
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