Messages in this thread Patch in this message | | | Date | Thu, 27 May 2004 23:35:23 +0200 (CEST) | From | Joris van Rantwijk <> | Subject | [PATCH] 2.6.6 Validate PM-Timer rate at boot time |
| |
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@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/
| |