cpu frequency change problems: at least warn; fix gettimeofday

Pavel Machek (pavel@suse.cz)
Fri, 19 Nov 1999 22:18:17 +0100


Hi!

It seems we have rather unsolvable problems with udelay() on
notebooks. This patch at least detects change of rdtsc speed, which is
sign of bogomips problems that might came later. [In future, we might
want to switch udelay() to use rdtsc, instead of bogomips. It should
be more accurate. If you want I can write such patch.] Yes and this
should fix gettimeofday to be monotonic on notebooks. So it is worth
applying after all.
Pavel

--- clean/arch/i386/kernel/time.c Sun Oct 10 11:22:47 1999
+++ linux/arch/i386/kernel/time.c Tue Nov 2 13:42:09 1999
@@ -144,7 +144,7 @@
* comp.protocols.time.ntp!
*/

-static unsigned long do_slow_gettimeoffset(void)
+static unsigned long do_read_hwtimer(void)
{
int count;

@@ -208,7 +209,7 @@

count -= 256;
#else
- printk("do_slow_gettimeoffset(): hardware timer problem?\n");
+ printk("do_read_hwtimer(): hardware timer problem?\n");
#endif
}
}
@@ -216,6 +217,12 @@
jiffies_p = jiffies_t;

count_p = count;
+ return count;
+}
+
+unsigned long do_slow_gettimeoffset(void)
+{
+ unsigned long count = do_read_hwtimer();

count = ((LATCH-1) - count) * TICK_SIZE;
count = (count + LATCH/2) / LATCH;
@@ -429,6 +466,27 @@
*/
write_lock(&xtime_lock);

+ if (use_tsc) {
+ long off = do_gettimeoffset();
+ static int faster = 0, slower = 0;
+ if (off > (1100000/HZ))
+ faster++;
+ else
+ faster = 0;
+
+ if (off < (900000/HZ))
+ slower++;
+ else
+ slower = 0;
+
+ if ((faster > 100) || (slower > 100)) {
+ printk( KERN_ERR "TSC is %s than it should be! ", (faster > 10) ? "faster" : "slower" );
+ big_calibrate_tsc();
+ faster = 0;
+ slower = 0;
+ }
+ }
+
if (use_tsc)
{
/*
@@ -547,7 +605,7 @@
#define CALIBRATE_LATCH (5 * LATCH)
#define CALIBRATE_TIME (5 * 1000020/HZ)

-static unsigned long __init calibrate_tsc(void)
+static unsigned long calibrate_tsc(void)
{
/* Set the Gate high, disable speaker */
outb((inb(0x61) & ~0x02) | 0x01, 0x61);
@@ -612,11 +670,39 @@
return 0;
}

+void big_calibrate_tsc(void)
+{
+ if (boot_cpu_data.x86_capability & X86_FEATURE_TSC) {
+ unsigned long tsc_quotient = calibrate_tsc();
+ if (tsc_quotient) {
+ fast_gettimeoffset_quotient = tsc_quotient;
+ use_tsc = 1;
+#ifndef do_gettimeoffset
+ do_gettimeoffset = do_fast_gettimeoffset;
+#endif
+ do_get_fast_time = do_gettimeofday;
+
+ /* report CPU clock rate in Hz.
+ * The formula is (10^6 * 2^32) / (2^32 * 1 / (clocks/us)) =
+ * clock/second. Our precision is about 100 ppm.
+ */
+ { unsigned long eax=0, edx=1000000;
+ __asm__("divl %2"
+ :"=a" (cpu_hz), "=d" (edx)
+ :"r" (tsc_quotient),
+ "0" (eax), "1" (edx));
+ printk("Detected %ld Hz processor.\n", cpu_hz);
+ }
+ }
+ }
+}
+
void __init time_init(void)
{
xtime.tv_sec = get_cmos_time();
xtime.tv_usec = 0;

+
/*
* If we have APM enabled or the CPU clock speed is variable
* (CPU stops clock on HLT or slows clock to save power)
@@ -644,29 +730,7 @@

dodgy_tsc();

- if (boot_cpu_data.x86_capability & X86_FEATURE_TSC) {
- unsigned long tsc_quotient = calibrate_tsc();
- if (tsc_quotient) {
- fast_gettimeoffset_quotient = tsc_quotient;
- use_tsc = 1;
-#ifndef do_gettimeoffset
- do_gettimeoffset = do_fast_gettimeoffset;
-#endif
- do_get_fast_time = do_gettimeofday;
-
- /* report CPU clock rate in Hz.
- * The formula is (10^6 * 2^32) / (2^32 * 1 / (clocks/us)) =
- * clock/second. Our precision is about 100 ppm.
- */
- { unsigned long eax=0, edx=1000000;
- __asm__("divl %2"
- :"=a" (cpu_hz), "=d" (edx)
- :"r" (tsc_quotient),
- "0" (eax), "1" (edx));
- printk("Detected %ld Hz processor.\n", cpu_hz);
- }
- }
- }
+ big_calibrate_tsc();

#ifdef CONFIG_VISWS
printk("Starting Cobalt Timer system clock\n");

-- 
I'm really pavel@ucw.cz. Look at http://195.113.31.123/~pavel.  Pavel
Hi! I'm a .signature virus! Copy me into your ~/.signature, please!

- To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majordomo@vger.rutgers.edu Please read the FAQ at http://www.tux.org/lkml/