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:
parent
199e667b0f
commit
93d494f9a5
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue