[PATCH] 2.6.6 Validate PM-Timer rate at boot time

From: Joris van Rantwijk
Date: Thu May 27 2004 - 16:37:36 EST



The patch below adds a check to the PM-Timer initialization code.
It validates the PM-Timer rate against PIT channel 2 and rejects
the PM-Timer if its rate is not withing 5% of the expected number.

Rationale:
The PMTMR timers of certain (older) mainboards are running at invalid
rates, often much faster than the rate expected by the PM-Timer code.
This causes the system clock to run much too fast.
See also http://bugme.osdl.org/show_bug.cgi?id=2375

Possible workarounds are disabling the PM-Timer in the kernel config
or disabling the PM-Timer at boot time through the "clock=tsc" parameter.
However, we believe it is more user friendly to automatically validate
the PM-Timer rate at boot time before using it as the system time source.

Patch is against linux 2.6.6.
Tested by me (with broken timer) and John Stultz (with good timer)
and believed to be ok.
Please consider applying.

Bye,
Joris.

--- linux-2.6.6/arch/i386/kernel/timers/timer_pm.orig.c Sun Apr 4 05:36:18 2004
+++ linux-2.6.6/arch/i386/kernel/timers/timer_pm.c Thu May 27 00:11:22 2004
@@ -19,10 +19,18 @@
#include <asm/timer.h>
#include <asm/smp.h>
#include <asm/io.h>
#include <asm/arch_hooks.h>

+#include <linux/timex.h>
+#include "mach_timer.h"
+
+/* Number of PMTMR ticks expected during calibration run */
+#define PMTMR_TICKS_PER_SEC 3579545
+#define PMTMR_EXPECTED_RATE \
+ ((CALIBRATE_LATCH * (PMTMR_TICKS_PER_SEC >> 10)) / (CLOCK_TICK_RATE>>10))
+

/* The I/O port the PMTMR resides at.
* The location is detected during setup_arch(),
* in arch/i386/acpi/boot.c */
u32 pmtmr_ioport = 0;
@@ -55,10 +63,37 @@

/* mask the output to 24 bits */
return v2 & ACPI_PM_MASK;
}

+
+/*
+ * Some boards have the PMTMR running way too fast. We check
+ * the PMTMR rate against PIT channel 2 to catch these cases.
+ */
+static int verify_pmtmr_rate(void)
+{
+ u32 value1, value2;
+ unsigned long count, delta;
+
+ mach_prepare_counter();
+ value1 = read_pmtmr();
+ mach_countup(&count);
+ value2 = read_pmtmr();
+ delta = (value2 - value1) & ACPI_PM_MASK;
+
+ /* Check that the PMTMR delta is within 5% of what we expect */
+ if (delta < (PMTMR_EXPECTED_RATE * 19) / 20 ||
+ delta > (PMTMR_EXPECTED_RATE * 21) / 20) {
+ printk(KERN_INFO "PM-Timer running at invalid rate: %lu%% of normal - aborting.\n", 100UL * delta / PMTMR_EXPECTED_RATE);
+ return -1;
+ }
+
+ return 0;
+}
+
+
static int init_pmtmr(char* override)
{
u32 value1, value2;
unsigned int i;

@@ -87,10 +122,13 @@
}
printk(KERN_INFO "PM-Timer had no reasonable result: 0x%#x - aborting.\n", value1);
return -ENODEV;

pm_good:
+ if (verify_pmtmr_rate() != 0)
+ return -ENODEV;
+
init_cpu_khz();
return 0;
}

static inline u32 cyc2us(u32 cycles)
-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/