1*a81c3ea0SDavid du Colombier #include "u.h"
2*a81c3ea0SDavid du Colombier #include "../port/lib.h"
3*a81c3ea0SDavid du Colombier #include "mem.h"
4*a81c3ea0SDavid du Colombier #include "dat.h"
5*a81c3ea0SDavid du Colombier #include "fns.h"
6*a81c3ea0SDavid du Colombier #include "io.h"
7*a81c3ea0SDavid du Colombier #include "../port/error.h"
8*a81c3ea0SDavid du Colombier
9*a81c3ea0SDavid du Colombier /*
10*a81c3ea0SDavid du Colombier * real time clock
11*a81c3ea0SDavid du Colombier */
12*a81c3ea0SDavid du Colombier
13*a81c3ea0SDavid du Colombier enum {
14*a81c3ea0SDavid du Colombier Paddr= 0x70, /* address port */
15*a81c3ea0SDavid du Colombier Pdata= 0x71, /* data port */
16*a81c3ea0SDavid du Colombier
17*a81c3ea0SDavid du Colombier Seconds= 0x00,
18*a81c3ea0SDavid du Colombier Minutes= 0x02,
19*a81c3ea0SDavid du Colombier Hours= 0x04,
20*a81c3ea0SDavid du Colombier Mday= 0x07,
21*a81c3ea0SDavid du Colombier Month= 0x08,
22*a81c3ea0SDavid du Colombier Year= 0x09,
23*a81c3ea0SDavid du Colombier Status= 0x0A,
24*a81c3ea0SDavid du Colombier };
25*a81c3ea0SDavid du Colombier
26*a81c3ea0SDavid du Colombier typedef struct Rtc Rtc;
27*a81c3ea0SDavid du Colombier struct Rtc
28*a81c3ea0SDavid du Colombier {
29*a81c3ea0SDavid du Colombier int sec;
30*a81c3ea0SDavid du Colombier int min;
31*a81c3ea0SDavid du Colombier int hour;
32*a81c3ea0SDavid du Colombier int mday;
33*a81c3ea0SDavid du Colombier int mon;
34*a81c3ea0SDavid du Colombier int year;
35*a81c3ea0SDavid du Colombier };
36*a81c3ea0SDavid du Colombier
37*a81c3ea0SDavid du Colombier enum{
38*a81c3ea0SDavid du Colombier Qdir = 0,
39*a81c3ea0SDavid du Colombier Qrtc,
40*a81c3ea0SDavid du Colombier };
41*a81c3ea0SDavid du Colombier
42*a81c3ea0SDavid du Colombier Dirtab rtcdir[]={
43*a81c3ea0SDavid du Colombier ".", {Qdir, 0, QTDIR}, 0, 0555,
44*a81c3ea0SDavid du Colombier "rtc", {Qrtc, 0}, 0, 0664,
45*a81c3ea0SDavid du Colombier };
46*a81c3ea0SDavid du Colombier
47*a81c3ea0SDavid du Colombier static Lock rtclock;
48*a81c3ea0SDavid du Colombier
49*a81c3ea0SDavid du Colombier static ulong rtc2sec(Rtc*);
50*a81c3ea0SDavid du Colombier static void sec2rtc(ulong, Rtc*);
51*a81c3ea0SDavid du Colombier
52*a81c3ea0SDavid du Colombier static Chan*
rtcattach(char * spec)53*a81c3ea0SDavid du Colombier rtcattach(char* spec)
54*a81c3ea0SDavid du Colombier {
55*a81c3ea0SDavid du Colombier return devattach('r', spec);
56*a81c3ea0SDavid du Colombier }
57*a81c3ea0SDavid du Colombier
58*a81c3ea0SDavid du Colombier static Walkqid*
rtcwalk(Chan * c,Chan * nc,char ** name,int nname)59*a81c3ea0SDavid du Colombier rtcwalk(Chan* c, Chan *nc, char** name, int nname)
60*a81c3ea0SDavid du Colombier {
61*a81c3ea0SDavid du Colombier return devwalk(c, nc, name, nname, rtcdir, nelem(rtcdir), devgen);
62*a81c3ea0SDavid du Colombier }
63*a81c3ea0SDavid du Colombier
64*a81c3ea0SDavid du Colombier static int
rtcstat(Chan * c,uchar * dp,int n)65*a81c3ea0SDavid du Colombier rtcstat(Chan* c, uchar* dp, int n)
66*a81c3ea0SDavid du Colombier {
67*a81c3ea0SDavid du Colombier return devstat(c, dp, n, rtcdir, nelem(rtcdir), devgen);
68*a81c3ea0SDavid du Colombier }
69*a81c3ea0SDavid du Colombier
70*a81c3ea0SDavid du Colombier static Chan*
rtcopen(Chan * c,int omode)71*a81c3ea0SDavid du Colombier rtcopen(Chan* c, int omode)
72*a81c3ea0SDavid du Colombier {
73*a81c3ea0SDavid du Colombier omode = openmode(omode);
74*a81c3ea0SDavid du Colombier switch((ulong)c->qid.path){
75*a81c3ea0SDavid du Colombier case Qrtc:
76*a81c3ea0SDavid du Colombier if(strcmp(up->user, eve)!=0 && omode!=OREAD)
77*a81c3ea0SDavid du Colombier error(Eperm);
78*a81c3ea0SDavid du Colombier break;
79*a81c3ea0SDavid du Colombier }
80*a81c3ea0SDavid du Colombier return devopen(c, omode, rtcdir, nelem(rtcdir), devgen);
81*a81c3ea0SDavid du Colombier }
82*a81c3ea0SDavid du Colombier
83*a81c3ea0SDavid du Colombier static void
rtcclose(Chan *)84*a81c3ea0SDavid du Colombier rtcclose(Chan*)
85*a81c3ea0SDavid du Colombier {
86*a81c3ea0SDavid du Colombier }
87*a81c3ea0SDavid du Colombier
88*a81c3ea0SDavid du Colombier static long
_rtctime(void)89*a81c3ea0SDavid du Colombier _rtctime(void)
90*a81c3ea0SDavid du Colombier {
91*a81c3ea0SDavid du Colombier Rtc rtc;
92*a81c3ea0SDavid du Colombier int i;
93*a81c3ea0SDavid du Colombier
94*a81c3ea0SDavid du Colombier /* don't do the read until the clock is no longer busy */
95*a81c3ea0SDavid du Colombier for(i = 0; i < 10000; i++){
96*a81c3ea0SDavid du Colombier outb(Paddr, Status);
97*a81c3ea0SDavid du Colombier if(inb(Pdata) & 0x80)
98*a81c3ea0SDavid du Colombier continue;
99*a81c3ea0SDavid du Colombier
100*a81c3ea0SDavid du Colombier /* read clock values */
101*a81c3ea0SDavid du Colombier outb(Paddr, Seconds); rtc.sec = inb(Pdata);
102*a81c3ea0SDavid du Colombier outb(Paddr, Minutes); rtc.min = inb(Pdata);
103*a81c3ea0SDavid du Colombier outb(Paddr, Hours); rtc.hour = inb(Pdata);
104*a81c3ea0SDavid du Colombier outb(Paddr, Mday); rtc.mday = inb(Pdata);
105*a81c3ea0SDavid du Colombier outb(Paddr, Month); rtc.mon = inb(Pdata);
106*a81c3ea0SDavid du Colombier outb(Paddr, Year); rtc.year = inb(Pdata);
107*a81c3ea0SDavid du Colombier
108*a81c3ea0SDavid du Colombier outb(Paddr, Status);
109*a81c3ea0SDavid du Colombier if((inb(Pdata) & 0x80) == 0)
110*a81c3ea0SDavid du Colombier break;
111*a81c3ea0SDavid du Colombier }
112*a81c3ea0SDavid du Colombier
113*a81c3ea0SDavid du Colombier /*
114*a81c3ea0SDavid du Colombier * the world starts jan 1 1970
115*a81c3ea0SDavid du Colombier */
116*a81c3ea0SDavid du Colombier if(rtc.year < 70)
117*a81c3ea0SDavid du Colombier rtc.year += 2000;
118*a81c3ea0SDavid du Colombier else
119*a81c3ea0SDavid du Colombier rtc.year += 1900;
120*a81c3ea0SDavid du Colombier return rtc2sec(&rtc);
121*a81c3ea0SDavid du Colombier }
122*a81c3ea0SDavid du Colombier
123*a81c3ea0SDavid du Colombier long
rtctime(void)124*a81c3ea0SDavid du Colombier rtctime(void)
125*a81c3ea0SDavid du Colombier {
126*a81c3ea0SDavid du Colombier int i;
127*a81c3ea0SDavid du Colombier long t, ot;
128*a81c3ea0SDavid du Colombier
129*a81c3ea0SDavid du Colombier ilock(&rtclock);
130*a81c3ea0SDavid du Colombier
131*a81c3ea0SDavid du Colombier /* loop till we get two reads in a row the same */
132*a81c3ea0SDavid du Colombier t = _rtctime();
133*a81c3ea0SDavid du Colombier for(i = 0; i < 100; i++){
134*a81c3ea0SDavid du Colombier ot = t;
135*a81c3ea0SDavid du Colombier t = _rtctime();
136*a81c3ea0SDavid du Colombier if(ot == t)
137*a81c3ea0SDavid du Colombier break;
138*a81c3ea0SDavid du Colombier }
139*a81c3ea0SDavid du Colombier if(i == 100) print("we are boofheads\n");
140*a81c3ea0SDavid du Colombier
141*a81c3ea0SDavid du Colombier iunlock(&rtclock);
142*a81c3ea0SDavid du Colombier
143*a81c3ea0SDavid du Colombier return t;
144*a81c3ea0SDavid du Colombier }
145*a81c3ea0SDavid du Colombier
146*a81c3ea0SDavid du Colombier static long
rtcread(Chan * c,void * buf,long n,vlong off)147*a81c3ea0SDavid du Colombier rtcread(Chan* c, void* buf, long n, vlong off)
148*a81c3ea0SDavid du Colombier {
149*a81c3ea0SDavid du Colombier ulong t;
150*a81c3ea0SDavid du Colombier ulong offset = off;
151*a81c3ea0SDavid du Colombier
152*a81c3ea0SDavid du Colombier if(c->qid.type & QTDIR)
153*a81c3ea0SDavid du Colombier return devdirread(c, buf, n, rtcdir, nelem(rtcdir), devgen);
154*a81c3ea0SDavid du Colombier
155*a81c3ea0SDavid du Colombier switch((ulong)c->qid.path){
156*a81c3ea0SDavid du Colombier case Qrtc:
157*a81c3ea0SDavid du Colombier t = rtctime();
158*a81c3ea0SDavid du Colombier n = readnum(offset, buf, n, t, 12);
159*a81c3ea0SDavid du Colombier return n;
160*a81c3ea0SDavid du Colombier }
161*a81c3ea0SDavid du Colombier error(Ebadarg);
162*a81c3ea0SDavid du Colombier return 0;
163*a81c3ea0SDavid du Colombier }
164*a81c3ea0SDavid du Colombier
165*a81c3ea0SDavid du Colombier static long
rtcwrite(Chan * c,void * buf,long n,vlong off)166*a81c3ea0SDavid du Colombier rtcwrite(Chan* c, void* buf, long n, vlong off)
167*a81c3ea0SDavid du Colombier {
168*a81c3ea0SDavid du Colombier Rtc rtc;
169*a81c3ea0SDavid du Colombier ulong secs;
170*a81c3ea0SDavid du Colombier char *cp, *ep;
171*a81c3ea0SDavid du Colombier ulong offset = off;
172*a81c3ea0SDavid du Colombier
173*a81c3ea0SDavid du Colombier if(offset!=0)
174*a81c3ea0SDavid du Colombier error(Ebadarg);
175*a81c3ea0SDavid du Colombier
176*a81c3ea0SDavid du Colombier switch((ulong)c->qid.path){
177*a81c3ea0SDavid du Colombier case Qrtc:
178*a81c3ea0SDavid du Colombier /*
179*a81c3ea0SDavid du Colombier * read the time
180*a81c3ea0SDavid du Colombier */
181*a81c3ea0SDavid du Colombier cp = ep = buf;
182*a81c3ea0SDavid du Colombier ep += n;
183*a81c3ea0SDavid du Colombier while(cp < ep){
184*a81c3ea0SDavid du Colombier if(*cp>='0' && *cp<='9')
185*a81c3ea0SDavid du Colombier break;
186*a81c3ea0SDavid du Colombier cp++;
187*a81c3ea0SDavid du Colombier }
188*a81c3ea0SDavid du Colombier secs = strtoul(cp, 0, 0);
189*a81c3ea0SDavid du Colombier
190*a81c3ea0SDavid du Colombier /*
191*a81c3ea0SDavid du Colombier * convert to rtc
192*a81c3ea0SDavid du Colombier */
193*a81c3ea0SDavid du Colombier sec2rtc(secs, &rtc);
194*a81c3ea0SDavid du Colombier
195*a81c3ea0SDavid du Colombier /*
196*a81c3ea0SDavid du Colombier * write the clock
197*a81c3ea0SDavid du Colombier */
198*a81c3ea0SDavid du Colombier ilock(&rtclock);
199*a81c3ea0SDavid du Colombier outb(Paddr, Seconds); outb(Pdata, rtc.sec);
200*a81c3ea0SDavid du Colombier outb(Paddr, Minutes); outb(Pdata, rtc.min);
201*a81c3ea0SDavid du Colombier outb(Paddr, Hours); outb(Pdata, rtc.hour);
202*a81c3ea0SDavid du Colombier outb(Paddr, Mday); outb(Pdata, rtc.mday);
203*a81c3ea0SDavid du Colombier outb(Paddr, Month); outb(Pdata, rtc.mon);
204*a81c3ea0SDavid du Colombier outb(Paddr, Year); outb(Pdata, rtc.year % 100);
205*a81c3ea0SDavid du Colombier iunlock(&rtclock);
206*a81c3ea0SDavid du Colombier return n;
207*a81c3ea0SDavid du Colombier }
208*a81c3ea0SDavid du Colombier error(Ebadarg);
209*a81c3ea0SDavid du Colombier return 0;
210*a81c3ea0SDavid du Colombier }
211*a81c3ea0SDavid du Colombier
212*a81c3ea0SDavid du Colombier Dev rtcdevtab = {
213*a81c3ea0SDavid du Colombier 'r',
214*a81c3ea0SDavid du Colombier "rtc",
215*a81c3ea0SDavid du Colombier
216*a81c3ea0SDavid du Colombier devreset,
217*a81c3ea0SDavid du Colombier devinit,
218*a81c3ea0SDavid du Colombier devshutdown,
219*a81c3ea0SDavid du Colombier rtcattach,
220*a81c3ea0SDavid du Colombier rtcwalk,
221*a81c3ea0SDavid du Colombier rtcstat,
222*a81c3ea0SDavid du Colombier rtcopen,
223*a81c3ea0SDavid du Colombier devcreate,
224*a81c3ea0SDavid du Colombier rtcclose,
225*a81c3ea0SDavid du Colombier rtcread,
226*a81c3ea0SDavid du Colombier devbread,
227*a81c3ea0SDavid du Colombier rtcwrite,
228*a81c3ea0SDavid du Colombier devbwrite,
229*a81c3ea0SDavid du Colombier devremove,
230*a81c3ea0SDavid du Colombier devwstat,
231*a81c3ea0SDavid du Colombier };
232*a81c3ea0SDavid du Colombier
233*a81c3ea0SDavid du Colombier #define SEC2MIN 60L
234*a81c3ea0SDavid du Colombier #define SEC2HOUR (60L*SEC2MIN)
235*a81c3ea0SDavid du Colombier #define SEC2DAY (24L*SEC2HOUR)
236*a81c3ea0SDavid du Colombier
237*a81c3ea0SDavid du Colombier /*
238*a81c3ea0SDavid du Colombier * days per month plus days/year
239*a81c3ea0SDavid du Colombier */
240*a81c3ea0SDavid du Colombier static int dmsize[] =
241*a81c3ea0SDavid du Colombier {
242*a81c3ea0SDavid du Colombier 365, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
243*a81c3ea0SDavid du Colombier };
244*a81c3ea0SDavid du Colombier static int ldmsize[] =
245*a81c3ea0SDavid du Colombier {
246*a81c3ea0SDavid du Colombier 366, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
247*a81c3ea0SDavid du Colombier };
248*a81c3ea0SDavid du Colombier
249*a81c3ea0SDavid du Colombier /*
250*a81c3ea0SDavid du Colombier * return the days/month for the given year
251*a81c3ea0SDavid du Colombier */
252*a81c3ea0SDavid du Colombier static int*
yrsize(int y)253*a81c3ea0SDavid du Colombier yrsize(int y)
254*a81c3ea0SDavid du Colombier {
255*a81c3ea0SDavid du Colombier if((y%4) == 0 && ((y%100) != 0 || (y%400) == 0))
256*a81c3ea0SDavid du Colombier return ldmsize;
257*a81c3ea0SDavid du Colombier else
258*a81c3ea0SDavid du Colombier return dmsize;
259*a81c3ea0SDavid du Colombier }
260*a81c3ea0SDavid du Colombier
261*a81c3ea0SDavid du Colombier /*
262*a81c3ea0SDavid du Colombier * compute seconds since Jan 1 1970
263*a81c3ea0SDavid du Colombier */
264*a81c3ea0SDavid du Colombier static ulong
rtc2sec(Rtc * rtc)265*a81c3ea0SDavid du Colombier rtc2sec(Rtc *rtc)
266*a81c3ea0SDavid du Colombier {
267*a81c3ea0SDavid du Colombier ulong secs;
268*a81c3ea0SDavid du Colombier int i;
269*a81c3ea0SDavid du Colombier int *d2m;
270*a81c3ea0SDavid du Colombier
271*a81c3ea0SDavid du Colombier secs = 0;
272*a81c3ea0SDavid du Colombier
273*a81c3ea0SDavid du Colombier /*
274*a81c3ea0SDavid du Colombier * seconds per year
275*a81c3ea0SDavid du Colombier */
276*a81c3ea0SDavid du Colombier for(i = 1970; i < rtc->year; i++){
277*a81c3ea0SDavid du Colombier d2m = yrsize(i);
278*a81c3ea0SDavid du Colombier secs += d2m[0] * SEC2DAY;
279*a81c3ea0SDavid du Colombier }
280*a81c3ea0SDavid du Colombier
281*a81c3ea0SDavid du Colombier /*
282*a81c3ea0SDavid du Colombier * seconds per month
283*a81c3ea0SDavid du Colombier */
284*a81c3ea0SDavid du Colombier d2m = yrsize(rtc->year);
285*a81c3ea0SDavid du Colombier for(i = 1; i < rtc->mon; i++)
286*a81c3ea0SDavid du Colombier secs += d2m[i] * SEC2DAY;
287*a81c3ea0SDavid du Colombier
288*a81c3ea0SDavid du Colombier secs += (rtc->mday-1) * SEC2DAY;
289*a81c3ea0SDavid du Colombier secs += rtc->hour * SEC2HOUR;
290*a81c3ea0SDavid du Colombier secs += rtc->min * SEC2MIN;
291*a81c3ea0SDavid du Colombier secs += rtc->sec;
292*a81c3ea0SDavid du Colombier
293*a81c3ea0SDavid du Colombier return secs;
294*a81c3ea0SDavid du Colombier }
295*a81c3ea0SDavid du Colombier
296*a81c3ea0SDavid du Colombier /*
297*a81c3ea0SDavid du Colombier * compute rtc from seconds since Jan 1 1970
298*a81c3ea0SDavid du Colombier */
299*a81c3ea0SDavid du Colombier static void
sec2rtc(ulong secs,Rtc * rtc)300*a81c3ea0SDavid du Colombier sec2rtc(ulong secs, Rtc *rtc)
301*a81c3ea0SDavid du Colombier {
302*a81c3ea0SDavid du Colombier int d;
303*a81c3ea0SDavid du Colombier long hms, day;
304*a81c3ea0SDavid du Colombier int *d2m;
305*a81c3ea0SDavid du Colombier
306*a81c3ea0SDavid du Colombier /*
307*a81c3ea0SDavid du Colombier * break initial number into days
308*a81c3ea0SDavid du Colombier */
309*a81c3ea0SDavid du Colombier hms = secs % SEC2DAY;
310*a81c3ea0SDavid du Colombier day = secs / SEC2DAY;
311*a81c3ea0SDavid du Colombier if(hms < 0) {
312*a81c3ea0SDavid du Colombier hms += SEC2DAY;
313*a81c3ea0SDavid du Colombier day -= 1;
314*a81c3ea0SDavid du Colombier }
315*a81c3ea0SDavid du Colombier
316*a81c3ea0SDavid du Colombier /*
317*a81c3ea0SDavid du Colombier * generate hours:minutes:seconds
318*a81c3ea0SDavid du Colombier */
319*a81c3ea0SDavid du Colombier rtc->sec = hms % 60;
320*a81c3ea0SDavid du Colombier d = hms / 60;
321*a81c3ea0SDavid du Colombier rtc->min = d % 60;
322*a81c3ea0SDavid du Colombier d /= 60;
323*a81c3ea0SDavid du Colombier rtc->hour = d;
324*a81c3ea0SDavid du Colombier
325*a81c3ea0SDavid du Colombier /*
326*a81c3ea0SDavid du Colombier * year number
327*a81c3ea0SDavid du Colombier */
328*a81c3ea0SDavid du Colombier if(day >= 0)
329*a81c3ea0SDavid du Colombier for(d = 1970; day >= *yrsize(d); d++)
330*a81c3ea0SDavid du Colombier day -= *yrsize(d);
331*a81c3ea0SDavid du Colombier else
332*a81c3ea0SDavid du Colombier for (d = 1970; day < 0; d--)
333*a81c3ea0SDavid du Colombier day += *yrsize(d-1);
334*a81c3ea0SDavid du Colombier rtc->year = d;
335*a81c3ea0SDavid du Colombier
336*a81c3ea0SDavid du Colombier /*
337*a81c3ea0SDavid du Colombier * generate month
338*a81c3ea0SDavid du Colombier */
339*a81c3ea0SDavid du Colombier d2m = yrsize(rtc->year);
340*a81c3ea0SDavid du Colombier for(d = 1; day >= d2m[d]; d++)
341*a81c3ea0SDavid du Colombier day -= d2m[d];
342*a81c3ea0SDavid du Colombier rtc->mday = day + 1;
343*a81c3ea0SDavid du Colombier rtc->mon = d;
344*a81c3ea0SDavid du Colombier }
345