aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorThomas Gleixner <tglx@linutronix.de>2007-02-16 04:28:06 -0500
committerLinus Torvalds <torvalds@woody.linux-foundation.org>2007-02-16 11:13:59 -0500
commitd36b49b91065dbfa305c5a66010b3497c741eee0 (patch)
tree2c1873a72da1b18900c5bf9984de1a33ee2ca38c
parente9e2cdb412412326c4827fc78ba27f410d837e6e (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.c365
-rw-r--r--include/asm-i386/apic.h2
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 */
60static int enable_local_apic __initdata = 0; 61static int enable_local_apic __initdata = 0;
61 62
62/* Enable local APIC timer for highres/dyntick on UP */ 63/* Local APIC timer verification ok */
63static int enable_local_apic_timer __initdata = 0; 64static 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);
82static struct clock_event_device lapic_clockevent = { 83static 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 */
181static 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 */
199static 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 */
219void (*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
234static void __setup_APIC_LVTT(unsigned int clocks, int oneshot, int irqen) 176static 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
338static int __init calibrate_APIC_clock(void) 290#define LAPIC_CAL_LOOPS (HZ/10)
291
292static __initdata volatile int lapic_cal_loops = -1;
293static __initdata long lapic_cal_t1, lapic_cal_t2;
294static __initdata unsigned long long lapic_cal_tsc1, lapic_cal_tsc2;
295static __initdata unsigned long lapic_cal_pm1, lapic_cal_pm2;
296static __initdata unsigned long lapic_cal_j1, lapic_cal_j2;
297
298/*
299 * Temporary interrupt handler.
300 */
301static 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;