Add timecounter support and borrow counter-based delay from i386.

From joerg;  testing and final tweaks by me.
This commit is contained in:
mhitch 2008-01-06 18:50:29 +00:00
parent 890508dbe9
commit c04c9a55d0
7 changed files with 125 additions and 175 deletions

View File

@ -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 * Copyright (c) 1994 Michael L. Hitch
@ -36,7 +36,7 @@
#include "opt_devreload.h" #include "opt_devreload.h"
#include <sys/cdefs.h> #include <sys/cdefs.h>
__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 <sys/param.h> #include <sys/param.h>
#include <sys/systm.h> #include <sys/systm.h>
@ -765,7 +765,6 @@ start_c(id, fphystart, fphysize, cphysize, esym_addr, flags, inh_sync,
void void
start_c_finish() start_c_finish()
{ {
extern u_int32_t delaydivisor;
#ifdef P5PPC68KBOARD #ifdef P5PPC68KBOARD
struct cfdev *cdp, *ecdp; struct cfdev *cdp, *ecdp;
#endif #endif
@ -901,21 +900,6 @@ start_c_finish()
} }
} }
#endif #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 void

View File

@ -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. * Copyright (c) 1980, 1990 The Regents of the University of California.
@ -1577,16 +1577,6 @@ Ldorebootend:
.align 2 .align 2
#endif #endif
nop 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 #ifdef M68060
ENTRY_NOPROFILE(intemu60) ENTRY_NOPROFILE(intemu60)
@ -1621,11 +1611,6 @@ GLOBAL(protorp)
GLOBAL(proc0paddr) GLOBAL(proc0paddr)
.long 0 | KVA of proc0 u-area .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 #ifdef DEBUG
ASGLOBAL(fulltflush) ASGLOBAL(fulltflush)
.long 0 .long 0

View File

