Commit 6d58b128 authored by Joris van Rantwijk's avatar Joris van Rantwijk Committed by Linus Torvalds

[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: default avatarAndrew Morton <akpm@osdl.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@osdl.org>
parent 261efef3
......@@ -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;
}
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment