xref: /plan9-contrib/sys/src/9/loongson/devrtc.c (revision a81c3ea0c7f009a3088ab7fe55ea9013d9d77a74)
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