/* $NetBSD: clock.c,v 1.97 2008/05/18 22:40:14 martin Exp $ */ /* * Copyright (c) 1992, 1993 * The Regents of the University of California. All rights reserved. * Copyright (c) 1994 Gordon W. Ross * Copyright (c) 1993 Adam Glass * Copyright (c) 1996 Paul Kranenburg * Copyright (c) 1996 * The President and Fellows of Harvard College. All rights reserved. * * This software was developed by the Computer Systems Engineering group * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and * contributed to Berkeley. * * All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by Harvard University. * This product includes software developed by the University of * California, Lawrence Berkeley Laboratory. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * This product includes software developed by Paul Kranenburg. * This product includes software developed by Harvard University. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * @(#)clock.c 8.1 (Berkeley) 6/11/93 * */ #include <sys/cdefs.h> __KERNEL_RCSID(0, "$NetBSD: clock.c,v 1.97 2008/05/18 22:40:14 martin Exp $"); #include "opt_multiprocessor.h" /* * Clock driver. This is the id prom and eeprom driver as well * and includes the timer register functions too. */ /* Define this for a 1/4s clock to ease debugging */ /* #define INTR_DEBUG */ #include <sys/param.h> #include <sys/kernel.h> #include <sys/device.h> #include <sys/proc.h> #include <sys/resourcevar.h> #include <sys/malloc.h> #include <sys/systm.h> #include <sys/timetc.h> #ifdef GPROF #include <sys/gmon.h> #endif #include <uvm/uvm_extern.h> #include <machine/bus.h> #include <machine/autoconf.h> #include <machine/eeprom.h> #include <machine/cpu.h> #include <machine/cpu_counter.h> #include <sparc64/sparc64/intreg.h> #include <sparc64/sparc64/timerreg.h> #include <sparc64/dev/iommureg.h> #include <sparc64/dev/sbusreg.h> #include <dev/sbus/sbusvar.h> #include <dev/ebus/ebusreg.h> #include <dev/ebus/ebusvar.h> /* * Clock assignments: * * machine hardclock statclock timecounter * counter-timer timer#0 timer#1 %tick * counter-timer + SMP timer#0/%tick - timer#1 or %tick * no counter-timer %tick - %tick */ /* * 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, * we discard one case and compensate. That is, a variance of 1024 would * give us offsets in [0..1023]. Instead, we take offsets in [1..1023]. * This is symmetric about the point 512, or statvar/2, and thus averages * to that value (assuming uniform random numbers). */ /* XXX fix comment to match value */ int statvar = 8192; int statmin; /* statclock interval - 1/2*variance */ int timerok; #ifndef MULTIPROCESSOR static int statscheddiv; #endif static struct intrhand level10 = { .ih_fun = clockintr }; #ifndef MULTIPROCESSOR static struct intrhand level14 = { .ih_fun = statintr }; static struct intrhand *schedint; #endif static int timermatch(struct device *, struct cfdata *, void *); static void timerattach(struct device *, struct device *, void *); struct timerreg_4u timerreg_4u; /* XXX - need more cleanup */ CFATTACH_DECL(timer, sizeof(struct device), timermatch, timerattach, NULL, NULL); struct chiptime; void stopcounter(struct timer_4u *); int timerblurb = 10; /* Guess a value; used before clock is attached */ static u_int tick_get_timecount(struct timecounter *); /* * define timecounter "tick-counter" */ static struct timecounter tick_timecounter = { tick_get_timecount, /* get_timecount */ 0, /* no poll_pps */ ~0u, /* counter_mask */ 0, /* frequency - set at initialisation */ "tick-counter", /* name */ 100, /* quality */ 0, /* private reference - UNUSED */ NULL /* next timecounter */ }; /* * tick_get_timecount provide current tick counter value */ static u_int tick_get_timecount(struct timecounter *tc) { return cpu_counter(); } #ifdef MULTIPROCESSOR static u_int counter_get_timecount(struct timecounter *); /* * define timecounter "counter-timer" */ static struct timecounter counter_timecounter = { counter_get_timecount, /* get_timecount */ 0, /* no poll_pps */ TMR_LIM_MASK, /* counter_mask */ 1000000, /* frequency */ "counter-timer", /* name */ 200, /* quality */ 0, /* private reference - UNUSED */ NULL /* next timecounter */ }; /* * counter_get_timecount provide current counter value */ static u_int counter_get_timecount(struct timecounter *tc) { return (u_int)ldxa((vaddr_t)&timerreg_4u.t_timer[1].t_count, ASI_NUCLEUS) & TMR_LIM_MASK; } #endif /* * The sun4u OPENPROMs call the timer the "counter-timer", except for * the lame UltraSPARC IIi PCI machines that don't have them. */ static int timermatch(struct device *parent, struct cfdata *cf, void *aux) { struct mainbus_attach_args *ma = aux; return (strcmp("counter-timer", ma->ma_name) == 0); } static void timerattach(struct device *parent, struct device *self, void *aux) { struct mainbus_attach_args *ma = aux; u_int *va = ma->ma_address; #if 0 volatile int64_t *cnt = NULL, *lim = NULL; #endif /* * What we should have are 3 sets of registers that reside on * different parts of SYSIO or PSYCHO. We'll use the prom * mappings cause we can't get rid of them and set up appropriate * pointers on the timerreg_4u structure. */ timerreg_4u.t_timer = (struct timer_4u *)(u_long)va[0]; timerreg_4u.t_clrintr = (int64_t *)(u_long)va[1]; timerreg_4u.t_mapintr = (int64_t *)(u_long)va[2]; /* * Disable interrupts for now. * N.B. By default timer[0] is disabled and timer[1] is enabled. */ stxa((vaddr_t)&timerreg_4u.t_mapintr[0], ASI_NUCLEUS, (timerreg_4u.t_mapintr[0] & ~(INTMAP_V|INTMAP_TID)) | (CPU_UPAID << INTMAP_TID_SHIFT)); stxa((vaddr_t)&timerreg_4u.t_mapintr[1], ASI_NUCLEUS, (timerreg_4u.t_mapintr[1] & ~(INTMAP_V|INTMAP_TID)) | (CPU_UPAID << INTMAP_TID_SHIFT)); /* Install the appropriate interrupt vector here */ level10.ih_number = ma->ma_interrupts[0]; level10.ih_clr = &timerreg_4u.t_clrintr[0]; intr_establish(PIL_CLOCK, true, &level10); printf(" irq vectors %lx", (u_long)level10.ih_number); #ifndef MULTIPROCESSOR /* * On SMP kernel, don't establish interrupt to use it as timecounter. */ level14.ih_number = ma->ma_interrupts[1]; level14.ih_clr = &timerreg_4u.t_clrintr[1]; intr_establish(PIL_STATCLOCK, true, &level14); printf(" and %lx", (u_long)level14.ih_number); #endif #if 0 cnt = &(timerreg_4u.t_timer[0].t_count); lim = &(timerreg_4u.t_timer[0].t_limit); /* * Calibrate delay() by tweaking the magic constant * until a delay(100) actually reads (at least) 100 us * on the clock. Since we're using the %tick register * which should be running at exactly the CPU clock rate, it * has a period of somewhere between 7ns and 3ns. */ #ifdef DEBUG printf("Delay calibrarion....\n"); #endif for (timerblurb = 1; timerblurb > 0; timerblurb++) { volatile int discard; register int t0, t1; /* Reset counter register by writing some large limit value */ discard = *lim; *lim = tmr_ustolim(TMR_MASK-1); t0 = *cnt; delay(100); t1 = *cnt; if (t1 & TMR_LIMIT) panic("delay calibration"); t0 = (t0 >> TMR_SHIFT) & TMR_MASK; t1 = (t1 >> TMR_SHIFT) & TMR_MASK; if (t1 >= t0 + 100) break; } printf(" delay constant %d\n", timerblurb); #endif printf("\n"); timerok = 1; } void stopcounter(struct timer_4u *creg) { /* Stop the clock */ volatile int discard; discard = creg->t_limit; creg->t_limit = 0; } /* * Untill interrupts are established per CPU, we rely on the special * handling of tickintr in locore.s. * We establish this interrupt if there is no real counter-timer on * the machine, or on secondary CPUs. The latter would normally not be * able to dispatch the interrupt (established on cpu0) to another cpu, * but the shortcut during dispatch makes it work. */ void tickintr_establish(int pil, int (*fun)(void *)) { int s; struct intrhand *ih; struct cpu_info *ci = curcpu(); ih = sparc_softintr_establish(pil, fun, NULL); ih->ih_number = 1; if (CPU_IS_PRIMARY(ci)) intr_establish(pil, true, ih); ci->ci_tick_ih = ih; /* set the next interrupt time */ ci->ci_tick_increment = ci->ci_cpu_clockrate[0] / hz; #ifdef DEBUG printf("Using %%tick -- intr in %ld cycles\n", ci->ci_tick_increment); #endif s = intr_disable(); next_tick(ci->ci_tick_increment); intr_restore(s); } /* * 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. */ void cpu_initclocks() { #ifndef MULTIPROCESSOR int statint, minint; #endif uint64_t start_time = 0; #ifdef DEBUG extern int intrdebug; #endif #ifdef DEBUG /* Set a 1s clock */ if (intrdebug) { hz = 1; tick = 1000000 / hz; printf("intrdebug set: 1Hz clock\n"); } #endif if (1000000 % hz) { printf("cannot get %d Hz clock; using 100 Hz\n", hz); hz = 100; tick = 1000000 / hz; } /* Make sure we have a sane cpu_clockrate -- we'll need it */ if (!curcpu()->ci_cpu_clockrate[0]) { /* Default to 200MHz clock XXXXX */ curcpu()->ci_cpu_clockrate[0] = 200000000; curcpu()->ci_cpu_clockrate[1] = 200000000 / 1000000; } /* Initialize the %tick register */ #ifdef __arch64__ __asm volatile("wrpr %0, 0, %%tick" : : "r" (start_time)); #else { int start_hi = (start_time>>32), start_lo = start_time; __asm volatile("sllx %1,32,%0; or %0,%2,%0; wrpr %0, 0, %%tick" : "=&r" (start_hi) /* scratch register */ : "r" ((int)(start_hi)), "r" ((int)(start_lo))); } #endif tick_timecounter.tc_frequency = curcpu()->ci_cpu_clockrate[0]; tc_init(&tick_timecounter); /* * Now handle machines w/o counter-timers. */ if (!timerreg_4u.t_timer || !timerreg_4u.t_clrintr) { printf("No counter-timer -- using %%tick at %luMHz as " "system clock.\n", (unsigned long)curcpu()->ci_cpu_clockrate[1]); /* We don't have a counter-timer -- use %tick */ tickintr_establish(PIL_CLOCK, tickintr); /* We only have one timer so we have no statclock */ stathz = 0; return; } #ifndef MULTIPROCESSOR if (stathz == 0) stathz = hz; if (1000000 % stathz) { printf("cannot get %d Hz statclock; using 100 Hz\n", stathz); stathz = 100; } profhz = stathz; /* always */ statint = 1000000 / stathz; minint = statint / 2 + 100; while (statvar > minint) statvar >>= 1; /* * Establish scheduler softint. */ schedint = sparc_softintr_establish(PIL_SCHED, schedintr, NULL); schedhz = 16; /* 16Hz is best according to kern/kern_clock.c */ statscheddiv = stathz / schedhz; if (statscheddiv <= 0) panic("statscheddiv"); #endif /* * Enable counter-timer #0 interrupt for clockintr. */ stxa((vaddr_t)&timerreg_4u.t_timer[0].t_limit, ASI_NUCLEUS, tmr_ustolim(tick)|TMR_LIM_IEN|TMR_LIM_PERIODIC|TMR_LIM_RELOAD); stxa((vaddr_t)&timerreg_4u.t_mapintr[0], ASI_NUCLEUS, timerreg_4u.t_mapintr[0]|INTMAP_V); #ifdef MULTIPROCESSOR /* * Use counter-timer #1 as timecounter. */ stxa((vaddr_t)&timerreg_4u.t_timer[1].t_limit, ASI_NUCLEUS, TMR_LIM_MASK); tc_init(&counter_timecounter); #else /* * Enable counter-timer #1 interrupt for statintr. */ #ifdef DEBUG if (intrdebug) /* Neglect to enable timer */ stxa((vaddr_t)&timerreg_4u.t_timer[1].t_limit, ASI_NUCLEUS, tmr_ustolim(statint)|TMR_LIM_RELOAD); else #endif stxa((vaddr_t)&timerreg_4u.t_timer[1].t_limit, ASI_NUCLEUS, tmr_ustolim(statint)|TMR_LIM_IEN|TMR_LIM_RELOAD); stxa((vaddr_t)&timerreg_4u.t_mapintr[1], ASI_NUCLEUS, timerreg_4u.t_mapintr[1]|INTMAP_V|(CPU_UPAID << INTMAP_TID_SHIFT)); statmin = statint - (statvar >> 1); #endif } /* * Dummy setstatclockrate(), since we know profhz==hz. */ /* ARGSUSED */ void setstatclockrate(newhz) int newhz; { /* nothing */ } /* * 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. */ #ifdef DEBUG static int clockcheck = 0; #endif int clockintr(void *cap) { #ifdef DEBUG static int64_t tick_base = 0; struct timeval ctime; int64_t t = (uint64_t)tick(); microtime(&ctime); if (!tick_base) { tick_base = (ctime.tv_sec * 1000000LL + ctime.tv_usec) / curcpu()->ci_cpu_clockrate[1]; tick_base -= t; } else if (clockcheck) { int64_t tk = t; int64_t clk = (ctime.tv_sec * 1000000LL + ctime.tv_usec); t -= tick_base; t = t / curcpu()->ci_cpu_clockrate[1]; if (t - clk > hz) { printf("Clock lost an interrupt!\n"); printf("Actual: %llx Expected: %llx tick %llx tick_base %llx\n", (long long)t, (long long)clk, (long long)tk, (long long)tick_base); tick_base = 0; } } #endif /* Let locore.s clear the interrupt for us. */ hardclock((struct clockframe *)cap); return (1); } /* * 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. * * %tick is really a level-14 interrupt. We need to remap this in * locore.s to a level 10. */ int tickintr(void *cap) { int s; hardclock((struct clockframe *)cap); s = intr_disable(); /* Reset the interrupt */ next_tick(curcpu()->ci_tick_increment); intr_restore(s); curcpu()->ci_tick_evcnt.ev_count++; return (1); } #ifndef MULTIPROCESSOR /* * Level 14 (stat clock) interrupt handler. */ int statintr(cap) void *cap; { register u_long newint, r, var; struct cpu_info *ci = curcpu(); #ifdef NOT_DEBUG printf("statclock: count %x:%x, limit %x:%x\n", timerreg_4u.t_timer[1].t_count, timerreg_4u.t_timer[1].t_limit); #endif #ifdef NOT_DEBUG prom_printf("!"); #endif statclock((struct clockframe *)cap); #ifdef NOTDEF_DEBUG /* Don't re-schedule the IRQ */ return 1; #endif /* * Compute new randomized interval. The intervals are uniformly * distributed on [statint - statvar / 2, statint + statvar / 2], * and therefore have mean statint, giving a stathz frequency clock. */ var = statvar; do { r = random() & (var - 1); } while (r == 0); newint = statmin + r; if (schedhz) if ((int)(--ci->ci_schedstate.spc_schedticks) <= 0) { send_softint(-1, PIL_SCHED, schedint); ci->ci_schedstate.spc_schedticks = statscheddiv; } stxa((vaddr_t)&timerreg_4u.t_timer[1].t_limit, ASI_NUCLEUS, tmr_ustolim(newint)|TMR_LIM_IEN|TMR_LIM_RELOAD); return (1); } int schedintr(void *arg) { schedclock(curcpu()->ci_data.cpu_onproc); return (1); } #endif