aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJoris van Rantwijk <joris@eljakim.nl>2004-06-17 18:00:52 -0700
committerLinus Torvalds <torvalds@ppc970.osdl.org>2004-06-17 18:00:52 -0700
commit6d58b1286c7ac88741374c158867f564e602b288 (patch)
treec72935cc5ca12b4c61bbf642567783d9e4618cb6
parent261efef31f15fff88ce6bddc760971565e8017f7 (diff)
downloadhistory-6d58b1286c7ac88741374c158867f564e602b288.tar.gz
[PATCH] Validate PM-Timer rate at boot time
Add 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. Tested by me (with broken timer) and John Stultz (with good timer) and believed to be ok. Signed-off-by: Andrew Morton <akpm@osdl.org> Signed-off-by: Linus Torvalds <torvalds@osdl.org>
-rw-r--r--arch/i386/kernel/timers/timer_pm.c38
1 files changed, 38 insertions, 0 deletions
diff --git a/arch/i386/kernel/timers/timer_pm.c b/arch/i386/kernel/timers/timer_pm.c
index 7aa63c9b8e18f3..737a0219d1937c 100644
--- a/arch/i386/kernel/timers/timer_pm.c
+++ b/arch/i386/kernel/timers/timer_pm.c
@@ -21,6 +21,14 @@
#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(),
@@ -57,6 +65,33 @@ static inline u32 read_pmtmr(void)
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;
@@ -89,6 +124,9 @@ static int init_pmtmr(char* override)
return -ENODEV;
pm_good:
+ if (verify_pmtmr_rate() != 0)
+ return -ENODEV;
+
init_cpu_khz();
return 0;
}