xref: /plan9/sys/src/9/kw/devrtc.c (revision 7365b686ae7154552580a79fd89a0ba5dc9297c4)
120c254cbSDavid du Colombier /*
220c254cbSDavid du Colombier  * devrtc - real-time clock, for kirkwood
320c254cbSDavid du Colombier  */
420c254cbSDavid du Colombier #include "u.h"
520c254cbSDavid du Colombier #include "../port/lib.h"
620c254cbSDavid du Colombier #include "mem.h"
720c254cbSDavid du Colombier #include "dat.h"
820c254cbSDavid du Colombier #include "fns.h"
920c254cbSDavid du Colombier #include "../port/error.h"
1020c254cbSDavid du Colombier #include "io.h"
1120c254cbSDavid du Colombier 
1220c254cbSDavid du Colombier typedef	struct	RtcReg	RtcReg;
1320c254cbSDavid du Colombier typedef	struct	Rtc	Rtc;
1420c254cbSDavid du Colombier 
1520c254cbSDavid du Colombier struct RtcReg
1620c254cbSDavid du Colombier {
1720c254cbSDavid du Colombier 	ulong	time;
1820c254cbSDavid du Colombier 	ulong	date;
1920c254cbSDavid du Colombier 	ulong	alarmtm;
2020c254cbSDavid du Colombier 	ulong	alarmdt;
2120c254cbSDavid du Colombier 	ulong	intrmask;
2220c254cbSDavid du Colombier 	ulong	intrcause;
2320c254cbSDavid du Colombier };
2420c254cbSDavid du Colombier 
2520c254cbSDavid du Colombier struct Rtc
2620c254cbSDavid du Colombier {
2720c254cbSDavid du Colombier 	int	sec;
2820c254cbSDavid du Colombier 	int	min;
2920c254cbSDavid du Colombier 	int	hour;
3020c254cbSDavid du Colombier 	int	wday;
3120c254cbSDavid du Colombier 	int	mday;
3220c254cbSDavid du Colombier 	int	mon;
3320c254cbSDavid du Colombier 	int	year;
3420c254cbSDavid du Colombier };
3520c254cbSDavid du Colombier 
3620c254cbSDavid du Colombier enum {
3720c254cbSDavid du Colombier 	Qdir,
3820c254cbSDavid du Colombier 	Qrtc,
3920c254cbSDavid du Colombier };
4020c254cbSDavid du Colombier 
4120c254cbSDavid du Colombier static Dirtab rtcdir[] = {
4220c254cbSDavid du Colombier 	".",	{Qdir, 0, QTDIR},	0,		0555,
4320c254cbSDavid du Colombier 	"rtc",	{Qrtc},			NUMSIZE,	0664,
4420c254cbSDavid du Colombier };
45*7365b686SDavid du Colombier static	RtcReg	*rtcreg;		/* filled in by attach */
4620c254cbSDavid du Colombier static	Lock	rtclock;
4720c254cbSDavid du Colombier 
4820c254cbSDavid du Colombier #define SEC2MIN	60
4920c254cbSDavid du Colombier #define SEC2HOUR (60*SEC2MIN)
5020c254cbSDavid du Colombier #define SEC2DAY (24L*SEC2HOUR)
5120c254cbSDavid du Colombier 
5220c254cbSDavid du Colombier /*
5320c254cbSDavid du Colombier  * days per month plus days/year
5420c254cbSDavid du Colombier  */
5520c254cbSDavid du Colombier static	int	dmsize[] =
5620c254cbSDavid du Colombier {
5720c254cbSDavid du Colombier 	365, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
5820c254cbSDavid du Colombier };
5920c254cbSDavid du Colombier static	int	ldmsize[] =
6020c254cbSDavid du Colombier {
6120c254cbSDavid du Colombier 	366, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
6220c254cbSDavid du Colombier };
6320c254cbSDavid du Colombier 
6420c254cbSDavid du Colombier /*
6520c254cbSDavid du Colombier  *  return the days/month for the given year
6620c254cbSDavid du Colombier  */
6720c254cbSDavid du Colombier static int *
yrsize(int yr)6820c254cbSDavid du Colombier yrsize(int yr)
6920c254cbSDavid du Colombier {
7020c254cbSDavid du Colombier 	if((yr % 4) == 0)
7120c254cbSDavid du Colombier 		return ldmsize;
7220c254cbSDavid du Colombier 	else
7320c254cbSDavid du Colombier 		return dmsize;
7420c254cbSDavid du Colombier }
7520c254cbSDavid du Colombier 
7620c254cbSDavid du Colombier /*
7720c254cbSDavid du Colombier  *  compute seconds since Jan 1 1970
7820c254cbSDavid du Colombier  */
7920c254cbSDavid du Colombier static ulong
rtc2sec(Rtc * rtc)8020c254cbSDavid du Colombier rtc2sec(Rtc *rtc)
8120c254cbSDavid du Colombier {
8220c254cbSDavid du Colombier 	ulong secs;
8320c254cbSDavid du Colombier 	int i;
8420c254cbSDavid du Colombier 	int *d2m;
8520c254cbSDavid du Colombier 
8620c254cbSDavid du Colombier 	/*
8720c254cbSDavid du Colombier 	 *  seconds per year
8820c254cbSDavid du Colombier 	 */
8920c254cbSDavid du Colombier 	secs = 0;
9020c254cbSDavid du Colombier 	for(i = 1970; i < rtc->year; i++){
9120c254cbSDavid du Colombier 		d2m = yrsize(i);
9220c254cbSDavid du Colombier 		secs += d2m[0] * SEC2DAY;
9320c254cbSDavid du Colombier 	}
9420c254cbSDavid du Colombier 
9520c254cbSDavid du Colombier 	/*
9620c254cbSDavid du Colombier 	 *  seconds per month
9720c254cbSDavid du Colombier 	 */
9820c254cbSDavid du Colombier 	d2m = yrsize(rtc->year);
9920c254cbSDavid du Colombier 	for(i = 1; i < rtc->mon; i++)
10020c254cbSDavid du Colombier 		secs += d2m[i] * SEC2DAY;
10120c254cbSDavid du Colombier 
10220c254cbSDavid du Colombier 	secs += (rtc->mday-1) * SEC2DAY;
10320c254cbSDavid du Colombier 	secs += rtc->hour * SEC2HOUR;
10420c254cbSDavid du Colombier 	secs += rtc->min * SEC2MIN;
10520c254cbSDavid du Colombier 	secs += rtc->sec;
10620c254cbSDavid du Colombier 
10720c254cbSDavid du Colombier 	return secs;
10820c254cbSDavid du Colombier }
10920c254cbSDavid du Colombier 
11020c254cbSDavid du Colombier /*
11120c254cbSDavid du Colombier  *  compute rtc from seconds since Jan 1 1970
11220c254cbSDavid du Colombier  */
11320c254cbSDavid du Colombier static void
sec2rtc(ulong secs,Rtc * rtc)11420c254cbSDavid du Colombier sec2rtc(ulong secs, Rtc *rtc)
11520c254cbSDavid du Colombier {
11620c254cbSDavid du Colombier 	int d;
11720c254cbSDavid du Colombier 	long hms, day;
11820c254cbSDavid du Colombier 	int *d2m;
11920c254cbSDavid du Colombier 
12020c254cbSDavid du Colombier 	/*
12120c254cbSDavid du Colombier 	 * break initial number into days
12220c254cbSDavid du Colombier 	 */
12320c254cbSDavid du Colombier 	hms = secs % SEC2DAY;
12420c254cbSDavid du Colombier 	day = secs / SEC2DAY;
12520c254cbSDavid du Colombier 	if(hms < 0) {
12620c254cbSDavid du Colombier 		hms += SEC2DAY;
12720c254cbSDavid du Colombier 		day -= 1;
12820c254cbSDavid du Colombier 	}
12920c254cbSDavid du Colombier 
13020c254cbSDavid du Colombier 	/*
13120c254cbSDavid du Colombier 	 * 19700101 was thursday
13220c254cbSDavid du Colombier 	 */
13320c254cbSDavid du Colombier 	rtc->wday = (day + 7340036L) % 7;
13420c254cbSDavid du Colombier 
13520c254cbSDavid du Colombier 	/*
13620c254cbSDavid du Colombier 	 * generate hours:minutes:seconds
13720c254cbSDavid du Colombier 	 */
13820c254cbSDavid du Colombier 	rtc->sec = hms % 60;
13920c254cbSDavid du Colombier 	d = hms / 60;
14020c254cbSDavid du Colombier 	rtc->min = d % 60;
14120c254cbSDavid du Colombier 	d /= 60;
14220c254cbSDavid du Colombier 	rtc->hour = d;
14320c254cbSDavid du Colombier 
14420c254cbSDavid du Colombier 	/*
14520c254cbSDavid du Colombier 	 * year number
14620c254cbSDavid du Colombier 	 */
14720c254cbSDavid du Colombier 	if(day >= 0)
14820c254cbSDavid du Colombier 		for(d = 1970; day >= *yrsize(d); d++)
14920c254cbSDavid du Colombier 			day -= *yrsize(d);
15020c254cbSDavid du Colombier 	else
15120c254cbSDavid du Colombier 		for (d = 1970; day < 0; d--)
15220c254cbSDavid du Colombier 			day += *yrsize(d-1);
15320c254cbSDavid du Colombier 	rtc->year = d;
15420c254cbSDavid du Colombier 
15520c254cbSDavid du Colombier 	/*
15620c254cbSDavid du Colombier 	 * generate month
15720c254cbSDavid du Colombier 	 */
15820c254cbSDavid du Colombier 	d2m = yrsize(rtc->year);
15920c254cbSDavid du Colombier 	for(d = 1; day >= d2m[d]; d++)
16020c254cbSDavid du Colombier 		day -= d2m[d];
16120c254cbSDavid du Colombier 	rtc->mday = day + 1;
16220c254cbSDavid du Colombier 	rtc->mon = d;
16320c254cbSDavid du Colombier }
16420c254cbSDavid du Colombier 
16520c254cbSDavid du Colombier enum {
16620c254cbSDavid du Colombier 	Rtcsec	= 0x00007f,
16720c254cbSDavid du Colombier 	Rtcmin	= 0x007f00,
16820c254cbSDavid du Colombier 	Rtcms	= 8,
16920c254cbSDavid du Colombier 	Rtchr12	= 0x1f0000,
17020c254cbSDavid du Colombier 	Rtchr24	= 0x3f0000,
17120c254cbSDavid du Colombier 	Rtchrs	= 16,
17220c254cbSDavid du Colombier 
17320c254cbSDavid du Colombier 	Rdmday	= 0x00003f,
17420c254cbSDavid du Colombier 	Rdmon	= 0x001f00,
17520c254cbSDavid du Colombier 	Rdms	= 8,
17620c254cbSDavid du Colombier 	Rdyear	= 0x7f0000,
17720c254cbSDavid du Colombier 	Rdys	= 16,
17820c254cbSDavid du Colombier 
17920c254cbSDavid du Colombier 	Rtcpm	= 1<<21,		/* pm bit */
18020c254cbSDavid du Colombier 	Rtc12	= 1<<22,		/* 12 hr clock */
18120c254cbSDavid du Colombier };
18220c254cbSDavid du Colombier 
18320c254cbSDavid du Colombier static ulong
bcd2dec(ulong bcd)18420c254cbSDavid du Colombier bcd2dec(ulong bcd)
18520c254cbSDavid du Colombier {
18620c254cbSDavid du Colombier 	ulong d, m, i;
18720c254cbSDavid du Colombier 
18820c254cbSDavid du Colombier 	d = 0;
18920c254cbSDavid du Colombier 	m = 1;
19020c254cbSDavid du Colombier 	for(i = 0; i < 2 * sizeof d; i++){
19120c254cbSDavid du Colombier 		d += ((bcd >> (4*i)) & 0xf) * m;
19220c254cbSDavid du Colombier 		m *= 10;
19320c254cbSDavid du Colombier 	}
19420c254cbSDavid du Colombier 	return d;
19520c254cbSDavid du Colombier }
19620c254cbSDavid du Colombier 
19720c254cbSDavid du Colombier static ulong
dec2bcd(ulong d)19820c254cbSDavid du Colombier dec2bcd(ulong d)
19920c254cbSDavid du Colombier {
20020c254cbSDavid du Colombier 	ulong bcd, i;
20120c254cbSDavid du Colombier 
20220c254cbSDavid du Colombier 	bcd = 0;
20320c254cbSDavid du Colombier 	for(i = 0; d != 0; i++){
20420c254cbSDavid du Colombier 		bcd |= (d%10) << (4*i);
20520c254cbSDavid du Colombier 		d /= 10;
20620c254cbSDavid du Colombier 	}
20720c254cbSDavid du Colombier 	return bcd;
20820c254cbSDavid du Colombier }
20920c254cbSDavid du Colombier 
21020c254cbSDavid du Colombier static long
_rtctime(void)21120c254cbSDavid du Colombier _rtctime(void)
21220c254cbSDavid du Colombier {
21320c254cbSDavid du Colombier 	ulong t, d;
21420c254cbSDavid du Colombier 	Rtc rtc;
21520c254cbSDavid du Colombier 
21620c254cbSDavid du Colombier 	t = rtcreg->time;
21720c254cbSDavid du Colombier 	d = rtcreg->date;
21820c254cbSDavid du Colombier 
21920c254cbSDavid du Colombier 	rtc.sec = bcd2dec(t & Rtcsec);
22020c254cbSDavid du Colombier 	rtc.min = bcd2dec((t & Rtcmin) >> Rtcms);
22120c254cbSDavid du Colombier 
22220c254cbSDavid du Colombier 	if(t & Rtc12){
22320c254cbSDavid du Colombier 		rtc.hour = bcd2dec((t & Rtchr12) >> Rtchrs) - 1; /* 1—12 */
22420c254cbSDavid du Colombier 		if(t & Rtcpm)
22520c254cbSDavid du Colombier 			rtc.hour += 12;
22620c254cbSDavid du Colombier 	}else
22720c254cbSDavid du Colombier 		rtc.hour = bcd2dec((t & Rtchr24) >> Rtchrs);	/* 0—23 */
22820c254cbSDavid du Colombier 
22920c254cbSDavid du Colombier 	rtc.mday = bcd2dec(d & Rdmday);				/* 1—31 */
23020c254cbSDavid du Colombier 	rtc.mon = bcd2dec((d & Rdmon) >> Rdms);			/* 1—12 */
23120c254cbSDavid du Colombier 	rtc.year = bcd2dec((d & Rdyear) >> Rdys) + 2000;	/* year%100 */
23220c254cbSDavid du Colombier 
23320c254cbSDavid du Colombier //	print("%0.2d:%0.2d:%.02d %0.2d/%0.2d/%0.2d\n", /* HH:MM:SS YY/MM/DD */
23420c254cbSDavid du Colombier //		rtc.hour, rtc.min, rtc.sec, rtc.year, rtc.mon, rtc.mday);
23520c254cbSDavid du Colombier 	return rtc2sec(&rtc);
23620c254cbSDavid du Colombier }
23720c254cbSDavid du Colombier 
23820c254cbSDavid du Colombier long
rtctime(void)23920c254cbSDavid du Colombier rtctime(void)
24020c254cbSDavid du Colombier {
24120c254cbSDavid du Colombier 	int i;
24220c254cbSDavid du Colombier 	long t, ot;
24320c254cbSDavid du Colombier 
24420c254cbSDavid du Colombier 	ilock(&rtclock);
24520c254cbSDavid du Colombier 
24620c254cbSDavid du Colombier 	/* loop until we get two reads in a row the same */
24720c254cbSDavid du Colombier 	t = _rtctime();
24820c254cbSDavid du Colombier 	ot = ~t;
24920c254cbSDavid du Colombier 	for(i = 0; i < 100 && ot != t; i++){
25020c254cbSDavid du Colombier 		ot = t;
25120c254cbSDavid du Colombier 		t = _rtctime();
25220c254cbSDavid du Colombier 	}
25320c254cbSDavid du Colombier 	if(ot != t)
25420c254cbSDavid du Colombier 		print("rtctime: we are boofheads\n");
25520c254cbSDavid du Colombier 
25620c254cbSDavid du Colombier 	iunlock(&rtclock);
25720c254cbSDavid du Colombier 	return t;
25820c254cbSDavid du Colombier }
25920c254cbSDavid du Colombier 
26020c254cbSDavid du Colombier static void
setrtc(Rtc * rtc)26120c254cbSDavid du Colombier setrtc(Rtc *rtc)
26220c254cbSDavid du Colombier {
26320c254cbSDavid du Colombier 	ilock(&rtclock);
26420c254cbSDavid du Colombier 	rtcreg->time = dec2bcd(rtc->wday) << 24 | dec2bcd(rtc->hour) << 16 |
26520c254cbSDavid du Colombier 		dec2bcd(rtc->min) << 8 | dec2bcd(rtc->sec);
26620c254cbSDavid du Colombier 	rtcreg->date = dec2bcd(rtc->year - 2000) << 16 |
26720c254cbSDavid du Colombier 		dec2bcd(rtc->mon) << 8 | dec2bcd(rtc->mday);
26820c254cbSDavid du Colombier 	iunlock(&rtclock);
26920c254cbSDavid du Colombier }
27020c254cbSDavid du Colombier 
27120c254cbSDavid du Colombier static Chan*
rtcattach(char * spec)27220c254cbSDavid du Colombier rtcattach(char *spec)
27320c254cbSDavid du Colombier {
274*7365b686SDavid du Colombier 	rtcreg = (RtcReg*)soc.rtc;
27520c254cbSDavid du Colombier 	return devattach(L'r', spec);
27620c254cbSDavid du Colombier }
27720c254cbSDavid du Colombier 
27820c254cbSDavid du Colombier static Walkqid*
rtcwalk(Chan * c,Chan * nc,char ** name,int nname)27920c254cbSDavid du Colombier rtcwalk(Chan *c, Chan *nc, char **name, int nname)
28020c254cbSDavid du Colombier {
28120c254cbSDavid du Colombier 	return devwalk(c, nc, name, nname, rtcdir, nelem(rtcdir), devgen);
28220c254cbSDavid du Colombier }
28320c254cbSDavid du Colombier 
28420c254cbSDavid du Colombier static int
rtcstat(Chan * c,uchar * dp,int n)28520c254cbSDavid du Colombier rtcstat(Chan *c, uchar *dp, int n)
28620c254cbSDavid du Colombier {
28720c254cbSDavid du Colombier 	return devstat(c, dp, n, rtcdir, nelem(rtcdir), devgen);
28820c254cbSDavid du Colombier }
28920c254cbSDavid du Colombier 
29020c254cbSDavid du Colombier static Chan*
rtcopen(Chan * c,int omode)29120c254cbSDavid du Colombier rtcopen(Chan *c, int omode)
29220c254cbSDavid du Colombier {
29320c254cbSDavid du Colombier 	return devopen(c, omode, rtcdir, nelem(rtcdir), devgen);
29420c254cbSDavid du Colombier }
29520c254cbSDavid du Colombier 
29620c254cbSDavid du Colombier static void
rtcclose(Chan *)29720c254cbSDavid du Colombier rtcclose(Chan*)
29820c254cbSDavid du Colombier {
29920c254cbSDavid du Colombier }
30020c254cbSDavid du Colombier 
30120c254cbSDavid du Colombier static long
rtcread(Chan * c,void * buf,long n,vlong off)30220c254cbSDavid du Colombier rtcread(Chan *c, void *buf, long n, vlong off)
30320c254cbSDavid du Colombier {
30420c254cbSDavid du Colombier 	if(c->qid.type & QTDIR)
30520c254cbSDavid du Colombier 		return devdirread(c, buf, n, rtcdir, nelem(rtcdir), devgen);
30620c254cbSDavid du Colombier 
30720c254cbSDavid du Colombier 	switch((ulong)c->qid.path){
30820c254cbSDavid du Colombier 	default:
30920c254cbSDavid du Colombier 		error(Egreg);
31020c254cbSDavid du Colombier 	case Qrtc:
31120c254cbSDavid du Colombier 		return readnum(off, buf, n, rtctime(), NUMSIZE);
31220c254cbSDavid du Colombier 	}
31320c254cbSDavid du Colombier }
31420c254cbSDavid du Colombier 
31520c254cbSDavid du Colombier static long
rtcwrite(Chan * c,void * buf,long n,vlong off)31620c254cbSDavid du Colombier rtcwrite(Chan *c, void *buf, long n, vlong off)
31720c254cbSDavid du Colombier {
31820c254cbSDavid du Colombier 	ulong offset = off;
31920c254cbSDavid du Colombier 	char *cp, sbuf[32];
32020c254cbSDavid du Colombier 	Rtc rtc;
32120c254cbSDavid du Colombier 
32220c254cbSDavid du Colombier 	switch((ulong)c->qid.path){
32320c254cbSDavid du Colombier 	default:
32420c254cbSDavid du Colombier 		error(Egreg);
32520c254cbSDavid du Colombier 	case Qrtc:
32620c254cbSDavid du Colombier 		if(offset != 0 || n >= sizeof(sbuf)-1)
32720c254cbSDavid du Colombier 			error(Ebadarg);
32820c254cbSDavid du Colombier 		memmove(sbuf, buf, n);
32920c254cbSDavid du Colombier 		sbuf[n] = '\0';
33020c254cbSDavid du Colombier 		for(cp = sbuf; *cp != '\0'; cp++)
33120c254cbSDavid du Colombier 			if(*cp >= '0' && *cp <= '9')
33220c254cbSDavid du Colombier 				break;
33320c254cbSDavid du Colombier 		sec2rtc(strtoul(cp, 0, 0), &rtc);
33420c254cbSDavid du Colombier 		setrtc(&rtc);
33520c254cbSDavid du Colombier 		return n;
33620c254cbSDavid du Colombier 	}
33720c254cbSDavid du Colombier }
33820c254cbSDavid du Colombier 
33920c254cbSDavid du Colombier Dev rtcdevtab = {
34020c254cbSDavid du Colombier 	L'r',
34120c254cbSDavid du Colombier 	"rtc",
34220c254cbSDavid du Colombier 
34320c254cbSDavid du Colombier 	devreset,
34420c254cbSDavid du Colombier 	devinit,
34520c254cbSDavid du Colombier 	devshutdown,
34620c254cbSDavid du Colombier 	rtcattach,
34720c254cbSDavid du Colombier 	rtcwalk,
34820c254cbSDavid du Colombier 	rtcstat,
34920c254cbSDavid du Colombier 	rtcopen,
35020c254cbSDavid du Colombier 	devcreate,
35120c254cbSDavid du Colombier 	rtcclose,
35220c254cbSDavid du Colombier 	rtcread,
35320c254cbSDavid du Colombier 	devbread,
35420c254cbSDavid du Colombier 	rtcwrite,
35520c254cbSDavid du Colombier 	devbwrite,
35620c254cbSDavid du Colombier 	devremove,
35720c254cbSDavid du Colombier 	devwstat,
35820c254cbSDavid du Colombier 	devpower,
35920c254cbSDavid du Colombier };
360