diff options
author | Michael Shalayeff <mickey@cvs.openbsd.org> | 2002-05-16 15:33:11 +0000 |
---|---|---|
committer | Michael Shalayeff <mickey@cvs.openbsd.org> | 2002-05-16 15:33:11 +0000 |
commit | 0067565f657d02c8a513c87a00d9fd2a43006a92 (patch) | |
tree | 0903cec7dc7a6c01ac610302a7c02a2cc199e45a /sys/arch/i386 | |
parent | d754e515aeddb505eb6d8e92aef1902f5759717a (diff) |
fix for a timer latch bug on the cyrix mediagx and gxm cpus.
based on freebsd pr#6630, netbsd pr#8654, openbsd pr#1492 .
does not affect other cpu models (cyrix or not).
asked by markus@ and testing.
Diffstat (limited to 'sys/arch/i386')
-rw-r--r-- | sys/arch/i386/i386/machdep.c | 7 | ||||
-rw-r--r-- | sys/arch/i386/isa/clock.c | 91 |
2 files changed, 80 insertions, 18 deletions
diff --git a/sys/arch/i386/i386/machdep.c b/sys/arch/i386/i386/machdep.c index 1c675f34d30..cdc59b4c026 100644 --- a/sys/arch/i386/i386/machdep.c +++ b/sys/arch/i386/i386/machdep.c @@ -1,4 +1,4 @@ -/* $OpenBSD: machdep.c,v 1.204 2002/03/30 09:42:28 mickey Exp $ */ +/* $OpenBSD: machdep.c,v 1.205 2002/05/16 15:33:05 mickey Exp $ */ /* $NetBSD: machdep.c,v 1.214 1996/11/10 03:16:17 thorpej Exp $ */ /*- @@ -1041,7 +1041,6 @@ cyrix6x86_cpu_setup(cpu_device, model, step) int model, step; { #if defined(I486_CPU) || defined(I586_CPU) || defined(I686_CPU) - extern int cpu_feature; switch (model) { case -1: /* M1 w/o cpuid */ @@ -1061,10 +1060,6 @@ cyrix6x86_cpu_setup(cpu_device, model, step) printf("%s: xchg bug workaround performed\n", cpu_device); break; /* fallthrough? */ - case 4: /* GXm */ - /* Unset the TSC bit until calibrate_delay() gets fixed. */ - cpu_feature &= ~CPUID_TSC; - break; } #endif } diff --git a/sys/arch/i386/isa/clock.c b/sys/arch/i386/isa/clock.c index 489a9874593..e86cda7777c 100644 --- a/sys/arch/i386/isa/clock.c +++ b/sys/arch/i386/isa/clock.c @@ -1,4 +1,4 @@ -/* $OpenBSD: clock.c,v 1.25 2002/03/14 01:26:33 millert Exp $ */ +/* $OpenBSD: clock.c,v 1.26 2002/05/16 15:33:10 mickey Exp $ */ /* $NetBSD: clock.c,v 1.39 1996/05/12 23:11:54 mycroft Exp $ */ /*- @@ -149,7 +149,7 @@ u_int mc146818_read(void *, u_int); void mc146818_write(void *, u_int, u_int); #if defined(I586_CPU) || defined(I686_CPU) -int pentium_mhz; +int pentium_mhz, clock_broken_latch; #endif #define SECMIN ((unsigned)60) /* seconds per minute */ @@ -227,6 +227,21 @@ initrtclock() /* Correct rounding will buy us a better precision in timekeeping */ outb(IO_TIMER1, TIMER_DIV(hz) % 256); outb(IO_TIMER1, TIMER_DIV(hz) / 256); + +#if defined(CPU_I586) || defined(CPU_I686) + { + extern int cpu_id; + switch (cpu_id) { + case 0x440: /* Cyrix MediaGX */ + case 0x540: /* Cyrix GXm */ + clock_broken_latch = 1; + break; + default: + clock_broken_latch = 0; + break; + } + } +#endif } int @@ -260,16 +275,68 @@ rtcintr(arg) int gettick() { - u_char lo, hi; - - /* Don't want someone screwing with the counter while we're here. */ - disable_intr(); - /* Select counter 0 and latch it. */ - outb(TIMER_MODE, TIMER_SEL0 | TIMER_LATCH); - lo = inb(TIMER_CNTR0); - hi = inb(TIMER_CNTR0); - enable_intr(); - return ((hi << 8) | lo); + +#if defined(I586_CPU) || defined(I686_CPU) + if (clock_broken_latch) { + int v1, v2, v3; + int w1, w2, w3; + + 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; + + enable_intr(); + + 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 + TIMER_DIV(hz); + w3 = v1 - v2; + if (w1 >= w2) { + if (w1 >= w3) + return (v1); + } else { + if (w2 >= w3) + return (v2); + } + return (v3); + } else +#endif + { + u_char lo, hi; + + disable_intr(); + /* Select counter 0 and latch it. */ + outb(TIMER_MODE, TIMER_SEL0 | TIMER_LATCH); + lo = inb(TIMER_CNTR0); + hi = inb(TIMER_CNTR0); + + enable_intr(); + return ((hi << 8) | lo); + } } /* |