Cyrix MediaGX/NSC Geode is a NatSemi's i386 CPU.
It's old integrated southbrige companion chip cs5510/5520 has a PIT latch bug.
this breaks TSC syncronization,  but I found work around about this.
After I posted lkml about this bug in Mar. 2002, I have tried to make this smart.
In past post, I say the dual calling of 'calibrate_tsc()' make things good.
but now I found the simple solution.
This PIT need the delay after 1st counter reset.
in timer_tsc.c, 
 	outb(0xb0, 0x43);			/* binary, mode 0, LSB/MSB, Ch 2 */
 	outb(CALIBRATE_LATCH & 0xff, 0x42);	/* LSB of count */
        outb(CALIBRATE_LATCH >> 8, 0x42);       /* MSB of count */
is changed to 
 	outb(0xb0, 0x43);			/* binary, mode 0, LSB/MSB, Ch 2 */
 	outb(CALIBRATE_LATCH & 0xff, 0x42);	/* LSB of count */
        outb_p(CALIBRATE_LATCH >> 8, 0x42);     /* add delay to start count */
this 'outb_p()' fixes this problem on startup. 
The same kind of problem exist in do_gettimeofday().
if we disables TSC on PC using Geode/CS5520, latch value of PIT has no
accuracy then many many timejumps or timebacks happens. (every 2-10 sec) 
with this patch, timejumps or timebacks don't occurs.
This patch also removes dodgy_tsc() workaround.
this patch tried on mini note-PC CASSIOPEIA FIVA http://www.casio.co.jp/mpc/103/
you can get spec from http://www.da-cha.org/fiva/fiva.html
I use with this workaround on 6 month and i think this work fine. 
I tried 2.4.18, 2.4.19, 2.5.43.
------------------------------------------------------------
--- linux-2.5.43-orig/arch/i386/config.in	2002-10-17 13:04:36.000000000 +0900
+++ linux-2.5.43/arch/i386/config.in	2002-10-17 13:02:44.000000000 +0900
@@ -30,6 +30,7 @@
 	 Winchip-C6				CONFIG_MWINCHIPC6 \
 	 Winchip-2				CONFIG_MWINCHIP2 \
 	 Winchip-2A/Winchip-3			CONFIG_MWINCHIP3D \
+	 MediaGX/Geode				CONFIG_GEODE \
 	 CyrixIII/VIA-C3			CONFIG_MCYRIXIII" Pentium-Pro
 #
 # Define implied options from the CPU selection here
@@ -67,6 +68,13 @@
    define_bool CONFIG_X86_PPRO_FENCE y
    define_bool CONFIG_X86_F00F_BUG y
 fi
+if [ "$CONFIG_GEODE" = "y" ]; then
+   define_int  CONFIG_X86_L1_CACHE_SHIFT 5
+   define_bool CONFIG_X86_USE_STRING_486 y
+   define_bool CONFIG_X86_ALIGNMENT_16 y
+   define_bool CONFIG_X86_TSC y
+   define_bool CONFIG_X86_PPRO_FENCE y
+fi
 if [ "$CONFIG_M586TSC" = "y" ]; then
    define_int  CONFIG_X86_L1_CACHE_SHIFT 5
    define_bool CONFIG_X86_USE_STRING_486 y
diff -urB -x .config -x '*.[oasS]' -x '*.in' -x '*.rej' -x '*.orig' linux-2.5.43-orig/arch/i386/kernel/cpu/common.c linux-2.5.43/arch/i386/kernel/cpu/common.c
--- linux-2.5.43-orig/arch/i386/kernel/cpu/common.c	2002-10-12 13:21:05.000000000 +0900
+++ linux-2.5.43/arch/i386/kernel/cpu/common.c	2002-10-14 20:40:46.000000000 +0900
@@ -356,17 +356,6 @@
 	       boot_cpu_data.x86_capability[2],
 	       boot_cpu_data.x86_capability[3]);
 }
