From ed5316d4457b35c7b4942af028d6b878174264f7 Mon Sep 17 00:00:00 2001 From: Jack Steiner Date: Thu, 11 Jan 2007 01:52:44 +0100 Subject: [PATCH] x86-64: - Ignore long SMI interrupts in clock calibration Ensure that no SMI interrupts occur between the read of the HPET & TSC in the clock calibration loop. I noticed that a 2.66GHz system incorrectly detected the processor clock speed about 1/7 of the time: time.c: Detected 2660.005 MHz processor. (most of the time) time.c: Detected 2988.203 MHz processor. (sometime) The problem is caused by an SMI interrupt occuring in hpet_calibrate_tsc() between the read of the HPET & TSC. Prior to switching the BIOS into ACPI mode, it appears that every 27msec an SMI interrupt occurs. The SMI interrupt takes 4.8 msec to process. Note: On my test system, TICK_MIN had to be >380. I picked 5000 to minimize risk of having a value that is too small for other platforms. Signed-off-by: Jack Steiner Signed-off-by: Andi Kleen arch/x86_64/kernel/time.c | 25 +++++++++++++++++++++---- 1 file changed, 21 insertions(+), 4 deletions(-) --- arch/x86_64/kernel/time.c | 25 +++++++++++++++++++++---- 1 file changed, 21 insertions(+), 4 deletions(-) (limited to 'arch/x86_64/kernel') diff --git a/arch/x86_64/kernel/time.c b/arch/x86_64/kernel/time.c index 9f05bc9b2dad..5cc76d0d331f 100644 --- a/arch/x86_64/kernel/time.c +++ b/arch/x86_64/kernel/time.c @@ -656,6 +656,25 @@ core_initcall(cpufreq_tsc); */ #define TICK_COUNT 100000000 +#define TICK_MIN 5000 + +/* + * Some platforms take periodic SMI interrupts with 5ms duration. Make sure none + * occurs between the reads of the hpet & TSC. + */ +static void __init read_hpet_tsc(int *hpet, int *tsc) +{ + int tsc1, tsc2, hpet1; + + do { + tsc1 = get_cycles_sync(); + hpet1 = hpet_readl(HPET_COUNTER); + tsc2 = get_cycles_sync(); + } while (tsc2 - tsc1 > TICK_MIN); + *hpet = hpet1; + *tsc = tsc2; +} + static unsigned int __init hpet_calibrate_tsc(void) { @@ -666,13 +685,11 @@ static unsigned int __init hpet_calibrate_tsc(void) local_irq_save(flags); local_irq_disable(); - hpet_start = hpet_readl(HPET_COUNTER); - rdtscl(tsc_start); + read_hpet_tsc(&hpet_start, &tsc_start); do { local_irq_disable(); - hpet_now = hpet_readl(HPET_COUNTER); - tsc_now = get_cycles_sync(); + read_hpet_tsc(&hpet_now, &tsc_now); local_irq_restore(flags); } while ((tsc_now - tsc_start) < TICK_COUNT && (hpet_now - hpet_start) < TICK_COUNT); -- cgit v1.2.3