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