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 * 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 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 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 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 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 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 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 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* 272 rtcattach(char *spec) 273 { 274 rtcreg = (RtcReg*)soc.rtc; 275 return devattach(L'r', spec); 276 } 277 278 static Walkqid* 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 285 rtcstat(Chan *c, uchar *dp, int n) 286 { 287 return devstat(c, dp, n, rtcdir, nelem(rtcdir), devgen); 288 } 289 290 static Chan* 291 rtcopen(Chan *c, int omode) 292 { 293 return devopen(c, omode, rtcdir, nelem(rtcdir), devgen); 294 } 295 296 static void 297 rtcclose(Chan*) 298 { 299 } 300 301 static long 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 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