-/*
- *	Perform early boot up checks for a valid TSC. See arch/i386/kernel/time.c
- */
- 
-void __init dodgy_tsc(void)
-{
-	get_cpu_vendor(&boot_cpu_data);
-	if (( boot_cpu_data.x86_vendor == X86_VENDOR_CYRIX ) ||
-	    ( boot_cpu_data.x86_vendor == X86_VENDOR_NSC   ))
-		cpu_devs[X86_VENDOR_CYRIX]->c_init(&boot_cpu_data);
-}
 
 void __init print_cpu_info(struct cpuinfo_x86 *c)
 {
Only in linux-2.5.43/arch/i386/kernel/cpu: common.c~
diff -urB -x .config -x '*.[oasS]' -x '*.in' -x '*.rej' -x '*.orig' linux-2.5.43-orig/arch/i386/kernel/cpu/cyrix.c linux-2.5.43/arch/i386/kernel/cpu/cyrix.c
--- linux-2.5.43-orig/arch/i386/kernel/cpu/cyrix.c	2002-10-12 13:21:34.000000000 +0900
+++ linux-2.5.43/arch/i386/kernel/cpu/cyrix.c	2002-10-14 21:34:57.000000000 +0900
@@ -170,7 +233,7 @@
 		c->coma_bug = 1;
 		break;
 
-	case 4: /* MediaGX/GXm */
+        case 4: /* MediaGX/GXm or Geode GXM/GXLV/GX1 */
 #ifdef CONFIG_PCI
 		/* It isn't really a PCI quirk directly, but the cure is the
 		   same. The MediaGX has deep magic SMM stuff that handles the
@@ -188,29 +247,26 @@
 		isa_dma_bridge_buggy = 2;
 #endif		
 		c->x86_cache_size=16;	/* Yep 16K integrated cache thats it */
+ 
+		/*
+		 *  The 5510/5520 companion chips have a funky PIT.
+		 */  
+		if (pci_find_device(PCI_VENDOR_ID_CYRIX, PCI_DEVICE_ID_CYRIX_5510, NULL) ||
+		    pci_find_device(PCI_VENDOR_ID_CYRIX, PCI_DEVICE_ID_CYRIX_5520, NULL))
+			pit_latch_buggy = 1;
 
 		/* GXm supports extended cpuid levels 'ala' AMD */
 		if (c->cpuid_level == 2) {
-			/* Enable Natsemi MMX extensions */
-			setCx86(CX86_CCR7, getCx86(CX86_CCR7) | 1);
+                        /* Enable cxMMX extensions (GX1 Datasheet 54) */
+                        setCx86(CX86_CCR7, getCx86(CX86_CCR7)|1);
 
 			get_model_name(c);  /* get CPU marketing name */
-			/*
-			 *  The 5510/5520 companion chips have a funky PIT
-			 *  that breaks the TSC synchronizing, so turn it off
-			 */
-			if (pci_find_device(PCI_VENDOR_ID_CYRIX, PCI_DEVICE_ID_CYRIX_5510, NULL) ||
-				pci_find_device(PCI_VENDOR_ID_CYRIX, PCI_DEVICE_ID_CYRIX_5520, NULL))
-				clear_bit(X86_FEATURE_TSC, c->x86_capability);
 			return;
 		}
 		else {  /* MediaGX */
 			Cx86_cb[2] = (dir0_lsn & 1) ? '3' : '4';
 			p = Cx86_cb+2;
 			c->x86_model = (dir1 & 0x20) ? 1 : 2;
-#ifndef CONFIG_CS5520
-			clear_bit(X86_FEATURE_TSC, c->x86_capability);
-#endif
 		}
 		break;
 
diff -urB -x .config -x '*.[oasS]' -x '*.in' -x '*.rej' -x '*.orig' linux-2.5.43-orig/arch/i386/kernel/time.c linux-2.5.43/arch/i386/kernel/time.c
--- linux-2.5.43-orig/arch/i386/kernel/time.c	2002-10-17 13:04:36.000000000 +0900
+++ linux-2.5.43/arch/i386/kernel/time.c	2002-10-17 13:02:44.000000000 +0900
@@ -61,6 +61,7 @@
 #include <asm/arch_hooks.h>
 
 extern spinlock_t i8259A_lock;
+int pit_latch_buggy;              /* extern */
 
 #include "do_timer.h"
 
diff -urB -x .config -x '*.[oasS]' -x '*.in' -x '*.rej' -x '*.orig' linux-2.5.43-orig/arch/i386/kernel/timers/timer_tsc.c linux-2.5.43/arch/i386/kernel/timers/timer_tsc.c
--- linux-2.5.43-orig/arch/i386/kernel/timers/timer_tsc.c	2002-10-17 13:04:36.000000000 +0900
+++ linux-2.5.43/arch/i386/kernel/timers/timer_tsc.c	2002-10-17 13:02:45.000000000 +0900
@@ -60,6 +60,10 @@
 static void mark_offset_tsc(void)
 {
 	int count;
+#ifdef CONFIG_GEODE
+        int countmp;
+        static int count1=0, count2=LATCH;
+#endif
 	/*
 	 * It is important that these two operations happen almost at
 	 * the same time. We do the RDTSC stuff first, since it's
@@ -83,6 +87,22 @@
 	count |= inb(0x40) << 8;
 	spin_unlock(&i8253_lock);
 
+#ifdef CONFIG_GEODE
+	if (pit_latch_buggy) {
+		/* get center value of last 3 time lutch */
+		if (count2 >= count && count >= count1
+		    || count1 >= count && count >= count2) {
+			count2 = count1; count1 = count;
+		} else if (count1 >= count2 && count2 >= count
+			   || count >= count2 && count2 >= count1) {
+			countmp = count;count = count2;
+			count2 = count1;count1 = countmp;
+		} else {
+			count2 = count1; count1 = count; count = count1;
+		}
+	}
+#endif
+
 	count = ((LATCH-1) - count) * TICK_SIZE;
 	delay_at_last_interrupt = (count + LATCH/2) / LATCH;
 }
@@ -114,7 +134,11 @@
 	 */
 	outb(0xb0, 0x43);			/* binary, mode 0, LSB/MSB, Ch 2 */
 	outb(CALIBRATE_LATCH & 0xff, 0x42);	/* LSB of count */
-	outb(CALIBRATE_LATCH >> 8, 0x42);	/* MSB of count */
+#ifndef CONFIG_GEODE
+        outb(CALIBRATE_LATCH >> 8, 0x42);       /* MSB of count */
+#else
+        outb_p(CALIBRATE_LATCH >> 8, 0x42);     /* add delay to start count */
+#endif
 
 	{
 		unsigned long startlow, starthigh;
@@ -238,8 +262,6 @@
  	 *	moaned if you have the only one in the world - you fix it!
  	 */
  
- 	dodgy_tsc();
- 	
 	if (cpu_has_tsc) {
 		unsigned long tsc_quotient = calibrate_tsc();
 		if (tsc_quotient) {
-- Hiroshi Miura --- http://www.da-cha.org/ NTTDATA Corp. Marketing & Business Strategy Planning Dept. --- miurahr@nttdata.co.jp Key fingerprint = 9117 9407 5684 FBF1 4063 15B4 401D D077 04AB 8617 -- My hacking life is happy as the day is long- To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/