diff options
-rw-r--r-- | arch/x86/kernel/apic.c | 81 |
1 files changed, 47 insertions, 34 deletions
diff --git a/arch/x86/kernel/apic.c b/arch/x86/kernel/apic.c index d730e8d31727..784116933c70 100644 --- a/arch/x86/kernel/apic.c +++ b/arch/x86/kernel/apic.c | |||
@@ -538,14 +538,51 @@ static void __init lapic_cal_handler(struct clock_event_device *dev) | |||
538 | } | 538 | } |
539 | } | 539 | } |
540 | 540 | ||
541 | static int __init calibrate_by_pmtimer(long deltapm, long *delta) | ||
542 | { | ||
543 | const long pm_100ms = PMTMR_TICKS_PER_SEC / 10; | ||
544 | const long pm_thresh = pm_100ms / 100; | ||
545 | unsigned long mult; | ||
546 | u64 res; | ||
547 | |||
548 | #ifndef CONFIG_X86_PM_TIMER | ||
549 | return -1; | ||
550 | #endif | ||
551 | |||
552 | apic_printk(APIC_VERBOSE, "... PM timer delta = %ld\n", deltapm); | ||
553 | |||
554 | /* Check, if the PM timer is available */ | ||
555 | if (!deltapm) | ||
556 | return -1; | ||
557 | |||
558 | mult = clocksource_hz2mult(PMTMR_TICKS_PER_SEC, 22); | ||
559 | |||
560 | if (deltapm > (pm_100ms - pm_thresh) && | ||
561 | deltapm < (pm_100ms + pm_thresh)) { | ||
562 | apic_printk(APIC_VERBOSE, "... PM timer result ok\n"); | ||
563 | } else { | ||
564 | res = (((u64)deltapm) * mult) >> 22; | ||
565 | do_div(res, 1000000); | ||
566 | printk(KERN_WARNING "APIC calibration not consistent " | ||
567 | "with PM Timer: %ldms instead of 100ms\n", | ||
568 | (long)res); | ||
569 | /* Correct the lapic counter value */ | ||
570 | res = (((u64)(*delta)) * pm_100ms); | ||
571 | do_div(res, deltapm); | ||
572 | printk(KERN_INFO "APIC delta adjusted to PM-Timer: " | ||
573 | "%lu (%ld)\n", (unsigned long)res, *delta); | ||
574 | *delta = (long)res; | ||
575 | } | ||
576 | |||
577 | return 0; | ||
578 | } | ||
579 | |||
541 | static int __init calibrate_APIC_clock(void) | 580 | static int __init calibrate_APIC_clock(void) |
542 | { | 581 | { |
543 | struct clock_event_device *levt = &__get_cpu_var(lapic_events); | 582 | struct clock_event_device *levt = &__get_cpu_var(lapic_events); |
544 | const long pm_100ms = PMTMR_TICKS_PER_SEC/10; | ||
545 | const long pm_thresh = pm_100ms/100; | ||
546 | void (*real_handler)(struct clock_event_device *dev); | 583 | void (*real_handler)(struct clock_event_device *dev); |
547 | unsigned long deltaj; | 584 | unsigned long deltaj; |
548 | long delta, deltapm; | 585 | long delta; |
549 | int pm_referenced = 0; | 586 | int pm_referenced = 0; |
550 | 587 | ||
551 | local_irq_disable(); | 588 | local_irq_disable(); |
@@ -575,36 +612,9 @@ static int __init calibrate_APIC_clock(void) | |||
575 | delta = lapic_cal_t1 - lapic_cal_t2; | 612 | delta = lapic_cal_t1 - lapic_cal_t2; |
576 | apic_printk(APIC_VERBOSE, "... lapic delta = %ld\n", delta); | 613 | apic_printk(APIC_VERBOSE, "... lapic delta = %ld\n", delta); |
577 | 614 | ||
578 | #ifdef CONFIG_X86_PM_TIMER | 615 | /* we trust the PM based calibration if possible */ |
579 | /* Check, if the PM timer is available */ | 616 | pm_referenced = !calibrate_by_pmtimer(lapic_cal_pm2 - lapic_cal_pm1, |
580 | deltapm = lapic_cal_pm2 - lapic_cal_pm1; | 617 | &delta); |
581 | apic_printk(APIC_VERBOSE, "... PM timer delta = %ld\n", deltapm); | ||
582 | |||
583 | if (deltapm) { | ||
584 | unsigned long mult; | ||
585 | u64 res; | ||
586 | |||
587 | mult = clocksource_hz2mult(PMTMR_TICKS_PER_SEC, 22); | ||
588 | |||
589 | if (deltapm > (pm_100ms - pm_thresh) && | ||
590 | deltapm < (pm_100ms + pm_thresh)) { | ||
591 | apic_printk(APIC_VERBOSE, "... PM timer result ok\n"); | ||
592 | } else { | ||
593 | res = (((u64) deltapm) * mult) >> 22; | ||
594 | do_div(res, 1000000); | ||
595 | printk(KERN_WARNING "APIC calibration not consistent " | ||
596 | "with PM Timer: %ldms instead of 100ms\n", | ||
597 | (long)res); | ||
598 | /* Correct the lapic counter value */ | ||
599 | res = (((u64) delta) * pm_100ms); | ||
600 | do_div(res, deltapm); | ||
601 | printk(KERN_INFO "APIC delta adjusted to PM-Timer: " | ||
602 | "%lu (%ld)\n", (unsigned long) res, delta); | ||
603 | delta = (long) res; | ||
604 | } | ||
605 | pm_referenced = 1; | ||
606 | } | ||
607 | #endif | ||
608 | 618 | ||
609 | /* Calculate the scaled math multiplication factor */ | 619 | /* Calculate the scaled math multiplication factor */ |
610 | lapic_clockevent.mult = div_sc(delta, TICK_NSEC * LAPIC_CAL_LOOPS, | 620 | lapic_clockevent.mult = div_sc(delta, TICK_NSEC * LAPIC_CAL_LOOPS, |
@@ -646,7 +656,10 @@ static int __init calibrate_APIC_clock(void) | |||
646 | 656 | ||
647 | levt->features &= ~CLOCK_EVT_FEAT_DUMMY; | 657 | levt->features &= ~CLOCK_EVT_FEAT_DUMMY; |
648 | 658 | ||
649 | /* We trust the pm timer based calibration */ | 659 | /* |
660 | * PM timer calibration failed or not turned on | ||
661 | * so lets try APIC timer based calibration | ||
662 | */ | ||
650 | if (!pm_referenced) { | 663 | if (!pm_referenced) { |
651 | apic_printk(APIC_VERBOSE, "... verify APIC timer\n"); | 664 | apic_printk(APIC_VERBOSE, "... verify APIC timer\n"); |
652 | 665 | ||