@ -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. * Copyright (c) 1982, 1986, 1990 The Regents of the University of California.
@ -85,7 +85,7 @@
#include "opt_panicbutton.h" #include "opt_panicbutton.h"
#include <sys/cdefs.h> #include <sys/cdefs.h>
__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 <sys/param.h> #include <sys/param.h>
#include <sys/systm.h> #include <sys/systm.h>
@ -765,39 +765,6 @@ dumpsys()
delay(5000000); /* 5 seconds */ 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 void
initcpu() initcpu()
{ {

View File

@ -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. * Copyright (c) 1982, 1990 The Regents of the University of California.
@ -77,12 +77,13 @@
*/ */
#include <sys/cdefs.h> #include <sys/cdefs.h>
__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 <sys/param.h> #include <sys/param.h>
#include <sys/kernel.h> #include <sys/kernel.h>
#include <sys/device.h> #include <sys/device.h>
#include <sys/systm.h> #include <sys/systm.h>
#include <sys/timetc.h>
#include <machine/psl.h> #include <machine/psl.h>
#include <machine/cpu.h> #include <machine/cpu.h>
#include <amiga/amiga/device.h> #include <amiga/amiga/device.h>
@ -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. /* the clocks run at NTSC: 715.909kHz or PAL: 709.379kHz.
We're using a 100 Hz clock. */ We're using a 100 Hz clock. */
#define CLK_INTERVAL amiga_clk_interval
int amiga_clk_interval; int amiga_clk_interval;
int eclockfreq; int eclockfreq;
unsigned int fast_delay_limit;
struct CIA *clockcia; 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. * Machine-dependent clock routines.
* *
@ -119,7 +132,7 @@ struct CIA *clockcia;
* Resettodr restores the time of day hardware after a time change. * Resettodr restores the time of day hardware after a time change.
* *
* A note on the real-time clock: * 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 * This is because the counter decrements to zero after N+1 enabled clock
* periods where N is the value loaded into the counter. * periods where N is the value loaded into the counter.
*/ */
@ -127,7 +140,6 @@ struct CIA *clockcia;
int clockmatch(struct device *, struct cfdata *, void *); int clockmatch(struct device *, struct cfdata *, void *);
void clockattach(struct device *, struct device *, void *); void clockattach(struct device *, struct device *, void *);
void cpu_initclocks(void); void cpu_initclocks(void);
void calibrate_delay(struct device *);
CFATTACH_DECL(clock, sizeof(struct device), CFATTACH_DECL(clock, sizeof(struct device),
clockmatch, clockattach, NULL, NULL); clockmatch, clockattach, NULL, NULL);
@ -152,15 +164,17 @@ clockattach(struct device *pdp, struct device *dp, void *auxp)
u_char dracorev; u_char dracorev;
#endif #endif
#ifdef DRACO
dracorev = is_draco();
#endif
if (eclockfreq == 0) if (eclockfreq == 0)
eclockfreq = 715909; /* guess NTSC */ eclockfreq = 715909; /* guess NTSC */
CLK_INTERVAL = (eclockfreq / 100);
#ifdef DRACO #ifdef DRACO
dracorev = is_draco();
if (dracorev >= 4) { if (dracorev >= 4) {
CLK_INTERVAL = (eclockfreq / 700); if (amiga_clk_interval == 0) /* Only do this 1st time */
eclockfreq /= 7;
clockchip = "QuickLogic"; clockchip = "QuickLogic";
} else if (dracorev) { } else if (dracorev) {
clockcia = (struct CIA *)CIAAbase; clockcia = (struct CIA *)CIAAbase;
@ -172,13 +186,18 @@ clockattach(struct device *pdp, struct device *dp, void *auxp)
clockchip = "CIA B"; 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, printf(": %s system hz %d hardware hz %d\n", clockchip, hz,
#ifdef DRACO
dracorev >= 4 ? eclockfreq / 7 : eclockfreq);
#else
eclockfreq); eclockfreq);
#endif tc_init(&clk_timecounter);
}
#ifdef DRACO #ifdef DRACO
if (dracorev >= 4) { if (dracorev >= 4) {
@ -187,10 +206,8 @@ clockattach(struct device *pdp, struct device *dp, void *auxp)
* but need this for delay calibration. * but need this for delay calibration.
*/ */
draco_ioct->io_timerlo = CLK_INTERVAL & 0xff; draco_ioct->io_timerlo = amiga_clk_interval & 0xff;
draco_ioct->io_timerhi = CLK_INTERVAL >> 8; draco_ioct->io_timerhi = amiga_clk_interval >> 8;
calibrate_delay(dp);
return; 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 * the clocks run at NTSC: 715.909kHz or PAL: 709.379kHz
* supprort for PAL WHEN?!?! XXX * supprort for PAL WHEN?!?! XXX
*/ */
interval = CLK_INTERVAL - 1; interval = amiga_clk_interval - 1;
/* /*
* order of setting is important ! * order of setting is important !
@ -218,67 +235,6 @@ clockattach(struct device *pdp, struct device *dp, void *auxp)
* start timer A in continuous mode * start timer A in continuous mode
*/ */
clockcia->cra = (clockcia->cra & 0xc0) | 1; 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 void
@ -288,8 +244,8 @@ cpu_initclocks(void)
unsigned char dracorev; unsigned char dracorev;
dracorev = is_draco(); dracorev = is_draco();
if (dracorev >= 4) { if (dracorev >= 4) {
draco_ioct->io_timerlo = CLK_INTERVAL & 0xFF; draco_ioct->io_timerlo = amiga_clk_interval & 0xFF;
draco_ioct->io_timerhi = CLK_INTERVAL >> 8; draco_ioct->io_timerhi = amiga_clk_interval >> 8;
draco_ioct->io_timerrst = 0; /* any value resets */ draco_ioct->io_timerrst = 0; /* any value resets */
single_inst_bset_b(draco_ioct->io_status2, DRSTAT2_TMRINTENA); 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). * (i.e. clock interrupt).
*/ */
u_long static u_int
clkread(void) clk_gettick(void)
{ {
u_int interval; u_int interval;
u_char hi, hi2, lo; u_char hi, hi2, lo;
@ -339,10 +295,10 @@ clkread(void)
hi = draco_ioct->io_timerhi; hi = draco_ioct->io_timerhi;
lo = draco_ioct->io_timerlo; lo = draco_ioct->io_timerlo;
interval = ((hi<<8) | lo); interval = ((hi<<8) | lo);
if (interval > CLK_INTERVAL) /* timer underflow */ if (interval > amiga_clk_interval) /* timer underflow */
interval = 65536 + CLK_INTERVAL - interval; interval = 65536 + amiga_clk_interval - interval;
else else
interval = CLK_INTERVAL - interval; interval = amiga_clk_interval - interval;
} else } else
#endif #endif
@ -355,7 +311,7 @@ clkread(void)
hi = hi2; 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 * 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 #if notyet
@ -589,12 +559,12 @@ initprofclock(void)
#endif #endif
/* /*
* The profile interrupt interval must be an even divisor * 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. * tick to a profile clock tick is possible using integer math.
*/ */
if (profint > CLK_INTERVAL || (CLK_INTERVAL % profint) != 0) if (profint > amiga_clk_interval || (amiga_clk_interval % profint) != 0)
profint = CLK_INTERVAL; profint = amiga_clk_interval;
profscale = CLK_INTERVAL / profint; profscale = amiga_clk_interval / profint;
} }
void void
@ -667,3 +637,51 @@ profclock(void *pc, int ps)
} }
#endif #endif
#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;
}
}

View File

@ -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. * 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) #define is_draco() ((machineid >> 24) == 0x7d ? (machineid >> 16) & 0xff : 0)
#endif #endif
/*
* Prototypes from clock.c
*/
u_long clkread __P((void));
#ifdef DRACO #ifdef DRACO
/* /*
* Prototypes from kbd.c * Prototypes from kbd.c

View File

@ -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. * 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; extern volatile unsigned short *amiga_intena_read, *amiga_intena_write;
#ifndef _LOCORE #ifndef _LOCORE
void delay __P((int)); void delay(unsigned int);
void DELAY __P((int)); #define DELAY(x) delay(x)
#endif /* !_LOCORE */ #endif /* !_LOCORE */
#endif /* _KERNEL */ #endif /* _KERNEL */

View File

@ -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_ #ifndef _MACHINE_TYPES_H_
#define _MACHINE_TYPES_H_ #define _MACHINE_TYPES_H_
@ -7,5 +7,6 @@
#define __GENERIC_SOFT_INTERRUPTS_ALL_LEVELS #define __GENERIC_SOFT_INTERRUPTS_ALL_LEVELS
#define __HAVE_GENERIC_TODR #define __HAVE_GENERIC_TODR
#define __HAVE_TIMECOUNTER
#endif #endif