Skip to content

Commit

Permalink
[PATCH] i386: trust the PM-Timer calibration of the local APIC timer
Browse files Browse the repository at this point in the history
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: Thomas Gleixner <tglx@linutronix.de>
Acked-by: Ingo Molnar <mingo@elte.hu>
Acked-by: Andi Kleen <ak@suse.de>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
  • Loading branch information
Thomas Gleixner authored and Linus Torvalds committed Mar 18, 2007
1 parent 303cd15 commit ca1b940
Showing 1 changed file with 27 additions and 51 deletions.
78 changes: 27 additions & 51 deletions arch/i386/kernel/apic.c
Original file line number Diff line number Diff line change
Expand Up @@ -338,6 +338,7 @@ void __init setup_boot_APIC_clock(void)
void (*real_handler)(struct clock_event_device *dev);
unsigned long deltaj;
long delta, deltapm;
int pm_referenced = 0;

apic_printk(APIC_VERBOSE, "Using local APIC timer interrupts.\n"
"calibrating APIC timer ...\n");
Expand All @@ -357,7 +358,8 @@ void __init setup_boot_APIC_clock(void)
/* Let the interrupts run */
local_irq_enable();

while(lapic_cal_loops <= LAPIC_CAL_LOOPS);
while (lapic_cal_loops <= LAPIC_CAL_LOOPS)
cpu_relax();

local_irq_disable();

Expand Down Expand Up @@ -394,6 +396,7 @@ void __init setup_boot_APIC_clock(void)
"%lu (%ld)\n", (unsigned long) res, delta);
delta = (long) res;
}
pm_referenced = 1;
}

/* Calculate the scaled math multiplication factor */
Expand Down Expand Up @@ -423,68 +426,41 @@ void __init setup_boot_APIC_clock(void)
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;
levt->event_handler = lapic_cal_handler;
lapic_timer_setup(CLOCK_EVT_MODE_PERIODIC, levt);
lapic_cal_loops = -1;

/* Let the interrupts run */
local_irq_enable();
/* We trust the pm timer based calibration */
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 */
lapic_timer_setup(CLOCK_EVT_MODE_SHUTDOWN, levt);
while(lapic_cal_loops <= LAPIC_CAL_LOOPS)
cpu_relax();

local_irq_enable();
local_irq_disable();

/* Jiffies delta */
deltaj = lapic_cal_j2 - lapic_cal_j1;
apic_printk(APIC_VERBOSE, "... jiffies delta = %lu\n", deltaj);
/* Stop the lapic timer */
lapic_timer_setup(CLOCK_EVT_MODE_SHUTDOWN, levt);

/* Check, if the PM timer is available */
deltapm = lapic_cal_pm2 - lapic_cal_pm1;
apic_printk(APIC_VERBOSE, "... PM timer delta = %ld\n", deltapm);
local_irq_enable();

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 */
if (deltaj >= LAPIC_CAL_LOOPS-2 &&
deltaj <= LAPIC_CAL_LOOPS+2) {
if (deltaj >= LAPIC_CAL_LOOPS-2 && deltaj <= LAPIC_CAL_LOOPS+2)
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) {
Expand Down

0 comments on commit ca1b940

Please sign in to comment.