diff options
| author | Thomas Gleixner <tglx@linutronix.de> | 2007-02-16 04:28:06 -0500 |
|---|---|---|
| committer | Linus Torvalds <torvalds@woody.linux-foundation.org> | 2007-02-16 11:13:59 -0500 |
| commit | d36b49b91065dbfa305c5a66010b3497c741eee0 (patch) | |
| tree | 2c1873a72da1b18900c5bf9984de1a33ee2ca38c | |
| parent | e9e2cdb412412326c4827fc78ba27f410d837e6e (diff) | |
[PATCH] i386 rework local apic timer calibration
The local apic timer calibration has two problem cases:
1. The calibration is based on readout of the PIT/HPET timer to detect the
wrap of the periodic tick. It happens that a box gets stuck in the
calibration loop due to a PIT with a broken readout function.
2. CoreDuo boxen show a sporadic PIT runs too slow defect, which results
in a wrong lapic calibration. The PIT goes back to normal operation once
the lapic timer is switched to periodic mode.
Both are existing and unfixed problems in the current upstream kernel and
prevent certain laptops and other systems from booting Linux.
Rework the code to address both problems:
- Make the calibration interrupt driven. This removes the wait_timer_tick
magic hackery from lapic.c and time_hpet.c. The clockevents framework
allows easy substitution of the global tick event handler for the
calibration. This is more accurate than monitoring jiffies. At this point
of the boot process, nothing disturbes the interrupt delivery, so the
results are very accurate.
- Verify the calibration against the PM timer, when available by using the
early access function. When the measured calibration period is outside of
an one percent window, then the lapic timer calibration is adjusted to the
pm timer result.
- Verify the calibration by running the lapic timer with the calibration
handler. Disable lapic timer in case of deviation.
This also removes the "synchronization" of the local apic timer to the global
tick. This synchronization never worked, as there is no way to synchronize
PIT(HPET) and local APIC timer. The synchronization by waiting for the tick
just alignes the local APIC timer for the first events, but later the events
drift away due to the different clocks. Removing the "sync" is just
randomizing the asynchronous behaviour at setup time.
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Signed-off-by: Ingo Molnar <mingo@elte.hu>
Cc: Zachary Amsden <zach@vmware.com>
Cc: "Eric W. Biederman" <ebiederm@xmission.com>
Cc: Rohit Seth <rohitseth@google.com>
Cc: Andi Kleen <ak@suse.de>
Cc: john stultz <johnstul@us.ibm.com>
Cc: Roman Zippel <zippel@linux-m68k.org>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
| -rw-r--r-- | arch/i386/kernel/apic.c | 365 | ||||
| -rw-r--r-- | include/asm-i386/apic.h | 2 |
2 files changed, 215 insertions, 152 deletions
diff --git a/arch/i386/kernel/apic.c b/arch/i386/kernel/apic.c index e98b5c750bdf..9655c233e6f1 100644 --- a/arch/i386/kernel/apic.c +++ b/arch/i386/kernel/apic.c | |||
| @@ -26,6 +26,7 @@ | |||
| 26 | #include <linux/sysdev.h> | 26 | #include <linux/sysdev.h> |
| 27 | #include <linux/cpu.h> | 27 | #include <linux/cpu.h> |
| 28 | #include <linux/clockchips.h> | 28 | #include <linux/clockchips.h> |
| 29 | #include <linux/acpi_pmtmr.h> | ||
| 29 | #include <linux/module.h> | 30 | #include <linux/module.h> |
| 30 | 31 | ||
| 31 | #include <asm/atomic.h> | 32 | #include <asm/atomic.h> |
| @@ -59,8 +60,8 @@ | |||
| 59 | */ | 60 | */ |
| 60 | static int enable_local_apic __initdata = 0; | 61 | static int enable_local_apic __initdata = 0; |
| 61 | 62 | ||
| 62 | /* Enable local APIC timer for highres/dyntick on UP */ | 63 | /* Local APIC timer verification ok */ |
| 63 | static int enable_local_apic_timer __initdata = 0; | 64 | static int local_apic_timer_verify_ok; |
| 64 | 65 | ||
| 65 | /* | 66 | /* |
| 66 | * Debug level, exported for io_apic.c | 67 | * Debug level, exported for io_apic.c |
| @@ -82,7 +83,7 @@ static void apic_pm_activate(void); | |||
| 82 | static struct clock_event_device lapic_clockevent = { | 83 | static struct clock_event_device lapic_clockevent = { |
| 83 | .name = "lapic", | 84 | .name = "lapic", |
| 84 | .features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT | 85 | .features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT |
| 85 | | CLOCK_EVT_FEAT_C3STOP, | 86 | | CLOCK_EVT_FEAT_C3STOP | CLOCK_EVT_FEAT_DUMMY, |
| 86 | .shift = 32, | 87 | .shift = 32, |
| 87 | .set_mode = lapic_timer_setup, | 88 | .set_mode = lapic_timer_setup, |
| 88 | .set_next_event = lapic_next_event, | 89 | .set_next_event = lapic_next_event, |
| @@ -159,64 +160,8 @@ int lapic_get_maxlvt(void) | |||
| 159 | * Local APIC timer | 160 | * Local APIC timer |
| 160 | */ | 161 | */ |
| 161 | 162 | ||
| 162 | /* | 163 | /* Clock divisor is set to 16 */ |
| 163 | * This part sets up the APIC 32 bit clock in LVTT1, with HZ interrupts | 164 | #define APIC_DIVISOR 16 |
| 164 | * per second. We assume that the caller has already set up the local | ||
| 165 | * APIC. | ||
| 166 | * | ||
| 167 | * The APIC timer is not exactly sync with the external timer chip, it | ||
| 168 | * closely follows bus clocks. | ||
| 169 | */ | ||
| 170 | |||
| 171 | /* | ||
| 172 | * FIXME: Move this to i8253.h. There is no need to keep the access to | ||
| 173 | * the PIT scattered all around the place -tglx | ||
| 174 | */ | ||
| 175 | |||
| 176 | /* | ||
| 177 | * The timer chip is already set up at HZ interrupts per second here, | ||
| 178 | * but we do not accept timer interrupts yet. We only allow the BP | ||
| 179 | * to calibrate. | ||
| 180 | */ | ||
| 181 | static unsigned int __devinit get_8254_timer_count(void) | ||
| 182 | { | ||
| 183 | unsigned long flags; | ||
| 184 | |||
| 185 | unsigned int count; | ||
| 186 | |||
| 187 | spin_lock_irqsave(&i8253_lock, flags); | ||
| 188 | |||
| 189 | outb_p(0x00, PIT_MODE); | ||
| 190 | count = inb_p(PIT_CH0); | ||
| 191 | count |= inb_p(PIT_CH0) << 8; | ||
| 192 | |||
| 193 | spin_unlock_irqrestore(&i8253_lock, flags); | ||
| 194 | |||
| 195 | return count; | ||
| 196 | } | ||
| 197 | |||
| 198 | /* next tick in 8254 can be caught by catching timer wraparound */ | ||
| 199 | static void __devinit wait_8254_wraparound(void) | ||
| 200 | { | ||
| 201 | unsigned int curr_count, prev_count; | ||
| 202 | |||
| 203 | curr_count = get_8254_timer_count(); | ||
| 204 | do { | ||
| 205 | prev_count = curr_count; | ||
| 206 | curr_count = get_8254_timer_count(); | ||
| 207 | |||
| 208 | /* workaround for broken Mercury/Neptune */ | ||
| 209 | if (prev_count >= curr_count + 0x100) | ||
| 210 | curr_count = get_8254_timer_count(); | ||
| 211 | |||
| 212 | } while (prev_count >= curr_count); | ||
| 213 | } | ||
| 214 | |||
| 215 | /* | ||
| 216 | * Default initialization for 8254 timers. If we use other timers like HPET, | ||
| 217 | * we override this later | ||
| 218 | */ | ||
| 219 | void (*wait_timer_tick)(void) __devinitdata = wait_8254_wraparound; | ||
| 220 | 165 | ||
| 221 | /* | 166 | /* |
| 222 | * This function sets up the local APIC timer, with a timeout of | 167 | * This function sets up the local APIC timer, with a timeout of |
| @@ -228,9 +173,6 @@ void (*wait_timer_tick)(void) __devinitdata = wait_8254_wraparound; | |||
| 228 | * We do reads before writes even if unnecessary, to get around the | 173 | * We do reads before writes even if unnecessary, to get around the |
| 229 | * P5 APIC double write bug. | 174 | * P5 APIC double write bug. |
| 230 | */ | 175 | */ |
| 231 | |||
| 232 | #define APIC_DIVISOR 16 | ||
| 233 | |||
| 234 | static void __setup_APIC_LVTT(unsigned int clocks, int oneshot, int irqen) | 176 | static void __setup_APIC_LVTT(unsigned int clocks, int oneshot, int irqen) |
| 235 | { | 177 | { |
| 236 | unsigned int lvtt_value, tmp_value; | 178 | unsigned int lvtt_value, tmp_value; |
| @@ -277,6 +219,10 @@ static void lapic_timer_setup(enum clock_event_mode mode, | |||
| 277 | unsigned long flags; | 219 | unsigned long flags; |
| 278 | unsigned int v; | 220 | unsigned int v; |
| 279 | 221 | ||
| 222 | /* Lapic used for broadcast ? */ | ||
| 223 | if (!local_apic_timer_verify_ok) | ||
| 224 | return; | ||
| 225 | |||
| 280 | local_irq_save(flags); | 226 | local_irq_save(flags); |
| 281 | 227 | ||
| 282 | switch (mode) { | 228 | switch (mode) { |
| @@ -321,111 +267,238 @@ static void __devinit setup_APIC_timer(void) | |||
| 321 | } | 267 | } |
| 322 | 268 | ||
| 323 | /* | 269 | /* |
| 324 | * In this function we calibrate APIC bus clocks to the external | 270 | * In this functions we calibrate APIC bus clocks to the external timer. |
| 325 | * timer. Unfortunately we cannot use jiffies and the timer irq | 271 | * |
| 326 | * to calibrate, since some later bootup code depends on getting | 272 | * We want to do the calibration only once since we want to have local timer |
| 327 | * the first irq? Ugh. | 273 | * irqs syncron. CPUs connected by the same APIC bus have the very same bus |
| 274 | * frequency. | ||
| 328 | * | 275 | * |
| 329 | * TODO: Fix this rather than saying "Ugh" -tglx | 276 | * This was previously done by reading the PIT/HPET and waiting for a wrap |
| 277 | * around to find out, that a tick has elapsed. I have a box, where the PIT | ||
| 278 | * readout is broken, so it never gets out of the wait loop again. This was | ||
| 279 | * also reported by others. | ||
| 330 | * | 280 | * |
| 331 | * We want to do the calibration only once since we | 281 | * Monitoring the jiffies value is inaccurate and the clockevents |
| 332 | * want to have local timer irqs syncron. CPUs connected | 282 | * infrastructure allows us to do a simple substitution of the interrupt |
| 333 | * by the same APIC bus have the very same bus frequency. | 283 | * handler. |
| 334 | * And we want to have irqs off anyways, no accidental | 284 | * |
| 335 | * APIC irq that way. | 285 | * The calibration routine also uses the pm_timer when possible, as the PIT |
| 286 | * happens to run way too slow (factor 2.3 on my VAIO CoreDuo, which goes | ||
| 287 | * back to normal later in the boot process). | ||
| 336 | */ | 288 | */ |
| 337 | 289 | ||
| 338 | static int __init calibrate_APIC_clock(void) | 290 | #define LAPIC_CAL_LOOPS (HZ/10) |
| 291 | |||
| 292 | static __initdata volatile int lapic_cal_loops = -1; | ||
| 293 | static __initdata long lapic_cal_t1, lapic_cal_t2; | ||
| 294 | static __initdata unsigned long long lapic_cal_tsc1, lapic_cal_tsc2; | ||
| 295 | static __initdata unsigned long lapic_cal_pm1, lapic_cal_pm2; | ||
| 296 | static __initdata unsigned long lapic_cal_j1, lapic_cal_j2; | ||
| 297 | |||
| 298 | /* | ||
| 299 | * Temporary interrupt handler. | ||
| 300 | */ | ||
| 301 | static void __init lapic_cal_handler(struct clock_event_device *dev) | ||
| 302 | { | ||
| 303 | unsigned long long tsc = 0; | ||
| 304 | long tapic = apic_read(APIC_TMCCT); | ||
| 305 | unsigned long pm = acpi_pm_read_early(); | ||
| 306 | |||
| 307 | if (cpu_has_tsc) | ||
| 308 | rdtscll(tsc); | ||
| 309 | |||
| 310 | switch (lapic_cal_loops++) { | ||
| 311 | case 0: | ||
| 312 | lapic_cal_t1 = tapic; | ||
| 313 | lapic_cal_tsc1 = tsc; | ||
