Commit ca1b940c authored by Thomas Gleixner's avatar Thomas Gleixner Committed by Linus Torvalds

[PATCH] i386: trust the PM-Timer calibration of the local APIC timer

When PM-Timer is available for local APIC timer calibration we can skip the
verification of the calibrated time value.  The resulting error is quite
small on a bunch of evaluated platforms and is less harming than the
observed false positives.

We need to keep the verification on systems, which have no PM-Timer to
avoid bogus local APIC timer calibrations in the range of factor 2-10,
which can be observed when swicthing off the PM-timer support in the kernel
configuration.

The wrong calibration values are probably caused by SMM code trying to
emulate a PS/2 keyboard from a (maybe connected or not) USB keyboard.  This
prohibits the accurate delivery of PIT interrupts, which are used to
calibrate the local APIC timer.  Unfortunately we have no way to disable
this BIOS misfeature in the early boot process.

Add also the dropped cpu_relax() back to the wait loops.
Signed-off-by: default avatarThomas Gleixner <tglx@linutronix.de>
Acked-by: default avatarIngo Molnar <mingo@elte.hu>
Acked-by: default avatarAndi Kleen <ak@suse.de>
Signed-off-by: default avatarAndrew Morton <akpm@linux-foundation.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@linux-foundation.org>
parent 303cd153
...@@ -338,6 +338,7 @@ void __init setup_boot_APIC_clock(void) ...@@ -338,6 +338,7 @@ void __init setup_boot_APIC_clock(void)
void (*real_handler)(struct clock_event_device *dev); void (*real_handler)(struct clock_event_device *dev);
unsigned long deltaj; unsigned long deltaj;
long delta, deltapm; long delta, deltapm;
int pm_referenced = 0;
apic_printk(APIC_VERBOSE, "Using local APIC timer interrupts.\n" apic_printk(APIC_VERBOSE, "Using local APIC timer interrupts.\n"
"calibrating APIC timer ...\n"); "calibrating APIC timer ...\n");
...@@ -357,7 +358,8 @@ void __init setup_boot_APIC_clock(void) ...@@ -357,7 +358,8 @@ void __init setup_boot_APIC_clock(void)
/* Let the interrupts run */ /* Let the interrupts run */
local_irq_enable(); local_irq_enable();
while(lapic_cal_loops <= LAPIC_CAL_LOOPS); while (lapic_cal_loops <= LAPIC_CAL_LOOPS)
cpu_relax();
local_irq_disable(); local_irq_disable();
...@@ -394,6 +396,7 @@ void __init setup_boot_APIC_clock(void) ...@@ -394,6 +396,7 @@ void __init setup_boot_APIC_clock(void)
"%lu (%ld)\n", (unsigned long) res, delta); "%lu (%ld)\n", (unsigned long) res, delta);
delta = (long) res; delta = (long) res;
} }
pm_referenced = 1;
} }
/* Calculate the scaled math multiplication factor */ /* Calculate the scaled math multiplication factor */
...@@ -423,68 +426,41 @@ void __init setup_boot_APIC_clock(void) ...@@ -423,68 +426,41 @@ void __init setup_boot_APIC_clock(void)
calibration_result / (1000000 / HZ), calibration_result / (1000000 / HZ),
calibration_result % (1000000 / HZ)); calibration_result % (1000000 / HZ));
apic_printk(APIC_VERBOSE, "... verify APIC timer\n");
/*
* Setup the apic timer manually
*/
local_apic_timer_verify_ok = 1; local_apic_timer_verify_ok = 1;
levt->event_handler = lapic_cal_handler;
lapic_timer_setup(CLOCK_EVT_MODE_PERIODIC, levt);
lapic_cal_loops = -1;
/* Let the interrupts run */ /* We trust the pm timer based calibration */
local_irq_enable(); if (!pm_referenced) {
apic_printk(APIC_VERBOSE, "... verify APIC timer\n");
while(lapic_cal_loops <= LAPIC_CAL_LOOPS); /*
* Setup the apic timer manually
*/
levt->event_handler = lapic_cal_handler;
lapic_timer_setup(CLOCK_EVT_MODE_PERIODIC, levt);
lapic_cal_loops = -1;
local_irq_disable(); /* Let the interrupts run */
local_irq_enable();
/* Stop the lapic timer */ while(lapic_cal_loops <= LAPIC_CAL_LOOPS)
lapic_timer_setup(CLOCK_EVT_MODE_SHUTDOWN, levt); cpu_relax();
local_irq_enable(); local_irq_disable();
/* Jiffies delta */ /* Stop the lapic timer */
deltaj = lapic_cal_j2 - lapic_cal_j1; lapic_timer_setup(CLOCK_EVT_MODE_SHUTDOWN, levt);
apic_printk(APIC_VERBOSE, "... jiffies delta = %lu\n", deltaj);
/* Check, if the PM timer is available */ local_irq_enable();
deltapm = lapic_cal_pm2 - lapic_cal_pm1;
apic_printk(APIC_VERBOSE, "... PM timer delta = %ld\n", deltapm);
local_apic_timer_verify_ok = 0; /* Jiffies delta */
deltaj = lapic_cal_j2 - lapic_cal_j1;
apic_printk(APIC_VERBOSE, "... jiffies delta = %lu\n", deltaj);
if (deltapm) {
if (deltapm > (pm_100ms - pm_thresh) &&
deltapm < (pm_100ms + pm_thresh)) {
apic_printk(APIC_VERBOSE, "... PM timer result ok\n");
/* Check, if the jiffies result is consistent */
if (deltaj < LAPIC_CAL_LOOPS-2 ||
deltaj > LAPIC_CAL_LOOPS+2) {
/*
* Not sure, what we can do about this one.
* When high resultion timers are active
* and the lapic timer does not stop in C3
* we are fine. Otherwise more trouble might
* be waiting. -- tglx
*/
printk(KERN_WARNING "Global event device %s "
"has wrong frequency "
"(%lu ticks instead of %d)\n",
global_clock_event->name, deltaj,
LAPIC_CAL_LOOPS);
}
local_apic_timer_verify_ok = 1;
}
} else {
/* Check, if the jiffies result is consistent */ /* Check, if the jiffies result is consistent */
if (deltaj >= LAPIC_CAL_LOOPS-2 && if (deltaj >= LAPIC_CAL_LOOPS-2 && deltaj <= LAPIC_CAL_LOOPS+2)
deltaj <= LAPIC_CAL_LOOPS+2) {
apic_printk(APIC_VERBOSE, "... jiffies result ok\n"); apic_printk(APIC_VERBOSE, "... jiffies result ok\n");
local_apic_timer_verify_ok = 1; else
} local_apic_timer_verify_ok = 0;
} }
if (!local_apic_timer_verify_ok) { if (!local_apic_timer_verify_ok) {
......
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