xref: /plan9/sys/src/9/kw/devrtc.c (revision 7365b686ae7154552580a79fd89a0ba5dc9297c4)
1 /*
2  * devrtc - real-time clock, for kirkwood
3  */
4 #include "u.h"
5 #include "../port/lib.h"
6 #include "mem.h"
7 #include "dat.h"
8 #include "fns.h"
9 #include "../port/error.h"
10 #include "io.h"
11 
12 typedef	struct	RtcReg	RtcReg;
13 typedef	struct	Rtc	Rtc;
14 
15 struct RtcReg
16 {
17 	ulong	time;
18 	ulong	date;
19 	ulong	alarmtm;
20 	ulong	alarmdt;
21 	ulong	intrmask;
22 	ulong	intrcause;
23 };
24 
25 struct Rtc
26 {
27 	int	sec;
28 	int	min;
29 	int	hour;
30 	int	wday;
31 	int	mday;
32 	int	mon;
33 	int	year;
34 };
35 
36 enum {
37 	Qdir,
38 	Qrtc,
39 };
40 
41 static Dirtab rtcdir[] = {
42 	".",	{Qdir, 0, QTDIR},	0,		0555,
43 	"rtc",	{Qrtc},			NUMSIZE,	0664,
44 };
45 static	RtcReg	*rtcreg;		/* filled in by attach */
46 static	Lock	rtclock;
47 
48 #define SEC2MIN	60
49 #define SEC2HOUR (60*SEC2MIN)
50 #define SEC2DAY (24L*SEC2HOUR)
51 
52 /*
53  * days per month plus days/year
54  */
55 static	int	dmsize[] =
56 {
57 	365, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
58 };
59 static	int	ldmsize[] =
60 {
61 	366, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
62 };
63 
64 /*
65  *  return the days/month for the given year
66  */
67 static int *
yrsize(int yr)68 yrsize(int yr)
69 {
70 	if((yr % 4) == 0)
71 		return ldmsize;
72 	else
73 		return dmsize;
74 }
75 
76 /*
77  *  compute seconds since Jan 1 1970
78  */
79 static ulong
rtc2sec(Rtc * rtc)80 rtc2sec(Rtc *rtc)
81 {
82 	ulong secs;
83 	int i;
84 	int *d2m;
85 
86 	/*
87 	 *  seconds per year
88 	 */
89 	secs = 0;
90 	for(i = 1970; i < rtc->year; i++){
91 		d2m = yrsize(i);
92 		secs += d2m[0] * SEC2DAY;
93 	}
94 
95 	/*
96 	 *  seconds per month
97 	 */
98 	d2m = yrsize(rtc->year);
99 	for(i = 1; i < rtc->mon; i++)
100 		secs += d2m[i] * SEC2DAY;
101 
102 	secs += (rtc->mday-1) * SEC2DAY;
103 	secs += rtc->hour * SEC2HOUR;
104 	secs += rtc->min * SEC2MIN;
105 	secs += rtc->sec;
106 
107 	return secs;
108 }
109 
110 /*
111  *  compute rtc from seconds since Jan 1 1970
112  */
113 static void
sec2rtc(ulong secs,Rtc * rtc)114 sec2rtc(ulong secs, Rtc *rtc)
115 {
116 	int d;
117 	long hms, day;
118 	int *d2m;
119 
120 	/*
121 	 * break initial number into days
122 	 */
123 	hms = secs % SEC2DAY;
124 	day = secs / SEC2DAY;
125 	if(hms < 0) {
126 		hms += SEC2DAY;
127 		day -= 1;
128 	}
129 
130 	/*
131 	 * 19700101 was thursday
132 	 */
133 	rtc->wday = (day + 7340036L) % 7;
134 
135 	/*
136 	 * generate hours:minutes:seconds
137 	 */
138 	rtc->sec = hms % 60;
139 	d = hms / 60;
140 	rtc->min = d % 60;
141 	d /= 60;
142 	rtc->hour = d;
143 
144 	/*
145 	 * year number
146 	 */
147 	if(day >= 0)
148 		for(d = 1970; day >= *yrsize(d); d++)
149 			day -= *yrsize(d);
150 	else
151 		for (d = 1970; day < 0; d--)
152 			day += *yrsize(d-1);
153 	rtc->year = d;
154 
155 	/*
156 	 * generate month
157 	 */
158 	d2m = yrsize(rtc->year);
159 	for(d = 1; day >= d2m[d]; d++)
160 		day -= d2m[d];
161 	rtc->mday = day + 1;
162 	rtc->mon = d;
163 }
164 
165 enum {
166 	Rtcsec	= 0x00007f,
167 	Rtcmin	= 0x007f00,
168 	Rtcms	= 8,
169 	Rtchr12	= 0x1f0000,
170 	Rtchr24	= 0x3f0000,
171 	Rtchrs	= 16,
172 
173 	Rdmday	= 0x00003f,
174 	Rdmon	= 0x001f00,
175 	Rdms	= 8,
176 	Rdyear	= 0x7f0000,
177 	Rdys	= 16,
178 
179 	Rtcpm	= 1<<21,		/* pm bit */
180 	Rtc12	= 1<<22,		/* 12 hr clock */
181 };
182 
183 static ulong
bcd2dec(ulong bcd)184 bcd2dec(ulong bcd)
185 {
186 	ulong d, m, i;
187 
188 	d = 0;
189 	m = 1;
190 	for(i = 0; i < 2 * sizeof d; i++){
191 		d += ((bcd >> (4*i)) & 0xf) * m;
192 		m *= 10;
193 	}
194 	return d;
195 }
196 
197 static ulong
dec2bcd(ulong d)198 dec2bcd(ulong d)
199 {
200 	ulong bcd, i;
201 
202 	bcd = 0;
203 	for(i = 0; d != 0; i++){
204 		bcd |= (d%10) << (4*i);
205 		d /= 10;
206 	}
207 	return bcd;
208 }
209 
210 static long
_rtctime(void)211 _rtctime(void)
212 {
213 	ulong t, d;
214 	Rtc rtc;
215 
216 	t = rtcreg->time;
217 	d = rtcreg->date;
218 
219 	rtc.sec = bcd2dec(t & Rtcsec);
220 	rtc.min = bcd2dec((t & Rtcmin) >> Rtcms);
221 
222 	if(t & Rtc12){
223 		rtc.hour = bcd2dec((t & Rtchr12) >> Rtchrs) - 1; /* 1—12 */
224 		if(t & Rtcpm)
225 			rtc.hour += 12;
226 	}else
227 		rtc.hour = bcd2dec((t & Rtchr24) >> Rtchrs);	/* 0—23 */
228 
229 	rtc.mday = bcd2dec(d & Rdmday);				/* 1—31 */
230 	rtc.mon = bcd2dec((d & Rdmon) >> Rdms);			/* 1—12 */
231 	rtc.year = bcd2dec((d & Rdyear) >> Rdys) + 2000;	/* year%100 */
232 
233 //	print("%0.2d:%0.2d:%.02d %0.2d/%0.2d/%0.2d\n", /* HH:MM:SS YY/MM/DD */
234 //		rtc.hour, rtc.min, rtc.sec, rtc.year, rtc.mon, rtc.mday);
235 	return rtc2sec(&rtc);
236 }
237 
238 long
rtctime(void)239 rtctime(void)
240 {
241 	int i;
242 	long t, ot;
243 
244 	ilock(&rtclock);
245 
246 	/* loop until we get two reads in a row the same */
247 	t = _rtctime();
248 	ot = ~t;
249 	for(i = 0; i < 100 && ot != t; i++){
250 		ot = t;
251 		t = _rtctime();
252 	}
253 	if(ot != t)
254 		print("rtctime: we are boofheads\n");
255 
256 	iunlock(&rtclock);
257 	return t;
258 }
259 
260 static void
setrtc(Rtc * rtc)261 setrtc(Rtc *rtc)
262 {
263 	ilock(&rtclock);
264 	rtcreg->time = dec2bcd(rtc->wday) << 24 | dec2bcd(rtc->hour) << 16 |
265 		dec2bcd(rtc->min) << 8 | dec2bcd(rtc->sec);
266 	rtcreg->date = dec2bcd(rtc->year - 2000) << 16 |
267 		dec2bcd(rtc->mon) << 8 | dec2bcd(rtc->mday);
268 	iunlock(&rtclock);
269 }
270 
271 static Chan*
rtcattach(char * spec)272 rtcattach(char *spec)
273 {
274 	rtcreg = (RtcReg*)soc.rtc;
275 	return devattach(L'r', spec);
276 }
277 
278 static Walkqid*
rtcwalk(Chan * c,Chan * nc,char ** name,int nname)279 rtcwalk(Chan *c, Chan *nc, char **name, int nname)
280 {
281 	return devwalk(c, nc, name, nname, rtcdir, nelem(rtcdir), devgen);
282 }
283 
284 static int
rtcstat(Chan * c,uchar * dp,int n)285 rtcstat(Chan *c, uchar *dp, int n)
286 {
287 	return devstat(c, dp, n, rtcdir, nelem(rtcdir), devgen);
288 }
289 
290 static Chan*
rtcopen(Chan * c,int omode)291 rtcopen(Chan *c, int omode)
292 {
293 	return devopen(c, omode, rtcdir, nelem(rtcdir), devgen);
294 }
295 
296 static void
rtcclose(Chan *)297 rtcclose(Chan*)
298 {
299 }
300 
301 static long
rtcread(Chan * c,void * buf,long n,vlong off)302 rtcread(Chan *c, void *buf, long n, vlong off)
303 {
304 	if(c->qid.type & QTDIR)
305 		return devdirread(c, buf, n, rtcdir, nelem(rtcdir), devgen);
306 
307 	switch((ulong)c->qid.path){
308 	default:
309 		error(Egreg);
310 	case Qrtc:
311 		return readnum(off, buf, n, rtctime(), NUMSIZE);
312 	}
313 }
314 
315 static long
rtcwrite(Chan * c,void * buf,long n,vlong off)316 rtcwrite(Chan *c, void *buf, long n, vlong off)
317 {
318 	ulong offset = off;
319 	char *cp, sbuf[32];
320 	Rtc rtc;
321 
322 	switch((ulong)c->qid.path){
323 	default:
324 		error(Egreg);
325 	case Qrtc:
326 		if(offset != 0 || n >= sizeof(sbuf)-1)
327 			error(Ebadarg);
328 		memmove(sbuf, buf, n);
329 		sbuf[n] = '\0';
330 		for(cp = sbuf; *cp != '\0'; cp++)
331 			if(*cp >= '0' && *cp <= '9')
332 				break;
333 		sec2rtc(strtoul(cp, 0, 0), &rtc);
334 		setrtc(&rtc);
335 		return n;
336 	}
337 }
338 
339 Dev rtcdevtab = {
340 	L'r',
341 	"rtc",
342 
343 	devreset,
344 	devinit,
345 	devshutdown,
346 	rtcattach,
347 	rtcwalk,
348 	rtcstat,
349 	rtcopen,
350 	devcreate,
351 	rtcclose,
352 	rtcread,
353 	devbread,
354 	rtcwrite,
355 	devbwrite,
356 	devremove,
357 	devwstat,
358 	devpower,
359 };
360