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