Annotation of sys/arch/hp300/hp300/clock.c, Revision 1.1
1.1 ! nbrk 1: /* $OpenBSD: clock.c,v 1.13 2005/12/03 21:36:17 brad Exp $ */
! 2: /* $NetBSD: clock.c,v 1.20 1997/04/27 20:43:38 thorpej Exp $ */
! 3:
! 4: /*
! 5: * Copyright (c) 1988 University of Utah.
! 6: * Copyright (c) 1982, 1990, 1993
! 7: * The Regents of the University of California. All rights reserved.
! 8: *
! 9: * This code is derived from software contributed to Berkeley by
! 10: * the Systems Programming Group of the University of Utah Computer
! 11: * Science Department.
! 12: *
! 13: * Redistribution and use in source and binary forms, with or without
! 14: * modification, are permitted provided that the following conditions
! 15: * are met:
! 16: * 1. Redistributions of source code must retain the above copyright
! 17: * notice, this list of conditions and the following disclaimer.
! 18: * 2. Redistributions in binary form must reproduce the above copyright
! 19: * notice, this list of conditions and the following disclaimer in the
! 20: * documentation and/or other materials provided with the distribution.
! 21: * 3. Neither the name of the University nor the names of its contributors
! 22: * may be used to endorse or promote products derived from this software
! 23: * without specific prior written permission.
! 24: *
! 25: * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
! 26: * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
! 27: * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
! 28: * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
! 29: * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
! 30: * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
! 31: * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
! 32: * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
! 33: * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
! 34: * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
! 35: * SUCH DAMAGE.
! 36: *
! 37: * from: Utah $Hdr: clock.c 1.18 91/01/21$
! 38: *
! 39: * @(#)clock.c 8.2 (Berkeley) 1/12/94
! 40: */
! 41:
! 42: /*
! 43: * HPs use the MC6840 PTM with the following arrangement:
! 44: * Timers 1 and 3 are externally driver from a 25MHz source.
! 45: * Output from timer 3 is tied to the input of timer 2.
! 46: * The latter makes it possible to use timers 3 and 2 together to get
! 47: * a 32-bit countdown timer.
! 48: */
! 49:
! 50: #include <sys/param.h>
! 51: #include <sys/systm.h>
! 52: #include <sys/kernel.h>
! 53: #include <sys/tty.h>
! 54: #include <sys/evcount.h>
! 55:
! 56: #include <machine/psl.h>
! 57: #include <machine/cpu.h>
! 58: #include <machine/hp300spu.h>
! 59:
! 60: #include <dev/hil/hilreg.h> /* for BBC */
! 61: #include <hp300/hp300/clockreg.h>
! 62:
! 63: #ifdef GPROF
! 64: #include <sys/gmon.h>
! 65: #endif
! 66:
! 67: int clkstd[1];
! 68:
! 69: static int clkint; /* clock interval, as loaded */
! 70: /*
! 71: * Statistics clock interval and variance, in usec. Variance must be a
! 72: * power of two. Since this gives us an even number, not an odd number,
! 73: * we discard one case and compensate. That is, a variance of 1024 would
! 74: * give us offsets in [0..1023]. Instead, we take offsets in [1..1023].
! 75: * This is symmetric about the point 512, or statvar/2, and thus averages
! 76: * to that value (assuming uniform random numbers).
! 77: */
! 78: static int statvar = 1024 / 4; /* {stat,prof}clock variance */
! 79: static int statmin; /* statclock interval - variance/2 */
! 80: static int profmin; /* profclock interval - variance/2 */
! 81: static int timer3min; /* current, from above choices */
! 82: static int statprev; /* previous value in stat timer */
! 83:
! 84: static int month_days[12] = {
! 85: 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
! 86: };
! 87: u_char bbc_registers[13];
! 88: volatile u_int8_t *bbcaddr = NULL;
! 89:
! 90: void clockintr(struct clockframe *);
! 91: void statintr(struct clockframe *);
! 92:
! 93: void hp300_calibrate_delay(void);
! 94: struct bbc_tm *gmt_to_bbc(long);
! 95: int bbc_to_gmt(u_long *);
! 96: void read_bbc(void);
! 97: u_char read_bbc_reg(int);
! 98: void send_clock_cmd(volatile u_int8_t *, u_int8_t, u_int8_t *,
! 99: u_int8_t, u_int8_t *);
! 100: u_char write_bbc_reg(int, u_int);
! 101:
! 102: static int clock_ipl = IPL_CLOCK;
! 103: static int stat_ipl = IPL_STATCLOCK;
! 104: struct evcount clockcnt;
! 105: struct evcount statcnt;
! 106:
! 107: /*
! 108: * Machine-dependent clock routines.
! 109: *
! 110: * A note on the real-time clock:
! 111: * We actually load the clock with interval-1 instead of interval.
! 112: * This is because the counter decrements to zero after N+1 enabled clock
! 113: * periods where N is the value loaded into the counter.
! 114: *
! 115: * The frequencies of the HP300 clocks must be a multiple of four
! 116: * microseconds (since the clock counts in 4 us units).
! 117: */
! 118: #define COUNTS_PER_SEC (1000000 / CLK_RESOLUTION)
! 119:
! 120: /*
! 121: * Calibrate the delay constant, based on Chuck Cranor's
! 122: * mvme68k delay calibration algorithm.
! 123: */
! 124: void
! 125: hp300_calibrate_delay()
! 126: {
! 127: extern int delay_divisor;
! 128: volatile struct clkreg *clk;
! 129: volatile u_char csr;
! 130: int intvl;
! 131:
! 132: clkstd[0] = IIOV(0x5F8000); /* XXX yuck */
! 133: clk = (volatile struct clkreg *)clkstd[0];
! 134:
! 135: /*
! 136: * Calibrate delay() using the 4 usec counter.
! 137: * We adjust delay_divisor until we get the result we want.
! 138: * We assume we've been called at splhigh().
! 139: */
! 140: for (delay_divisor = 140; delay_divisor > 1; delay_divisor--) {
! 141: /* Reset clock chip */
! 142: clk->clk_cr2 = CLK_CR1;
! 143: clk->clk_cr1 = CLK_RESET;
! 144:
! 145: /*
! 146: * Prime the timer. We're looking for
! 147: * 10,000 usec (10ms). See interval comment
! 148: * above.
! 149: */
! 150: intvl = (10000 / CLK_RESOLUTION) - 1;
! 151: asm volatile(" movpw %0,%1@(5)" : : "d" (intvl), "a" (clk));
! 152:
! 153: /* Enable the timer */
! 154: clk->clk_cr2 = CLK_CR1;
! 155: clk->clk_cr1 = CLK_IENAB;
! 156:
! 157: delay(10000);
! 158:
! 159: /* Timer1 interrupt flag high? */
! 160: csr = clk->clk_sr;
! 161: if (csr & CLK_INT1) {
! 162: /*
! 163: * Got it. Clear interrupt and get outta here.
! 164: */
! 165: asm volatile(" movpw %0@(5),%1" : :
! 166: "a" (clk), "d" (intvl));
! 167: break;
! 168: }
! 169:
! 170: /*
! 171: * Nope. Poll for completion of the interval,
! 172: * clear interrupt, and try again.
! 173: */
! 174: do {
! 175: csr = clk->clk_sr;
! 176: } while ((csr & CLK_INT1) == 0);
! 177:
! 178: asm volatile(" movpw %0@(5),%1" : : "a" (clk), "d" (intvl));
! 179: }
! 180:
! 181: /*
! 182: * Make sure the clock interrupt is disabled. Otherwise,
! 183: * we can end up calling hardclock() before proc0 is set up,
! 184: * causing a bad pointer deref.
! 185: */
! 186: clk->clk_cr2 = CLK_CR1;
! 187: clk->clk_cr1 = CLK_RESET;
! 188:
! 189: /*
! 190: * Sanity check the delay_divisor value. If we totally lost,
! 191: * assume a 50MHz CPU;
! 192: */
! 193: if (delay_divisor == 0)
! 194: delay_divisor = 2048 / 50;
! 195:
! 196: /* Calculate CPU speed. */
! 197: cpuspeed = 2048 / delay_divisor;
! 198: }
! 199:
! 200: /*
! 201: * Set up the real-time and statistics clocks. Leave stathz 0 only if
! 202: * no alternative timer is available.
! 203: */
! 204: void
! 205: cpu_initclocks()
! 206: {
! 207: volatile struct clkreg *clk;
! 208: int intvl, statint, profint, minint;
! 209:
! 210: clkstd[0] = IIOV(0x5F8000); /* XXX grot */
! 211: clk = (volatile struct clkreg *)clkstd[0];
! 212:
! 213: if (COUNTS_PER_SEC % hz) {
! 214: printf("cannot get %d Hz clock; using 100 Hz\n", hz);
! 215: hz = 100;
! 216: }
! 217: /*
! 218: * Clock has several counters, so we can always use separate
! 219: * statclock.
! 220: */
! 221: if (stathz == 0) /* XXX should be set in param.c */
! 222: stathz = hz;
! 223: else if (COUNTS_PER_SEC % stathz) {
! 224: printf("cannot get %d Hz statclock; using 100 Hz\n", stathz);
! 225: stathz = 100;
! 226: }
! 227: if (profhz == 0) /* XXX should be set in param.c */
! 228: profhz = stathz * 5;
! 229: else if (profhz < stathz || COUNTS_PER_SEC % profhz) {
! 230: printf("cannot get %d Hz profclock; using %d Hz\n",
! 231: profhz, stathz);
! 232: profhz = stathz;
! 233: }
! 234:
! 235: intvl = COUNTS_PER_SEC / hz;
! 236: statint = COUNTS_PER_SEC / stathz;
! 237: profint = COUNTS_PER_SEC / profhz;
! 238: minint = statint / 2 + 100;
! 239: while (statvar > minint)
! 240: statvar >>= 1;
! 241:
! 242: tick = intvl * CLK_RESOLUTION;
! 243:
! 244: /* adjust interval counts, per note above */
! 245: intvl--;
! 246: statint--;
! 247: profint--;
! 248:
! 249: /* calculate base reload values */
! 250: clkint = intvl;
! 251: statmin = statint - (statvar >> 1);
! 252: profmin = profint - (statvar >> 1);
! 253: timer3min = statmin;
! 254: statprev = statint;
! 255:
! 256: evcount_attach(&statcnt, "stat", &stat_ipl, &evcount_intr);
! 257: evcount_attach(&clockcnt, "clock", &clock_ipl, &evcount_intr);
! 258:
! 259: /* finally, load hardware */
! 260: clk->clk_cr2 = CLK_CR1;
! 261: clk->clk_cr1 = CLK_RESET;
! 262: asm volatile(" movpw %0,%1@(5)" : : "d" (intvl), "a" (clk));
! 263: asm volatile(" movpw %0,%1@(9)" : : "d" (0), "a" (clk));
! 264: asm volatile(" movpw %0,%1@(13)" : : "d" (statint), "a" (clk));
! 265: clk->clk_cr2 = CLK_CR1;
! 266: clk->clk_cr1 = CLK_IENAB;
! 267: clk->clk_cr2 = CLK_CR3;
! 268: clk->clk_cr3 = CLK_IENAB;
! 269: }
! 270:
! 271: /*
! 272: * We assume newhz is either stathz or profhz, and that neither will
! 273: * change after being set up above. Could recalculate intervals here
! 274: * but that would be a drag.
! 275: */
! 276: void
! 277: setstatclockrate(newhz)
! 278: int newhz;
! 279: {
! 280:
! 281: if (newhz == stathz)
! 282: timer3min = statmin;
! 283: else
! 284: timer3min = profmin;
! 285: }
! 286:
! 287: /*
! 288: * Timer clock interrupt.
! 289: */
! 290: void
! 291: clockintr(fp)
! 292: struct clockframe *fp;
! 293: {
! 294: clockcnt.ec_count++;
! 295: hardclock(fp);
! 296: }
! 297:
! 298: /*
! 299: * Statistics/profiling clock interrupt. Compute a new interval.
! 300: * Interrupt has already been cleared.
! 301: */
! 302: void
! 303: statintr(fp)
! 304: struct clockframe *fp;
! 305: {
! 306: volatile struct clkreg *clk;
! 307: int newint, r, var;
! 308:
! 309: clk = (volatile struct clkreg *)clkstd[0];
! 310: var = statvar;
! 311: do {
! 312: r = random() & (var - 1);
! 313: } while (r == 0);
! 314: newint = timer3min + r;
! 315:
! 316: /*
! 317: * The timer was automatically reloaded with the previous latch
! 318: * value at the time of the interrupt. Compensate now for the
! 319: * amount of time that has run off since then (minimum of 2-12
! 320: * timer ticks depending on CPU type) plus one tick roundoff.
! 321: * This should keep us closer to the mean.
! 322: */
! 323: asm volatile(" clrl %0; movpw %1@(13),%0" : "=d" (r) : "a" (clk));
! 324: newint -= (statprev - r + 1);
! 325:
! 326: asm volatile(" movpw %0,%1@(13)" : : "d" (newint), "a" (clk));
! 327: statprev = newint;
! 328: statcnt.ec_count++;
! 329: statclock(fp);
! 330: }
! 331:
! 332: /*
! 333: * Return the best possible estimate of the current time.
! 334: */
! 335: void
! 336: microtime(tvp)
! 337: struct timeval *tvp;
! 338: {
! 339: volatile struct clkreg *clk;
! 340: int s, u, t, u2, s2;
! 341:
! 342: /*
! 343: * Read registers from slowest-changing to fastest-changing,
! 344: * then re-read out to slowest. If the values read before the
! 345: * innermost match those read after, the innermost value is
! 346: * consistent with the outer values. If not, it may not be and
! 347: * we must retry. Typically this loop runs only once; occasionally
! 348: * it runs twice, and only rarely does it run longer.
! 349: *
! 350: * (Using this loop avoids the need to block interrupts.)
! 351: */
! 352: clk = (volatile struct clkreg *)clkstd[0];
! 353: do {
! 354: s = time.tv_sec;
! 355: u = time.tv_usec;
! 356: asm volatile (" clrl %0; movpw %1@(5),%0"
! 357: : "=d" (t) : "a" (clk));
! 358: u2 = time.tv_usec;
! 359: s2 = time.tv_sec;
! 360: } while (u != u2 || s != s2);
! 361:
! 362: u += (clkint - t) * CLK_RESOLUTION;
! 363: if (u >= 1000000) { /* normalize */
! 364: s++;
! 365: u -= 1000000;
! 366: }
! 367: tvp->tv_sec = s;
! 368: tvp->tv_usec = u;
! 369: }
! 370:
! 371: /*
! 372: * Initialize the time of day register, based on the time base which is, e.g.
! 373: * from a filesystem.
! 374: */
! 375: void
! 376: inittodr(base)
! 377: time_t base;
! 378: {
! 379: u_long timbuf = base; /* assume no battery clock exists */
! 380: static int bbcinited = 0;
! 381:
! 382: /* XXX */
! 383: if (!bbcinited) {
! 384: if (machineid == HP_425 && mmuid == MMUID_425_E)
! 385: bbcaddr = NULL;
! 386: else {
! 387: if (badbaddr((caddr_t)(BBCADDR + HILP_STAT)))
! 388: printf("WARNING: no battery clock\n");
! 389: else
! 390: bbcaddr = BBCADDR;
! 391: }
! 392: bbcinited = 1;
! 393: }
! 394:
! 395: /*
! 396: * bbc_to_gmt converts and stores the gmt in timbuf.
! 397: * If an error is detected in bbc_to_gmt, or if the filesystem
! 398: * time is more recent than the gmt time in the clock,
! 399: * then use the filesystem time and warn the user.
! 400: */
! 401: if (bbcaddr != NULL) {
! 402: if (!bbc_to_gmt(&timbuf) || timbuf < base) {
! 403: printf("WARNING: bad date in battery clock\n");
! 404: timbuf = base;
! 405: }
! 406: }
! 407: if (base < 5*SECYR) {
! 408: printf("WARNING: preposterous time in file system");
! 409: timbuf = 6*SECYR + 186*SECDAY + SECDAY/2;
! 410: printf(" -- CHECK AND RESET THE DATE!\n");
! 411: }
! 412:
! 413: /* Battery clock does not store usec's, so forget about it. */
! 414: time.tv_sec = timbuf;
! 415: }
! 416:
! 417: /*
! 418: * Restore the time of day hardware after a time change.
! 419: */
! 420: void
! 421: resettodr()
! 422: {
! 423: int i;
! 424: struct bbc_tm *tmptr;
! 425:
! 426: if (bbcaddr == NULL)
! 427: return;
! 428:
! 429: tmptr = gmt_to_bbc(time.tv_sec);
! 430:
! 431: decimal_to_bbc(0, 1, tmptr->tm_sec);
! 432: decimal_to_bbc(2, 3, tmptr->tm_min);
! 433: decimal_to_bbc(4, 5, tmptr->tm_hour);
! 434: decimal_to_bbc(7, 8, tmptr->tm_mday);
! 435: decimal_to_bbc(9, 10, tmptr->tm_mon);
! 436: decimal_to_bbc(11, 12, tmptr->tm_year);
! 437:
! 438: /* Some bogusness to deal with seemingly broken hardware. Nonsense */
! 439: bbc_registers[5] = ((tmptr->tm_hour / 10) & 0x03) + 8;
! 440:
! 441: write_bbc_reg(15, 13); /* reset prescalar */
! 442:
! 443: for (i = 0; i <= NUM_BBC_REGS; i++)
! 444: if (bbc_registers[i] != write_bbc_reg(i, bbc_registers[i])) {
! 445: printf("Cannot set battery backed clock\n");
! 446: break;
! 447: }
! 448: }
! 449:
! 450: struct bbc_tm *
! 451: gmt_to_bbc(tim)
! 452: long tim;
! 453: {
! 454: int i;
! 455: long hms, day;
! 456: static struct bbc_tm rt;
! 457:
! 458: day = tim / SECDAY;
! 459: hms = tim % SECDAY;
! 460:
! 461: /* Hours, minutes, seconds are easy */
! 462: rt.tm_hour = hms / 3600;
! 463: rt.tm_min = (hms % 3600) / 60;
! 464: rt.tm_sec = (hms % 3600) % 60;
! 465:
! 466: /* Number of years in days */
! 467: for (i = STARTOFTIME - 1900; day >= days_in_year(i); i++)
! 468: day -= days_in_year(i);
! 469: rt.tm_year = i;
! 470:
! 471: /* Number of months in days left */
! 472: if (leapyear(rt.tm_year))
! 473: days_in_month(FEBRUARY) = 29;
! 474: for (i = 1; day >= days_in_month(i); i++)
! 475: day -= days_in_month(i);
! 476: days_in_month(FEBRUARY) = 28;
! 477: rt.tm_mon = i;
! 478:
! 479: /* Days are what is left over (+1) from all that. */
! 480: rt.tm_mday = day + 1;
! 481:
! 482: return(&rt);
! 483: }
! 484:
! 485: int
! 486: bbc_to_gmt(timbuf)
! 487: u_long *timbuf;
! 488: {
! 489: int i;
! 490: u_long tmp;
! 491: int year, month, day, hour, min, sec;
! 492:
! 493: read_bbc();
! 494:
! 495: sec = bbc_to_decimal(1, 0);
! 496: min = bbc_to_decimal(3, 2);
! 497:
! 498: /*
! 499: * Hours are different for some reason. Makes no sense really.
! 500: */
! 501: hour = ((bbc_registers[5] & 0x03) * 10) + bbc_registers[4];
! 502: day = bbc_to_decimal(8, 7);
! 503: month = bbc_to_decimal(10, 9);
! 504: year = bbc_to_decimal(12, 11) + 1900;
! 505:
! 506: range_test(hour, 0, 23);
! 507: range_test(day, 1, 31);
! 508: range_test(month, 1, 12);
! 509: range_test(year, STARTOFTIME, 2038); /* 2038 is the end of time. */
! 510:
! 511: tmp = 0;
! 512:
! 513: for (i = STARTOFTIME; i < year; i++)
! 514: tmp += days_in_year(i);
! 515: if (leapyear(year) && month > FEBRUARY)
! 516: tmp++;
! 517:
! 518: for (i = 1; i < month; i++)
! 519: tmp += days_in_month(i);
! 520:
! 521: tmp += (day - 1);
! 522: tmp = ((tmp * 24 + hour) * 60 + min) * 60 + sec;
! 523:
! 524: *timbuf = tmp;
! 525: return(1);
! 526: }
! 527:
! 528: void
! 529: read_bbc()
! 530: {
! 531: int i, read_okay;
! 532:
! 533: read_okay = 0;
! 534: while (!read_okay) {
! 535: read_okay = 1;
! 536: for (i = 0; i <= NUM_BBC_REGS; i++)
! 537: bbc_registers[i] = read_bbc_reg(i);
! 538: for (i = 0; i <= NUM_BBC_REGS; i++)
! 539: if (bbc_registers[i] != read_bbc_reg(i))
! 540: read_okay = 0;
! 541: }
! 542: }
! 543:
! 544: u_char
! 545: read_bbc_reg(reg)
! 546: int reg;
! 547: {
! 548: u_char data = reg;
! 549:
! 550: if (bbcaddr != NULL) {
! 551: send_clock_cmd(bbcaddr, BBC_SET_REG, &data, 1, NULL);
! 552: send_clock_cmd(bbcaddr, BBC_READ_REG, NULL, 0, &data);
! 553: }
! 554: return(data);
! 555: }
! 556:
! 557: u_char
! 558: write_bbc_reg(reg, data)
! 559: int reg;
! 560: u_int data;
! 561: {
! 562: u_char tmp;
! 563:
! 564: tmp = (u_char) ((data << HIL_SSHIFT) | reg);
! 565:
! 566: if (bbcaddr != NULL) {
! 567: send_clock_cmd(bbcaddr, BBC_SET_REG, &tmp, 1, NULL);
! 568: send_clock_cmd(bbcaddr, BBC_WRITE_REG, NULL, 0, NULL);
! 569: send_clock_cmd(bbcaddr, BBC_READ_REG, NULL, 0, &tmp);
! 570: }
! 571: return(tmp);
! 572: }
! 573:
! 574: /*
! 575: * Battery-backed clock command interface.
! 576: * The BBC appears to have an HIL-like command interface, but can not attach
! 577: * as a complete HIL device to an HIL controller driver.
! 578: * The following routine is a simplified command loop.
! 579: */
! 580: void
! 581: send_clock_cmd(volatile u_int8_t *address, u_int8_t cmd, u_int8_t *data,
! 582: u_int8_t dlen, u_int8_t *rdata)
! 583: {
! 584: u_int8_t status;
! 585: int s;
! 586:
! 587: s = splvm();
! 588:
! 589: while ((address[HILP_STAT] & HIL_BUSY) != 0)
! 590: DELAY(1);
! 591: address[HILP_CMD] = cmd;
! 592: while (dlen--) {
! 593: while ((address[HILP_STAT] & HIL_BUSY) != 0)
! 594: DELAY(1);
! 595: address[HILP_DATA] = *data++;
! 596: DELAY(1);
! 597: }
! 598: if (rdata != NULL) {
! 599: do {
! 600: while ((address[HILP_STAT] & HIL_DATA_RDY) == 0)
! 601: DELAY(1);
! 602: status = address[HILP_STAT];
! 603: *rdata = address[HILP_DATA];
! 604: DELAY(1);
! 605: } while (((status >> HIL_SSHIFT) & HIL_SMASK) != HIL_68K);
! 606: }
! 607:
! 608: splx(s);
! 609: }
CVSweb