diff options
author | Yinghai Lu <yhlu.kernel@gmail.com> | 2008-08-24 05:01:54 -0400 |
---|---|---|
committer | Ingo Molnar <mingo@elte.hu> | 2008-10-16 10:53:03 -0400 |
commit | 2f04fa888d270951b9e0fe9e641ddd560d77ad1b (patch) | |
tree | 7206b41bb6b072187640f2cb1cfb273ef2f2dbf0 /arch | |
parent | dc1528dd864a0b79fa67b60b3ca5674fe94fdce5 (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')
-rw-r--r-- | arch/x86/kernel/apic_32.c | 86 | ||||
-rw-r--r-- | arch/x86/kernel/apic_64.c | 215 |
2 files changed, 301 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 | |||
443 | static 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 | * |
diff --git a/arch/x86/kernel/apic_64.c b/arch/x86/kernel/apic_64.c index c728885e4f4a..6e99af5ce678 100644 --- a/arch/x86/kernel/apic_64.c +++ b/arch/x86/kernel/apic_64.c | |||
@@ -478,6 +478,7 @@ static void __cpuinit setup_APIC_timer(void) | |||
478 | clockevents_register_device(levt); | 478 | clockevents_register_device(levt); |
479 | } | 479 | } |
480 | 480 | ||
481 | #ifdef CONFIG_X86_64 | ||
481 | /* | 482 | /* |
482 | * In this function we calibrate APIC bus clocks to the external | 483 | * In this function we calibrate APIC bus clocks to the external |
483 | * timer. Unfortunately we cannot use jiffies and the timer irq | 484 | * timer. Unfortunately we cannot use jiffies and the timer irq |
@@ -560,6 +561,220 @@ static int __init calibrate_APIC_clock(void) | |||
560 | return 0; | 561 | return 0; |
561 | } | 562 | } |
562 | 563 | ||
564 | #else | ||
565 | /* | ||
566 | * In this functions we calibrate APIC bus clocks to the external timer. | ||
567 | * | ||
568 | * We want to do the calibration only once since we want to have local timer | ||
569 | * irqs syncron. CPUs connected by the same APIC bus have the very same bus | ||
570 | * frequency. | ||
571 | * | ||
572 | * This was previously done by reading the PIT/HPET and waiting for a wrap | ||
573 | * around to find out, that a tick has elapsed. I have a box, where the PIT | ||
574 | * readout is broken, so it never gets out of the wait loop again. This was | ||
575 | * also reported by others. | ||
576 | * | ||
577 | * Monitoring the jiffies value is inaccurate and the clockevents | ||
578 | * infrastructure allows us to do a simple substitution of the interrupt | ||
579 | * handler. | ||
580 | * | ||
581 | * The calibration routine also uses the pm_timer when possible, as the PIT | ||
582 | * happens to run way too slow (factor 2.3 on my VAIO CoreDuo, which goes | ||
583 | * back to normal later in the boot process). | ||
584 | */ | ||
585 | |||
586 | #define LAPIC_CAL_LOOPS (HZ/10) | ||
587 | |||
588 | static __initdata int lapic_cal_loops = -1; | ||
589 | static __initdata long lapic_cal_t1, lapic_cal_t2; | ||
590 | static __initdata unsigned long long lapic_cal_tsc1, lapic_cal_tsc2; | ||
591 | static __initdata unsigned long lapic_cal_pm1, lapic_cal_pm2; | ||
592 | static __initdata unsigned long lapic_cal_j1, lapic_cal_j2; | ||
593 | |||
594 | /* | ||
595 | * Temporary interrupt handler. | ||
596 | */ | ||
597 | static void __init lapic_cal_handler(struct clock_event_device *dev) | ||
598 | { | ||
599 | unsigned long long tsc = 0; | ||
600 | long tapic = apic_read(APIC_TMCCT); | ||
601 | unsigned long pm = acpi_pm_read_early(); | ||
602 | |||
603 | if (cpu_has_tsc) | ||
604 | rdtscll(tsc); | ||
605 | |||
606 | switch (lapic_cal_loops++) { | ||
607 | case 0: | ||
608 | lapic_cal_t1 = tapic; | ||
609 | lapic_cal_tsc1 = tsc; | ||
610 | lapic_cal_pm1 = pm; | ||
611 | lapic_cal_j1 = jiffies; | ||
612 | break; | ||
613 | |||
614 | case LAPIC_CAL_LOOPS: | ||
615 | lapic_cal_t2 = tapic; | ||
616 | lapic_cal_tsc2 = tsc; | ||
617 | if (pm < lapic_cal_pm1) | ||
618 | pm += ACPI_PM_OVRRUN; | ||
619 | lapic_cal_pm2 = pm; | ||
620 | lapic_cal_j2 = jiffies; | ||
621 | break; | ||
622 | } | ||
623 | } | ||
624 | |||
625 | static int __init calibrate_APIC_clock(void) | ||
626 | { | ||
627 | struct clock_event_device *levt = &__get_cpu_var(lapic_events); | ||
628 | const long pm_100ms = PMTMR_TICKS_PER_SEC/10; | ||
629 | const long pm_thresh = pm_100ms/100; | ||
630 | void (*real_handler)(struct clock_event_device *dev); | ||
631 | unsigned long deltaj; | ||
632 | long delta, deltapm; | ||
633 | int pm_referenced = 0; | ||
634 | |||
635 | local_irq_disable(); | ||
636 | |||
637 | /* Replace the global interrupt handler */ | ||
638 | real_handler = global_clock_event->event_handler; | ||
639 | global_clock_event->event_handler = lapic_cal_handler; | ||
640 | |||
641 | /* | ||
642 | * Setup the APIC counter to 1e9. There is no way the lapic | ||
643 | * can underflow in the 100ms detection time frame | ||
644 | */ | ||
645 | __setup_APIC_LVTT(1000000000, 0, 0); | ||
646 | |||
647 | /* Let the interrupts run */ | ||
648 | local_irq_enable(); | ||
649 | |||
650 | while (lapic_cal_loops <= LAPIC_CAL_LOOPS) | ||
651 | cpu_relax(); | ||
652 | |||
653 | local_irq_disable(); | ||
654 | |||
655 | /* Restore the real event handler */ | ||
656 | global_clock_event->event_handler = real_handler; | ||
657 | |||
658 | /* Build delta t1-t2 as apic timer counts down */ | ||
659 | delta = lapic_cal_t1 - lapic_cal_t2; | ||
660 | apic_printk(APIC_VERBOSE, "... lapic delta = %ld\n", delta); | ||
661 | |||
662 | /* Check, if the PM timer is available */ | ||
663 | deltapm = lapic_cal_pm2 - lapic_cal_pm1; | ||
664 | apic_printk(APIC_VERBOSE, "... PM timer delta = %ld\n", deltapm); | ||
665 | |||
666 | if (deltapm) { | ||
667 | unsigned long mult; | ||
668 | u64 res; | ||
669 | |||
670 | mult = clocksource_hz2mult(PMTMR_TICKS_PER_SEC, 22); | ||
671 | |||
672 | if (deltapm > (pm_100ms - pm_thresh) && | ||
673 | deltapm < (pm_100ms + pm_thresh)) { | ||
674 | apic_printk(APIC_VERBOSE, "... PM timer result ok\n"); | ||
675 | } else { | ||
676 | res = (((u64) deltapm) * mult) >> 22; | ||
677 | do_div(res, 1000000); | ||
678 | printk(KERN_WARNING "APIC calibration not consistent " | ||
679 | "with PM Timer: %ldms instead of 100ms\n", | ||
680 | (long)res); | ||
681 | /* Correct the lapic counter value */ | ||
682 | res = (((u64) delta) * pm_100ms); | ||
683 | do_div(res, deltapm); | ||
684 | printk(KERN_INFO "APIC delta adjusted to PM-Timer: " | ||
685 | "%lu (%ld)\n", (unsigned long) res, delta); | ||
686 | delta = (long) res; | ||
687 | } | ||
688 | pm_referenced = 1; | ||
689 | } | ||
690 | |||
691 | /* Calculate the scaled math multiplication factor */ | ||
692 | lapic_clockevent.mult = div_sc(delta, TICK_NSEC * LAPIC_CAL_LOOPS, | ||
693 | lapic_clockevent.shift); | ||
694 | lapic_clockevent.max_delta_ns = | ||
695 | clockevent_delta2ns(0x7FFFFF, &lapic_clockevent); | ||
696 | lapic_clockevent.min_delta_ns = | ||
697 | clockevent_delta2ns(0xF, &lapic_clockevent); | ||
698 | |||
699 | calibration_result = (delta * APIC_DIVISOR) / LAPIC_CAL_LOOPS; | ||
700 | |||
701 | apic_printk(APIC_VERBOSE, "..... delta %ld\n", delta); | ||
702 | apic_printk(APIC_VERBOSE, "..... mult: %ld\n", lapic_clockevent.mult); | ||
703 | apic_printk(APIC_VERBOSE, "..... calibration result: %u\n", | ||
704 | calibration_result); | ||
705 | |||
706 | if (cpu_has_tsc) { | ||
707 | delta = (long)(lapic_cal_tsc2 - lapic_cal_tsc1); | ||
708 | apic_printk(APIC_VERBOSE, "..... CPU clock speed is " | ||
709 | "%ld.%04ld MHz.\n", | ||
710 | (delta / LAPIC_CAL_LOOPS) / (1000000 / HZ), | ||
711 | (delta / LAPIC_CAL_LOOPS) % (1000000 / HZ)); | ||
712 | } | ||
713 | |||
714 | apic_printk(APIC_VERBOSE, "..... host bus clock speed is " | ||
715 | "%u.%04u MHz.\n", | ||
716 | calibration_result / (1000000 / HZ), | ||
717 | calibration_result % (1000000 / HZ)); | ||
718 | |||
719 | /* | ||
720 | * Do a sanity check on the APIC calibration result | ||
721 | */ | ||
722 | if (calibration_result < (1000000 / HZ)) { | ||
723 | local_irq_enable(); | ||
724 | printk(KERN_WARNING | ||
725 | "APIC frequency too slow, disabling apic timer\n"); | ||
726 | return -1; | ||
727 | } | ||
728 | |||
729 | levt->features &= ~CLOCK_EVT_FEAT_DUMMY; | ||
730 | |||
731 | /* We trust the pm timer based calibration */ | ||
732 | if (!pm_referenced) { | ||
733 | apic_printk(APIC_VERBOSE, "... verify APIC timer\n"); | ||
734 | |||
735 | /* | ||
736 | * Setup the apic timer manually | ||
737 | */ | ||
738 | levt->event_handler = lapic_cal_handler; | ||
739 | lapic_timer_setup(CLOCK_EVT_MODE_PERIODIC, levt); | ||
740 | lapic_cal_loops = -1; | ||
741 | |||
742 | /* Let the interrupts run */ | ||
743 | local_irq_enable(); | ||
744 | |||
745 | while (lapic_cal_loops <= LAPIC_CAL_LOOPS) | ||
746 | cpu_relax(); | ||
747 | |||
748 | local_irq_disable(); | ||
749 | |||
750 | /* Stop the lapic timer */ | ||
751 | lapic_timer_setup(CLOCK_EVT_MODE_SHUTDOWN, levt); | ||
752 | |||
753 | local_irq_enable(); | ||
754 | |||
755 | /* Jiffies delta */ | ||
756 | deltaj = lapic_cal_j2 - lapic_cal_j1; | ||
757 | apic_printk(APIC_VERBOSE, "... jiffies delta = %lu\n", deltaj); | ||
758 | |||
759 | /* Check, if the jiffies result is consistent */ | ||
760 | if (deltaj >= LAPIC_CAL_LOOPS-2 && deltaj <= LAPIC_CAL_LOOPS+2) | ||
761 | apic_printk(APIC_VERBOSE, "... jiffies result ok\n"); | ||
762 | else | ||
763 | levt->features |= CLOCK_EVT_FEAT_DUMMY; | ||
764 | } else | ||
765 | local_irq_enable(); | ||
766 | |||
767 | if (levt->features & CLOCK_EVT_FEAT_DUMMY) { | ||
768 | printk(KERN_WARNING | ||
769 | "APIC timer disabled due to verification failure.\n"); | ||
770 | return -1; | ||
771 | } | ||
772 | |||
773 | return 0; | ||
774 | } | ||
775 | |||
776 | #endif | ||
777 | |||
563 | /* | 778 | /* |
564 | * Setup the boot APIC | 779 | * Setup the boot APIC |
565 | * | 780 | * |