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