diff options
Diffstat (limited to 'arch/i386/kernel/apic.c')
-rw-r--r-- | arch/i386/kernel/apic.c | 117 |
1 files changed, 65 insertions, 52 deletions
diff --git a/arch/i386/kernel/apic.c b/arch/i386/kernel/apic.c index 2383bcf18c5d..93aa911646ad 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 |
@@ -338,6 +344,23 @@ void __init setup_boot_APIC_clock(void) | |||
338 | void (*real_handler)(struct clock_event_device *dev); | 344 | void (*real_handler)(struct clock_event_device *dev); |
339 | unsigned long deltaj; | 345 | unsigned long deltaj; |
340 | long delta, deltapm; | 346 | long delta, deltapm; |
347 | int pm_referenced = 0; | ||
348 | |||
349 | if (boot_cpu_has(X86_FEATURE_LAPIC_TIMER_BROKEN)) | ||
350 | local_apic_timer_disabled = 1; | ||
351 | |||
352 | /* | ||
353 | * The local apic timer can be disabled via the kernel | ||
354 | * commandline or from the test above. Register the lapic | ||
355 | * timer as a dummy clock event source on SMP systems, so the | ||
356 | * broadcast mechanism is used. On UP systems simply ignore it. | ||
357 | */ | ||
358 | if (local_apic_timer_disabled) { | ||
359 | /* No broadcast on UP ! */ | ||
360 | if (num_possible_cpus() > 1) | ||
361 | setup_APIC_timer(); | ||
362 | return; | ||
363 | } | ||
341 | 364 | ||
342 | apic_printk(APIC_VERBOSE, "Using local APIC timer interrupts.\n" | 365 | apic_printk(APIC_VERBOSE, "Using local APIC timer interrupts.\n" |
343 | "calibrating APIC timer ...\n"); | 366 | "calibrating APIC timer ...\n"); |
@@ -357,7 +380,8 @@ void __init setup_boot_APIC_clock(void) | |||
357 | /* Let the interrupts run */ | 380 | /* Let the interrupts run */ |
358 | local_irq_enable(); | 381 | local_irq_enable(); |
359 | 382 | ||
360 | while(lapic_cal_loops <= LAPIC_CAL_LOOPS); | 383 | while (lapic_cal_loops <= LAPIC_CAL_LOOPS) |
384 | cpu_relax(); | ||
361 | 385 | ||
362 | local_irq_disable(); | 386 | local_irq_disable(); |
363 | 387 | ||
@@ -394,6 +418,7 @@ void __init setup_boot_APIC_clock(void) | |||
394 | "%lu (%ld)\n", (unsigned long) res, delta); | 418 | "%lu (%ld)\n", (unsigned long) res, delta); |
395 | delta = (long) res; | 419 | delta = (long) res; |
396 | } | 420 | } |
421 | pm_referenced = 1; | ||
397 | } | 422 | } |
398 | 423 | ||
399 | /* Calculate the scaled math multiplication factor */ | 424 | /* Calculate the scaled math multiplication factor */ |
@@ -423,69 +448,43 @@ void __init setup_boot_APIC_clock(void) | |||
423 | calibration_result / (1000000 / HZ), | 448 | calibration_result / (1000000 / HZ), |
424 | calibration_result % (1000000 / HZ)); | 449 | calibration_result % (1000000 / HZ)); |
425 | 450 | ||
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; | 451 | 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 | 452 | ||
437 | /* Let the interrupts run */ | 453 | /* We trust the pm timer based calibration */ |
438 | local_irq_enable(); | 454 | if (!pm_referenced) { |
455 | apic_printk(APIC_VERBOSE, "... verify APIC timer\n"); | ||
439 | 456 | ||
440 | while(lapic_cal_loops <= LAPIC_CAL_LOOPS); | 457 | /* |
458 | * Setup the apic timer manually | ||
459 | */ | ||
460 | levt->event_handler = lapic_cal_handler; | ||
461 | lapic_timer_setup(CLOCK_EVT_MODE_PERIODIC, levt); | ||
462 | lapic_cal_loops = -1; | ||
441 | 463 | ||
442 | local_irq_disable(); | 464 | /* Let the interrupts run */ |
465 | local_irq_enable(); | ||
443 | 466 | ||
444 | /* Stop the lapic timer */ | 467 | while(lapic_cal_loops <= LAPIC_CAL_LOOPS) |
445 | lapic_timer_setup(CLOCK_EVT_MODE_SHUTDOWN, levt); | 468 | cpu_relax(); |
446 | 469 | ||
447 | local_irq_enable(); | 470 | local_irq_disable(); |
448 | 471 | ||
449 | /* Jiffies delta */ | 472 | /* Stop the lapic timer */ |
450 | deltaj = lapic_cal_j2 - lapic_cal_j1; | 473 | lapic_timer_setup(CLOCK_EVT_MODE_SHUTDOWN, levt); |
451 | apic_printk(APIC_VERBOSE, "... jiffies delta = %lu\n", deltaj); | ||
452 | 474 | ||
453 | /* Check, if the PM timer is available */ | 475 | local_irq_enable(); |
454 | deltapm = lapic_cal_pm2 - lapic_cal_pm1; | ||
455 | apic_printk(APIC_VERBOSE, "... PM timer delta = %ld\n", deltapm); | ||
456 | 476 | ||
457 | local_apic_timer_verify_ok = 0; | 477 | /* Jiffies delta */ |
478 | deltaj = lapic_cal_j2 - lapic_cal_j1; | ||
479 | apic_printk(APIC_VERBOSE, "... jiffies delta = %lu\n", deltaj); | ||
458 | 480 | ||
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 */ | 481 | /* Check, if the jiffies result is consistent */ |
483 | if (deltaj >= LAPIC_CAL_LOOPS-2 && | 482 | 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"); | 483 | apic_printk(APIC_VERBOSE, "... jiffies result ok\n"); |
486 | local_apic_timer_verify_ok = 1; | 484 | else |
487 | } | 485 | local_apic_timer_verify_ok = 0; |
488 | } | 486 | } else |
487 | local_irq_enable(); | ||
489 | 488 | ||
490 | if (!local_apic_timer_verify_ok) { | 489 | if (!local_apic_timer_verify_ok) { |
491 | printk(KERN_WARNING | 490 | printk(KERN_WARNING |
@@ -1203,6 +1202,20 @@ static int __init parse_nolapic(char *arg) | |||
1203 | } | 1202 | } |
1204 | early_param("nolapic", parse_nolapic); | 1203 | early_param("nolapic", parse_nolapic); |
1205 | 1204 | ||
1205 | static int __init parse_disable_lapic_timer(char *arg) | ||
1206 | { | ||
1207 | local_apic_timer_disabled = 1; | ||
1208 | return 0; | ||
1209 | } | ||
1210 | early_param("nolapic_timer", parse_disable_lapic_timer); | ||
1211 | |||
1212 | static int __init parse_lapic_timer_c2_ok(char *arg) | ||
1213 | { | ||
1214 | local_apic_timer_c2_ok = 1; | ||
1215 | return 0; | ||
1216 | } | ||
1217 | early_param("lapic_timer_c2_ok", parse_lapic_timer_c2_ok); | ||
1218 | |||
1206 | static int __init apic_set_verbosity(char *str) | 1219 | static int __init apic_set_verbosity(char *str) |
1207 | { | 1220 | { |
1208 | if (strcmp("debug", str) == 0) | 1221 | if (strcmp("debug", str) == 0) |