Module Name: src Committed By: phx Date: Sat Dec 12 13:10:36 UTC 2009
Modified Files: src/sys/arch/amiga/amiga: amiga_init.c locore.s src/sys/arch/amiga/dev: clock.c kbd.c Log Message: Reverted the CIA-timer based delay() to the pre-5.0 method of a calibrated delay loop. This fixes keyboard handshaking problems with some A1200 models since 5.0 and restores the precision for short delays on DraCo systems (the QuickLogic timer has only a seventh of the CIA precision). Changed the keyboard handshaking delay from 2000 back to 200ms, although even the recommended 85ms were successfully tested on the most problematic A1200 keyboards. All those changes were tested on an A3000 and A1200 with 68060/50 CPU, and previously discussed on the port-amiga ML. To generate a diff of this commit: cvs rdiff -u -r1.117 -r1.118 src/sys/arch/amiga/amiga/amiga_init.c cvs rdiff -u -r1.148 -r1.149 src/sys/arch/amiga/amiga/locore.s cvs rdiff -u -r1.50 -r1.51 src/sys/arch/amiga/dev/clock.c cvs rdiff -u -r1.52 -r1.53 src/sys/arch/amiga/dev/kbd.c Please note that diffs are not public domain; they are subject to the copyright notices on the relevant files.
Modified files: Index: src/sys/arch/amiga/amiga/amiga_init.c diff -u src/sys/arch/amiga/amiga/amiga_init.c:1.117 src/sys/arch/amiga/amiga/amiga_init.c:1.118 --- src/sys/arch/amiga/amiga/amiga_init.c:1.117 Fri Dec 11 22:23:08 2009 +++ src/sys/arch/amiga/amiga/amiga_init.c Sat Dec 12 13:10:36 2009 @@ -1,4 +1,4 @@ -/* $NetBSD: amiga_init.c,v 1.117 2009/12/11 22:23:08 tsutsui Exp $ */ +/* $NetBSD: amiga_init.c,v 1.118 2009/12/12 13:10:36 phx Exp $ */ /* * Copyright (c) 1994 Michael L. Hitch @@ -36,7 +36,7 @@ #include "opt_devreload.h" #include <sys/cdefs.h> -__KERNEL_RCSID(0, "$NetBSD: amiga_init.c,v 1.117 2009/12/11 22:23:08 tsutsui Exp $"); +__KERNEL_RCSID(0, "$NetBSD: amiga_init.c,v 1.118 2009/12/12 13:10:36 phx Exp $"); #include <sys/param.h> #include <sys/systm.h> @@ -765,6 +765,7 @@ void start_c_finish(void) { + extern u_int32_t delaydivisor; #ifdef P5PPC68KBOARD struct cfdev *cdp, *ecdp; #endif @@ -895,6 +896,21 @@ } } #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 Index: src/sys/arch/amiga/amiga/locore.s diff -u src/sys/arch/amiga/amiga/locore.s:1.148 src/sys/arch/amiga/amiga/locore.s:1.149 --- src/sys/arch/amiga/amiga/locore.s:1.148 Fri Dec 4 17:28:54 2009 +++ src/sys/arch/amiga/amiga/locore.s Sat Dec 12 13:10:36 2009 @@ -1,4 +1,4 @@ -/* $NetBSD: locore.s,v 1.148 2009/12/04 17:28:54 tsutsui Exp $ */ +/* $NetBSD: locore.s,v 1.149 2009/12/12 13:10:36 phx Exp $ */ /* * Copyright (c) 1980, 1990 The Regents of the University of California. @@ -1573,6 +1573,16 @@ .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) @@ -1603,7 +1613,11 @@ .long FPU_NONE GLOBAL(protorp) .long 0x80000002,0 | prototype root pointer - +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 Index: src/sys/arch/amiga/dev/clock.c diff -u src/sys/arch/amiga/dev/clock.c:1.50 src/sys/arch/amiga/dev/clock.c:1.51 --- src/sys/arch/amiga/dev/clock.c:1.50 Fri Sep 11 19:43:08 2009 +++ src/sys/arch/amiga/dev/clock.c Sat Dec 12 13:10:36 2009 @@ -1,4 +1,4 @@ -/* $NetBSD: clock.c,v 1.50 2009/09/11 19:43:08 phx Exp $ */ +/* $NetBSD: clock.c,v 1.51 2009/12/12 13:10:36 phx Exp $ */ /* * Copyright (c) 1982, 1990 The Regents of the University of California. @@ -77,7 +77,7 @@ */ #include <sys/cdefs.h> -__KERNEL_RCSID(0, "$NetBSD: clock.c,v 1.50 2009/09/11 19:43:08 phx Exp $"); +__KERNEL_RCSID(0, "$NetBSD: clock.c,v 1.51 2009/12/12 13:10:36 phx Exp $"); #include <sys/param.h> #include <sys/kernel.h> @@ -100,26 +100,6 @@ #include <sys/PROF.h> #endif -/* the clocks run at NTSC: 715.909kHz or PAL: 709.379kHz. - We're using a 100 Hz clock. */ -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. * @@ -140,6 +120,26 @@ int clockmatch(struct device *, struct cfdata *, void *); void clockattach(struct device *, struct device *, void *); void cpu_initclocks(void); +static void calibrate_delay(struct device *); + +/* the clocks run at NTSC: 715.909kHz or PAL: 709.379kHz. + We're using a 100 Hz clock. */ +int amiga_clk_interval; +int eclockfreq; +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 */ +}; CFATTACH_DECL(clock, sizeof(struct device), clockmatch, clockattach, NULL, NULL); @@ -160,21 +160,20 @@ { const char *clockchip; unsigned short interval; + int chipfreq; #ifdef DRACO u_char dracorev; #endif -#ifdef DRACO - dracorev = is_draco(); -#endif - if (eclockfreq == 0) eclockfreq = 715909; /* guess NTSC */ + chipfreq = eclockfreq; + #ifdef DRACO + dracorev = is_draco(); if (dracorev >= 4) { - if (amiga_clk_interval == 0) /* Only do this 1st time */ - eclockfreq /= 7; + chipfreq = eclockfreq / 7; clockchip = "QuickLogic"; } else if (dracorev) { clockcia = (struct CIA *)CIAAbase; @@ -186,16 +185,14 @@ clockchip = "CIA B"; } - amiga_clk_interval = (eclockfreq / hz); - - clk_timecounter.tc_name = clockchip; - clk_timecounter.tc_frequency = eclockfreq; - - fast_delay_limit = UINT_MAX / amiga_clk_interval; + amiga_clk_interval = chipfreq / hz; if (dp != NULL) { /* real autoconfig? */ printf(": %s system hz %d hardware hz %d\n", clockchip, hz, - eclockfreq); + chipfreq); + + clk_timecounter.tc_name = clockchip; + clk_timecounter.tc_frequency = chipfreq; tc_init(&clk_timecounter); } @@ -209,6 +206,8 @@ draco_ioct->io_timerlo = amiga_clk_interval & 0xff; draco_ioct->io_timerhi = amiga_clk_interval >> 8; + calibrate_delay(dp); + return; } #endif @@ -222,7 +221,6 @@ /* * load interval into registers. * the clocks run at NTSC: 715.909kHz or PAL: 709.379kHz - * supprort for PAL WHEN?!?! XXX */ interval = amiga_clk_interval - 1; @@ -235,6 +233,8 @@ * start timer A in continuous mode */ clockcia->cra = (clockcia->cra & 0xc0) | 1; + + calibrate_delay(dp); } void @@ -280,7 +280,7 @@ } /* - * Returns ticks since last recorded clock "tick" + * Returns ticks since last recorded clock "tick" * (i.e. clock interrupt). */ static u_int @@ -326,30 +326,88 @@ static u_int clk_getcounter(struct timecounter *tc) { - static int last_hardclock_ticks; - static u_int last_clock_tick = 0; - int old_hardclock_ticks; - u_int clock_tick; + static int prev_hardclock; + static u_int prev_counter; + int cur_hardclock; + u_int counter; do { - old_hardclock_ticks = hardclock_ticks; - clock_tick = clk_gettick(); - } while (old_hardclock_ticks != hardclock_ticks); + cur_hardclock = hardclock_ticks; + counter = clk_gettick(); + } while (cur_hardclock != hardclock_ticks); /* * Handle the situation of a wrapped interval counter, while * the hardclock() interrupt was not yet executed to update * hardclock_ticks. */ - if (last_hardclock_ticks > old_hardclock_ticks) - old_hardclock_ticks = last_hardclock_ticks; - if (clock_tick < last_clock_tick && - old_hardclock_ticks == last_hardclock_ticks) - old_hardclock_ticks++; - last_hardclock_ticks = old_hardclock_ticks; - last_clock_tick = clock_tick; + if (cur_hardclock < prev_hardclock) + cur_hardclock = prev_hardclock; + if (counter < prev_counter && cur_hardclock == prev_hardclock) + cur_hardclock++; + + prev_hardclock = cur_hardclock; + prev_counter = counter; + + return cur_hardclock * amiga_clk_interval + counter; +} + +/* + * 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 clk_gettick(), 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% + */ +static void +calibrate_delay(struct device *dp) +{ + unsigned long t1, t2; + extern u_int32_t delaydivisor; + /* XXX this should be defined elsewhere */ - return old_hardclock_ticks * amiga_clk_interval + clock_tick; + if (dp) + printf("Calibrating delay loop... "); + + do { + t1 = clk_gettick(); + delay(1024); + t2 = clk_gettick(); + } while (t2 <= t1); + t2 = ((t2 - t1) * 1000000) / (amiga_clk_interval * hz); + delaydivisor = (delaydivisor * t2 + 1023) >> 10; +#ifdef DEBUG + if (dp) + printf("\ndiff %ld us, new divisor %u/1024 us\n", t2, + delaydivisor); + do { + t1 = clk_gettick(); + delay(1024); + t2 = clk_gettick(); + } while (t2 <= t1); + t2 = ((t2 - t1) * 1000000) / (amiga_clk_interval * hz); + delaydivisor = (delaydivisor * t2 + 1023) >> 10; + if (dp) + printf("diff %ld us, new divisor %u/1024 us\n", t2, + delaydivisor); +#endif + do { + t1 = clk_gettick(); + delay(1024); + t2 = clk_gettick(); + } while (t2 <= t1); + t2 = ((t2 - t1) * 1000000) / (amiga_clk_interval * hz); + 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); } #if notyet @@ -652,51 +710,3 @@ } #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; - } -} Index: src/sys/arch/amiga/dev/kbd.c diff -u src/sys/arch/amiga/dev/kbd.c:1.52 src/sys/arch/amiga/dev/kbd.c:1.53 --- src/sys/arch/amiga/dev/kbd.c:1.52 Sat Mar 14 15:36:01 2009 +++ src/sys/arch/amiga/dev/kbd.c Sat Dec 12 13:10:36 2009 @@ -1,4 +1,4 @@ -/* $NetBSD: kbd.c,v 1.52 2009/03/14 15:36:01 dsl Exp $ */ +/* $NetBSD: kbd.c,v 1.53 2009/12/12 13:10:36 phx Exp $ */ /* * Copyright (c) 1982, 1986, 1990 The Regents of the University of California. @@ -32,7 +32,7 @@ */ #include <sys/cdefs.h> -__KERNEL_RCSID(0, "$NetBSD: kbd.c,v 1.52 2009/03/14 15:36:01 dsl Exp $"); +__KERNEL_RCSID(0, "$NetBSD: kbd.c,v 1.53 2009/12/12 13:10:36 phx Exp $"); #include <sys/param.h> #include <sys/systm.h> @@ -560,7 +560,7 @@ } #endif /* wait 200 microseconds (for bloody Cherry keyboards..) */ - DELAY(2000); /* fudge delay a bit for some keyboards */ + DELAY(200); /* fudge delay a bit for some keyboards */ ciaa.cra &= ~(1 << 6); /* process the character */ @@ -652,7 +652,7 @@ ciaa.cra |= (1 << 6); /* serial line output */ ciaa.sdr = 0xff; /* ack */ /* wait 200 microseconds */ - DELAY(2000); /* XXXX only works as long as DELAY doesn't + DELAY(200); /* XXXX only works as long as DELAY doesn't * use a timer and waits.. */ ciaa.cra &= ~(1 << 6); ciaa.sdr = in;