Take care of National Geode (Cyrix MediaGX) built-in clock module bug.

Patch supplied by INOUE Yoshinari <pf5y-inue@asahi-net.or.jp>, kern/8654.
This commit is contained in:
minoura 2000-02-02 15:26:27 +00:00
parent 199e667b0f
commit 93d494f9a5
1 changed files with 139 additions and 1 deletions

View File

@ -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 <dev/isa/pcppivar.h>
#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;
}
}