1 #include "u.h" 2 #include "../port/lib.h" 3 #include "mem.h" 4 #include "dat.h" 5 #include "fns.h" 6 #include "../port/error.h" 7 8 #include "io.h" 9 10 /* 11 * Mostek MK48T12-15 Zeropower/Timekeeper 12 * This driver is actually portable. 13 */ 14 typedef struct Rtc Rtc; 15 struct Rtc 16 { 17 int sec; 18 int min; 19 int hour; 20 int wday; 21 int mday; 22 int mon; 23 int year; 24 }; 25 26 static uchar rtcgencksum(void); 27 static void setrtc(Rtc *rtc); 28 static long rtctime(void); 29 static int *yrsize(int yr); 30 static int *yrsize(int yr); 31 static ulong rtc2sec(Rtc *rtc); 32 static void sec2rtc(ulong secs, Rtc *rtc); 33 34 static struct 35 { 36 uchar *cksum; 37 uchar *ram; 38 RTCdev *rtc; 39 }nvr; 40 41 enum{ 42 Qdir, 43 Qrtc, 44 Qnvram, 45 }; 46 47 QLock rtclock; /* mutex on clock operations */ 48 49 static Dirtab rtcdir[]={ 50 ".", {Qdir, 0, QTDIR}, 0, 0555, 51 "rtc", {Qrtc, 0}, 0, 0666, 52 "nvram", {Qnvram, 0}, NVWRITE, 0666, 53 }; 54 #define NRTC (sizeof(rtcdir)/sizeof(rtcdir[0])) 55 56 static void 57 rtcinit(void) 58 { 59 KMap *k; 60 61 k = kmappa(NVR_CKSUM_PHYS, PTENOCACHE|PTEIO); 62 nvr.cksum = (uchar*)VA(k); 63 64 k = kmappa(NVR_PHYS, PTENOCACHE|PTEIO); 65 nvr.ram = (uchar*)VA(k); 66 nvr.rtc = (RTCdev*)(VA(k)+RTCOFF); 67 68 rtcgencksum(); 69 } 70 71 static Chan* 72 rtcattach(char *spec) 73 { 74 return devattach('r',spec); 75 } 76 77 static Walkqid* 78 rtcwalk(Chan *c, Chan *nc, char **name, int nname) 79 { 80 return devwalk(c, nc, name, nname, rtcdir, NRTC, devgen); 81 } 82 83 static int 84 rtcstat(Chan *c, uchar *dp, int n) 85 { 86 return devstat(c, dp, n, rtcdir, NRTC, devgen); 87 } 88 89 static Chan* 90 rtcopen(Chan *c, int omode) 91 { 92 omode = openmode(omode); 93 switch((ulong)c->qid.path){ 94 case Qrtc: 95 if(strcmp(up->env->user, eve)!=0 && omode!=OREAD) 96 error(Eperm); 97 break; 98 case Qnvram: 99 if(strcmp(up->env->user, eve)!=0) 100 error(Eperm); 101 } 102 return devopen(c, omode, rtcdir, NRTC, devgen); 103 } 104 105 static void 106 rtccreate(Chan *c, char *name, int omode, ulong perm) 107 { 108 USED(c, name, omode, perm); 109 error(Eperm); 110 } 111 112 static void 113 rtcclose(Chan *c) 114 { 115 USED(c); 116 } 117 118 static long 119 rtcread(Chan *c, void *buf, long n, vlong offset) 120 { 121 ulong t, ot; 122 123 if(c->qid.type & QTDIR) 124 return devdirread(c, buf, n, rtcdir, NRTC, devgen); 125 126 switch((ulong)c->qid.path){ 127 case Qrtc: 128 qlock(&rtclock); 129 t = rtctime(); 130 do{ 131 ot = t; 132 t = rtctime(); /* make sure there's no skew */ 133 }while(t != ot); 134 qunlock(&rtclock); 135 n = readnum(offset, buf, n, t, 12); 136 return n; 137 case Qnvram: 138 if(offset > NVREAD) 139 return 0; 140 if(n > NVREAD - offset) 141 n = NVREAD - offset; 142 qlock(&rtclock); 143 memmove(buf, nvr.ram+offset, n); 144 qunlock(&rtclock); 145 return n; 146 } 147 error(Egreg); 148 return 0; /* not reached */ 149 } 150 151 /* 152 * XXX - Tad: fixme to generate the correct checksum 153 */ 154 static uchar 155 rtcgencksum(void) 156 { 157 uchar cksum; 158 int i; 159 static uchar p1cksum = 0; 160 static uchar p1cksumvalid=0; 161 162 if(!p1cksumvalid) { 163 for(i=1; i < 0x1000 ; i++) 164 p1cksum ^= nvr.cksum[i]; 165 p1cksumvalid = 1; 166 } 167 168 cksum = p1cksum; 169 170 for(i=0; i < 0xfdf ; i++) { 171 cksum ^= nvr.ram[i]; 172 } 173 174 return cksum; 175 } 176 177 static long 178 rtcwrite(Chan *c, void *buf, long n, vlong offset) 179 { 180 Rtc rtc; 181 ulong secs; 182 char *cp, sbuf[32]; 183 184 switch((ulong)c->qid.path){ 185 case Qrtc: 186 /* 187 * read the time 188 */ 189 if(offset != 0 || n >= sizeof(sbuf)-1) 190 error(Ebadarg); 191 memmove(sbuf, buf, n); 192 sbuf[n] = '\0'; 193 cp = sbuf; 194 while(*cp){ 195 if(*cp>='0' && *cp<='9') 196 break; 197 cp++; 198 } 199 secs = strtoul(cp, 0, 0); 200 /* 201 * convert to bcd 202 */ 203 sec2rtc(secs, &rtc); 204 /* 205 * write it 206 */ 207 qlock(&rtclock); 208 setrtc(&rtc); 209 qunlock(&rtclock); 210 return n; 211 case Qnvram: 212 if(offset > NVWRITE) 213 return 0; 214 if(n > NVWRITE - offset) 215 n = NVWRITE - offset; 216 qlock(&rtclock); 217 memmove(nvr.ram+offset, buf, n); 218 *nvr.cksum = rtcgencksum(); 219 qunlock(&rtclock); 220 return n; 221 } 222 error(Egreg); 223 return 0; /* not reached */ 224 } 225 226 #define bcd2dec(bcd) (((((bcd)>>4) & 0x0F) * 10) + ((bcd) & 0x0F)) 227 #define dec2bcd(dec) ((((dec)/10)<<4)|((dec)%10)) 228 229 static void 230 setrtc(Rtc *rtc) 231 { 232 struct RTCdev *dev; 233 234 dev = nvr.rtc; 235 dev->control |= RTCWRITE; 236 wbflush(); 237 dev->year = dec2bcd(rtc->year % 100); 238 dev->mon = dec2bcd(rtc->mon); 239 dev->mday = dec2bcd(rtc->mday); 240 dev->hour = dec2bcd(rtc->hour); 241 dev->min = dec2bcd(rtc->min); 242 dev->sec = dec2bcd(rtc->sec); 243 wbflush(); 244 dev->control &= ~RTCWRITE; 245 wbflush(); 246 } 247 248 static long 249 rtctime(void) 250 { 251 struct RTCdev *dev; 252 Rtc rtc; 253 254 dev = nvr.rtc; 255 dev->control |= RTCREAD; 256 wbflush(); 257 rtc.sec = bcd2dec(dev->sec) & 0x7F; 258 rtc.min = bcd2dec(dev->min & 0x7F); 259 rtc.hour = bcd2dec(dev->hour & 0x3F); 260 rtc.mday = bcd2dec(dev->mday & 0x3F); 261 rtc.mon = bcd2dec(dev->mon & 0x3F); 262 rtc.year = bcd2dec(dev->year); 263 dev->control &= ~RTCREAD; 264 wbflush(); 265 266 if (rtc.mon < 1 || rtc.mon > 12) 267 return 0; 268 /* 269 * the world starts Jan 1 1970 270 */ 271 if(rtc.year < 70) 272 rtc.year += 2000; 273 else 274 rtc.year += 1900; 275 276 return rtc2sec(&rtc); 277 } 278 279 #define SEC2MIN 60L 280 #define SEC2HOUR (60L*SEC2MIN) 281 #define SEC2DAY (24L*SEC2HOUR) 282 283 /* 284 * days per month plus days/year 285 */ 286 static int dmsize[] = 287 { 288 365, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 289 }; 290 static int ldmsize[] = 291 { 292 366, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 293 }; 294 295 /* 296 * return the days/month for the given year 297 */ 298 static int * 299 yrsize(int yr) 300 { 301 if((yr % 4) == 0) 302 return ldmsize; 303 else 304 return dmsize; 305 } 306 307 /* 308 * compute seconds since Jan 1 1970 309 */ 310 static ulong 311 rtc2sec(Rtc *rtc) 312 { 313 ulong secs; 314 int i; 315 int *d2m; 316 317 secs = 0; 318 319 /* 320 * seconds per year 321 */ 322 for(i = 1970; i < rtc->year; i++){ 323 d2m = yrsize(i); 324 secs += d2m[0] * SEC2DAY; 325 } 326 327 /* 328 * seconds per month 329 */ 330 d2m = yrsize(rtc->year); 331 for(i = 1; i < rtc->mon; i++) 332 secs += d2m[i] * SEC2DAY; 333 334 secs += (rtc->mday-1) * SEC2DAY; 335 secs += rtc->hour * SEC2HOUR; 336 secs += rtc->min * SEC2MIN; 337 secs += rtc->sec; 338 339 return secs; 340 } 341 342 /* 343 * compute rtc from seconds since Jan 1 1970 344 */ 345 static void 346 sec2rtc(ulong secs, Rtc *rtc) 347 { 348 int d; 349 long hms, day; 350 int *d2m; 351 352 /* 353 * break initial number into days 354 */ 355 hms = secs % SEC2DAY; 356 day = secs / SEC2DAY; 357 if(hms < 0) { 358 hms += SEC2DAY; 359 day -= 1; 360 } 361 362 /* 363 * generate hours:minutes:seconds 364 */ 365 rtc->sec = hms % 60; 366 d = hms / 60; 367 rtc->min = d % 60; 368 d /= 60; 369 rtc->hour = d; 370 371 /* 372 * year number 373 */ 374 if(day >= 0) 375 for(d = 1970; day >= *yrsize(d); d++) 376 day -= *yrsize(d); 377 else 378 for (d = 1970; day < 0; d--) 379 day += *yrsize(d-1); 380 rtc->year = d; 381 382 /* 383 * generate month 384 */ 385 d2m = yrsize(rtc->year); 386 for(d = 1; day >= d2m[d]; d++) 387 day -= d2m[d]; 388 rtc->mday = day + 1; 389 rtc->mon = d; 390 391 return; 392 } 393 394 Dev rtcdevtab = { 395 'r', 396 "rtc", 397 398 devreset, 399 rtcinit, 400 devshutdown, 401 rtcattach, 402 rtcwalk, 403 rtcstat, 404 rtcopen, 405 rtccreate, 406 rtcclose, 407 rtcread, 408 devbread, 409 rtcwrite, 410 devbwrite, 411 devremove, 412 devwstat, 413 }; 414