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: