aboutsummaryrefslogtreecommitdiffstats
path: root/arch/x86/kernel/apic_32.c
diff options
context:
space:
mode:
authorYinghai Lu <yhlu.kernel@gmail.com>2008-08-24 05:01:54 -0400
committerIngo Molnar <mingo@elte.hu>2008-10-16 10:53:03 -0400
commit2f04fa888d270951b9e0fe9e641ddd560d77ad1b (patch)
tree7206b41bb6b072187640f2cb1cfb273ef2f2dbf0 /arch/x86/kernel/apic_32.c
parentdc1528dd864a0b79fa67b60b3ca5674fe94fdce5 (diff)
x86: apic copy calibrate_APIC_clock to each other in apic_32/64.c
Signed-off-by: Yinghai Lu <yhlu.kernel@gmail.com> Signed-off-by: Ingo Molnar <mingo@elte.hu>
Diffstat (limited to 'arch/x86/kernel/apic_32.c')
-rw-r--r--arch/x86/kernel/apic_32.c86
1 files changed, 86 insertions, 0 deletions
diff --git a/arch/x86/kernel/apic_32.c b/arch/x86/kernel/apic_32.c
index 60a901b2d4fe..05498c37f8d9 100644
--- a/arch/x86/kernel/apic_32.c
+++ b/arch/x86/kernel/apic_32.c
@@ -424,6 +424,90 @@ static void __cpuinit setup_APIC_timer(void)
424 clockevents_register_device(levt); 424 clockevents_register_device(levt);
425} 425}
426 426
427#ifdef CONFIG_X86_64
428/*
429 * In this function we calibrate APIC bus clocks to the external
430 * timer. Unfortunately we cannot use jiffies and the timer irq
431 * to calibrate, since some later bootup code depends on getting
432 * the first irq? Ugh.
433 *
434 * We want to do the calibration only once since we
435 * want to have local timer irqs syncron. CPUs connected
436 * by the same APIC bus have the very same bus frequency.
437 * And we want to have irqs off anyways, no accidental
438 * APIC irq that way.
439 */
440
441#define TICK_COUNT 100000000
442
443static int __init calibrate_APIC_clock(void)
444{
445 unsigned apic, apic_start;
446 unsigned long tsc, tsc_start;
447 int result;
448
449 local_irq_disable();
450
451 /*
452 * Put whatever arbitrary (but long enough) timeout
453 * value into the APIC clock, we just want to get the
454 * counter running for calibration.
455 *
456 * No interrupt enable !
457 */
458 __setup_APIC_LVTT(250000000, 0, 0);
459
460 apic_start = apic_read(APIC_TMCCT);
461#ifdef CONFIG_X86_PM_TIMER
462 if (apic_calibrate_pmtmr && pmtmr_ioport) {
463 pmtimer_wait(5000); /* 5ms wait */
464 apic = apic_read(APIC_TMCCT);
465 result = (apic_start - apic) * 1000L / 5;
466 } else
467#endif
468 {
469 rdtscll(tsc_start);
470
471 do {
472 apic = apic_read(APIC_TMCCT);
473 rdtscll(tsc);
474 } while ((tsc - tsc_start) < TICK_COUNT &&
475 (apic_start - apic) < TICK_COUNT);
476
477 result = (apic_start - apic) * 1000L * tsc_khz /
478 (tsc - tsc_start);
479 }
480
481 local_irq_enable();
482
483 printk(KERN_DEBUG "APIC timer calibration result %d\n", result);
484
485 printk(KERN_INFO "Detected %d.%03d MHz APIC timer.\n",
486 result / 1000 / 1000, result / 1000 % 1000);
487
488 /* Calculate the scaled math multiplication factor */
489 lapic_clockevent.mult = div_sc(result, NSEC_PER_SEC,
490 lapic_clockevent.shift);
491 lapic_clockevent.max_delta_ns =
492 clockevent_delta2ns(0x7FFFFF, &lapic_clockevent);
493 lapic_clockevent.min_delta_ns =
494 clockevent_delta2ns(0xF, &lapic_clockevent);
495
496 calibration_result = (result * APIC_DIVISOR) / HZ;
497
498 /*
499 * Do a sanity check on the APIC calibration result
500 */
501 if (calibration_result < (1000000 / HZ)) {
502 printk(KERN_WARNING
503 "APIC frequency too slow, disabling apic timer\n");
504 return -1;
505 }
506
507 return 0;
508}
509
510#else
427/* 511/*
428 * In this functions we calibrate APIC bus clocks to the external timer. 512 * In this functions we calibrate APIC bus clocks to the external timer.
429 * 513 *
@@ -635,6 +719,8 @@ static int __init calibrate_APIC_clock(void)
635 return 0; 719 return 0;
636} 720}
637 721
722#endif
723
638/* 724/*
639 * Setup the boot APIC 725 * Setup the boot APIC
640 * 726 *