Subject: kern/8654: MediaGX(m) clock bug
To: None <gnats-bugs@gnats.netbsd.org>
From: None <pf5y-inue@asahi-net.or.jp>
List: netbsd-bugs
Date: 10/20/1999 10:21:56
>Number: 8654
>Category: kern
>Synopsis: MediaGX(m) clock bug
>Confidential: no
>Severity: serious
>Priority: medium
>Responsible: kern-bug-people (Kernel Bug People)
>State: open
>Class: sw-bug
>Submitter-Id: net
>Arrival-Date: Wed Oct 20 10:21:00 1999
>Last-Modified:
>Originator: INOUE Yoshinari
>Organization:
ASAHI-NET, JAPAN
>Release: NetBSD/i386 1.4.1
>Environment:
Casio FIVA 101 (MediaGXm 200Mhz)
System: NetBSD fiva.dummy 1.4.1 NetBSD 1.4.1 (J) #56: Thu Oct 21 01:45:53 JST 1999 inoue@fiva.dummy:/home/inoue/src/NetBSD/sys.bak/arch/i386/compile/J i386
>Description:
delay() is broken on a machine with MediaGX(m) CPU. This is
because a bug of the CPU. Plsese see FreeBSD's GNATS kern/6630 for
detail.
>How-To-Repeat:
Boot kernel in debug mode and call 'delay(0t10000000)'.
It returns very fast on a MediaGX(m) machine.
>Fix:
Apply the following patch to /sys/arch/i386/isa/clock.c.
I am not sure whether the function cyrix6x86_cpu_setup() should also be
modified for MediaGX(m).
--- /sys/arch/i386/isa/clock.c Tue Mar 30 02:54:34 1999
+++ clock.c Thu Oct 21 01:51:04 1999
@@ -85,6 +85,9 @@
WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
+#define CLOCKDEBUG
+/* #define CLOCK_PARANOIA */
+
/*
* Primitive clock interrupt routines.
*/
@@ -110,6 +113,16 @@
#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
+
+static void check_clock_bug __P((void));
+static inline int gettick_broken_latch __P((void));
+
int sysbeepmatch __P((struct device *, struct cfdata *, void *));
void sysbeepattach __P((struct device *, struct device *, void *));
@@ -157,6 +170,103 @@
}
static u_long rtclock_tval;
+static int clock_broken_latch = 0;
+
+#ifdef CLOCK_PARANOIA
+static int ticks[6];
+#endif
+
+/*
+ * check i8254 latch routine:
+ * set the variable 'clock_broken_latch'
+ * 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() */
static void
@@ -180,6 +290,8 @@
outb(IO_TIMER1, tval / 256);
rtclock_tval = tval;
+
+ check_clock_bug();
}
/*
@@ -298,6 +410,9 @@
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();
@@ -370,11 +485,32 @@
}
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;
}
}
>Audit-Trail:
>Unformatted: