1 /* 2 * M48T59/559 Timekeeper 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 11 #include "io.h" 12 13 enum{ 14 STB0 = 0x74, 15 STB1 = 0x75, 16 Data = 0x77, 17 18 NVOFF= 0, 19 NVLEN= 0x1ff0, /* length in bytes of NV RAM */ 20 21 /* 22 * register offsets into time of day clock 23 */ 24 NVflags= 0x1ff0, 25 NVwatchdog= 0x1ff7, 26 NVctl= 0x1ff8, 27 NVsec, 28 NVmin, 29 NVhour, 30 NVday, /* (1 = Sun) */ 31 NVmday, /* (1-31) */ 32 NVmon, /* (1-12) */ 33 NVyear, /* (0-99) */ 34 35 /* NVctl */ 36 RTwrite = (1<<7), 37 RTread = (1<<6), 38 RTsign = (1<<5), 39 RTcal = 0x1f, 40 41 /* NVwatchdog */ 42 WDsteer = (1<<7), /* 0 -> intr, 1 -> reset */ 43 WDmult = (1<<2), /* 5 bits of multiplier */ 44 WDres0 = (0<<0), /* 1/16 sec resolution */ 45 WDres1 = (1<<0), /* 1/4 sec resolution */ 46 WDres2 = (2<<0), /* 1 sec resolution */ 47 WDres3 = (3<<0), /* 4 sec resolution */ 48 49 Qdir = 0, 50 Qrtc, 51 Qnvram, 52 }; 53 54 /* 55 * broken down time 56 */ 57 typedef struct 58 { 59 int sec; 60 int min; 61 int hour; 62 int mday; 63 int mon; 64 int year; 65 } Rtc; 66 67 QLock rtclock; /* mutex on nvram operations */ 68 69 static Dirtab rtcdir[]={ 70 ".", {Qdir, 0, QTDIR}, 0, DMDIR|0555, 71 "rtc", {Qrtc, 0}, 0, 0644, 72 "nvram", {Qnvram, 0}, 0, 0600, 73 }; 74 75 static ulong rtc2sec(Rtc*); 76 static void sec2rtc(ulong, Rtc*); 77 static void setrtc(Rtc*); 78 static void nvcksum(void); 79 static void nvput(int, uchar); 80 static uchar nvget(int); 81 82 static Chan* 83 rtcattach(char *spec) 84 { 85 return devattach('r', spec); 86 } 87 88 static Walkqid* 89 rtcwalk(Chan *c, Chan *nc, char **name, int nname) 90 { 91 return devwalk(c, nc, name, nname, rtcdir, nelem(rtcdir), devgen); 92 } 93 94 static int 95 rtcstat(Chan *c, uchar *dp, int n) 96 { 97 return devstat(c, dp, n, rtcdir, nelem(rtcdir), devgen); 98 } 99 100 static Chan* 101 rtcopen(Chan *c, int omode) 102 { 103 omode = openmode(omode); 104 switch((ulong)c->qid.path){ 105 case Qrtc: 106 if(strcmp(up->user, eve)!=0 && omode!=OREAD) 107 error(Eperm); 108 break; 109 case Qnvram: 110 if(strcmp(up->user, eve)!=0 || !cpuserver) 111 error(Eperm); 112 } 113 return devopen(c, omode, rtcdir, nelem(rtcdir), devgen); 114 } 115 116 static void 117 rtcclose(Chan*) 118 { 119 } 120 121 static long 122 rtcread(Chan *c, void *buf, long n, vlong off) 123 { 124 char *p; 125 ulong t; 126 int i; 127 ulong offset = off; 128 129 if(c->qid.type & QTDIR) 130 return devdirread(c, buf, n, rtcdir, nelem(rtcdir), devgen); 131 132 switch((ulong)c->qid.path){ 133 case Qrtc: 134 qlock(&rtclock); 135 t = rtctime(); 136 qunlock(&rtclock); 137 n = readnum(offset, buf, n, t, 12); 138 return n; 139 case Qnvram: 140 offset += NVOFF; 141 if(offset > NVLEN) 142 return 0; 143 if(n > NVLEN - offset) 144 n = NVLEN - offset; 145 p = buf; 146 qlock(&rtclock); 147 for(i = 0; i < n; i++) 148 p[i] = nvget(i+offset); 149 qunlock(&rtclock); 150 return n; 151 } 152 error(Egreg); 153 return -1; /* never reached */ 154 } 155 156 static long 157 rtcwrite(Chan *c, void *buf, long n, vlong off) 158 { 159 Rtc rtc; 160 ulong secs; 161 char *cp, *ep; 162 int i; 163 ulong offset = off; 164 165 switch((ulong)c->qid.path){ 166 case Qrtc: 167 if(offset!=0) 168 error(Ebadarg); 169 /* 170 * read the time 171 */ 172 cp = ep = buf; 173 ep += n; 174 while(cp < ep){ 175 if(*cp>='0' && *cp<='9') 176 break; 177 cp++; 178 } 179 secs = strtoul(cp, 0, 0); 180 /* 181 * convert to bcd 182 */ 183 sec2rtc(secs, &rtc); 184 /* 185 * write it 186 */ 187 qlock(&rtclock); 188 setrtc(&rtc); 189 qunlock(&rtclock); 190 return n; 191 case Qnvram: 192 offset += NVOFF; 193 if(offset > NVLEN) 194 return 0; 195 if(n > NVLEN - offset) 196 n = NVLEN - offset; 197 qlock(&rtclock); 198 for(i = 0; i < n; i++) 199 nvput(i+offset, ((uchar*)buf)[i]); 200 nvcksum(); 201 qunlock(&rtclock); 202 return n; 203 } 204 error(Egreg); 205 return -1; /* never reached */ 206 } 207 208 long 209 rtcbwrite(Chan *c, Block *bp, ulong offset) 210 { 211 return devbwrite(c, bp, offset); 212 } 213 214 Dev rtcdevtab = { 215 'r', 216 "rtc", 217 218 devreset, 219 devinit, 220 devshutdown, 221 rtcattach, 222 rtcwalk, 223 rtcstat, 224 rtcopen, 225 devcreate, 226 rtcclose, 227 rtcread, 228 devbread, 229 rtcwrite, 230 devbwrite, 231 devremove, 232 devwstat, 233 }; 234 235 static void 236 nvput(int offset, uchar val) 237 { 238 outb(STB0, offset); 239 outb(STB1, offset>>8); 240 outb(Data, val); 241 } 242 243 static uchar 244 nvget(int offset) 245 { 246 outb(STB0, offset); 247 outb(STB1, offset>>8); 248 return inb(Data); 249 } 250 251 static void 252 nvcksum(void) 253 { 254 } 255 256 void 257 watchreset(void) 258 { 259 splhi(); 260 nvput(NVwatchdog, WDsteer|(1*WDmult)|WDres0); 261 for(;;); 262 } 263 264 static int 265 getbcd(int bcd) 266 { 267 return (bcd&0x0f) + 10 * (bcd>>4); 268 } 269 270 static int 271 putbcd(int val) 272 { 273 return (val % 10) | (((val/10) % 10) << 4); 274 } 275 276 long 277 rtctime(void) 278 { 279 int ctl; 280 Rtc rtc; 281 282 /* 283 * convert from BCD 284 */ 285 ctl = nvget(NVctl); 286 ctl &= RTsign|RTcal; 287 nvput(NVctl, ctl|RTread); 288 289 rtc.sec = getbcd(nvget(NVsec) & 0x7f); 290 rtc.min = getbcd(nvget(NVmin)); 291 rtc.hour = getbcd(nvget(NVhour)); 292 rtc.mday = getbcd(nvget(NVmday)); 293 rtc.mon = getbcd(nvget(NVmon)); 294 rtc.year = getbcd(nvget(NVyear)); 295 if(rtc.year < 70) 296 rtc.year += 2000; 297 else 298 rtc.year += 1900; 299 300 nvput(NVctl, ctl); 301 302 return rtc2sec(&rtc); 303 } 304 305 static void 306 setrtc(Rtc *rtc) 307 { 308 int ctl; 309 310 ctl = nvget(NVctl); 311 ctl &= RTsign|RTcal; 312 nvput(NVctl, ctl|RTwrite); 313 314 nvput(NVsec, putbcd(rtc->sec)); 315 nvput(NVmin, putbcd(rtc->min)); 316 nvput(NVhour, putbcd(rtc->hour)); 317 nvput(NVmday, putbcd(rtc->mday)); 318 nvput(NVmon, putbcd(rtc->mon)); 319 nvput(NVyear, putbcd(rtc->year % 100)); 320 321 nvput(NVctl, ctl); 322 } 323 324 #define SEC2MIN 60L 325 #define SEC2HOUR (60L*SEC2MIN) 326 #define SEC2DAY (24L*SEC2HOUR) 327 328 /* 329 * days per month plus days/year 330 */ 331 static int dmsize[] = 332 { 333 365, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 334 }; 335 static int ldmsize[] = 336 { 337 366, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 338 }; 339 340 /* 341 * return the days/month for the given year 342 */ 343 static int * 344 yrsize(int y) 345 { 346 347 if((y%4) == 0 && ((y%100) != 0 || (y%400) == 0)) 348 return ldmsize; 349 else 350 return dmsize; 351 } 352 353 /* 354 * compute seconds since Jan 1 1970 355 */ 356 static ulong 357 rtc2sec(Rtc *rtc) 358 { 359 ulong secs; 360 int i; 361 int *d2m; 362 363 secs = 0; 364 365 /* 366 * seconds per year 367 */ 368 for(i = 1970; i < rtc->year; i++){ 369 d2m = yrsize(i); 370 secs += d2m[0] * SEC2DAY; 371 } 372 373 /* 374 * seconds per month 375 */ 376 d2m = yrsize(rtc->year); 377 for(i = 1; i < rtc->mon; i++) 378 secs += d2m[i] * SEC2DAY; 379 380 secs += (rtc->mday-1) * SEC2DAY; 381 secs += rtc->hour * SEC2HOUR; 382 secs += rtc->min * SEC2MIN; 383 secs += rtc->sec; 384 385 return secs; 386 } 387 388 /* 389 * compute rtc from seconds since Jan 1 1970 390 */ 391 static void 392 sec2rtc(ulong secs, Rtc *rtc) 393 { 394 int d; 395 long hms, day; 396 int *d2m; 397 398 /* 399 * break initial number into days 400 */ 401 hms = secs % SEC2DAY; 402 day = secs / SEC2DAY; 403 if(hms < 0) { 404 hms += SEC2DAY; 405 day -= 1; 406 } 407 408 /* 409 * generate hours:minutes:seconds 410 */ 411 rtc->sec = hms % 60; 412 d = hms / 60; 413 rtc->min = d % 60; 414 d /= 60; 415 rtc->hour = d; 416 417 /* 418 * year number 419 */ 420 if(day >= 0) 421 for(d = 1970; day >= *yrsize(d); d++) 422 day -= *yrsize(d); 423 else 424 for (d = 1970; day < 0; d--) 425 day += *yrsize(d-1); 426 rtc->year = d; 427 428 /* 429 * generate month 430 */ 431 d2m = yrsize(rtc->year); 432 for(d = 1; day >= d2m[d]; d++) 433 day -= d2m[d]; 434 rtc->mday = day + 1; 435 rtc->mon = d; 436 437 return; 438 } 439