1 /* 2 * Copyright (c) 1992 The Regents of the University of California. 3 * All rights reserved. 4 * 5 * This software was developed by the Computer Systems Engineering group 6 * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and 7 * contributed to Berkeley. 8 * 9 * %sccs.include.redist.c% 10 * 11 * @(#)clock.c 7.1 (Berkeley) 07/13/92 12 * 13 * from: $Header: clock.c,v 1.14 92/07/07 05:34:08 leres Exp $ (LBL) 14 */ 15 16 /* 17 * Clock driver. This is the id prom (``eeprom'') driver as well 18 * and includes the timer register functions too. 19 */ 20 21 #include "sys/param.h" 22 #include "sys/kernel.h" 23 #include "sys/device.h" 24 #include "sys/proc.h" 25 #include "sys/resourcevar.h" 26 #ifdef GPROF 27 #include "sys/gmon.h" 28 #endif 29 30 #include "vm/vm.h" 31 32 #include "machine/autoconf.h" 33 #ifdef notdef 34 #include "machine/psl.h" 35 #endif 36 37 #include "clockreg.h" 38 #include "intreg.h" 39 #include "timerreg.h" 40 41 /* 42 * Statistics clock interval and variance, in usec. Variance must be a 43 * power of two. Since this gives us an even number, not an odd number, 44 * we discard one case and compensate. That is, a variance of 1024 would 45 * give us offsets in [0..1023]. Instead, we take offsets in [1..1023]. 46 * This is symmetric about the point 512, or statvar/2, and thus averages 47 * to that value (assuming uniform random numbers). 48 */ 49 static int statvar = 1024; 50 static int statmin; /* statclock interval - 1/2*variance */ 51 52 static void clockattach __P((struct device *, struct device *, void *)); 53 struct cfdriver clockcd = 54 { NULL, "eeprom", matchbyname, clockattach, 55 DV_DULL, sizeof(struct device) }; 56 57 static void timerattach __P((struct device *, struct device *, void *)); 58 struct cfdriver timercd = 59 { NULL, "counter-timer", matchbyname, timerattach, DV_DULL, 60 sizeof(struct device) }; 61 62 /* ARGSUSED */ 63 static void 64 clockattach(parent, self, aux) 65 struct device *parent, *self; 66 void *aux; 67 { 68 register int h; 69 register struct clockreg *cl; 70 struct romaux *ra = aux; 71 72 printf(": %s\n", getpropstring(ra->ra_node, "model")); 73 /* 74 * We ignore any existing virtual address as we need to map 75 * this read-only and make it read-write only temporarily, 76 * whenever we read or write the clock chip. The clock also 77 * contains the ID ``PROM'', and I have already had the pleasure 78 * of reloading the cpu type, Ethernet address, etc, by hand from 79 * the console FORTH interpreter. I intend not to enjoy it again. 80 */ 81 cl = (struct clockreg *)mapiodev(ra->ra_paddr, sizeof *clockreg); 82 pmap_changeprot(kernel_pmap, (vm_offset_t)clockreg, VM_PROT_READ, 1); 83 h = cl->cl_idprom.id_machine << 24; 84 h |= cl->cl_idprom.id_hostid[0] << 16; 85 h |= cl->cl_idprom.id_hostid[1] << 8; 86 h |= cl->cl_idprom.id_hostid[0]; 87 hostid = h; 88 clockreg = cl; 89 } 90 91 /* ARGSUSED */ 92 static void 93 timerattach(parent, self, aux) 94 struct device *parent, *self; 95 void *aux; 96 { 97 register struct romaux *ra = aux; 98 99 printf("\n"); 100 /* 101 * This time, we ignore any existing virtual address because 102 * we have a fixed virtual address for the timer, to make 103 * microtime() faster. 104 */ 105 (void)mapdev(ra->ra_paddr, TIMERREG_VA, sizeof(struct timerreg)); 106 /* should link interrupt handlers here, rather than compiled-in? */ 107 } 108 109 /* 110 * Write en/dis-able clock registers. We coordinate so that several 111 * writers can run simultaneously. 112 */ 113 void 114 clk_wenable(onoff) 115 int onoff; 116 { 117 register int s; 118 register vm_prot_t prot;/* nonzero => change prot */ 119 static int writers; 120 121 s = splhigh(); 122 if (onoff) 123 prot = writers++ == 0 ? VM_PROT_READ|VM_PROT_WRITE : 0; 124 else 125 prot = --writers == 0 ? VM_PROT_READ : 0; 126 splx(s); 127 if (prot) 128 pmap_changeprot(kernel_pmap, (vm_offset_t)clockreg, prot, 1); 129 } 130 131 /* 132 * XXX this belongs elsewhere 133 */ 134 void 135 myetheraddr(cp) 136 u_char *cp; 137 { 138 register struct clockreg *cl = clockreg; 139 140 cp[0] = cl->cl_idprom.id_ether[0]; 141 cp[1] = cl->cl_idprom.id_ether[1]; 142 cp[2] = cl->cl_idprom.id_ether[2]; 143 cp[3] = cl->cl_idprom.id_ether[3]; 144 cp[4] = cl->cl_idprom.id_ether[4]; 145 cp[5] = cl->cl_idprom.id_ether[5]; 146 } 147 148 /* 149 * Delay: wait for `about' n microseconds to pass. 150 * This is easy to do on the SparcStation since we have 151 * freerunning microsecond timers -- no need to guess at 152 * cpu speed factors. We just wait for it to change n times 153 * (if we calculated a limit, we might overshoot, and precision 154 * is irrelevant here---we want less object code). 155 */ 156 delay(n) 157 register int n; 158 { 159 register int c, t; 160 161 if (timercd.cd_ndevs == 0) 162 panic("delay"); 163 c = TIMERREG->t_c10.t_counter; 164 while (--n >= 0) { 165 while ((t = TIMERREG->t_c10.t_counter) == c) 166 continue; 167 c = t; 168 } 169 } 170 171 /* 172 * Set up the real-time and statistics clocks. Leave stathz 0 only if 173 * no alternative timer is available. 174 * 175 * The frequencies of these clocks must be an even number of microseconds. 176 */ 177 cpu_initclocks() 178 { 179 register int statint, minint; 180 181 if (1000000 % hz) { 182 printf("cannot get %d Hz clock; using 100 Hz\n", hz); 183 hz = 100; 184 tick = 1000000 / hz; 185 } 186 if (stathz == 0) 187 stathz = hz; 188 if (1000000 % stathz) { 189 printf("cannot get %d Hz statclock; using 100 Hz\n", stathz); 190 stathz = 100; 191 } 192 profhz = stathz; /* always */ 193 194 statint = 1000000 / stathz; 195 minint = statint / 2 + 100; 196 while (statvar > minint) 197 statvar >>= 1; 198 TIMERREG->t_c10.t_limit = tmr_ustolim(tick); 199 TIMERREG->t_c14.t_limit = tmr_ustolim(statint); 200 statmin = statint - (statvar >> 1); 201 ienab_bis(IE_L14 | IE_L10); 202 } 203 204 /* 205 * Dummy setstatclockrate(), since we know profhz==hz. 206 */ 207 /* ARGSUSED */ 208 void 209 setstatclockrate(newhz) 210 int newhz; 211 { 212 /* nothing */ 213 } 214 215 /* 216 * Level 10 (clock) interrupts. If we are using the FORTH PROM for 217 * console input, we need to check for that here as well, and generate 218 * a software interrupt to read it. 219 */ 220 int 221 clockintr(cap) 222 void *cap; 223 { 224 register int discard; 225 extern int rom_console_input; 226 227 /* read the limit register to clear the interrupt */ 228 discard = TIMERREG->t_c10.t_limit; 229 hardclock((struct clockframe *)cap); 230 if (rom_console_input && cnrom()) 231 setsoftint(); 232 233 return (1); 234 } 235 236 /* 237 * Level 14 (stat clock) interrupt handler. 238 */ 239 int 240 statintr(cap) 241 void *cap; 242 { 243 register int discard; 244 register u_long newint, r, var; 245 246 /* read the limit register to clear the interrupt */ 247 discard = TIMERREG->t_c14.t_limit; 248 statclock((struct clockframe *)cap); 249 250 /* 251 * Compute new randomized interval. The intervals are uniformly 252 * distributed on [statint - statvar / 2, statint + statvar / 2], 253 * and therefore have mean statint, giving a stathz frequency clock. 254 */ 255 var = statvar; 256 do { 257 r = random() & (var - 1); 258 } while (r == 0); 259 newint = statmin + r; 260 261 TIMERREG->t_c14.t_limit = tmr_ustolim(newint); 262 return (1); 263 } 264 265 /* 266 * BCD to decimal and decimal to BCD. 267 */ 268 #define FROMBCD(x) (((x) >> 4) * 10 + ((x) & 0xf)) 269 #define TOBCD(x) (((x) / 10 * 16) + ((x) % 10)) 270 271 #define SECDAY (24 * 60 * 60) 272 #define SECYR (SECDAY * 365) 273 #define LEAPYEAR(y) (((y) & 3) == 0) 274 275 /* 276 * This code is defunct after 2068. 277 * Will Unix still be here then?? 278 */ 279 const short dayyr[12] = 280 { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 }; 281 282 chiptotime(sec, min, hour, day, mon, year) 283 register int sec, min, hour, day, mon, year; 284 { 285 register int days, yr; 286 287 sec = FROMBCD(sec); 288 min = FROMBCD(min); 289 hour = FROMBCD(hour); 290 day = FROMBCD(day); 291 mon = FROMBCD(mon); 292 year = FROMBCD(year) + YEAR0; 293 294 /* simple sanity checks */ 295 if (year < 70 || mon < 1 || mon > 12 || day < 1 || day > 31) 296 return (0); 297 days = 0; 298 for (yr = 70; yr < year; yr++) 299 days += LEAPYEAR(yr) ? 366 : 365; 300 days += dayyr[mon - 1] + day - 1; 301 if (LEAPYEAR(yr) && mon > 2) 302 days++; 303 /* now have days since Jan 1, 1970; the rest is easy... */ 304 return (days * SECDAY + hour * 3600 + min * 60 + sec); 305 } 306 307 struct chiptime { 308 int sec; 309 int min; 310 int hour; 311 int wday; 312 int day; 313 int mon; 314 int year; 315 }; 316 317 timetochip(c) 318 register struct chiptime *c; 319 { 320 register int t, t2, t3, now = time.tv_sec; 321 322 /* compute the year */ 323 t2 = now / SECDAY; 324 t3 = (t2 + 2) % 7; /* day of week */ 325 c->wday = TOBCD(t3 + 1); 326 327 t = 69; 328 while (t2 >= 0) { /* whittle off years */ 329 t3 = t2; 330 t++; 331 t2 -= LEAPYEAR(t) ? 366 : 365; 332 } 333 c->year = t; 334 335 /* t3 = month + day; separate */ 336 t = LEAPYEAR(t); 337 for (t2 = 1; t2 < 12; t2++) 338 if (t3 < dayyr[t2] + (t && t2 > 1)) 339 break; 340 341 /* t2 is month */ 342 c->mon = t2; 343 c->day = t3 - dayyr[t2 - 1] + 1; 344 if (t && t2 > 2) 345 c->day--; 346 347 /* the rest is easy */ 348 t = now % SECDAY; 349 c->hour = t / 3600; 350 t %= 3600; 351 c->min = t / 60; 352 c->sec = t % 60; 353 354 c->sec = TOBCD(c->sec); 355 c->min = TOBCD(c->min); 356 c->hour = TOBCD(c->hour); 357 c->day = TOBCD(c->day); 358 c->mon = TOBCD(c->mon); 359 c->year = TOBCD(c->year - YEAR0); 360 } 361 362 /* 363 * Set up the system's time, given a `reasonable' time value. 364 */ 365 inittodr(base) 366 time_t base; 367 { 368 register struct clockreg *cl = clockreg; 369 int sec, min, hour, day, mon, year; 370 int badbase = 0; 371 372 if (base < 5 * SECYR) { 373 printf("WARNING: preposterous time in file system\n"); 374 /* not going to use it anyway, if the chip is readable */ 375 base = 21*SECYR + 186*SECDAY + SECDAY/2; 376 badbase = 1; 377 } 378 clk_wenable(1); 379 cl->cl_csr |= CLK_READ; /* enable read (stop time) */ 380 sec = cl->cl_sec; 381 min = cl->cl_min; 382 hour = cl->cl_hour; 383 day = cl->cl_mday; 384 mon = cl->cl_month; 385 year = cl->cl_year; 386 cl->cl_csr &= ~CLK_READ; /* time wears on */ 387 clk_wenable(0); 388 if ((time.tv_sec = chiptotime(sec, min, hour, day, mon, year)) == 0) { 389 printf("WARNING: bad date in battery clock"); 390 /* 391 * Believe the time in the file system for lack of 392 * anything better, resetting the clock. 393 */ 394 time.tv_sec = base; 395 if (!badbase) 396 resettodr(); 397 } else { 398 int deltat = time.tv_sec - base; 399 400 if (deltat < 0) 401 deltat = -deltat; 402 if (deltat < 2 * SECDAY) 403 return; 404 printf("WARNING: clock %s %d days", 405 time.tv_sec < base ? "lost" : "gained", deltat / SECDAY); 406 } 407 printf(" -- CHECK AND RESET THE DATE!\n"); 408 } 409 410 /* 411 * Reset the clock based on the current time. 412 * Used when the current clock is preposterous, when the time is changed, 413 * and when rebooting. Do nothing if the time is not yet known, e.g., 414 * when crashing during autoconfig. 415 */ 416 resettodr() 417 { 418 register struct clockreg *cl; 419 struct chiptime c; 420 421 if (!time.tv_sec || (cl = clockreg) == NULL) 422 return; 423 timetochip(&c); 424 clk_wenable(1); 425 cl->cl_csr |= CLK_WRITE; /* enable write */ 426 cl->cl_sec = c.sec; 427 cl->cl_min = c.min; 428 cl->cl_hour = c.hour; 429 cl->cl_wday = c.wday; 430 cl->cl_mday = c.day; 431 cl->cl_month = c.mon; 432 cl->cl_year = c.year; 433 cl->cl_csr &= ~CLK_WRITE; /* load them up */ 434 clk_wenable(0); 435 } 436