diff options
Diffstat (limited to 'arch/i386/kernel/apic.c')
-rw-r--r-- | arch/i386/kernel/apic.c | 143 |
1 files changed, 91 insertions, 52 deletions
diff --git a/arch/i386/kernel/apic.c b/arch/i386/kernel/apic.c index 2383bcf18c5..e88415282a6 100644 --- a/arch/i386/kernel/apic.c +++ b/arch/i386/kernel/apic.c | |||
@@ -28,6 +28,7 @@ | |||
28 | #include <linux/clockchips.h> | 28 | #include <linux/clockchips.h> |
29 | #include <linux/acpi_pmtmr.h> | 29 | #include <linux/acpi_pmtmr.h> |
30 | #include <linux/module.h> | 30 | #include <linux/module.h> |
31 | #include <linux/dmi.h> | ||
31 | 32 | ||
32 | #include <asm/atomic.h> | 33 | #include <asm/atomic.h> |
33 | #include <asm/smp.h> | 34 | #include <asm/smp.h> |
@@ -61,6 +62,11 @@ static int enable_local_apic __initdata = 0; | |||
61 | 62 | ||
62 | /* Local APIC timer verification ok */ | 63 | /* Local APIC timer verification ok */ |
63 | static int local_apic_timer_verify_ok; | 64 | static int local_apic_timer_verify_ok; |
65 | /* Disable local APIC timer from the kernel commandline or via dmi quirk */ | ||
66 | static int local_apic_timer_disabled; | ||
67 | /* Local APIC timer works in C2 */ | ||
68 | int local_apic_timer_c2_ok; | ||
69 | EXPORT_SYMBOL_GPL(local_apic_timer_c2_ok); | ||
64 | 70 | ||
65 | /* | 71 | /* |
66 | * Debug level, exported for io_apic.c | 72 | * Debug level, exported for io_apic.c |
@@ -266,6 +272,32 @@ static void __devinit setup_APIC_timer(void) | |||
266 | } | 272 | } |
267 | 273 | ||
268 | /* | 274 | /* |
275 | * Detect systems with known broken BIOS implementations | ||
276 | */ | ||
277 | static int __init lapic_check_broken_bios(struct dmi_system_id *d) | ||
278 | { | ||
279 | printk(KERN_NOTICE "%s detected: disabling lapic timer.\n", | ||
280 | d->ident); | ||
281 | local_apic_timer_disabled = 1; | ||
282 | return 0; | ||
283 | } | ||
284 | |||
285 | static struct dmi_system_id __initdata broken_bios_dmi_table[] = { | ||
286 | { | ||
287 | /* | ||
288 | * BIOS exports only C1 state, but uses deeper power | ||
289 | * modes behind the kernels back. | ||
290 | */ | ||
291 | .callback = lapic_check_broken_bios, | ||
292 | .ident = "HP nx6325", | ||
293 | .matches = { | ||
294 | DMI_MATCH(DMI_PRODUCT_NAME, "HP Compaq nx6325"), | ||
295 | }, | ||
296 | }, | ||
297 | {} | ||
298 | }; | ||
299 | |||
300 | /* | ||
269 | * In this functions we calibrate APIC bus clocks to the external timer. | 301 | * In this functions we calibrate APIC bus clocks to the external timer. |
270 | * | 302 | * |
271 | * We want to do the calibration only once since we want to have local timer | 303 | * We want to do the calibration only once since we want to have local timer |
@@ -338,6 +370,23 @@ void __init setup_boot_APIC_clock(void) | |||
338 | void (*real_handler)(struct clock_event_device *dev); | 370 | void (*real_handler)(struct clock_event_device *dev); |
339 | unsigned long deltaj; | 371 | unsigned long deltaj; |
340 | long delta, deltapm; | 372 | long delta, deltapm; |
373 | int pm_referenced = 0; | ||
374 | |||
375 | /* Detect know broken systems */ | ||
376 | dmi_check_system(broken_bios_dmi_table); | ||
377 | |||
378 | /* | ||
379 | * The local apic timer can be disabled via the kernel | ||
380 | * commandline or from the dmi quirk above. Register the lapic | ||
381 | * timer as a dummy clock event source on SMP systems, so the | ||
382 | * broadcast mechanism is used. On UP systems simply ignore it. | ||
383 | */ | ||
384 | if (local_apic_timer_disabled) { | ||
385 | /* No broadcast on UP ! */ | ||
386 | if (num_possible_cpus() > 1) | ||
387 | setup_APIC_timer(); | ||
388 | return; | ||
389 | } | ||
341 | 390 | ||
342 | apic_printk(APIC_VERBOSE, "Using local APIC timer interrupts.\n" | 391 | apic_printk(APIC_VERBOSE, "Using local APIC timer interrupts.\n" |
343 | "calibrating APIC timer ...\n"); | 392 | "calibrating APIC timer ...\n"); |
@@ -357,7 +406,8 @@ void __init setup_boot_APIC_clock(void) | |||
357 | /* Let the interrupts run */ | 406 | /* Let the interrupts run */ |
358 | local_irq_enable(); | 407 | local_irq_enable(); |
359 | 408 | ||
360 | while(lapic_cal_loops <= LAPIC_CAL_LOOPS); | 409 | while (lapic_cal_loops <= LAPIC_CAL_LOOPS) |
410 | cpu_relax(); | ||
361 | 411 | ||
362 | local_irq_disable(); | 412 | local_irq_disable(); |
363 | 413 | ||
@@ -394,6 +444,7 @@ void __init setup_boot_APIC_clock(void) | |||
394 | "%lu (%ld)\n", (unsigned long) res, delta); | 444 | "%lu (%ld)\n", (unsigned long) res, delta); |
395 | delta = (long) res; | 445 | delta = (long) res; |
396 | } | 446 | } |
447 | pm_referenced = 1; | ||
397 | } | 448 | } |
398 | 449 | ||
399 | /* Calculate the scaled math multiplication factor */ | 450 | /* Calculate the scaled math multiplication factor */ |
@@ -423,69 +474,43 @@ void __init setup_boot_APIC_clock(void) | |||
423 | calibration_result / (1000000 / HZ), | 474 | calibration_result / (1000000 / HZ), |
424 | calibration_result % (1000000 / HZ)); | 475 | calibration_result % (1000000 / HZ)); |
425 | 476 | ||
426 | |||
427 | apic_printk(APIC_VERBOSE, "... verify APIC timer\n"); | ||
428 | |||
429 | /* | ||
430 | * Setup the apic timer manually | ||
431 | */ | ||
432 | local_apic_timer_verify_ok = 1; | 477 | local_apic_timer_verify_ok = 1; |
433 | levt->event_handler = lapic_cal_handler; | ||
434 | lapic_timer_setup(CLOCK_EVT_MODE_PERIODIC, levt); | ||
435 | lapic_cal_loops = -1; | ||
436 | 478 | ||
437 | /* Let the interrupts run */ | 479 | /* We trust the pm timer based calibration */ |
438 | local_irq_enable(); | 480 | if (!pm_referenced) { |
481 | apic_printk(APIC_VERBOSE, "... verify APIC timer\n"); | ||
439 | 482 | ||
440 | while(lapic_cal_loops <= LAPIC_CAL_LOOPS); | 483 | /* |
484 | * Setup the apic timer manually | ||
485 | */ | ||
486 | levt->event_handler = lapic_cal_handler; | ||
487 | lapic_timer_setup(CLOCK_EVT_MODE_PERIODIC, levt); | ||
488 | lapic_cal_loops = -1; | ||
441 | 489 | ||
442 | local_irq_disable(); | 490 | /* Let the interrupts run */ |
491 | local_irq_enable(); | ||
443 | 492 | ||
444 | /* Stop the lapic timer */ | 493 | while(lapic_cal_loops <= LAPIC_CAL_LOOPS) |
445 | lapic_timer_setup(CLOCK_EVT_MODE_SHUTDOWN, levt); | 494 | cpu_relax(); |
446 | 495 | ||
447 | local_irq_enable(); | 496 | local_irq_disable(); |
448 | 497 | ||
449 | /* Jiffies delta */ | 498 | /* Stop the lapic timer */ |
450 | deltaj = lapic_cal_j2 - lapic_cal_j1; | 499 | lapic_timer_setup(CLOCK_EVT_MODE_SHUTDOWN, levt); |
451 | apic_printk(APIC_VERBOSE, "... jiffies delta = %lu\n", deltaj); | ||
452 | 500 | ||
453 | /* Check, if the PM timer is available */ | 501 | local_irq_enable(); |
454 | deltapm = lapic_cal_pm2 - lapic_cal_pm1; | ||
455 | apic_printk(APIC_VERBOSE, "... PM timer delta = %ld\n", deltapm); | ||
456 | 502 | ||
457 | local_apic_timer_verify_ok = 0; | 503 | /* Jiffies delta */ |
504 | deltaj = lapic_cal_j2 - lapic_cal_j1; | ||
505 | apic_printk(APIC_VERBOSE, "... jiffies delta = %lu\n", deltaj); | ||
458 | 506 | ||
459 | if (deltapm) { | ||
460 | if (deltapm > (pm_100ms - pm_thresh) && | ||
461 | deltapm < (pm_100ms + pm_thresh)) { | ||
462 | apic_printk(APIC_VERBOSE, "... PM timer result ok\n"); | ||
463 | /* Check, if the jiffies result is consistent */ | ||
464 | if (deltaj < LAPIC_CAL_LOOPS-2 || | ||
465 | deltaj > LAPIC_CAL_LOOPS+2) { | ||
466 | /* | ||
467 | * Not sure, what we can do about this one. | ||
468 | * When high resultion timers are active | ||
469 | * and the lapic timer does not stop in C3 | ||
470 | * we are fine. Otherwise more trouble might | ||
471 | * be waiting. -- tglx | ||
472 | */ | ||
473 | printk(KERN_WARNING "Global event device %s " | ||
474 | "has wrong frequency " | ||
475 | "(%lu ticks instead of %d)\n", | ||
476 | global_clock_event->name, deltaj, | ||
477 | LAPIC_CAL_LOOPS); | ||
478 | } | ||
479 | local_apic_timer_verify_ok = 1; | ||
480 | } | ||
481 | } else { | ||
482 | /* Check, if the jiffies result is consistent */ | 507 | /* Check, if the jiffies result is consistent */ |
483 | if (deltaj >= LAPIC_CAL_LOOPS-2 && | 508 | if (deltaj >= LAPIC_CAL_LOOPS-2 && deltaj <= LAPIC_CAL_LOOPS+2) |
484 | deltaj <= LAPIC_CAL_LOOPS+2) { | ||
485 | apic_printk(APIC_VERBOSE, "... jiffies result ok\n"); | 509 | apic_printk(APIC_VERBOSE, "... jiffies result ok\n"); |
486 | local_apic_timer_verify_ok = 1; | 510 | else |
487 | } | 511 | local_apic_timer_verify_ok = 0; |
488 | } | 512 | } else |
513 | local_irq_enable(); | ||
489 | 514 | ||
490 | if (!local_apic_timer_verify_ok) { | 515 | if (!local_apic_timer_verify_ok) { |
491 | printk(KERN_WARNING | 516 | printk(KERN_WARNING |
@@ -1203,6 +1228,20 @@ static int __init parse_nolapic(char *arg) | |||
1203 | } | 1228 | } |
1204 | early_param("nolapic", parse_nolapic); | 1229 | early_param("nolapic", parse_nolapic); |
1205 | 1230 | ||
1231 | static int __init parse_disable_lapic_timer(char *arg) | ||
1232 | { | ||
1233 | local_apic_timer_disabled = 1; | ||
1234 | return 0; | ||
1235 | } | ||
1236 | early_param("nolapic_timer", parse_disable_lapic_timer); | ||
1237 | |||
1238 | static int __init parse_lapic_timer_c2_ok(char *arg) | ||
1239 | { | ||
1240 | local_apic_timer_c2_ok = 1; | ||
1241 | return 0; | ||
1242 | } | ||
1243 | early_param("lapic_timer_c2_ok", parse_lapic_timer_c2_ok); | ||
1244 | |||
1206 | static int __init apic_set_verbosity(char *str) | 1245 | static int __init apic_set_verbosity(char *str) |
1207 | { | 1246 | { |
1208 | if (strcmp("debug", str) == 0) | 1247 | if (strcmp("debug", str) == 0) |