diff options
author | Jack Steiner <steiner@sgi.com> | 2007-01-10 19:52:44 -0500 |
---|---|---|
committer | Andi Kleen <andi@basil.nowhere.org> | 2007-01-10 19:52:44 -0500 |
commit | ed5316d4457b35c7b4942af028d6b878174264f7 (patch) | |
tree | ebaa1e04b31be2ae81d18411fa8ff41a2ed817f5 | |
parent | 9d24a81e84cee7cbf4656d178842838ac5ab23a4 (diff) |
[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 <steiner@sgi.com>
Signed-off-by: Andi Kleen <ak@suse.de>
arch/x86_64/kernel/time.c | 25 +++++++++++++++++++++----
1 file changed, 21 insertions(+), 4 deletions(-)
-rw-r--r-- | arch/x86_64/kernel/time.c | 25 |
1 files changed, 21 insertions, 4 deletions
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); | |||
656 | */ | 656 | */ |
657 | 657 | ||
658 | #define TICK_COUNT 100000000 | 658 | #define TICK_COUNT 100000000 |
659 | #define TICK_MIN 5000 | ||
660 | |||
661 | /* | ||
662 | * Some platforms take periodic SMI interrupts with 5ms duration. Make sure none | ||
663 | * occurs between the reads of the hpet & TSC. | ||
664 | */ | ||
665 | static void __init read_hpet_tsc(int *hpet, int *tsc) | ||
666 | { | ||
667 | int tsc1, tsc2, hpet1; | ||
668 | |||
669 | do { | ||
670 | tsc1 = get_cycles_sync(); | ||
671 | hpet1 = hpet_readl(HPET_COUNTER); | ||
672 | tsc2 = get_cycles_sync(); | ||
673 | } while (tsc2 - tsc1 > TICK_MIN); | ||
674 | *hpet = hpet1; | ||
675 | *tsc = tsc2; | ||
676 | } | ||
677 | |||
659 | 678 | ||
660 | static unsigned int __init hpet_calibrate_tsc(void) | 679 | static unsigned int __init hpet_calibrate_tsc(void) |
661 | { | 680 | { |
@@ -666,13 +685,11 @@ static unsigned int __init hpet_calibrate_tsc(void) | |||
666 | local_irq_save(flags); | 685 | local_irq_save(flags); |
667 | local_irq_disable(); | 686 | local_irq_disable(); |
668 | 687 | ||
669 | hpet_start = hpet_readl(HPET_COUNTER); | 688 | read_hpet_tsc(&hpet_start, &tsc_start); |
670 | rdtscl(tsc_start); | ||
671 | 689 | ||
672 | do { | 690 | do { |
673 | local_irq_disable(); | 691 | local_irq_disable(); |
674 | hpet_now = hpet_readl(HPET_COUNTER); | 692 | read_hpet_tsc(&hpet_now, &tsc_now); |
675 | tsc_now = get_cycles_sync(); | ||
676 | local_irq_restore(flags); | 693 | local_irq_restore(flags); |
677 | } while ((tsc_now - tsc_start) < TICK_COUNT && | 694 | } while ((tsc_now - tsc_start) < TICK_COUNT && |
678 | (hpet_now - hpet_start) < TICK_COUNT); | 695 | (hpet_now - hpet_start) < TICK_COUNT); |