diff --git a/sys/arch/sparc/sparc/clock.c b/sys/arch/sparc/sparc/clock.c index 486061dfd6f2..188c2036ee96 100644 --- a/sys/arch/sparc/sparc/clock.c +++ b/sys/arch/sparc/sparc/clock.c @@ -1,4 +1,4 @@ -/* $NetBSD: clock.c,v 1.82 2001/12/04 00:05:06 darrenr Exp $ */ +/* $NetBSD: clock.c,v 1.83 2001/12/11 04:17:48 uwe Exp $ */ /* * Copyright (c) 1992, 1993 @@ -60,6 +60,30 @@ */ #include "opt_sparc_arch.h" +/* + * This file lumps together a lot of loosely related stuff with + * confusingly similar names. + * + * First, there are kernel's clocks provided by "timer" device. The + * hardclock is provided by the timer register (aka system counter). + * The statclock is provided by per cpu counter register(s) (aka + * processor counter(s)). + * + * The "clock" device is the time-of-day clock provided by MK48Txx. + * idprom is located in the NVRAM area of the chip. + * + * microSPARC-IIep machines use DS1287A at EBus for TOD clock and the + * driver for it ("rtc") is in a separate file to prevent this file + * from being cluttered even further. + */ + + +/* + * ifdef out mk48txx TOD clock code for the sake of ms-IIep so that + * this file doesn't require obio, iommu and sbus to link the kernel. + */ +#include "mk48txx.h" + #include #include #include @@ -87,6 +111,20 @@ #include #include +#if defined(MSIIEP) +#include +/* XXX: move this stuff to msiiepreg.h? */ + +/* ms-IIep PCIC registers mapped at fixed VA (see vaddrs.h) */ +#define msiiep ((volatile struct msiiep_pcic_reg *)MSIIEP_PCIC_VA) + +/* + * ms-IIep counters tick every 4 cpu clock @100MHz. + * counter is reset to 1 when new limit is written. + */ +#define tmr_ustolimIIep(n) ((n)* 25 + 1) +#endif /* MSIIEP */ + /* * Statistics clock interval and variance, in usec. Variance must be a * power of two. Since this gives us an even number, not an odd number, @@ -124,11 +162,7 @@ extern struct idprom sun4_idprom_store; static int oldclk = 0; bus_space_tag_t i7_bt; bus_space_handle_t i7_bh; -#endif - -/* Location and size of the MK48xx TOD clock, if present */ -static bus_space_handle_t mk_nvram_base; -static bus_size_t mk_nvram_size; +#endif /* SUN4 */ static int oclockmatch(struct device *, struct cfdata *, void *); static void oclockattach(struct device *, struct device *, void *); @@ -137,11 +171,6 @@ struct cfattach oclock_ca = { sizeof(struct device), oclockmatch, oclockattach }; -extern struct cfdriver oclock_cd; - -static struct intrhand level10 = { clockintr }; -static struct intrhand level14 = { statintr }; - /* * Sun 4 machines use the old-style (a'la Sun 3) EEPROM. On the * 4/100's and 4/200's, this is at a separate obio space. On the @@ -168,7 +197,13 @@ struct cfattach eeprom_ca = { sizeof(struct device), eeprom_match, eeprom_attach }; -extern struct cfdriver eeprom_cd; + +#if NMK48TXX > 0 +/* Location and size of the MK48xx TOD clock, if present */ +static bus_space_handle_t mk_nvram_base; +static bus_size_t mk_nvram_size; + +static int clk_wenable(todr_chip_handle_t, int); static int clockmatch_mainbus (struct device *, struct cfdata *, void *); static int clockmatch_obio(struct device *, struct cfdata *, void *); @@ -184,8 +219,11 @@ struct cfattach clock_mainbus_ca = { struct cfattach clock_obio_ca = { sizeof(struct device), clockmatch_obio, clockattach_obio }; +#endif /* NMK48TXX > 0 */ -extern struct cfdriver clock_cd; + +static struct intrhand level10 = { clockintr }; +static struct intrhand level14 = { statintr }; static int timermatch_mainbus(struct device *, struct cfdata *, void *); static int timermatch_obio(struct device *, struct cfdata *, void *); @@ -194,6 +232,13 @@ static void timerattach_obio(struct device *, struct device *, void *); static void timerattach(volatile int *, volatile int *); +/* + * On ms-IIep counters are part of PCIC, so msiiep_attach will simply + * call this function to configure the counters. + */ +void timerattach_msiiep(void); + + /*struct counter_4m *counterreg_4m;*/ struct timer_4m *timerreg4m; #define counterreg4m cpuinfo.counterreg_4m @@ -209,16 +254,15 @@ struct cfattach timer_obio_ca = { }; /* Global TOD clock handle & idprom pointer */ -static todr_chip_handle_t todr_handle; +todr_chip_handle_t todr_handle; struct idprom *idprom; -static int clk_wenable(todr_chip_handle_t, int); -static void stopcounter(struct counter_4m *); -static void establish_hostid(struct idprom *); +void establish_hostid(struct idprom *); void myetheraddr(u_char *); int timerblurb = 10; /* Guess a value; used before clock is attached */ + /* * old clock match routine */ @@ -324,7 +368,7 @@ oclockattach(parent, self, aux) if ((todr_handle = intersil7170_attach(bt, bh, 1968)) == NULL) panic("Can't attach tod clock"); - establish_hostid(idprom = &sun4_idprom_store); + establish_hostid(&sun4_idprom_store); #endif /* SUN4 */ } @@ -393,6 +437,9 @@ eeprom_attach(parent, self, aux) #endif /* SUN4 */ } + +#if NMK48TXX > 0 + /* * The OPENPROM calls the clock the "eeprom", so we have to have our * own special match function to call it the "clock". @@ -573,6 +620,42 @@ clockattach(node, bt, bh) establish_hostid(idp); } +/* + * Write en/dis-able TOD clock registers. This is a safety net to + * save idprom (part of mk48txx TOD clock) from being accidentally + * stomped on by a buggy code. We coordinate so that several writers + * can run simultaneously. + */ +int +clk_wenable(handle, onoff) + todr_chip_handle_t handle; + int onoff; +{ + int s; + vm_prot_t prot;/* nonzero => change prot */ + int npages; + vaddr_t base; + static int writers; + + /* XXX - we ignore `handle' here... */ + + s = splhigh(); + if (onoff) + prot = writers++ == 0 ? VM_PROT_READ|VM_PROT_WRITE : 0; + else + prot = --writers == 0 ? VM_PROT_READ : 0; + splx(s); + + npages = round_page((vsize_t)mk_nvram_size) << PAGE_SHIFT; + base = trunc_page((vaddr_t)mk_nvram_base); + if (prot) + pmap_changeprot(pmap_kernel(), base, prot, npages); + + return (0); +} + +#endif /* NMK48TXX > 0 */ /* "clock" device driver */ + /* * The sun4c OPENPROM calls the timer the "counter-timer". */ @@ -783,48 +866,59 @@ timerattach(cntreg, limreg) timerok = 1; } + +#if defined(MSIIEP) /* - * Write en/dis-able clock registers. We coordinate so that several - * writers can run simultaneously. + * Attach system and cpu counters (kernel hard and stat clocks) for ms-IIep. + * Counters are part of the PCIC and there's no PROM node for them. + * msiiep_attach() will call this function directly. */ -int -clk_wenable(handle, onoff) - todr_chip_handle_t handle; - int onoff; -{ - int s; - vm_prot_t prot;/* nonzero => change prot */ - int npages; - vaddr_t base; - static int writers; - - /* XXX - we ignore `handle' here... */ - - s = splhigh(); - if (onoff) - prot = writers++ == 0 ? VM_PROT_READ|VM_PROT_WRITE : 0; - else - prot = --writers == 0 ? VM_PROT_READ : 0; - splx(s); - - npages = round_page((vsize_t)mk_nvram_size) << PAGE_SHIFT; - base = trunc_page((vaddr_t)mk_nvram_base); - if (prot) - pmap_changeprot(pmap_kernel(), base, prot, npages); - - return (0); -} - void -stopcounter(creg) - struct counter_4m *creg; +timerattach_msiiep() { - /* Stop the clock */ - volatile int discard; - discard = creg->t_limit; - creg->t_limit = 0; - creg->t_ss = 0; + /* Put processor counter in "counter" mode */ + msiiep->pcic_pc_ctl = 0; /* stop user timer (just in case) */ + msiiep->pcic_pc_cfg = 0; /* timer mode disabled (processor counter) */ + + /* + * Calibrate delay() by tweaking the magic constant + * until a delay(100) actually reads (at least) 100 us on the clock. + * Note: ms-IIep clocks ticks every 4 processor cycles. + */ + for (timerblurb = 1; ; ++timerblurb) { + volatile int discard; + int t; + + discard = msiiep->pcic_pclr; /* clear the limit bit */ + msiiep->pcic_pclr = 0; /* reset counter to 1, free run */ + delay(100); + t = msiiep->pcic_pccr; + + if (t & TMR_LIMIT) /* cannot happen */ + panic("delay calibration"); + + /* counter ticks -> usec, inverse of tmr_ustolimIIep */ + t = (t - 1) / 25; + if (t >= 100) + break; + } + printf(" delay constant %d\n", timerblurb); + + /* + * Set counter interrupt priority assignment: + * upper 4 bits are for system counter: level 10 + * lower 4 bits are for processor counter: level 14 + */ + msiiep->pcic_cipar = 0xae; + + /* link interrupt handlers */ + intr_establish(10, &level10); + intr_establish(14, &level14); + + timerok = 1; } +#endif /* MSIIEP */ + /* * XXX this belongs elsewhere @@ -863,8 +957,8 @@ establish_hostid(idp) /* - * Set up the real-time and statistics clocks. Leave stathz 0 only if - * no alternative timer is available. + * Set up the real-time and statistics clocks. + * Leave stathz 0 only if no alternative timer is available. * * The frequencies of these clocks must be an even number of microseconds. */ @@ -911,7 +1005,9 @@ cpu_initclocks() minint = statint / 2 + 100; while (statvar > minint) statvar >>= 1; + statmin = statint - (statvar >> 1); +#if defined(SUN4M) && !defined(MSIIEP) if (CPU_ISSUN4M) { int n; timerreg4m->t_limit = tmr_ustolim4m(tick); @@ -921,23 +1017,25 @@ cpu_initclocks() continue; cpi->counterreg_4m->t_limit = tmr_ustolim4m(statint); } + ienab_bic(SINTR_T); } +#endif +#if defined(MSIIEP) + /* ms-IIep kernels support *only* IIep */ { + msiiep->pcic_sclr = tmr_ustolimIIep(tick); + msiiep->pcic_pclr = tmr_ustolimIIep(statint); + /* XXX: ensure interrupt target mask doesn't masks them? */ + } +#endif + +#if defined(SUN4) || defined(SUN4C) if (CPU_ISSUN4OR4C) { timerreg4->t_c10.t_limit = tmr_ustolim(tick); timerreg4->t_c14.t_limit = tmr_ustolim(statint); - } - - statmin = statint - (statvar >> 1); - -#if defined(SUN4M) - if (CPU_ISSUN4M) - ienab_bic(SINTR_T); -#endif - - if (CPU_ISSUN4OR4C) ienab_bis(IE_L14 | IE_L10); - + } +#endif } /* @@ -952,9 +1050,9 @@ setstatclockrate(newhz) } /* - * Level 10 (clock) interrupts. If we are using the FORTH PROM for - * console input, we need to check for that here as well, and generate - * a software interrupt to read it. + * Level 10 (clock) interrupts from system counter. + * If we are using the FORTH PROM for console input, we need to check + * for that here as well, and generate a software interrupt to read it. */ int clockintr(cap) @@ -979,9 +1077,14 @@ clockintr(cap) goto forward; } #endif + /* read the limit register to clear the interrupt */ if (CPU_ISSUN4M) { +#if !defined(MSIIEP) discard = timerreg4m->t_limit; +#else + discard = msiiep->pcic_sclr; +#endif } if (CPU_ISSUN4OR4C) { @@ -997,7 +1100,7 @@ forward: } /* - * Level 14 (stat clock) interrupt handler. + * Level 14 (stat clock) interrupts from processor counter. */ int statintr(cap) @@ -1015,12 +1118,27 @@ statintr(cap) /* read the limit register to clear the interrupt */ if (CPU_ISSUN4M) { +#if !defined(MSIIEP) discard = counterreg4m->t_limit; +#else + discard = msiiep->pcic_pclr; +#endif if (timerok == 0) { /* Stop the clock */ printf("note: counter running!\n"); - stopcounter(counterreg4m); +#if !defined(MSIIEP) + discard = counterreg4m->t_limit; + counterreg4m->t_limit = 0; + counterreg4m->t_ss = 0; timerreg4m->t_cfg = TMR_CFG_USER; +#else + /* + * Turn interrupting processor counter + * into non-interrupting user timer. + */ + msiiep->pcic_pc_cfg = 1; /* make it a user timer */ + msiiep->pcic_pc_ctl = 0; /* stop user timer */ +#endif return 1; } } @@ -1028,6 +1146,7 @@ statintr(cap) if (CPU_ISSUN4OR4C) { discard = timerreg4->t_c14.t_limit; } + statclock((struct clockframe *)cap); /* @@ -1047,7 +1166,11 @@ statintr(cap) * loose the counter ticks that happened since this * interrupt was raised. */ +#if !defined(MSIIEP) counterreg4m->t_limit_nr = tmr_ustolim4m(newint); +#else + msiiep->pcic_pclr_nr = tmr_ustolimIIep(newint); +#endif } if (CPU_ISSUN4OR4C) {