diff options
Diffstat (limited to 'sys/arch/mvme88k/dev/clock.c')
-rw-r--r-- | sys/arch/mvme88k/dev/clock.c | 371 |
1 files changed, 371 insertions, 0 deletions
diff --git a/sys/arch/mvme88k/dev/clock.c b/sys/arch/mvme88k/dev/clock.c new file mode 100644 index 00000000000..352bc426dfc --- /dev/null +++ b/sys/arch/mvme88k/dev/clock.c @@ -0,0 +1,371 @@ +/* $NetBSD: clock.c,v 1.22 1995/05/29 23:57:15 pk Exp $ */ + +/* + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * Copyright (c) 1995 Nivas Madhur + * Copyright (c) 1994 Gordon W. Ross + * Copyright (c) 1993 Adam Glass + * + * 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 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. + * 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 + */ + +/* + * Clock driver. Has both interval timer as well as statistics timer. + */ + +#include <sys/param.h> +#include <sys/kernel.h> +#include <sys/device.h> +#ifdef GPROF +#include <sys/gmon.h> +#endif + +#include <machine/autoconf.h> +#include <machine/cpu.h> + +#include <mvme88k/dev/pcctworeg.h> + +/* + * 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 4096 would + * give us offsets in [0..4095]. Instead, we take offsets in [1..4095]. + * This is symmetric about the point 2048, or statvar/2, and thus averages + * to that value (assuming uniform random numbers). + */ +int statvar = 8192; +int statmin; /* statclock interval - 1/2*variance */ +int timerok; + +u_long delay_factor = 1; + +static int clockmatch __P((struct device *, void *, void *)); +static void clockattach __P((struct device *, struct device *, void *)); +int clockintr __P((void *, void *)); +int statintr __P((void *, void *)); + +struct clocksoftc { + struct device sc_dev; + volatile struct pcc2reg *sc_pcc2reg; +}; + +struct cfattach clock_ca = { + sizeof(struct clocksoftc), clockmatch, clockattach +}; + +struct cfdriver clock_cd = { + NULL, "clock", DV_DULL, 0 +}; + +struct intrhand clockintrhand, statintrhand; + +static int +clockmatch(struct device *parent, void *self, void *aux) +{ + register struct confargs *ca = aux; + register struct cfdata *cf = self; + + if (ca->ca_bustype != BUS_PCCTWO || + strcmp(cf->cf_driver->cd_name, "clock")) { + return (0); + } + + /* + * clock has to be at ipl 5 + * We return the ipl here so that the parent can print + * a message if it is different from what ioconf.c says. + */ + ca->ca_ipl = IPL_CLOCK; + /* set size to 0 - see pcctwo.c:match for details */ + ca->ca_size = 0; + + return 1; +} + +/* ARGSUSED */ +static void +clockattach(struct device *parent, struct device *self, void *aux) +{ + struct confargs *ca = aux; + struct clocksoftc *sc = (struct clocksoftc *)self; + u_long elapsedtime; + + extern void delay(u_long); + extern int cpuspeed; + + /* + * save virtual address of the pcc2 block since our + * registers are in that block. + */ + sc->sc_pcc2reg = (struct pcc2reg *)ca->ca_vaddr; + + /* + * calibrate for delay() calls. + * We do this by using tick timer1 in free running mode before + * cpu_initclocks() is called so turn on clock interrupts etc. + * + * the approach is: + * set count in timer to 0 + * call delay(1000) for a 1000 us delay + * after return, stop count and figure out + * how many us went by (call it x) + * now the factor to multiply the arg. passed to + * delay would be (x/1000) rounded up to an int. + */ + printf("\n"); + sc->sc_pcc2reg->pcc2_t1ctl &= ~PCC2_TICTL_CEN; + sc->sc_pcc2reg->pcc2_psclkadj = 256 - cpuspeed; + sc->sc_pcc2reg->pcc2_t1irq &= ~PCC2_TTIRQ_IEN; + sc->sc_pcc2reg->pcc2_t1cntr = 0; + sc->sc_pcc2reg->pcc2_t1ctl |= PCC2_TICTL_CEN; + delay(1000); /* delay for 1 ms */ + sc->sc_pcc2reg->pcc2_t1ctl &= ~PCC2_TICTL_CEN; + elapsedtime = sc->sc_pcc2reg->pcc2_t1cntr; + + delay_factor = (u_long)(elapsedtime / 1000 + 1); + + /* + * program clock to interrupt at IPL_CLOCK. Set everything + * except compare registers, interrupt enable and counter + * enable registers. + */ + sc->sc_pcc2reg->pcc2_t1ctl &= ~(PCC2_TICTL_CEN); + sc->sc_pcc2reg->pcc2_t1cntr= 0; + sc->sc_pcc2reg->pcc2_t1ctl |= (PCC2_TICTL_COC|PCC2_TICTL_COVF); + sc->sc_pcc2reg->pcc2_t1irq = (PCC2_TTIRQ_ICLR|IPL_CLOCK); + + sc->sc_pcc2reg->pcc2_t2ctl &= ~(PCC2_TICTL_CEN); + sc->sc_pcc2reg->pcc2_t2cntr= 0; + sc->sc_pcc2reg->pcc2_t2ctl |= (PCC2_TICTL_COC|PCC2_TICTL_COVF); + sc->sc_pcc2reg->pcc2_t2irq = (PCC2_TTIRQ_ICLR|IPL_CLOCK); + + /* + * Establish inerrupt handlers. + */ + clockintrhand.ih_fn = clockintr; + clockintrhand.ih_arg = 0; /* don't want anything */ + clockintrhand.ih_ipl = IPL_CLOCK; + clockintrhand.ih_wantframe = 1; + intr_establish(PCC2_VECT+9, &clockintrhand); + + statintrhand.ih_fn = statintr; + statintrhand.ih_arg = 0; /* don't want anything */ + statintrhand.ih_ipl = IPL_CLOCK; + statintrhand.ih_wantframe = 1; + intr_establish(PCC2_VECT+8, &statintrhand); + + timerok = 1; +} + +/* + * Set up the real-time and statistics clocks. Leave stathz 0 only if + * no alternative timer is available. mvme167/mvme187 has 2 tick timers + * in pcc2 - we are using timer 1 for clock interrupt and timer 2 for + * statistics. + * + * The frequencies of these clocks must be an even number of microseconds. + */ +cpu_initclocks() +{ + register int statint, minint; + volatile struct pcc2reg *pcc2reg; + + pcc2reg = ((struct clocksoftc *)clock_cd.cd_devs[0])->sc_pcc2reg; + + if (1000000 % hz) { + printf("cannot get %d Hz clock; using 100 Hz\n", hz); + hz = 100; + tick = 1000000 / hz; + } + 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; + /* + * hz value 100 means we want the clock to interrupt 100 + * times a sec or 100 times in 1000000 us ie, 1 interrupt + * every 10000 us. Program the tick timer compare register + * to this value. + */ + pcc2reg->pcc2_t1cmp = tick; + pcc2reg->pcc2_t2cmp = statint; + statmin = statint - (statvar >> 1); + + /* start the clocks ticking */ + pcc2reg->pcc2_t1ctl = (PCC2_TICTL_CEN|PCC2_TICTL_COC|PCC2_TICTL_COVF); + pcc2reg->pcc2_t2ctl = (PCC2_TICTL_CEN|PCC2_TICTL_COC|PCC2_TICTL_COVF); + /* and enable those interrupts */ + pcc2reg->pcc2_t1irq |= (PCC2_TTIRQ_IEN|PCC2_TTIRQ_ICLR); + pcc2reg->pcc2_t2irq |= (PCC2_TTIRQ_IEN|PCC2_TTIRQ_ICLR); +} + +/* + * Dummy setstatclockrate(), since we know profhz==hz. + */ +/* ARGSUSED */ +void +setstatclockrate(int newhz) +{ + /* nothing */ +} + +/* + * Delay: wait for `about' n microseconds to pass. + */ +void +delay(volatile u_long n) +{ + volatile u_long cnt = n * delay_factor; + + while (cnt-- > 0) { + asm volatile(""); + } +} + +/* + * Clock interrupt handler. Calls hardclock() after setting up a + * clockframe. + */ +int +clockintr(void *cap, void *frame) +{ + volatile struct pcc2reg *reg; + + reg = ((struct clocksoftc *)clock_cd.cd_devs[0])->sc_pcc2reg; + + /* Clear the interrupt */ + reg->pcc2_t1irq = (PCC2_TTIRQ_IEN|PCC2_TTIRQ_ICLR|IPL_CLOCK); +#if 0 + reg->pcc2_t1irq |= PCC2_TTIRQ_ICLR; +#endif /* 0 */ + + hardclock((struct clockframe *)frame); +#include "bugtty.h" +#if NBUGTTY > 0 + bugtty_chkinput(); +#endif /* NBUGTTY */ + + return (1); +} + +/* + * Stat clock interrupt handler. + */ +int +statintr(void *cap, void *frame) +{ + volatile struct pcc2reg *reg; + register u_long newint, r, var; + + reg = ((struct clocksoftc *)clock_cd.cd_devs[0])->sc_pcc2reg; + + /* Clear the interrupt */ +#if 0 + reg->pcc2_t2irq |= PCC2_TTIRQ_ICLR; +#endif /* 0 */ + reg->pcc2_t2irq = (PCC2_TTIRQ_IEN|PCC2_TTIRQ_ICLR|IPL_CLOCK); + + statclock((struct clockframe *)frame); + + /* + * 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; + + /* + * reprogram statistics timer to interrupt at + * newint us intervals. + */ + reg->pcc2_t2ctl = ~(PCC2_TICTL_CEN); + reg->pcc2_t2cntr = 0; + reg->pcc2_t2cmp = newint; + reg->pcc2_t2ctl = (PCC2_TICTL_CEN|PCC2_TICTL_COC|PCC2_TICTL_COVF); + reg->pcc2_t2irq |= (PCC2_TTIRQ_ICLR|PCC2_TTIRQ_IEN); + + return (1); +} + +/* + * Return the best possible estimate of the time in the timeval + * to which tvp points. We do this by returning the current time + * plus the amount of time since the last clock interrupt. + * + * Check that this time is no less than any previously-reported time, + * which could happen around the time of a clock adjustment. Just for + * fun, we guarantee that the time will be greater than the value + * obtained by a previous call. + */ +void +microtime(tvp) + register struct timeval *tvp; +{ + int s; + static struct timeval lasttime; + + s = splhigh(); + *tvp = time; + while (tvp->tv_usec > 1000000) { + tvp->tv_sec++; + tvp->tv_usec -= 1000000; + } + if (tvp->tv_sec == lasttime.tv_sec && + tvp->tv_usec <= lasttime.tv_usec && + (tvp->tv_usec = lasttime.tv_usec + 1) > 1000000) { + tvp->tv_sec++; + tvp->tv_usec -= 1000000; + } + lasttime = *tvp; + splx(s); +} |