1 /* 2 * Copyright (c) 1988 University of Utah. 3 * Copyright (c) 1982, 1990 The Regents of the University of California. 4 * All rights reserved. 5 * 6 * This code is derived from software contributed to Berkeley by 7 * the Systems Programming Group of the University of Utah Computer 8 * Science Department. 9 * 10 * %sccs.include.redist.c% 11 * 12 * from: Utah $Hdr: clock.c 1.18 91/01/21$ 13 * 14 * @(#)clock.c 7.16 (Berkeley) 10/11/92 15 */ 16 17 #include <sys/param.h> 18 #include <sys/kernel.h> 19 #include <hp/dev/hilreg.h> 20 #include <hp300/hp300/clockreg.h> 21 22 #include <machine/psl.h> 23 #include <machine/cpu.h> 24 25 #ifdef GPROF 26 #include <sys/gmon.h> 27 #endif 28 29 int clkstd[1]; 30 31 static int clkint; /* clock interval, as loaded */ 32 /* 33 * Statistics clock interval and variance, in usec. Variance must be a 34 * power of two. Since this gives us an even number, not an odd number, 35 * we discard one case and compensate. That is, a variance of 1024 would 36 * give us offsets in [0..1023]. Instead, we take offsets in [1..1023]. 37 * This is symmetric about the point 512, or statvar/2, and thus averages 38 * to that value (assuming uniform random numbers). 39 */ 40 static int statvar = 1024 / 4; /* {stat,prof}clock variance */ 41 static int statmin; /* statclock interval - variance/2 */ 42 static int profmin; /* profclock interval - variance/2 */ 43 static int timer3min; /* current, from above choices */ 44 45 static int month_days[12] = { 46 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 47 }; 48 struct bbc_tm *gmt_to_bbc(); 49 u_char bbc_registers[13]; 50 u_char write_bbc_reg(), read_bbc_reg(); 51 struct hil_dev *bbcaddr = NULL; 52 53 /* 54 * Machine-dependent clock routines. 55 * 56 * A note on the real-time clock: 57 * We actually load the clock with interval-1 instead of interval. 58 * This is because the counter decrements to zero after N+1 enabled clock 59 * periods where N is the value loaded into the counter. 60 * 61 * The frequencies of the HP300 clocks must be a multiple of four 62 * microseconds (since the clock counts in 4 us units). 63 */ 64 #define COUNTS_PER_SEC (1000000 / CLK_RESOLUTION) 65 66 /* 67 * Set up the real-time and statistics clocks. Leave stathz 0 only if 68 * no alternative timer is available. 69 * 70 */ 71 cpu_initclocks() 72 { 73 register volatile struct clkreg *clk; 74 register int intvl, statint, profint, minint; 75 76 clkstd[0] = IIOV(0x5F8000); /* XXX grot */ 77 clk = (volatile struct clkreg *)clkstd[0]; 78 79 if (COUNTS_PER_SEC % hz) { 80 printf("cannot get %d Hz clock; using 100 Hz\n", hz); 81 hz = 100; 82 } 83 /* 84 * Clock has several counters, so we can always use separate 85 * statclock. 86 */ 87 if (stathz == 0) /* XXX should be set in param.c */ 88 stathz = hz; 89 else if (COUNTS_PER_SEC % stathz) { 90 printf("cannot get %d Hz statclock; using 100 Hz\n", stathz); 91 stathz = 100; 92 } 93 if (profhz == 0) /* XXX should be set in param.c */ 94 profhz = stathz * 5; 95 else if (profhz < stathz || COUNTS_PER_SEC % profhz) { 96 printf("cannot get %d Hz profclock; using %d Hz\n", 97 profhz, stathz); 98 profhz = stathz; 99 } 100 101 intvl = COUNTS_PER_SEC / hz; 102 statint = COUNTS_PER_SEC / stathz; 103 profint = COUNTS_PER_SEC / profhz; 104 minint = statint / 2 + 100; 105 while (statvar > minint) 106 statvar >>= 1; 107 108 tick = intvl * CLK_RESOLUTION; 109 110 /* adjust interval counts, per note above */ 111 intvl--; 112 statint--; 113 profint--; 114 115 /* calculate base reload values */ 116 clkint = intvl; 117 statmin = statint - (statvar >> 1); 118 profmin = profint - (statvar >> 1); 119 timer3min = statmin; 120 121 /* finally, load hardware */ 122 clk->clk_cr2 = CLK_CR1; 123 clk->clk_cr1 = CLK_RESET; 124 clk->clk_msb1 = intvl >> 8; 125 clk->clk_lsb1 = intvl; 126 clk->clk_msb2 = 0; 127 clk->clk_lsb2 = 0; 128 clk->clk_msb3 = statint >> 8; 129 clk->clk_lsb3 = statint; 130 clk->clk_cr2 = CLK_CR1; 131 clk->clk_cr1 = CLK_IENAB; 132 clk->clk_cr2 = CLK_CR3; 133 clk->clk_cr3 = CLK_IENAB; 134 } 135 136 /* 137 * We assume newhz is either stathz or profhz, and that neither will 138 * change after being set up above. Could recalculate intervals here 139 * but that would be a drag. 140 */ 141 void 142 setstatclockrate(newhz) 143 int newhz; 144 { 145 146 if (newhz == stathz) 147 timer3min = statmin; 148 else 149 timer3min = profmin; 150 } 151 152 /* 153 * Statistics/profiling clock interrupt. Clear the interrupt and 154 * compute a new interval. 155 * 156 * DO THIS INLINE IN locore.s? 157 */ 158 void 159 statintr(fp) 160 struct clockframe *fp; 161 { 162 register volatile struct clkreg *clk; 163 register int newint, r, var; 164 register u_char discard; 165 166 clk = (volatile struct clkreg *)clkstd[0]; 167 discard = clk->clk_msb3; /* clear interrupt */ 168 var = statvar; 169 do { 170 r = random() & (var - 1); 171 } while (r == 0); 172 newint = timer3min + r; 173 clk->clk_msb3 = newint >> 8; 174 clk->clk_lsb3 = newint; 175 statclock(fp); 176 } 177 178 /* 179 * Return the best possible estimate of the current time. 180 */ 181 microtime(tvp) 182 register struct timeval *tvp; 183 { 184 register volatile struct clkreg *clk; 185 register int s, u, h, l, sr, l2, h2, u2, s2; 186 187 /* 188 * Read registers from slowest-changing to fastest-changing, 189 * then re-read out to slowest. If the values read before the 190 * innermost match those read after, the innermost value is 191 * consistent with the outer values. If not, it may not be and 192 * we must retry. Typically this loop runs only once; occasionally 193 * it runs twice, and only rarely does it run longer. 194 * 195 * (Using this loop avoids the need to block interrupts.) 196 */ 197 clk = (volatile struct clkreg *)clkstd[0]; 198 do { 199 s = time.tv_sec; 200 u = time.tv_usec; 201 h = clk->clk_msb1; 202 l = clk->clk_lsb1; 203 sr = clk->clk_sr; 204 l2 = clk->clk_lsb1; 205 h2 = clk->clk_msb1; 206 u2 = time.tv_usec; 207 s2 = time.tv_sec; 208 } while (l != l2 || h != h2 || u != u2 || s != s2); 209 210 /* 211 * Pending interrupt means that the counter wrapped and we did not 212 * take the interrupt yet (can only happen if clock interrupts are 213 * blocked). If so, add one tick. Then in any case, add remaining 214 * count. This should leave u < 2 seconds, since we can add at most 215 * two clock intervals (assuming hz > 2!). 216 */ 217 if (sr & CLK_INT1) 218 u += tick; 219 u += clkint - ((h << 8) | l); 220 if (u >= 1000000) { /* normalize */ 221 s++; 222 u -= 1000000; 223 } 224 tvp->tv_sec = s; 225 tvp->tv_usec = u; 226 } 227 228 /* 229 * Initialize the time of day register, based on the time base which is, e.g. 230 * from a filesystem. 231 */ 232 inittodr(base) 233 time_t base; 234 { 235 u_long timbuf = base; /* assume no battery clock exists */ 236 static int bbcinited = 0; 237 238 /* XXX */ 239 if (!bbcinited) { 240 if (badbaddr(&BBCADDR->hil_stat)) 241 printf("WARNING: no battery clock\n"); 242 else 243 bbcaddr = BBCADDR; 244 bbcinited = 1; 245 } 246 247 /* 248 * bbc_to_gmt converts and stores the gmt in timbuf. 249 * If an error is detected in bbc_to_gmt, or if the filesystem 250 * time is more recent than the gmt time in the clock, 251 * then use the filesystem time and warn the user. 252 */ 253 if (!bbc_to_gmt(&timbuf) || timbuf < base) { 254 printf("WARNING: bad date in battery clock\n"); 255 timbuf = base; 256 } 257 if (base < 5*SECYR) { 258 printf("WARNING: preposterous time in file system"); 259 timbuf = 6*SECYR + 186*SECDAY + SECDAY/2; 260 printf(" -- CHECK AND RESET THE DATE!\n"); 261 } 262 263 /* Battery clock does not store usec's, so forget about it. */ 264 time.tv_sec = timbuf; 265 } 266 267 /* 268 * Restore the time of day hardware after a time change. 269 */ 270 resettodr() 271 { 272 register int i; 273 register struct bbc_tm *tmptr; 274 275 tmptr = gmt_to_bbc(time.tv_sec); 276 277 decimal_to_bbc(0, 1, tmptr->tm_sec); 278 decimal_to_bbc(2, 3, tmptr->tm_min); 279 decimal_to_bbc(4, 5, tmptr->tm_hour); 280 decimal_to_bbc(7, 8, tmptr->tm_mday); 281 decimal_to_bbc(9, 10, tmptr->tm_mon); 282 decimal_to_bbc(11, 12, tmptr->tm_year); 283 284 /* Some bogusness to deal with seemingly broken hardware. Nonsense */ 285 bbc_registers[5] = ((tmptr->tm_hour / 10) & 0x03) + 8; 286 287 write_bbc_reg(15, 13); /* reset prescalar */ 288 289 for (i = 0; i <= NUM_BBC_REGS; i++) 290 if (bbc_registers[i] != write_bbc_reg(i, bbc_registers[i])) { 291 printf("Cannot set battery backed clock\n"); 292 break; 293 } 294 } 295 296 struct bbc_tm * 297 gmt_to_bbc(tim) 298 long tim; 299 { 300 register int i; 301 register long hms, day; 302 static struct bbc_tm rt; 303 304 day = tim / SECDAY; 305 hms = tim % SECDAY; 306 307 /* Hours, minutes, seconds are easy */ 308 rt.tm_hour = hms / 3600; 309 rt.tm_min = (hms % 3600) / 60; 310 rt.tm_sec = (hms % 3600) % 60; 311 312 /* Number of years in days */ 313 for (i = STARTOFTIME - 1900; day >= days_in_year(i); i++) 314 day -= days_in_year(i); 315 rt.tm_year = i; 316 317 /* Number of months in days left */ 318 if (leapyear(rt.tm_year)) 319 days_in_month(FEBRUARY) = 29; 320 for (i = 1; day >= days_in_month(i); i++) 321 day -= days_in_month(i); 322 days_in_month(FEBRUARY) = 28; 323 rt.tm_mon = i; 324 325 /* Days are what is left over (+1) from all that. */ 326 rt.tm_mday = day + 1; 327 328 return(&rt); 329 } 330 331 bbc_to_gmt(timbuf) 332 u_long *timbuf; 333 { 334 register int i; 335 register u_long tmp; 336 int year, month, day, hour, min, sec; 337 338 read_bbc(); 339 340 sec = bbc_to_decimal(1, 0); 341 min = bbc_to_decimal(3, 2); 342 343 /* 344 * Hours are different for some reason. Makes no sense really. 345 */ 346 hour = ((bbc_registers[5] & 0x03) * 10) + bbc_registers[4]; 347 day = bbc_to_decimal(8, 7); 348 month = bbc_to_decimal(10, 9); 349 year = bbc_to_decimal(12, 11) + 1900; 350 351 range_test(hour, 0, 23); 352 range_test(day, 1, 31); 353 range_test(month, 1, 12); 354 range_test(year, STARTOFTIME, 2000); 355 356 tmp = 0; 357 358 for (i = STARTOFTIME; i < year; i++) 359 tmp += days_in_year(i); 360 if (leapyear(year) && month > FEBRUARY) 361 tmp++; 362 363 for (i = 1; i < month; i++) 364 tmp += days_in_month(i); 365 366 tmp += (day - 1); 367 tmp = ((tmp * 24 + hour) * 60 + min) * 60 + sec; 368 369 *timbuf = tmp; 370 return(1); 371 } 372 373 read_bbc() 374 { 375 register int i, read_okay; 376 377 read_okay = 0; 378 while (!read_okay) { 379 read_okay = 1; 380 for (i = 0; i <= NUM_BBC_REGS; i++) 381 bbc_registers[i] = read_bbc_reg(i); 382 for (i = 0; i <= NUM_BBC_REGS; i++) 383 if (bbc_registers[i] != read_bbc_reg(i)) 384 read_okay = 0; 385 } 386 } 387 388 u_char 389 read_bbc_reg(reg) 390 int reg; 391 { 392 u_char data = reg; 393 394 if (bbcaddr) { 395 send_hil_cmd(bbcaddr, BBC_SET_REG, &data, 1, NULL); 396 send_hil_cmd(bbcaddr, BBC_READ_REG, NULL, 0, &data); 397 } 398 return(data); 399 } 400 401 u_char 402 write_bbc_reg(reg, data) 403 u_int data; 404 { 405 u_char tmp; 406 407 tmp = (u_char) ((data << HIL_SSHIFT) | reg); 408 409 if (bbcaddr) { 410 send_hil_cmd(bbcaddr, BBC_SET_REG, &tmp, 1, NULL); 411 send_hil_cmd(bbcaddr, BBC_WRITE_REG, NULL, 0, NULL); 412 send_hil_cmd(bbcaddr, BBC_READ_REG, NULL, 0, &tmp); 413 } 414 return(tmp); 415 } 416