From c04c9a55d0f4a763770d215cd0d498b79aff15a9 Mon Sep 17 00:00:00 2001 From: mhitch Date: Sun, 6 Jan 2008 18:50:29 +0000 Subject: [PATCH] Add timecounter support and borrow counter-based delay from i386. From joerg; testing and final tweaks by me. --- sys/arch/amiga/amiga/amiga_init.c | 20 +-- sys/arch/amiga/amiga/locore.s | 17 +-- sys/arch/amiga/amiga/machdep.c | 37 +----- sys/arch/amiga/dev/clock.c | 210 ++++++++++++++++-------------- sys/arch/amiga/include/cpu.h | 7 +- sys/arch/amiga/include/param.h | 6 +- sys/arch/amiga/include/types.h | 3 +- 7 files changed, 125 insertions(+), 175 deletions(-) diff --git a/sys/arch/amiga/amiga/amiga_init.c b/sys/arch/amiga/amiga/amiga_init.c index e21e750ac704..f809d1d8f892 100644 --- a/sys/arch/amiga/amiga/amiga_init.c +++ b/sys/arch/amiga/amiga/amiga_init.c @@ -1,4 +1,4 @@ -/* $NetBSD: amiga_init.c,v 1.98 2007/10/17 19:53:12 garbled Exp $ */ +/* $NetBSD: amiga_init.c,v 1.99 2008/01/06 18:50:29 mhitch Exp $ */ /* * Copyright (c) 1994 Michael L. Hitch @@ -36,7 +36,7 @@ #include "opt_devreload.h" #include -__KERNEL_RCSID(0, "$NetBSD: amiga_init.c,v 1.98 2007/10/17 19:53:12 garbled Exp $"); +__KERNEL_RCSID(0, "$NetBSD: amiga_init.c,v 1.99 2008/01/06 18:50:29 mhitch Exp $"); #include #include @@ -765,7 +765,6 @@ start_c(id, fphystart, fphysize, cphysize, esym_addr, flags, inh_sync, void start_c_finish() { - extern u_int32_t delaydivisor; #ifdef P5PPC68KBOARD struct cfdev *cdp, *ecdp; #endif @@ -901,21 +900,6 @@ start_c_finish() } } #endif - /* - * preliminary delay divisor value - */ - - if (machineid & AMIGA_68060) - delaydivisor = (1024 * 1) / 80; /* 80 MHz 68060 w. BTC */ - - else if (machineid & AMIGA_68040) - delaydivisor = (1024 * 3) / 40; /* 40 MHz 68040 */ - - else if (machineid & AMIGA_68030) - delaydivisor = (1024 * 8) / 50; /* 50 MHz 68030 */ - - else - delaydivisor = (1024 * 8) / 33; /* 33 MHz 68020 */ } void diff --git a/sys/arch/amiga/amiga/locore.s b/sys/arch/amiga/amiga/locore.s index 9718dd0d2b1a..0df46da34b49 100644 --- a/sys/arch/amiga/amiga/locore.s +++ b/sys/arch/amiga/amiga/locore.s @@ -1,4 +1,4 @@ -/* $NetBSD: locore.s,v 1.144 2007/10/17 19:53:12 garbled Exp $ */ +/* $NetBSD: locore.s,v 1.145 2008/01/06 18:50:30 mhitch Exp $ */ /* * Copyright (c) 1980, 1990 The Regents of the University of California. @@ -1577,16 +1577,6 @@ Ldorebootend: .align 2 #endif nop -ENTRY_NOPROFILE(delay) -ENTRY_NOPROFILE(DELAY) - movql #10,%d1 | 2 +2 - movl %sp@(4),%d0 | 4 +4 - lsll %d1,%d0 | 8 +2 - movl _C_LABEL(delaydivisor),%d1 | A +6 -Ldelay: | longword aligned again. - subl %d1,%d0 - jcc Ldelay - rts #ifdef M68060 ENTRY_NOPROFILE(intemu60) @@ -1621,11 +1611,6 @@ GLOBAL(protorp) GLOBAL(proc0paddr) .long 0 | KVA of proc0 u-area -GLOBAL(delaydivisor) - .long 12 | should be enough for 80 MHz 68060 - | will be adapted to other CPUs in - | start_c_cleanup and calibrated - | at clock attach time. #ifdef DEBUG ASGLOBAL(fulltflush) .long 0 diff --git a/sys/arch/amiga/amiga/machdep.c b/sys/arch/amiga/amiga/machdep.c index 510b74a44876..147cc1794b0c 100644 --- a/sys/arch/amiga/amiga/machdep.c +++ b/sys/arch/amiga/amiga/machdep.c @@ -1,4 +1,4 @@ -/* $NetBSD: machdep.c,v 1.207 2007/12/03 15:33:10 ad Exp $ */ +/* $NetBSD: machdep.c,v 1.208 2008/01/06 18:50:30 mhitch Exp $ */ /* * Copyright (c) 1982, 1986, 1990 The Regents of the University of California. @@ -85,7 +85,7 @@ #include "opt_panicbutton.h" #include -__KERNEL_RCSID(0, "$NetBSD: machdep.c,v 1.207 2007/12/03 15:33:10 ad Exp $"); +__KERNEL_RCSID(0, "$NetBSD: machdep.c,v 1.208 2008/01/06 18:50:30 mhitch Exp $"); #include #include @@ -765,39 +765,6 @@ dumpsys() delay(5000000); /* 5 seconds */ } -/* - * 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 (clock.c:clkread). - * - * 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 = spl7(); - static struct timeval lasttime; - - *tvp = time; - tvp->tv_usec += clkread(); - 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); -} - void initcpu() { diff --git a/sys/arch/amiga/dev/clock.c b/sys/arch/amiga/dev/clock.c index e94d7117881b..051486d87c80 100644 --- a/sys/arch/amiga/dev/clock.c +++ b/sys/arch/amiga/dev/clock.c @@ -1,4 +1,4 @@ -/* $NetBSD: clock.c,v 1.46 2007/03/04 05:59:17 christos Exp $ */ +/* $NetBSD: clock.c,v 1.47 2008/01/06 18:50:31 mhitch Exp $ */ /* * Copyright (c) 1982, 1990 The Regents of the University of California. @@ -77,12 +77,13 @@ */ #include -__KERNEL_RCSID(0, "$NetBSD: clock.c,v 1.46 2007/03/04 05:59:17 christos Exp $"); +__KERNEL_RCSID(0, "$NetBSD: clock.c,v 1.47 2008/01/06 18:50:31 mhitch Exp $"); #include #include #include #include +#include #include #include #include @@ -101,12 +102,24 @@ __KERNEL_RCSID(0, "$NetBSD: clock.c,v 1.46 2007/03/04 05:59:17 christos Exp $"); /* the clocks run at NTSC: 715.909kHz or PAL: 709.379kHz. We're using a 100 Hz clock. */ - -#define CLK_INTERVAL amiga_clk_interval int amiga_clk_interval; int eclockfreq; +unsigned int fast_delay_limit; struct CIA *clockcia; +static u_int clk_getcounter(struct timecounter *); + +static struct timecounter clk_timecounter = { + clk_getcounter, /* get_timecount */ + 0, /* no poll_pps */ + ~0u, /* counter_mask */ + 0, /* frequency */ + "clock", /* name, overriden later */ + 100, /* quality */ + NULL, /* prev */ + NULL, /* next */ +}; + /* * Machine-dependent clock routines. * @@ -119,7 +132,7 @@ struct CIA *clockcia; * Resettodr restores the time of day hardware after a time change. * * A note on the real-time clock: - * We actually load the clock with CLK_INTERVAL-1 instead of CLK_INTERVAL. + * We actually load the clock with amiga_clk_interval-1 instead of amiga_clk_interval. * This is because the counter decrements to zero after N+1 enabled clock * periods where N is the value loaded into the counter. */ @@ -127,7 +140,6 @@ struct CIA *clockcia; int clockmatch(struct device *, struct cfdata *, void *); void clockattach(struct device *, struct device *, void *); void cpu_initclocks(void); -void calibrate_delay(struct device *); CFATTACH_DECL(clock, sizeof(struct device), clockmatch, clockattach, NULL, NULL); @@ -152,15 +164,17 @@ clockattach(struct device *pdp, struct device *dp, void *auxp) u_char dracorev; #endif +#ifdef DRACO + dracorev = is_draco(); +#endif + if (eclockfreq == 0) eclockfreq = 715909; /* guess NTSC */ - CLK_INTERVAL = (eclockfreq / 100); - #ifdef DRACO - dracorev = is_draco(); if (dracorev >= 4) { - CLK_INTERVAL = (eclockfreq / 700); + if (amiga_clk_interval == 0) /* Only do this 1st time */ + eclockfreq /= 7; clockchip = "QuickLogic"; } else if (dracorev) { clockcia = (struct CIA *)CIAAbase; @@ -172,13 +186,18 @@ clockattach(struct device *pdp, struct device *dp, void *auxp) clockchip = "CIA B"; } - if (dp) + amiga_clk_interval = (eclockfreq / hz); + + clk_timecounter.tc_name = clockchip; + clk_timecounter.tc_frequency = eclockfreq; + + fast_delay_limit = UINT_MAX / amiga_clk_interval; + + if (dp != NULL) { /* real autoconfig? */ printf(": %s system hz %d hardware hz %d\n", clockchip, hz, -#ifdef DRACO - dracorev >= 4 ? eclockfreq / 7 : eclockfreq); -#else - eclockfreq); -#endif + eclockfreq); + tc_init(&clk_timecounter); + } #ifdef DRACO if (dracorev >= 4) { @@ -187,10 +206,8 @@ clockattach(struct device *pdp, struct device *dp, void *auxp) * but need this for delay calibration. */ - draco_ioct->io_timerlo = CLK_INTERVAL & 0xff; - draco_ioct->io_timerhi = CLK_INTERVAL >> 8; - - calibrate_delay(dp); + draco_ioct->io_timerlo = amiga_clk_interval & 0xff; + draco_ioct->io_timerhi = amiga_clk_interval >> 8; return; } @@ -207,7 +224,7 @@ clockattach(struct device *pdp, struct device *dp, void *auxp) * the clocks run at NTSC: 715.909kHz or PAL: 709.379kHz * supprort for PAL WHEN?!?! XXX */ - interval = CLK_INTERVAL - 1; + interval = amiga_clk_interval - 1; /* * order of setting is important ! @@ -218,67 +235,6 @@ clockattach(struct device *pdp, struct device *dp, void *auxp) * start timer A in continuous mode */ clockcia->cra = (clockcia->cra & 0xc0) | 1; - - calibrate_delay(dp); -} - -/* - * Calibrate delay loop. - * We use two iterations because we don't have enough bits to do a factor of - * 8 with better than 1%. - * - * XXX Note that we MUST stay below 1 tick if using clkread(), even for - * underestimated values of delaydivisor. - * - * XXX the "ns" below is only correct for a shift of 10 bits, and even then - * off by 2.4% - */ - -void -calibrate_delay(struct device *dp) -{ - unsigned long t1, t2; - extern u_int32_t delaydivisor; - /* XXX this should be defined elsewhere */ - - if (dp) - printf("Calibrating delay loop... "); - - do { - t1 = clkread(); - delay(1024); - t2 = clkread(); - } while (t2 <= t1); - t2 -= t1; - delaydivisor = (delaydivisor * t2 + 1023) >> 10; -#ifdef DEBUG - if (dp) - printf("\ndiff %ld us, new divisor %u/1024 us\n", t2, - delaydivisor); - do { - t1 = clkread(); - delay(1024); - t2 = clkread(); - } while (t2 <= t1); - t2 -= t1; - delaydivisor = (delaydivisor * t2 + 1023) >> 10; - if (dp) - printf("diff %ld us, new divisor %u/1024 us\n", t2, - delaydivisor); -#endif - do { - t1 = clkread(); - delay(1024); - t2 = clkread(); - } while (t2 <= t1); - t2 -= t1; - delaydivisor = (delaydivisor * t2 + 1023) >> 10; -#ifdef DEBUG - if (dp) - printf("diff %ld us, new divisor ", t2); -#endif - if (dp) - printf("%u/1024 us\n", delaydivisor); } void @@ -288,8 +244,8 @@ cpu_initclocks(void) unsigned char dracorev; dracorev = is_draco(); if (dracorev >= 4) { - draco_ioct->io_timerlo = CLK_INTERVAL & 0xFF; - draco_ioct->io_timerhi = CLK_INTERVAL >> 8; + draco_ioct->io_timerlo = amiga_clk_interval & 0xFF; + draco_ioct->io_timerhi = amiga_clk_interval >> 8; draco_ioct->io_timerrst = 0; /* any value resets */ single_inst_bset_b(draco_ioct->io_status2, DRSTAT2_TMRINTENA); @@ -324,11 +280,11 @@ setstatclockrate(int hertz) } /* - * Returns number of usec since last recorded clock "tick" + * Returns ticks since last recorded clock "tick" * (i.e. clock interrupt). */ -u_long -clkread(void) +static u_int +clk_gettick(void) { u_int interval; u_char hi, hi2, lo; @@ -339,10 +295,10 @@ clkread(void) hi = draco_ioct->io_timerhi; lo = draco_ioct->io_timerlo; interval = ((hi<<8) | lo); - if (interval > CLK_INTERVAL) /* timer underflow */ - interval = 65536 + CLK_INTERVAL - interval; + if (interval > amiga_clk_interval) /* timer underflow */ + interval = 65536 + amiga_clk_interval - interval; else - interval = CLK_INTERVAL - interval; + interval = amiga_clk_interval - interval; } else #endif @@ -355,7 +311,7 @@ clkread(void) hi = hi2; } - interval = (CLK_INTERVAL - 1) - ((hi<<8) | lo); + interval = (amiga_clk_interval - 1) - ((hi<<8) | lo); /* * should read ICR and if there's an int pending, adjust @@ -364,7 +320,21 @@ clkread(void) */ } - return((interval * tick) / CLK_INTERVAL); + return interval; +} + +static u_int +clk_getcounter(struct timecounter *tc) +{ + int old_hardclock_ticks; + u_int clock_tick; + + do { + old_hardclock_ticks = hardclock_ticks; + clock_tick = clk_gettick(); + } while (old_hardclock_ticks != hardclock_ticks); + + return old_hardclock_ticks * amiga_clk_interval + clock_tick; } #if notyet @@ -589,12 +559,12 @@ initprofclock(void) #endif /* * The profile interrupt interval must be an even divisor - * of the CLK_INTERVAL so that scaling from a system clock + * of the amiga_clk_interval so that scaling from a system clock * tick to a profile clock tick is possible using integer math. */ - if (profint > CLK_INTERVAL || (CLK_INTERVAL % profint) != 0) - profint = CLK_INTERVAL; - profscale = CLK_INTERVAL / profint; + if (profint > amiga_clk_interval || (amiga_clk_interval % profint) != 0) + profint = amiga_clk_interval; + profscale = amiga_clk_interval / profint; } void @@ -667,3 +637,51 @@ profclock(void *pc, int ps) } #endif #endif + +void +delay(unsigned int n) +{ + unsigned int cur_tick, initial_tick; + int remaining; + + /* + * Read the counter first, so that the rest of the setup overhead is + * counted. + */ + initial_tick = clk_gettick(); + + if (amiga_clk_interval == 0) { + /* + * Clock is not initialised yet, + * so just do some ad-hoc loop. + */ + static uint32_t dummy; + + n *= 4; + while (n--) + dummy *= eclockfreq; + return; + } + + if (n <= fast_delay_limit) { + /* + * For unsigned arithmetic, division can be replaced with + * multiplication with the inverse and a shift. + */ + remaining = n * eclockfreq / 1000000; + } else { + /* This is a very long delay. + * Being slow here doesn't matter. + */ + remaining = (unsigned long long) n * eclockfreq / 1000000; + } + + while (remaining > 0) { + cur_tick = clk_gettick(); + if (cur_tick > initial_tick) + remaining -= amiga_clk_interval - (cur_tick - initial_tick); + else + remaining -= initial_tick - cur_tick; + initial_tick = cur_tick; + } +} diff --git a/sys/arch/amiga/include/cpu.h b/sys/arch/amiga/include/cpu.h index 1edcd490d097..4594baada491 100644 --- a/sys/arch/amiga/include/cpu.h +++ b/sys/arch/amiga/include/cpu.h @@ -1,4 +1,4 @@ -/* $NetBSD: cpu.h,v 1.68 2007/10/17 19:53:25 garbled Exp $ */ +/* $NetBSD: cpu.h,v 1.69 2008/01/06 18:50:31 mhitch Exp $ */ /* * Copyright (c) 1982, 1990 The Regents of the University of California. @@ -206,11 +206,6 @@ int is_a4000 __P((void)); #define is_draco() ((machineid >> 24) == 0x7d ? (machineid >> 16) & 0xff : 0) #endif -/* - * Prototypes from clock.c - */ -u_long clkread __P((void)); - #ifdef DRACO /* * Prototypes from kbd.c diff --git a/sys/arch/amiga/include/param.h b/sys/arch/amiga/include/param.h index bdfcf3b50aae..44e0adf55583 100644 --- a/sys/arch/amiga/include/param.h +++ b/sys/arch/amiga/include/param.h @@ -1,4 +1,4 @@ -/* $NetBSD: param.h,v 1.44 2005/12/11 12:16:36 christos Exp $ */ +/* $NetBSD: param.h,v 1.45 2008/01/06 18:50:32 mhitch Exp $ */ /* * Copyright (c) 1982, 1986, 1990 The Regents of the University of California. @@ -120,8 +120,8 @@ extern volatile unsigned short *amiga_intena_read, *amiga_intena_write; #ifndef _LOCORE -void delay __P((int)); -void DELAY __P((int)); +void delay(unsigned int); +#define DELAY(x) delay(x) #endif /* !_LOCORE */ #endif /* _KERNEL */ diff --git a/sys/arch/amiga/include/types.h b/sys/arch/amiga/include/types.h index c3bf73e3e025..5a3e06924dd5 100644 --- a/sys/arch/amiga/include/types.h +++ b/sys/arch/amiga/include/types.h @@ -1,4 +1,4 @@ -/* $NetBSD: types.h,v 1.20 2007/10/17 19:53:26 garbled Exp $ */ +/* $NetBSD: types.h,v 1.21 2008/01/06 18:50:32 mhitch Exp $ */ #ifndef _MACHINE_TYPES_H_ #define _MACHINE_TYPES_H_ @@ -7,5 +7,6 @@ #define __GENERIC_SOFT_INTERRUPTS_ALL_LEVELS #define __HAVE_GENERIC_TODR +#define __HAVE_TIMECOUNTER #endif