diff --git a/sys/arch/i386/isa/clock.c b/sys/arch/i386/isa/clock.c index ee78f0f71340..62e4dcef7a6f 100644 --- a/sys/arch/i386/isa/clock.c +++ b/sys/arch/i386/isa/clock.c @@ -1,4 +1,4 @@ -/* $NetBSD: clock.c,v 1.62 1999/11/06 01:14:01 enami Exp $ */ +/* $NetBSD: clock.c,v 1.63 2000/02/02 15:26:27 minoura Exp $ */ /*- * Copyright (c) 1993, 1994 Charles M. Hannum. @@ -85,6 +85,9 @@ NEGLIGENCE, OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +/* #define CLOCKDEBUG */ +/* #define CLOCK_PARANOIA */ + /* * Primitive clock interrupt routines. */ @@ -110,6 +113,13 @@ WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. #if (NPCPPI > 0) #include +#ifdef CLOCKDEBUG +int clock_debug = 0; +#define DPRINTF(arg) if (clock_debug) printf arg +#else +#define DPRINTF(arg) +#endif + int sysbeepmatch __P((struct device *, struct cfdata *, void *)); void sysbeepattach __P((struct device *, struct device *, void *)); @@ -131,6 +141,9 @@ void rtcput __P((mc_todregs *)); int bcdtobin __P((int)); int bintobcd __P((int)); +static void check_clock_bug __P((void)); +static inline int gettick_broken_latch __P((void)); + __inline u_int mc146818_read __P((void *, u_int)); __inline void mc146818_write __P((void *, u_int, u_int)); @@ -156,6 +169,105 @@ mc146818_write(sc, reg, datum) } static u_long rtclock_tval; +static int clock_broken_latch = 0; + +#ifdef CLOCK_PARANOIA +static int ticks[6]; +#endif + +/* + * i8254 latch check routine: + * National Geode (formerly Cyrix MediaGX) has a sirious bug in + * its built-in i8254-compatible clock module. + * Set the variable 'clock_broken_latch' to indicate it. + * XXX check only cpu_id + */ +static void +check_clock_bug() +{ + extern int cpu_id; + + switch (cpu_id) { + case 0x440: /* Cyrix MediaGX */ + case 0x540: /* GXm */ + clock_broken_latch = 1; + break; + default: + clock_broken_latch = 0; + break; + } +} + +int +gettick_broken_latch() +{ + u_long ef; + int v1, v2, v3; + int w1, w2, w3; + + /* Don't want someone screwing with the counter + while we're here. */ + ef = read_eflags(); + disable_intr(); + + v1 = inb(TIMER_CNTR0); + v1 |= inb(TIMER_CNTR0) << 8; + v2 = inb(TIMER_CNTR0); + v2 |= inb(TIMER_CNTR0) << 8; + v3 = inb(TIMER_CNTR0); + v3 |= inb(TIMER_CNTR0) << 8; + + write_eflags(ef); + +#ifdef CLOCK_PARANOIA + if (clock_debug) { + ticks[0] = ticks[3]; + ticks[1] = ticks[4]; + ticks[2] = ticks[5]; + ticks[3] = v1; + ticks[4] = v2; + ticks[5] = v3; + } +#endif + + if (v1 >= v2 && v2 >= v3 && v1 - v3 < 0x200) + return (v2); + +#define _swap_val(a, b) do { \ + int c = a; \ + a = b; \ + b = c; \ +} while (0) + + /* + * sort v1 v2 v3 + */ + if (v1 < v2) + _swap_val(v1, v2); + if (v2 < v3) + _swap_val(v2, v3); + if (v1 < v2) + _swap_val(v1, v2); + + /* + * compute the middle value + */ + + if (v1 - v3 < 0x200) + return (v2); + + w1 = v2 - v3; + w2 = v3 - v1 + rtclock_tval; + w3 = v1 - v2; + if (w1 >= w2) { + if (w1 >= w3) + return (v1); + } else { + if (w2 >= w3) + return (v2); + } + return (v3); +} /* minimal initialization, enough for delay() */ void @@ -179,6 +291,8 @@ initrtclock() outb(IO_TIMER1, tval / 256); rtclock_tval = tval; + + check_clock_bug(); } /* @@ -297,6 +411,9 @@ gettick() u_long ef; u_char lo, hi; + if (clock_broken_latch) + return (gettick_broken_latch()); + /* Don't want someone screwing with the counter while we're here. */ ef = read_eflags(); disable_intr(); @@ -369,11 +486,32 @@ delay(n) } while (n > 0) { +#ifdef CLOCK_PARANOIA + int delta; + tick = gettick(); + if (tick > otick) + delta = rtclock_tval - (tick - otick); + else + delta = otick - tick; + if (delta < 0 || delta >= rtclock_tval / 2) { + DPRINTF(("delay: ignore ticks %.4x-%.4x", + otick, tick)); + if (clock_broken_latch) { + DPRINTF((" (%.4x %.4x %.4x %.4x %.4x %.4x)\n", + ticks[0], ticks[1], ticks[2], + ticks[3], ticks[4], ticks[5])); + } else { + DPRINTF(("\n")); + } + } else + n -= delta; +#else tick = gettick(); if (tick > otick) n -= rtclock_tval - (tick - otick); else n -= otick - tick; +#endif otick = tick; } }