xref: /plan9/sys/src/9/pc/devrtc.c (revision 219b2ee8daee37f4aad58d63f21287faa8e4ffdc)
13e12c5d1SDavid du Colombier #include	"u.h"
23e12c5d1SDavid du Colombier #include	"../port/lib.h"
33e12c5d1SDavid du Colombier #include	"mem.h"
43e12c5d1SDavid du Colombier #include	"dat.h"
53e12c5d1SDavid du Colombier #include	"fns.h"
63e12c5d1SDavid du Colombier #include	"../port/error.h"
73e12c5d1SDavid du Colombier #include	"devtab.h"
83e12c5d1SDavid du Colombier 
93e12c5d1SDavid du Colombier /*
103e12c5d1SDavid du Colombier  *  real time clock and non-volatile ram
113e12c5d1SDavid du Colombier  */
123e12c5d1SDavid du Colombier 
133e12c5d1SDavid du Colombier enum {
143e12c5d1SDavid du Colombier 	Paddr=		0x70,	/* address port */
153e12c5d1SDavid du Colombier 	Pdata=		0x71,	/* data port */
163e12c5d1SDavid du Colombier 
173e12c5d1SDavid du Colombier 	Seconds=	0x00,
183e12c5d1SDavid du Colombier 	Minutes=	0x02,
193e12c5d1SDavid du Colombier 	Hours=		0x04,
203e12c5d1SDavid du Colombier 	Mday=		0x07,
213e12c5d1SDavid du Colombier 	Month=		0x08,
223e12c5d1SDavid du Colombier 	Year=		0x09,
233e12c5d1SDavid du Colombier 	Status=		0x0A,
243e12c5d1SDavid du Colombier 
25*219b2ee8SDavid du Colombier 	Nvoff=		128,	/* where usable nvram lives */
26*219b2ee8SDavid du Colombier 	Nvsize=		256,
27*219b2ee8SDavid du Colombier 
283e12c5d1SDavid du Colombier 	Nbcd=		6,
293e12c5d1SDavid du Colombier };
303e12c5d1SDavid du Colombier 
313e12c5d1SDavid du Colombier typedef struct Rtc	Rtc;
323e12c5d1SDavid du Colombier struct Rtc
333e12c5d1SDavid du Colombier {
343e12c5d1SDavid du Colombier 	int	sec;
353e12c5d1SDavid du Colombier 	int	min;
363e12c5d1SDavid du Colombier 	int	hour;
373e12c5d1SDavid du Colombier 	int	mday;
383e12c5d1SDavid du Colombier 	int	mon;
393e12c5d1SDavid du Colombier 	int	year;
403e12c5d1SDavid du Colombier };
413e12c5d1SDavid du Colombier 
423e12c5d1SDavid du Colombier QLock rtclock;	/* mutex on clock operations */
433e12c5d1SDavid du Colombier 
443e12c5d1SDavid du Colombier enum{
453e12c5d1SDavid du Colombier 	Qrtc = 1,
463e12c5d1SDavid du Colombier 	Qnvram,
473e12c5d1SDavid du Colombier };
483e12c5d1SDavid du Colombier 
49*219b2ee8SDavid du Colombier #define	NRTC	2
503e12c5d1SDavid du Colombier Dirtab rtcdir[]={
51*219b2ee8SDavid du Colombier 	"nvram",	{Qnvram, 0},	Nvsize,	0664,
52*219b2ee8SDavid du Colombier 	"rtc",		{Qrtc, 0},	0,	0664,
533e12c5d1SDavid du Colombier };
543e12c5d1SDavid du Colombier 
553e12c5d1SDavid du Colombier ulong rtc2sec(Rtc*);
563e12c5d1SDavid du Colombier void sec2rtc(ulong, Rtc*);
573e12c5d1SDavid du Colombier int *yrsize(int);
583e12c5d1SDavid du Colombier 
593e12c5d1SDavid du Colombier void
603e12c5d1SDavid du Colombier rtcreset(void)
613e12c5d1SDavid du Colombier {
623e12c5d1SDavid du Colombier }
633e12c5d1SDavid du Colombier 
643e12c5d1SDavid du Colombier void
653e12c5d1SDavid du Colombier rtcinit(void)
663e12c5d1SDavid du Colombier {
673e12c5d1SDavid du Colombier }
683e12c5d1SDavid du Colombier 
693e12c5d1SDavid du Colombier Chan*
703e12c5d1SDavid du Colombier rtcattach(char *spec)
713e12c5d1SDavid du Colombier {
723e12c5d1SDavid du Colombier 	return devattach('r', spec);
733e12c5d1SDavid du Colombier }
743e12c5d1SDavid du Colombier 
753e12c5d1SDavid du Colombier Chan*
763e12c5d1SDavid du Colombier rtcclone(Chan *c, Chan *nc)
773e12c5d1SDavid du Colombier {
783e12c5d1SDavid du Colombier 	return devclone(c, nc);
793e12c5d1SDavid du Colombier }
803e12c5d1SDavid du Colombier 
813e12c5d1SDavid du Colombier int
823e12c5d1SDavid du Colombier rtcwalk(Chan *c, char *name)
833e12c5d1SDavid du Colombier {
843e12c5d1SDavid du Colombier 	return devwalk(c, name, rtcdir, NRTC, devgen);
853e12c5d1SDavid du Colombier }
863e12c5d1SDavid du Colombier 
873e12c5d1SDavid du Colombier void
883e12c5d1SDavid du Colombier rtcstat(Chan *c, char *dp)
893e12c5d1SDavid du Colombier {
903e12c5d1SDavid du Colombier 	devstat(c, dp, rtcdir, NRTC, devgen);
913e12c5d1SDavid du Colombier }
923e12c5d1SDavid du Colombier 
933e12c5d1SDavid du Colombier Chan*
943e12c5d1SDavid du Colombier rtcopen(Chan *c, int omode)
953e12c5d1SDavid du Colombier {
963e12c5d1SDavid du Colombier 	omode = openmode(omode);
973e12c5d1SDavid du Colombier 	switch(c->qid.path){
983e12c5d1SDavid du Colombier 	case Qrtc:
993e12c5d1SDavid du Colombier 		if(strcmp(u->p->user, eve)!=0 && omode!=OREAD)
1003e12c5d1SDavid du Colombier 			error(Eperm);
1013e12c5d1SDavid du Colombier 		break;
1023e12c5d1SDavid du Colombier 	case Qnvram:
103*219b2ee8SDavid du Colombier 		if(strcmp(u->p->user, eve)!=0)
1043e12c5d1SDavid du Colombier 			error(Eperm);
1053e12c5d1SDavid du Colombier 	}
1063e12c5d1SDavid du Colombier 	return devopen(c, omode, rtcdir, NRTC, devgen);
1073e12c5d1SDavid du Colombier }
1083e12c5d1SDavid du Colombier 
1093e12c5d1SDavid du Colombier void
1103e12c5d1SDavid du Colombier rtccreate(Chan *c, char *name, int omode, ulong perm)
1113e12c5d1SDavid du Colombier {
1123e12c5d1SDavid du Colombier 	USED(c, name, omode, perm);
1133e12c5d1SDavid du Colombier 	error(Eperm);
1143e12c5d1SDavid du Colombier }
1153e12c5d1SDavid du Colombier 
1163e12c5d1SDavid du Colombier void
1173e12c5d1SDavid du Colombier rtcclose(Chan *c)
1183e12c5d1SDavid du Colombier {
1193e12c5d1SDavid du Colombier 	USED(c);
1203e12c5d1SDavid du Colombier }
1213e12c5d1SDavid du Colombier 
1223e12c5d1SDavid du Colombier #define GETBCD(o) ((bcdclock[o]&0xf) + 10*(bcdclock[o]>>4))
1233e12c5d1SDavid du Colombier 
1243e12c5d1SDavid du Colombier long
1253e12c5d1SDavid du Colombier rtctime(void)
1263e12c5d1SDavid du Colombier {
1273e12c5d1SDavid du Colombier 	uchar bcdclock[Nbcd];
1283e12c5d1SDavid du Colombier 	Rtc rtc;
129bd389b36SDavid du Colombier 	int i;
1303e12c5d1SDavid du Colombier 
131bd389b36SDavid du Colombier 	for(i = 0; i < 10000; i++){
1323e12c5d1SDavid du Colombier 		outb(Paddr, Status);
133bd389b36SDavid du Colombier 		if((inb(Pdata) & 1) == 0)
134bd389b36SDavid du Colombier 			break;
135bd389b36SDavid du Colombier 	}
1363e12c5d1SDavid du Colombier 	outb(Paddr, Seconds);	bcdclock[0] = inb(Pdata);
1373e12c5d1SDavid du Colombier 	outb(Paddr, Minutes);	bcdclock[1] = inb(Pdata);
1383e12c5d1SDavid du Colombier 	outb(Paddr, Hours);	bcdclock[2] = inb(Pdata);
1393e12c5d1SDavid du Colombier 	outb(Paddr, Mday);	bcdclock[3] = inb(Pdata);
1403e12c5d1SDavid du Colombier 	outb(Paddr, Month);	bcdclock[4] = inb(Pdata);
1413e12c5d1SDavid du Colombier 	outb(Paddr, Year);	bcdclock[5] = inb(Pdata);
1423e12c5d1SDavid du Colombier 
1433e12c5d1SDavid du Colombier 	/*
1443e12c5d1SDavid du Colombier 	 *  convert from BCD
1453e12c5d1SDavid du Colombier 	 */
1463e12c5d1SDavid du Colombier 	rtc.sec = GETBCD(0);
1473e12c5d1SDavid du Colombier 	rtc.min = GETBCD(1);
1483e12c5d1SDavid du Colombier 	rtc.hour = GETBCD(2);
1493e12c5d1SDavid du Colombier 	rtc.mday = GETBCD(3);
1503e12c5d1SDavid du Colombier 	rtc.mon = GETBCD(4);
1513e12c5d1SDavid du Colombier 	rtc.year = GETBCD(5);
1523e12c5d1SDavid du Colombier 
1533e12c5d1SDavid du Colombier 	/*
1543e12c5d1SDavid du Colombier 	 *  the world starts jan 1 1970
1553e12c5d1SDavid du Colombier 	 */
1563e12c5d1SDavid du Colombier 	if(rtc.year < 70)
1573e12c5d1SDavid du Colombier 		rtc.year += 2000;
1583e12c5d1SDavid du Colombier 	else
1593e12c5d1SDavid du Colombier 		rtc.year += 1900;
1603e12c5d1SDavid du Colombier 	return rtc2sec(&rtc);
1613e12c5d1SDavid du Colombier }
1623e12c5d1SDavid du Colombier 
1633e12c5d1SDavid du Colombier long
1643e12c5d1SDavid du Colombier rtcread(Chan *c, void *buf, long n, ulong offset)
1653e12c5d1SDavid du Colombier {
1663e12c5d1SDavid du Colombier 	ulong t, ot;
167*219b2ee8SDavid du Colombier 	char *a;
1683e12c5d1SDavid du Colombier 
1693e12c5d1SDavid du Colombier 	if(c->qid.path & CHDIR)
1703e12c5d1SDavid du Colombier 		return devdirread(c, buf, n, rtcdir, NRTC, devgen);
1713e12c5d1SDavid du Colombier 
172*219b2ee8SDavid du Colombier 	switch(c->qid.path){
173*219b2ee8SDavid du Colombier 	case Qrtc:
1743e12c5d1SDavid du Colombier 		qlock(&rtclock);
1753e12c5d1SDavid du Colombier 		t = rtctime();
1763e12c5d1SDavid du Colombier 		do{
1773e12c5d1SDavid du Colombier 			ot = t;
1783e12c5d1SDavid du Colombier 			t = rtctime();	/* make sure there's no skew */
1793e12c5d1SDavid du Colombier 		}while(t != ot);
1803e12c5d1SDavid du Colombier 		qunlock(&rtclock);
1813e12c5d1SDavid du Colombier 		n = readnum(offset, buf, n, t, 12);
1823e12c5d1SDavid du Colombier 		return n;
183*219b2ee8SDavid du Colombier 	case Qnvram:
184*219b2ee8SDavid du Colombier 		a = buf;
185*219b2ee8SDavid du Colombier 		if(waserror()){
186*219b2ee8SDavid du Colombier 			qunlock(&rtclock);
187*219b2ee8SDavid du Colombier 			nexterror();
188*219b2ee8SDavid du Colombier 		}
189*219b2ee8SDavid du Colombier 		qlock(&rtclock);
190*219b2ee8SDavid du Colombier 		for(t = offset; t < offset + n; t++){
191*219b2ee8SDavid du Colombier 			if(t >= Nvsize)
192*219b2ee8SDavid du Colombier 				break;
193*219b2ee8SDavid du Colombier 			outb(Paddr, Nvoff+t);
194*219b2ee8SDavid du Colombier 			delay(1);
195*219b2ee8SDavid du Colombier 			*a++ = inb(Pdata);
196*219b2ee8SDavid du Colombier 		}
197*219b2ee8SDavid du Colombier 		qunlock(&rtclock);
198*219b2ee8SDavid du Colombier 		poperror();
199*219b2ee8SDavid du Colombier 		return t - offset;
200*219b2ee8SDavid du Colombier 	}
201*219b2ee8SDavid du Colombier 	error(Ebadarg);
202*219b2ee8SDavid du Colombier 	return 0;
2033e12c5d1SDavid du Colombier }
2043e12c5d1SDavid du Colombier 
2053e12c5d1SDavid du Colombier #define PUTBCD(n,o) bcdclock[o] = (n % 10) | (((n / 10) % 10)<<4)
2063e12c5d1SDavid du Colombier 
2073e12c5d1SDavid du Colombier long
2083e12c5d1SDavid du Colombier rtcwrite(Chan *c, void *buf, long n, ulong offset)
2093e12c5d1SDavid du Colombier {
210*219b2ee8SDavid du Colombier 	int t;
211*219b2ee8SDavid du Colombier 	char *a;
2123e12c5d1SDavid du Colombier 	Rtc rtc;
2133e12c5d1SDavid du Colombier 	ulong secs;
2143e12c5d1SDavid du Colombier 	uchar bcdclock[Nbcd];
2153e12c5d1SDavid du Colombier 	char *cp, *ep;
2163e12c5d1SDavid du Colombier 
2173e12c5d1SDavid du Colombier 	USED(c);
2183e12c5d1SDavid du Colombier 	if(offset!=0)
2193e12c5d1SDavid du Colombier 		error(Ebadarg);
2203e12c5d1SDavid du Colombier 
221*219b2ee8SDavid du Colombier 
222*219b2ee8SDavid du Colombier 	switch(c->qid.path){
223*219b2ee8SDavid du Colombier 	case Qrtc:
2243e12c5d1SDavid du Colombier 		/*
2253e12c5d1SDavid du Colombier 		 *  read the time
2263e12c5d1SDavid du Colombier 		 */
2273e12c5d1SDavid du Colombier 		cp = ep = buf;
2283e12c5d1SDavid du Colombier 		ep += n;
2293e12c5d1SDavid du Colombier 		while(cp < ep){
2303e12c5d1SDavid du Colombier 			if(*cp>='0' && *cp<='9')
2313e12c5d1SDavid du Colombier 				break;
2323e12c5d1SDavid du Colombier 			cp++;
2333e12c5d1SDavid du Colombier 		}
2343e12c5d1SDavid du Colombier 		secs = strtoul(cp, 0, 0);
2353e12c5d1SDavid du Colombier 
2363e12c5d1SDavid du Colombier 		/*
2373e12c5d1SDavid du Colombier 		 *  convert to bcd
2383e12c5d1SDavid du Colombier 		 */
2393e12c5d1SDavid du Colombier 		sec2rtc(secs, &rtc);
2403e12c5d1SDavid du Colombier 		PUTBCD(rtc.sec, 0);
2413e12c5d1SDavid du Colombier 		PUTBCD(rtc.min, 1);
2423e12c5d1SDavid du Colombier 		PUTBCD(rtc.hour, 2);
2433e12c5d1SDavid du Colombier 		PUTBCD(rtc.mday, 3);
2443e12c5d1SDavid du Colombier 		PUTBCD(rtc.mon, 4);
2453e12c5d1SDavid du Colombier 		PUTBCD(rtc.year, 5);
2463e12c5d1SDavid du Colombier 
2473e12c5d1SDavid du Colombier 		/*
2483e12c5d1SDavid du Colombier 		 *  write the clock
2493e12c5d1SDavid du Colombier 		 */
2503e12c5d1SDavid du Colombier 		qlock(&rtclock);
2513e12c5d1SDavid du Colombier 		outb(Paddr, Seconds);	outb(Pdata, bcdclock[0]);
2523e12c5d1SDavid du Colombier 		outb(Paddr, Minutes);	outb(Pdata, bcdclock[1]);
2533e12c5d1SDavid du Colombier 		outb(Paddr, Hours);	outb(Pdata, bcdclock[2]);
2543e12c5d1SDavid du Colombier 		outb(Paddr, Mday);	outb(Pdata, bcdclock[3]);
2553e12c5d1SDavid du Colombier 		outb(Paddr, Month);	outb(Pdata, bcdclock[4]);
2563e12c5d1SDavid du Colombier 		outb(Paddr, Year);	outb(Pdata, bcdclock[5]);
2573e12c5d1SDavid du Colombier 		qunlock(&rtclock);
2583e12c5d1SDavid du Colombier 		return n;
259*219b2ee8SDavid du Colombier 	case Qnvram:
260*219b2ee8SDavid du Colombier 		a = buf;
261*219b2ee8SDavid du Colombier 		if(waserror()){
262*219b2ee8SDavid du Colombier 			qunlock(&rtclock);
263*219b2ee8SDavid du Colombier 			nexterror();
264*219b2ee8SDavid du Colombier 		}
265*219b2ee8SDavid du Colombier 		qlock(&rtclock);
266*219b2ee8SDavid du Colombier 		for(t = offset; t < offset + n; t++){
267*219b2ee8SDavid du Colombier 			if(t >= Nvsize)
268*219b2ee8SDavid du Colombier 				break;
269*219b2ee8SDavid du Colombier 			outb(Paddr, Nvoff+t);
270*219b2ee8SDavid du Colombier 			outb(Pdata, *a++);
271*219b2ee8SDavid du Colombier 		}
272*219b2ee8SDavid du Colombier 		qunlock(&rtclock);
273*219b2ee8SDavid du Colombier 		poperror();
274*219b2ee8SDavid du Colombier 		return t - offset;
275*219b2ee8SDavid du Colombier 	}
276*219b2ee8SDavid du Colombier 	error(Ebadarg);
277*219b2ee8SDavid du Colombier 	return 0;
2783e12c5d1SDavid du Colombier }
2793e12c5d1SDavid du Colombier 
2803e12c5d1SDavid du Colombier void
2813e12c5d1SDavid du Colombier rtcremove(Chan *c)
2823e12c5d1SDavid du Colombier {
2833e12c5d1SDavid du Colombier 	USED(c);
2843e12c5d1SDavid du Colombier 	error(Eperm);
2853e12c5d1SDavid du Colombier }
2863e12c5d1SDavid du Colombier 
2873e12c5d1SDavid du Colombier void
2883e12c5d1SDavid du Colombier rtcwstat(Chan *c, char *dp)
2893e12c5d1SDavid du Colombier {
2903e12c5d1SDavid du Colombier 	USED(c, dp);
2913e12c5d1SDavid du Colombier 	error(Eperm);
2923e12c5d1SDavid du Colombier }
2933e12c5d1SDavid du Colombier 
2943e12c5d1SDavid du Colombier #define SEC2MIN 60L
2953e12c5d1SDavid du Colombier #define SEC2HOUR (60L*SEC2MIN)
2963e12c5d1SDavid du Colombier #define SEC2DAY (24L*SEC2HOUR)
2973e12c5d1SDavid du Colombier 
2983e12c5d1SDavid du Colombier /*
2993e12c5d1SDavid du Colombier  *  days per month plus days/year
3003e12c5d1SDavid du Colombier  */
3013e12c5d1SDavid du Colombier static	int	dmsize[] =
3023e12c5d1SDavid du Colombier {
3033e12c5d1SDavid du Colombier 	365, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
3043e12c5d1SDavid du Colombier };
3053e12c5d1SDavid du Colombier static	int	ldmsize[] =
3063e12c5d1SDavid du Colombier {
3073e12c5d1SDavid du Colombier 	366, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
3083e12c5d1SDavid du Colombier };
3093e12c5d1SDavid du Colombier 
3103e12c5d1SDavid du Colombier /*
3113e12c5d1SDavid du Colombier  *  return the days/month for the given year
3123e12c5d1SDavid du Colombier  */
3133e12c5d1SDavid du Colombier int *
3143e12c5d1SDavid du Colombier yrsize(int yr)
3153e12c5d1SDavid du Colombier {
3163e12c5d1SDavid du Colombier 	if((yr % 4) == 0)
3173e12c5d1SDavid du Colombier 		return ldmsize;
3183e12c5d1SDavid du Colombier 	else
3193e12c5d1SDavid du Colombier 		return dmsize;
3203e12c5d1SDavid du Colombier }
3213e12c5d1SDavid du Colombier 
3223e12c5d1SDavid du Colombier /*
3233e12c5d1SDavid du Colombier  *  compute seconds since Jan 1 1970
3243e12c5d1SDavid du Colombier  */
3253e12c5d1SDavid du Colombier ulong
3263e12c5d1SDavid du Colombier rtc2sec(Rtc *rtc)
3273e12c5d1SDavid du Colombier {
3283e12c5d1SDavid du Colombier 	ulong secs;
3293e12c5d1SDavid du Colombier 	int i;
3303e12c5d1SDavid du Colombier 	int *d2m;
3313e12c5d1SDavid du Colombier 
3323e12c5d1SDavid du Colombier 	secs = 0;
3333e12c5d1SDavid du Colombier 
3343e12c5d1SDavid du Colombier 	/*
3353e12c5d1SDavid du Colombier 	 *  seconds per year
3363e12c5d1SDavid du Colombier 	 */
3373e12c5d1SDavid du Colombier 	for(i = 1970; i < rtc->year; i++){
3383e12c5d1SDavid du Colombier 		d2m = yrsize(i);
3393e12c5d1SDavid du Colombier 		secs += d2m[0] * SEC2DAY;
3403e12c5d1SDavid du Colombier 	}
3413e12c5d1SDavid du Colombier 
3423e12c5d1SDavid du Colombier 	/*
3433e12c5d1SDavid du Colombier 	 *  seconds per month
3443e12c5d1SDavid du Colombier 	 */
3453e12c5d1SDavid du Colombier 	d2m = yrsize(rtc->year);
3463e12c5d1SDavid du Colombier 	for(i = 1; i < rtc->mon; i++)
3473e12c5d1SDavid du Colombier 		secs += d2m[i] * SEC2DAY;
3483e12c5d1SDavid du Colombier 
3493e12c5d1SDavid du Colombier 	secs += (rtc->mday-1) * SEC2DAY;
3503e12c5d1SDavid du Colombier 	secs += rtc->hour * SEC2HOUR;
3513e12c5d1SDavid du Colombier 	secs += rtc->min * SEC2MIN;
3523e12c5d1SDavid du Colombier 	secs += rtc->sec;
3533e12c5d1SDavid du Colombier 
3543e12c5d1SDavid du Colombier 	return secs;
3553e12c5d1SDavid du Colombier }
3563e12c5d1SDavid du Colombier 
3573e12c5d1SDavid du Colombier /*
3583e12c5d1SDavid du Colombier  *  compute rtc from seconds since Jan 1 1970
3593e12c5d1SDavid du Colombier  */
3603e12c5d1SDavid du Colombier void
3613e12c5d1SDavid du Colombier sec2rtc(ulong secs, Rtc *rtc)
3623e12c5d1SDavid du Colombier {
3633e12c5d1SDavid du Colombier 	int d;
3643e12c5d1SDavid du Colombier 	long hms, day;
3653e12c5d1SDavid du Colombier 	int *d2m;
3663e12c5d1SDavid du Colombier 
3673e12c5d1SDavid du Colombier 	/*
3683e12c5d1SDavid du Colombier 	 * break initial number into days
3693e12c5d1SDavid du Colombier 	 */
3703e12c5d1SDavid du Colombier 	hms = secs % SEC2DAY;
3713e12c5d1SDavid du Colombier 	day = secs / SEC2DAY;
3723e12c5d1SDavid du Colombier 	if(hms < 0) {
3733e12c5d1SDavid du Colombier 		hms += SEC2DAY;
3743e12c5d1SDavid du Colombier 		day -= 1;
3753e12c5d1SDavid du Colombier 	}
3763e12c5d1SDavid du Colombier 
3773e12c5d1SDavid du Colombier 	/*
3783e12c5d1SDavid du Colombier 	 * generate hours:minutes:seconds
3793e12c5d1SDavid du Colombier 	 */
3803e12c5d1SDavid du Colombier 	rtc->sec = hms % 60;
3813e12c5d1SDavid du Colombier 	d = hms / 60;
3823e12c5d1SDavid du Colombier 	rtc->min = d % 60;
3833e12c5d1SDavid du Colombier 	d /= 60;
3843e12c5d1SDavid du Colombier 	rtc->hour = d;
3853e12c5d1SDavid du Colombier 
3863e12c5d1SDavid du Colombier 	/*
3873e12c5d1SDavid du Colombier 	 * year number
3883e12c5d1SDavid du Colombier 	 */
3893e12c5d1SDavid du Colombier 	if(day >= 0)
3903e12c5d1SDavid du Colombier 		for(d = 1970; day >= *yrsize(d); d++)
3913e12c5d1SDavid du Colombier 			day -= *yrsize(d);
3923e12c5d1SDavid du Colombier 	else
3933e12c5d1SDavid du Colombier 		for (d = 1970; day < 0; d--)
3943e12c5d1SDavid du Colombier 			day += *yrsize(d-1);
3953e12c5d1SDavid du Colombier 	rtc->year = d;
3963e12c5d1SDavid du Colombier 
3973e12c5d1SDavid du Colombier 	/*
3983e12c5d1SDavid du Colombier 	 * generate month
3993e12c5d1SDavid du Colombier 	 */
4003e12c5d1SDavid du Colombier 	d2m = yrsize(rtc->year);
4013e12c5d1SDavid du Colombier 	for(d = 1; day >= d2m[d]; d++)
4023e12c5d1SDavid du Colombier 		day -= d2m[d];
4033e12c5d1SDavid du Colombier 	rtc->mday = day + 1;
4043e12c5d1SDavid du Colombier 	rtc->mon = d;
4053e12c5d1SDavid du Colombier 
4063e12c5d1SDavid du Colombier 	return;
4073e12c5d1SDavid du Colombier }
408bd389b36SDavid du Colombier 
409bd389b36SDavid du Colombier uchar
410bd389b36SDavid du Colombier nvramread(int offset)
411bd389b36SDavid du Colombier {
412bd389b36SDavid du Colombier 	outb(Paddr, offset);
413*219b2ee8SDavid du Colombier 	delay(1);
414bd389b36SDavid du Colombier 	return inb(Pdata);
415bd389b36SDavid du Colombier }
416