Commit ed5316d4 authored by Jack Steiner's avatar Jack Steiner Committed by Andi Kleen

[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: default avatarJack Steiner <steiner@sgi.com>
Signed-off-by: default avatarAndi Kleen <ak@suse.de>

 arch/x86_64/kernel/time.c |   25 +++++++++++++++++++++----
 1 file changed, 21 insertions(+), 4 deletions(-)
parent 9d24a81e
...@@ -656,6 +656,25 @@ core_initcall(cpufreq_tsc); ...@@ -656,6 +656,25 @@ core_initcall(cpufreq_tsc);
*/ */
#define TICK_COUNT 100000000 #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) static unsigned int __init hpet_calibrate_tsc(void)
{ {
...@@ -666,13 +685,11 @@ 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_save(flags);
local_irq_disable(); local_irq_disable();
hpet_start = hpet_readl(HPET_COUNTER); read_hpet_tsc(&hpet_start, &tsc_start);
rdtscl(tsc_start);
do { do {
local_irq_disable(); local_irq_disable();
hpet_now = hpet_readl(HPET_COUNTER); read_hpet_tsc(&hpet_now, &tsc_now);
tsc_now = get_cycles_sync();
local_irq_restore(flags); local_irq_restore(flags);
} while ((tsc_now - tsc_start) < TICK_COUNT && } while ((tsc_now - tsc_start) < TICK_COUNT &&
(hpet_now - hpet_start) < TICK_COUNT); (hpet_now - hpet_start) < TICK_COUNT);
......
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