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