Annotation of sys/arch/sparc/sparc/clock.c, Revision 1.1
1.1 ! nbrk 1: /* $OpenBSD: clock.c,v 1.24 2006/12/10 16:12:41 miod Exp $ */
! 2: /* $NetBSD: clock.c,v 1.52 1997/05/24 20:16:05 pk Exp $ */
! 3:
! 4: /*
! 5: * Copyright (c) 1992, 1993
! 6: * The Regents of the University of California. All rights reserved.
! 7: * Copyright (c) 1994 Gordon W. Ross
! 8: * Copyright (c) 1993 Adam Glass
! 9: * Copyright (c) 1996 Paul Kranenburg
! 10: * Copyright (c) 1996
! 11: * The President and Fellows of Harvard College. All rights reserved.
! 12: *
! 13: * This software was developed by the Computer Systems Engineering group
! 14: * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and
! 15: * contributed to Berkeley.
! 16: *
! 17: * All advertising materials mentioning features or use of this software
! 18: * must display the following acknowledgement:
! 19: * This product includes software developed by Harvard University.
! 20: * This product includes software developed by the University of
! 21: * California, Lawrence Berkeley Laboratory.
! 22: *
! 23: * Redistribution and use in source and binary forms, with or without
! 24: * modification, are permitted provided that the following conditions
! 25: * are met:
! 26: *
! 27: * 1. Redistributions of source code must retain the above copyright
! 28: * notice, this list of conditions and the following disclaimer.
! 29: * 2. Redistributions in binary form must reproduce the above copyright
! 30: * notice, this list of conditions and the following disclaimer in the
! 31: * documentation and/or other materials provided with the distribution.
! 32: * 3. All advertising materials mentioning features or use of this software
! 33: * must display the following acknowledgement:
! 34: * This product includes software developed by the University of
! 35: * California, Berkeley and its contributors.
! 36: * This product includes software developed by Paul Kranenburg.
! 37: * This product includes software developed by Harvard University.
! 38: * 4. Neither the name of the University nor the names of its contributors
! 39: * may be used to endorse or promote products derived from this software
! 40: * without specific prior written permission.
! 41: *
! 42: * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
! 43: * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
! 44: * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
! 45: * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
! 46: * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
! 47: * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
! 48: * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
! 49: * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
! 50: * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
! 51: * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
! 52: * SUCH DAMAGE.
! 53: *
! 54: * @(#)clock.c 8.1 (Berkeley) 6/11/93
! 55: *
! 56: */
! 57:
! 58: /*
! 59: * Clock driver. This is the id prom and eeprom driver as well
! 60: * and includes the timer register functions too.
! 61: */
! 62:
! 63: #include <sys/param.h>
! 64: #include <sys/kernel.h>
! 65: #include <sys/device.h>
! 66: #include <sys/proc.h>
! 67: #include <sys/resourcevar.h>
! 68: #include <sys/malloc.h>
! 69: #include <sys/systm.h>
! 70: #ifdef GPROF
! 71: #include <sys/gmon.h>
! 72: #endif
! 73:
! 74: #include <uvm/uvm_extern.h>
! 75:
! 76: #include <machine/autoconf.h>
! 77: #include <machine/eeprom.h>
! 78: #include <machine/cpu.h>
! 79:
! 80: #include <sparc/sparc/vaddrs.h>
! 81: #include <sparc/sparc/cpuvar.h>
! 82: #include <sparc/sparc/clockreg.h>
! 83: #include <sparc/sparc/intreg.h>
! 84: #include <sparc/sparc/timerreg.h>
! 85:
! 86: /*
! 87: * Statistics clock interval and variance, in usec. Variance must be a
! 88: * power of two. Since this gives us an even number, not an odd number,
! 89: * we discard one case and compensate. That is, a variance of 1024 would
! 90: * give us offsets in [0..1023]. Instead, we take offsets in [1..1023].
! 91: * This is symmetric about the point 512, or statvar/2, and thus averages
! 92: * to that value (assuming uniform random numbers).
! 93: */
! 94: /* XXX fix comment to match value */
! 95: int statvar = 8192;
! 96: int statmin; /* statclock interval - 1/2*variance */
! 97: int timerok;
! 98:
! 99: #include <dev/ic/intersil7170.h>
! 100:
! 101: extern struct idprom idprom;
! 102:
! 103: #define intersil_command(run, interrupt) \
! 104: (run | interrupt | INTERSIL_CMD_FREQ_32K | INTERSIL_CMD_24HR_MODE | \
! 105: INTERSIL_CMD_NORMAL_MODE)
! 106:
! 107: #define intersil_disable(CLOCK) \
! 108: CLOCK->clk_cmd_reg = \
! 109: intersil_command(INTERSIL_CMD_RUN, INTERSIL_CMD_IDISABLE)
! 110:
! 111: #define intersil_enable(CLOCK) \
! 112: CLOCK->clk_cmd_reg = \
! 113: intersil_command(INTERSIL_CMD_RUN, INTERSIL_CMD_IENABLE)
! 114:
! 115: #define intersil_clear(CLOCK) CLOCK->clk_intr_reg
! 116:
! 117: #if defined(SUN4)
! 118: /*
! 119: * OCLOCK support: 4/100's and 4/200's have the old clock.
! 120: */
! 121: static int oldclk = 0;
! 122: struct intersil7170 *i7;
! 123:
! 124: long oclk_get_secs(void);
! 125: void oclk_get_dt(struct intersil_dt *);
! 126: void oclk_set_dt(struct intersil_dt *);
! 127: void oclk_set_secs(long);
! 128: #endif
! 129:
! 130: int oclockmatch(struct device *, void *, void *);
! 131: void oclockattach(struct device *, struct device *, void *);
! 132:
! 133: struct cfattach oclock_ca = {
! 134: sizeof(struct device), oclockmatch, oclockattach
! 135: };
! 136:
! 137: struct cfdriver oclock_cd = {
! 138: NULL, "oclock", DV_DULL
! 139: };
! 140:
! 141: /*
! 142: * Sun 4 machines use the old-style (a'la Sun 3) EEPROM. On the
! 143: * 4/100's and 4/200's, this is at a separate obio space. On the
! 144: * 4/300's and 4/400's, however, it is the cl_nvram[] chunk of the
! 145: * Mostek chip. Therefore, eeprom_match will only return true on
! 146: * the 100/200 models, and the eeprom will be attached separately.
! 147: * On the 300/400 models, the eeprom will be dealt with when the clock is
! 148: * attached.
! 149: */
! 150: char *eeprom_va = NULL;
! 151: #if defined(SUN4)
! 152: static int eeprom_busy = 0;
! 153: static int eeprom_wanted = 0;
! 154: static int eeprom_nvram = 0; /* non-zero if eeprom is on Mostek */
! 155: int eeprom_take(void);
! 156: void eeprom_give(void);
! 157: int eeprom_update(char *, int, int);
! 158: #endif
! 159:
! 160: int eeprom_match(struct device *, void *, void *);
! 161: void eeprom_attach(struct device *, struct device *, void *);
! 162:
! 163: struct cfattach eeprom_ca = {
! 164: sizeof(struct device), eeprom_match, eeprom_attach
! 165: };
! 166:
! 167: struct cfdriver eeprom_cd = {
! 168: NULL, "eeprom", DV_DULL
! 169: };
! 170:
! 171: int clockmatch(struct device *, void *, void *);
! 172: void clockattach(struct device *, struct device *, void *);
! 173:
! 174: struct cfattach clock_ca = {
! 175: sizeof(struct device), clockmatch, clockattach
! 176: };
! 177:
! 178: struct cfdriver clock_cd = {
! 179: NULL, "clock", DV_DULL
! 180: };
! 181:
! 182: int timermatch(struct device *, void *, void *);
! 183: void timerattach(struct device *, struct device *, void *);
! 184:
! 185: struct timer_4m *timerreg_4m; /* XXX - need more cleanup */
! 186: struct counter_4m *counterreg_4m;
! 187: #define timerreg4 ((struct timerreg_4 *)TIMERREG_VA)
! 188:
! 189: struct cfattach timer_ca = {
! 190: sizeof(struct device), timermatch, timerattach
! 191: };
! 192:
! 193: struct cfdriver timer_cd = {
! 194: NULL, "timer", DV_DULL
! 195: };
! 196:
! 197: void clk_wenable(int);
! 198: void myetheraddr(u_char *);
! 199:
! 200: int timerblurb = 10; /* Guess a value; used before clock is attached */
! 201:
! 202: /*
! 203: * old clock match routine
! 204: */
! 205: int
! 206: oclockmatch(parent, vcf, aux)
! 207: struct device *parent;
! 208: void *vcf, *aux;
! 209: {
! 210: struct confargs *ca = aux;
! 211:
! 212: /* Only these sun4s have oclock */
! 213: if (!CPU_ISSUN4 ||
! 214: (cpuinfo.cpu_type != CPUTYP_4_100 &&
! 215: cpuinfo.cpu_type != CPUTYP_4_200))
! 216: return (0);
! 217:
! 218: /* Check configuration name */
! 219: if (strcmp(oclock_cd.cd_name, ca->ca_ra.ra_name) != 0)
! 220: return (0);
! 221:
! 222: /* Make sure there is something there */
! 223: if (probeget(ca->ca_ra.ra_vaddr, 1) == -1)
! 224: return (0);
! 225:
! 226: return (1);
! 227: }
! 228:
! 229: /* ARGSUSED */
! 230: void
! 231: oclockattach(parent, self, aux)
! 232: struct device *parent, *self;
! 233: void *aux;
! 234: {
! 235: #if defined(SUN4)
! 236: struct confargs *ca = aux;
! 237: struct romaux *ra = &ca->ca_ra;
! 238: struct idprom *idp;
! 239: int h;
! 240:
! 241: oldclk = 1; /* we've got an oldie! */
! 242:
! 243: i7 = (struct intersil7170 *) mapiodev(ra->ra_reg, 0, sizeof(*i7));
! 244:
! 245: idp = &idprom;
! 246: h = idp->id_machine << 24;
! 247: h |= idp->id_hostid[0] << 16;
! 248: h |= idp->id_hostid[1] << 8;
! 249: h |= idp->id_hostid[2];
! 250: hostid = h;
! 251:
! 252: /*
! 253: * calibrate delay()
! 254: */
! 255: ienab_bic(IE_L14 | IE_L10); /* disable all clock intrs */
! 256: for (timerblurb = 1; ; timerblurb++) {
! 257: volatile register char *ireg = &i7->clk_intr_reg;
! 258: int ival;
! 259: *ireg = INTERSIL_INTER_CSECONDS; /* 1/100 sec */
! 260: intersil_enable(i7); /* enable clock */
! 261: while ((*ireg & INTERSIL_INTER_PENDING) == 0)
! 262: /* sync with interrupt */;
! 263: while ((*ireg & INTERSIL_INTER_PENDING) == 0)
! 264: /* XXX: do it again, seems to need it */;
! 265: delay(10000); /* Probe 1/100 sec delay */
! 266: ival = *ireg; /* clear, save value */
! 267: intersil_disable(i7); /* disable clock */
! 268: if (ival & INTERSIL_INTER_PENDING) {
! 269: printf(" delay constant %d%s\n", timerblurb,
! 270: (timerblurb == 1) ? " [TOO SMALL?]" : "");
! 271: break;
! 272: }
! 273: if (timerblurb > 10) {
! 274: printf("\noclock: calibration failing; clamped at %d\n",
! 275: timerblurb);
! 276: break;
! 277: }
! 278: }
! 279: #endif /* SUN4 */
! 280: }
! 281:
! 282: /*
! 283: * Sun 4/100, 4/200 EEPROM match routine.
! 284: */
! 285: int
! 286: eeprom_match(parent, vcf, aux)
! 287: struct device *parent;
! 288: void *vcf, *aux;
! 289: {
! 290: struct cfdata *cf = vcf;
! 291: struct confargs *ca = aux;
! 292:
! 293: if (!CPU_ISSUN4)
! 294: return (0);
! 295:
! 296: if (cf->cf_unit != 0)
! 297: return (0);
! 298:
! 299: if (cpuinfo.cpu_type != CPUTYP_4_100 &&
! 300: cpuinfo.cpu_type != CPUTYP_4_200)
! 301: return (0);
! 302:
! 303: if (strcmp(eeprom_cd.cd_name, ca->ca_ra.ra_name) != 0)
! 304: return (0);
! 305:
! 306: /*
! 307: * Make sure there's something there...
! 308: * This is especially important if we want to
! 309: * use the same kernel on a 4/100 as a 4/200.
! 310: */
! 311: if (probeget(ca->ca_ra.ra_vaddr, 1) == -1)
! 312: return (0);
! 313:
! 314: /* Passed all tests */
! 315: return (1);
! 316: }
! 317:
! 318: void
! 319: eeprom_attach(parent, self, aux)
! 320: struct device *parent, *self;
! 321: void *aux;
! 322: {
! 323: #if defined(SUN4)
! 324: struct confargs *ca = aux;
! 325: struct romaux *ra = &ca->ca_ra;
! 326:
! 327: printf("\n");
! 328:
! 329: eeprom_va = (char *)mapiodev(ra->ra_reg, 0, EEPROM_SIZE);
! 330:
! 331: eeprom_nvram = 0;
! 332: #endif /* SUN4 */
! 333: }
! 334:
! 335: /*
! 336: * The OPENPROM calls the clock the "eeprom", so we have to have our
! 337: * own special match function to call it the "clock".
! 338: */
! 339: int
! 340: clockmatch(parent, vcf, aux)
! 341: struct device *parent;
! 342: void *vcf, *aux;
! 343: {
! 344: struct confargs *ca = aux;
! 345:
! 346: if (CPU_ISSUN4) {
! 347: /* Only these sun4s have "clock" (others have "oclock") */
! 348: if (cpuinfo.cpu_type != CPUTYP_4_300 &&
! 349: cpuinfo.cpu_type != CPUTYP_4_400)
! 350: return (0);
! 351:
! 352: if (strcmp(clock_cd.cd_name, ca->ca_ra.ra_name) != 0)
! 353: return (0);
! 354:
! 355: /* Make sure there is something there */
! 356: if (probeget(ca->ca_ra.ra_vaddr, 1) == -1)
! 357: return (0);
! 358:
! 359: return (1);
! 360: }
! 361:
! 362: return (strcmp("eeprom", ca->ca_ra.ra_name) == 0);
! 363: }
! 364:
! 365: /* ARGSUSED */
! 366: void
! 367: clockattach(parent, self, aux)
! 368: struct device *parent, *self;
! 369: void *aux;
! 370: {
! 371: int h;
! 372: struct clockreg *cl;
! 373: struct idprom *idp;
! 374: struct confargs *ca = aux;
! 375: struct romaux *ra = &ca->ca_ra;
! 376: char *prop = NULL;
! 377:
! 378: if (CPU_ISSUN4)
! 379: prop = "mk48t02";
! 380:
! 381: else if (CPU_ISSUN4COR4M)
! 382: prop = getpropstring(ra->ra_node, "model");
! 383:
! 384: printf(": %s (eeprom)\n", prop);
! 385:
! 386: /*
! 387: * We ignore any existing virtual address as we need to map
! 388: * this read-only and make it read-write only temporarily,
! 389: * whenever we read or write the clock chip. The clock also
! 390: * contains the ID ``PROM'', and I have already had the pleasure
! 391: * of reloading the cpu type, Ethernet address, etc, by hand from
! 392: * the console FORTH interpreter. I intend not to enjoy it again.
! 393: */
! 394: if (strcmp(prop, "mk48t08") == 0) {
! 395: /*
! 396: * the MK48T08 is 8K
! 397: */
! 398: cl = (struct clockreg *)mapiodev(ra->ra_reg, 0, 8192);
! 399: pmap_changeprot(pmap_kernel(), (vaddr_t)cl, VM_PROT_READ, 1);
! 400: pmap_changeprot(pmap_kernel(), (vaddr_t)cl + 4096,
! 401: VM_PROT_READ, 1);
! 402: cl = (struct clockreg *)((int)cl + CLK_MK48T08_OFF);
! 403: } else {
! 404: /*
! 405: * the MK48T02 is 2K
! 406: */
! 407: cl = (struct clockreg *)mapiodev(ra->ra_reg, 0,
! 408: sizeof *clockreg);
! 409: pmap_changeprot(pmap_kernel(), (vaddr_t)cl, VM_PROT_READ, 1);
! 410: }
! 411: idp = &cl->cl_idprom;
! 412:
! 413: #if defined(SUN4)
! 414: if (CPU_ISSUN4) {
! 415: idp = &idprom;
! 416:
! 417: if (cpuinfo.cpu_type == CPUTYP_4_300 ||
! 418: cpuinfo.cpu_type == CPUTYP_4_400) {
! 419: eeprom_va = (char *)cl->cl_nvram;
! 420: eeprom_nvram = 1;
! 421: }
! 422: }
! 423: #endif
! 424:
! 425: h = idp->id_machine << 24;
! 426: h |= idp->id_hostid[0] << 16;
! 427: h |= idp->id_hostid[1] << 8;
! 428: h |= idp->id_hostid[2];
! 429: hostid = h;
! 430: clockreg = cl;
! 431: }
! 432:
! 433: /*
! 434: * The OPENPROM calls the timer the "counter-timer".
! 435: */
! 436: int
! 437: timermatch(parent, vcf, aux)
! 438: struct device *parent;
! 439: void *vcf, *aux;
! 440: {
! 441: struct confargs *ca = aux;
! 442:
! 443: if (CPU_ISSUN4) {
! 444: if (cpuinfo.cpu_type != CPUTYP_4_300 &&
! 445: cpuinfo.cpu_type != CPUTYP_4_400)
! 446: return (0);
! 447:
! 448: if (strcmp("timer", ca->ca_ra.ra_name) != 0)
! 449: return (0);
! 450:
! 451: /* Make sure there is something there */
! 452: if (probeget(ca->ca_ra.ra_vaddr, 4) == -1)
! 453: return (0);
! 454:
! 455: return (1);
! 456: }
! 457:
! 458: if (CPU_ISSUN4C) {
! 459: return (strcmp("counter-timer", ca->ca_ra.ra_name) == 0);
! 460: }
! 461:
! 462: if (CPU_ISSUN4M) {
! 463: return (strcmp("counter", ca->ca_ra.ra_name) == 0);
! 464: }
! 465:
! 466: return (0);
! 467: }
! 468:
! 469: /* ARGSUSED */
! 470: void
! 471: timerattach(parent, self, aux)
! 472: struct device *parent, *self;
! 473: void *aux;
! 474: {
! 475: struct confargs *ca = aux;
! 476: struct romaux *ra = &ca->ca_ra;
! 477: volatile int *cnt = NULL, *lim = NULL;
! 478: /* XXX: must init to NULL to avoid stupid gcc -Wall warning */
! 479:
! 480: if (CPU_ISSUN4M) {
! 481: (void)mapdev(&ra->ra_reg[ra->ra_nreg-1], TIMERREG_VA, 0,
! 482: sizeof(struct timer_4m));
! 483: (void)mapdev(&ra->ra_reg[0], COUNTERREG_VA, 0,
! 484: sizeof(struct counter_4m));
! 485: timerreg_4m = (struct timer_4m *)TIMERREG_VA;
! 486: counterreg_4m = (struct counter_4m *)COUNTERREG_VA;
! 487:
! 488: /* Put processor counter in "timer" mode */
! 489: timerreg_4m->t_cfg = 0;
! 490:
! 491: cnt = &counterreg_4m->t_counter;
! 492: lim = &counterreg_4m->t_limit;
! 493: }
! 494:
! 495: if (CPU_ISSUN4OR4C) {
! 496: /*
! 497: * This time, we ignore any existing virtual address because
! 498: * we have a fixed virtual address for the timer, to make
! 499: * microtime() faster (in SUN4/SUN4C kernel only).
! 500: */
! 501: (void)mapdev(ra->ra_reg, TIMERREG_VA, 0,
! 502: sizeof(struct timerreg_4));
! 503:
! 504: cnt = &timerreg4->t_c14.t_counter;
! 505: lim = &timerreg4->t_c14.t_limit;
! 506: }
! 507:
! 508: timerok = 1;
! 509:
! 510: /*
! 511: * Calibrate delay() by tweaking the magic constant
! 512: * until a delay(100) actually reads (at least) 100 us on the clock.
! 513: * Note: sun4m clocks tick with 500ns periods.
! 514: */
! 515:
! 516: for (timerblurb = 1; ; timerblurb++) {
! 517: volatile int discard;
! 518: int t0, t1;
! 519:
! 520: /* Reset counter register by writing some large limit value */
! 521: discard = *lim;
! 522: *lim = tmr_ustolim(TMR_MASK-1);
! 523:
! 524: t0 = *cnt;
! 525: delay(100);
! 526: t1 = *cnt;
! 527:
! 528: if (t1 & TMR_LIMIT)
! 529: panic("delay calibration");
! 530:
! 531: t0 = (t0 >> TMR_SHIFT) & TMR_MASK;
! 532: t1 = (t1 >> TMR_SHIFT) & TMR_MASK;
! 533:
! 534: if (t1 >= t0 + 100)
! 535: break;
! 536:
! 537: }
! 538:
! 539: printf(" delay constant %d\n", timerblurb);
! 540:
! 541: /* should link interrupt handlers here, rather than compiled-in? */
! 542: }
! 543:
! 544: /*
! 545: * Write en/dis-able clock registers. We coordinate so that several
! 546: * writers can run simultaneously.
! 547: */
! 548: void
! 549: clk_wenable(onoff)
! 550: int onoff;
! 551: {
! 552: int s;
! 553: vm_prot_t prot;/* nonzero => change prot */
! 554: static int writers;
! 555:
! 556: s = splhigh();
! 557: if (onoff)
! 558: prot = writers++ == 0 ? VM_PROT_READ|VM_PROT_WRITE : 0;
! 559: else
! 560: prot = --writers == 0 ? VM_PROT_READ : 0;
! 561: splx(s);
! 562: if (prot)
! 563: pmap_changeprot(pmap_kernel(), (vaddr_t)clockreg & ~(NBPG-1),
! 564: prot, 1);
! 565: }
! 566:
! 567: /*
! 568: * XXX this belongs elsewhere
! 569: */
! 570: void
! 571: myetheraddr(cp)
! 572: u_char *cp;
! 573: {
! 574: struct clockreg *cl = clockreg;
! 575: struct idprom *idp = &cl->cl_idprom;
! 576:
! 577: #if defined(SUN4)
! 578: if (CPU_ISSUN4)
! 579: idp = &idprom;
! 580: #endif
! 581:
! 582: cp[0] = idp->id_ether[0];
! 583: cp[1] = idp->id_ether[1];
! 584: cp[2] = idp->id_ether[2];
! 585: cp[3] = idp->id_ether[3];
! 586: cp[4] = idp->id_ether[4];
! 587: cp[5] = idp->id_ether[5];
! 588: }
! 589:
! 590: /*
! 591: * Set up the real-time and statistics clocks. Leave stathz 0 only if
! 592: * no alternative timer is available.
! 593: *
! 594: * The frequencies of these clocks must be an even number of microseconds.
! 595: */
! 596: void
! 597: cpu_initclocks()
! 598: {
! 599: int statint, minint;
! 600:
! 601: #if defined(SUN4)
! 602: if (oldclk) {
! 603: int dummy;
! 604:
! 605: if (hz != 100) {
! 606: printf("oclock0: cannot get %d Hz clock; using 100 Hz\n", hz);
! 607: }
! 608:
! 609: profhz = hz = 100;
! 610: tick = 1000000 / hz;
! 611:
! 612: i7->clk_intr_reg = INTERSIL_INTER_CSECONDS; /* 1/100 sec */
! 613:
! 614: ienab_bic(IE_L14 | IE_L10); /* disable all clock intrs */
! 615: intersil_disable(i7); /* disable clock */
! 616: dummy = intersil_clear(i7); /* clear interrupts */
! 617: ienab_bis(IE_L10); /* enable l10 interrupt */
! 618: intersil_enable(i7); /* enable clock */
! 619:
! 620: return;
! 621: }
! 622: #endif /* SUN4 */
! 623:
! 624: if (1000000 % hz) {
! 625: printf("clock0: cannot get %d Hz clock; using 100 Hz\n", hz);
! 626: hz = 100;
! 627: tick = 1000000 / hz;
! 628: }
! 629: if (stathz == 0)
! 630: stathz = hz;
! 631: if (1000000 % stathz) {
! 632: printf("clock0: cannot get %d Hz statclock; using 100 Hz\n", stathz);
! 633: stathz = 100;
! 634: }
! 635: profhz = stathz; /* always */
! 636:
! 637: statint = 1000000 / stathz;
! 638: minint = statint / 2 + 100;
! 639: while (statvar > minint)
! 640: statvar >>= 1;
! 641:
! 642: if (CPU_ISSUN4M) {
! 643: timerreg_4m->t_limit = tmr_ustolim4m(tick);
! 644: counterreg_4m->t_limit = tmr_ustolim4m(statint);
! 645: }
! 646:
! 647: if (CPU_ISSUN4OR4C) {
! 648: timerreg4->t_c10.t_limit = tmr_ustolim(tick);
! 649: timerreg4->t_c14.t_limit = tmr_ustolim(statint);
! 650: }
! 651:
! 652: statmin = statint - (statvar >> 1);
! 653:
! 654: #if defined(SUN4M)
! 655: if (CPU_ISSUN4M)
! 656: ienab_bic(SINTR_T);
! 657: #endif
! 658:
! 659: if (CPU_ISSUN4OR4C)
! 660: ienab_bis(IE_L14 | IE_L10);
! 661:
! 662: }
! 663:
! 664: /*
! 665: * Dummy setstatclockrate(), since we know profhz==hz.
! 666: */
! 667: /* ARGSUSED */
! 668: void
! 669: setstatclockrate(newhz)
! 670: int newhz;
! 671: {
! 672: /* nothing */
! 673: }
! 674:
! 675: /*
! 676: * Level 10 (clock) interrupts. If we are using the FORTH PROM for
! 677: * console input, we need to check for that here as well, and generate
! 678: * a software interrupt to read it.
! 679: */
! 680: int
! 681: clockintr(cap)
! 682: void *cap;
! 683: {
! 684: volatile int discard;
! 685: int s;
! 686:
! 687: /*
! 688: * Protect the clearing of the clock interrupt. If we don't
! 689: * do this, and we're interrupted (by the zs, for example),
! 690: * the clock stops!
! 691: * XXX WHY DOES THIS HAPPEN?
! 692: */
! 693: s = splhigh();
! 694:
! 695: #if defined(SUN4)
! 696: if (oldclk) {
! 697: discard = intersil_clear(i7);
! 698: ienab_bic(IE_L10); /* clear interrupt */
! 699: ienab_bis(IE_L10); /* enable interrupt */
! 700: goto forward;
! 701: }
! 702: #endif
! 703: #if defined(SUN4M)
! 704: /* read the limit register to clear the interrupt */
! 705: if (CPU_ISSUN4M) {
! 706: discard = timerreg_4m->t_limit;
! 707: }
! 708: #endif
! 709: #if defined(SUN4) || defined(SUN4C)
! 710: if (CPU_ISSUN4OR4C) {
! 711: discard = timerreg4->t_c10.t_limit;
! 712: }
! 713: #endif
! 714: #if defined(SUN4)
! 715: forward:
! 716: #endif
! 717: splx(s);
! 718:
! 719: hardclock((struct clockframe *)cap);
! 720:
! 721: return (1);
! 722: }
! 723:
! 724: /*
! 725: * Level 14 (stat clock) interrupt handler.
! 726: */
! 727: int
! 728: statintr(cap)
! 729: void *cap;
! 730: {
! 731: volatile int discard;
! 732: u_long newint, r, var;
! 733:
! 734: #if defined(SUN4)
! 735: if (oldclk) {
! 736: panic("oldclk statintr");
! 737: return (1);
! 738: }
! 739: #endif
! 740:
! 741: /* read the limit register to clear the interrupt */
! 742: if (CPU_ISSUN4M) {
! 743: discard = counterreg_4m->t_limit;
! 744: if (timerok == 0) {
! 745: /* Stop the clock */
! 746: counterreg_4m->t_limit = 0;
! 747: counterreg_4m->t_ss = 0;
! 748: timerreg_4m->t_cfg = TMR_CFG_USER;
! 749: return 1;
! 750: }
! 751: }
! 752:
! 753: if (CPU_ISSUN4OR4C) {
! 754: discard = timerreg4->t_c14.t_limit;
! 755: }
! 756: statclock((struct clockframe *)cap);
! 757:
! 758: /*
! 759: * Compute new randomized interval. The intervals are uniformly
! 760: * distributed on [statint - statvar / 2, statint + statvar / 2],
! 761: * and therefore have mean statint, giving a stathz frequency clock.
! 762: */
! 763: var = statvar;
! 764: do {
! 765: r = random() & (var - 1);
! 766: } while (r == 0);
! 767: newint = statmin + r;
! 768:
! 769: if (CPU_ISSUN4M) {
! 770: counterreg_4m->t_limit = tmr_ustolim4m(newint);
! 771: }
! 772:
! 773: if (CPU_ISSUN4OR4C) {
! 774: timerreg4->t_c14.t_limit = tmr_ustolim(newint);
! 775: }
! 776: return (1);
! 777: }
! 778:
! 779: /*
! 780: * Set up the system's time, given a `reasonable' time value.
! 781: */
! 782: void
! 783: inittodr(base)
! 784: time_t base;
! 785: {
! 786: struct clockreg *cl = clockreg;
! 787: struct clock_ymdhms dt;
! 788: int badbase = 0, waszero = base == 0;
! 789: char *bad = NULL;
! 790:
! 791: if (base < 5 * SECYR) {
! 792: /*
! 793: * If base is 0, assume filesystem time is just unknown
! 794: * in stead of preposterous. Don't bark.
! 795: */
! 796: if (base != 0)
! 797: printf("WARNING: preposterous time in file system\n");
! 798: /* not going to use it anyway, if the chip is readable */
! 799: base = 21*SECYR + 186*SECDAY + SECDAY/2;
! 800: badbase = 1;
! 801: }
! 802: #if defined(SUN4)
! 803: if (oldclk) {
! 804: time.tv_sec = oclk_get_secs();
! 805: goto forward;
! 806: }
! 807: #endif
! 808: clk_wenable(1);
! 809: cl->cl_csr |= CLK_READ; /* enable read (stop time) */
! 810: dt.dt_sec = FROMBCD(cl->cl_sec);
! 811: dt.dt_min = FROMBCD(cl->cl_min);
! 812: dt.dt_hour = FROMBCD(cl->cl_hour);
! 813: dt.dt_day = FROMBCD(cl->cl_mday);
! 814: dt.dt_mon = FROMBCD(cl->cl_month);
! 815: dt.dt_year = FROMBCD(cl->cl_year) + CLOCK_BASE_YEAR;
! 816: cl->cl_csr &= ~CLK_READ; /* time wears on */
! 817: clk_wenable(0);
! 818: time.tv_sec = clock_ymdhms_to_secs(&dt);
! 819:
! 820: #if defined(SUN4)
! 821: forward:
! 822: #endif
! 823: if (time.tv_sec == 0) {
! 824: /*
! 825: * Believe the time in the file system for lack of
! 826: * anything better, resetting the clock.
! 827: */
! 828: bad = "WARNING: bad date in battery clock";
! 829: time.tv_sec = base;
! 830: if (!badbase)
! 831: resettodr();
! 832: } else {
! 833: int deltat = time.tv_sec - base;
! 834:
! 835: if (deltat < 0)
! 836: deltat = -deltat;
! 837: if (waszero || deltat < 2 * SECDAY)
! 838: return;
! 839:
! 840: #ifndef SMALL_KERNEL
! 841: printf("WARNING: clock %s %d days",
! 842: time.tv_sec < base ? "lost" : "gained", deltat / SECDAY);
! 843: bad = "";
! 844: #endif
! 845: }
! 846: if (bad) {
! 847: printf("%s", bad);
! 848: printf(" -- CHECK AND RESET THE DATE!\n");
! 849: }
! 850: }
! 851:
! 852: /*
! 853: * Reset the clock based on the current time.
! 854: * Used when the current clock is preposterous, when the time is changed,
! 855: * and when rebooting. Do nothing if the time is not yet known, e.g.,
! 856: * when crashing during autoconfig.
! 857: */
! 858: void
! 859: resettodr()
! 860: {
! 861: struct clockreg *cl;
! 862: struct clock_ymdhms dt;
! 863:
! 864: #if defined(SUN4)
! 865: if (oldclk) {
! 866: if (!time.tv_sec || i7 == NULL)
! 867: return;
! 868: oclk_set_secs(time.tv_sec);
! 869: return;
! 870: }
! 871: #endif
! 872:
! 873: if (!time.tv_sec || (cl = clockreg) == NULL)
! 874: return;
! 875:
! 876: clock_secs_to_ymdhms(time.tv_sec, &dt);
! 877:
! 878: clk_wenable(1);
! 879: cl->cl_csr |= CLK_WRITE; /* enable write */
! 880: cl->cl_sec = TOBCD(dt.dt_sec);
! 881: cl->cl_min = TOBCD(dt.dt_min);
! 882: cl->cl_hour = TOBCD(dt.dt_hour);
! 883: cl->cl_wday = TOBCD(dt.dt_wday);
! 884: cl->cl_mday = TOBCD(dt.dt_day);
! 885: cl->cl_month = TOBCD(dt.dt_mon);
! 886: cl->cl_year = TOBCD(dt.dt_year - CLOCK_BASE_YEAR);
! 887: cl->cl_csr &= ~CLK_WRITE; /* load them up */
! 888: clk_wenable(0);
! 889: }
! 890:
! 891: #if defined(SUN4)
! 892: /*
! 893: * Now routines to get and set clock as POSIX time.
! 894: */
! 895: long
! 896: oclk_get_secs()
! 897: {
! 898: struct intersil_dt idt;
! 899: struct clock_ymdhms dt;
! 900:
! 901: oclk_get_dt(&idt);
! 902: dt.dt_sec = idt.dt_sec;
! 903: dt.dt_min = idt.dt_min;
! 904: dt.dt_hour = idt.dt_hour;
! 905: dt.dt_day = idt.dt_day;
! 906: dt.dt_mon = idt.dt_month;
! 907: dt.dt_year = idt.dt_year + CLOCK_BASE_YEAR;
! 908: return clock_ymdhms_to_secs(&dt);
! 909: }
! 910:
! 911: void
! 912: oclk_set_secs(secs)
! 913: long secs;
! 914: {
! 915: struct intersil_dt idt;
! 916: struct clock_ymdhms dt;
! 917:
! 918: clock_secs_to_ymdhms(secs, &dt);
! 919:
! 920: idt.dt_hour = dt.dt_hour;
! 921: idt.dt_min = dt.dt_min;
! 922: idt.dt_sec = dt.dt_sec;
! 923: idt.dt_month = dt.dt_mon;
! 924: idt.dt_day = dt.dt_day;
! 925: idt.dt_year = dt.dt_year - CLOCK_BASE_YEAR;
! 926: idt.dt_dow = dt.dt_wday;
! 927: oclk_set_dt(&idt);
! 928: }
! 929:
! 930: /*
! 931: * Routine to copy state into and out of the clock.
! 932: * The clock registers have to be read or written
! 933: * in sequential order (or so it appears). -gwr
! 934: */
! 935: void
! 936: oclk_get_dt(dt)
! 937: struct intersil_dt *dt;
! 938: {
! 939: int s;
! 940: register volatile char *src, *dst;
! 941:
! 942: src = (char *) &i7->counters;
! 943:
! 944: s = splhigh();
! 945: i7->clk_cmd_reg =
! 946: intersil_command(INTERSIL_CMD_STOP, INTERSIL_CMD_IENABLE);
! 947:
! 948: dst = (char *) dt;
! 949: dt++; /* end marker */
! 950: do {
! 951: *dst++ = *src++;
! 952: } while (dst < (char *)dt);
! 953:
! 954: i7->clk_cmd_reg =
! 955: intersil_command(INTERSIL_CMD_RUN, INTERSIL_CMD_IENABLE);
! 956: splx(s);
! 957: }
! 958:
! 959: void
! 960: oclk_set_dt(dt)
! 961: struct intersil_dt *dt;
! 962: {
! 963: int s;
! 964: register volatile char *src, *dst;
! 965:
! 966: dst = (char *) &i7->counters;
! 967:
! 968: s = splhigh();
! 969: i7->clk_cmd_reg =
! 970: intersil_command(INTERSIL_CMD_STOP, INTERSIL_CMD_IENABLE);
! 971:
! 972: src = (char *) dt;
! 973: dt++; /* end marker */
! 974: do {
! 975: *dst++ = *src++;
! 976: } while (src < (char *)dt);
! 977:
! 978: i7->clk_cmd_reg =
! 979: intersil_command(INTERSIL_CMD_RUN, INTERSIL_CMD_IENABLE);
! 980: splx(s);
! 981: }
! 982: #endif /* SUN4 */
! 983:
! 984: #if defined(SUN4)
! 985: /*
! 986: * Return the best possible estimate of the time in the timeval
! 987: * to which tvp points. We do this by returning the current time
! 988: * plus the amount of time since the last clock interrupt.
! 989: *
! 990: * Check that this time is no less than any previously-reported time,
! 991: * which could happen around the time of a clock adjustment. Just for
! 992: * fun, we guarantee that the time will be greater than the value
! 993: * obtained by a previous call.
! 994: */
! 995: void
! 996: microtime(tvp)
! 997: struct timeval *tvp;
! 998: {
! 999: int s;
! 1000: static struct timeval lasttime;
! 1001: static struct timeval oneusec = {0, 1};
! 1002:
! 1003: if (!oldclk) {
! 1004: lo_microtime(tvp);
! 1005: return;
! 1006: }
! 1007:
! 1008: s = splhigh();
! 1009: *tvp = time;
! 1010: splx(s);
! 1011:
! 1012: if (timercmp(tvp, &lasttime, <=))
! 1013: timeradd(&lasttime, &oneusec, tvp);
! 1014:
! 1015: lasttime = *tvp;
! 1016: }
! 1017: #endif /* SUN4 */
! 1018:
! 1019: /*
! 1020: * XXX: these may actually belong somewhere else, but since the
! 1021: * EEPROM is so closely tied to the clock on some models, perhaps
! 1022: * it needs to stay here...
! 1023: */
! 1024: int
! 1025: eeprom_uio(uio)
! 1026: struct uio *uio;
! 1027: {
! 1028: #if defined(SUN4)
! 1029: int error;
! 1030: int off; /* NOT off_t */
! 1031: u_int cnt, bcnt;
! 1032: caddr_t buf = NULL;
! 1033:
! 1034: if (!CPU_ISSUN4)
! 1035: return (ENODEV);
! 1036:
! 1037: off = uio->uio_offset;
! 1038: if (off > EEPROM_SIZE)
! 1039: return (EFAULT);
! 1040:
! 1041: cnt = uio->uio_resid;
! 1042: if (cnt > (EEPROM_SIZE - off))
! 1043: cnt = (EEPROM_SIZE - off);
! 1044:
! 1045: if ((error = eeprom_take()) != 0)
! 1046: return (error);
! 1047:
! 1048: if (eeprom_va == NULL) {
! 1049: error = ENXIO;
! 1050: goto out;
! 1051: }
! 1052:
! 1053: /*
! 1054: * The EEPROM can only be accessed one byte at a time, yet
! 1055: * uiomove() will attempt long-word access. To circumvent
! 1056: * this, we byte-by-byte copy the eeprom contents into a
! 1057: * temporary buffer.
! 1058: */
! 1059: buf = malloc(EEPROM_SIZE, M_DEVBUF, M_WAITOK);
! 1060:
! 1061: if (uio->uio_rw == UIO_READ)
! 1062: for (bcnt = 0; bcnt < EEPROM_SIZE; ++bcnt)
! 1063: *(char *)(buf + bcnt) = *(char *)(eeprom_va + bcnt);
! 1064:
! 1065: if ((error = uiomove(buf + off, (int)cnt, uio)) != 0)
! 1066: goto out;
! 1067:
! 1068: if (uio->uio_rw != UIO_READ)
! 1069: error = eeprom_update(buf, off, cnt);
! 1070:
! 1071: out:
! 1072: if (buf)
! 1073: free(buf, M_DEVBUF);
! 1074: eeprom_give();
! 1075: return (error);
! 1076: #else /* ! SUN4 */
! 1077: return (ENODEV);
! 1078: #endif /* SUN4 */
! 1079: }
! 1080:
! 1081: #if defined(SUN4)
! 1082: /*
! 1083: * Update the EEPROM from the passed buf.
! 1084: */
! 1085: int
! 1086: eeprom_update(buf, off, cnt)
! 1087: char *buf;
! 1088: int off, cnt;
! 1089: {
! 1090: int error = 0;
! 1091: volatile char *ep;
! 1092: char *bp;
! 1093:
! 1094: if (eeprom_va == NULL)
! 1095: return (ENXIO);
! 1096:
! 1097: ep = eeprom_va + off;
! 1098: bp = buf + off;
! 1099:
! 1100: if (eeprom_nvram)
! 1101: clk_wenable(1);
! 1102:
! 1103: while (cnt > 0) {
! 1104: /*
! 1105: * DO NOT WRITE IT UNLESS WE HAVE TO because the
! 1106: * EEPROM has a limited number of write cycles.
! 1107: * After some number of writes it just fails!
! 1108: */
! 1109: if (*ep != *bp) {
! 1110: *ep = *bp;
! 1111: /*
! 1112: * We have written the EEPROM, so now we must
! 1113: * sleep for at least 10 milliseconds while
! 1114: * holding the lock to prevent all access to
! 1115: * the EEPROM while it recovers.
! 1116: */
! 1117: (void)tsleep(eeprom_va, PZERO - 1, "eeprom", hz/50);
! 1118: }
! 1119: /* Make sure the write worked. */
! 1120: if (*ep != *bp) {
! 1121: error = EIO;
! 1122: goto out;
! 1123: }
! 1124: ++ep;
! 1125: ++bp;
! 1126: --cnt;
! 1127: }
! 1128: out:
! 1129: if (eeprom_nvram)
! 1130: clk_wenable(0);
! 1131:
! 1132: return (error);
! 1133: }
! 1134:
! 1135: /* Take a lock on the eeprom. */
! 1136: int
! 1137: eeprom_take()
! 1138: {
! 1139: int error = 0;
! 1140:
! 1141: while (eeprom_busy) {
! 1142: eeprom_wanted = 1;
! 1143: error = tsleep(&eeprom_busy, PZERO | PCATCH, "eeprom", 0);
! 1144: eeprom_wanted = 0;
! 1145: if (error) /* interrupted */
! 1146: goto out;
! 1147: }
! 1148: eeprom_busy = 1;
! 1149: out:
! 1150: return (error);
! 1151: }
! 1152:
! 1153: /* Give a lock on the eeprom away. */
! 1154: void
! 1155: eeprom_give()
! 1156: {
! 1157:
! 1158: eeprom_busy = 0;
! 1159: if (eeprom_wanted) {
! 1160: eeprom_wanted = 0;
! 1161: wakeup(&eeprom_busy);
! 1162: }
! 1163: }
! 1164: #endif /* SUN4 */
CVSweb