aboutsummaryrefslogtreecommitdiffstats
path: root/arch/x86_64/kernel/apic.c
diff options
context:
space:
mode:
authorAndi Kleen <ak@suse.de>2006-02-03 15:51:41 -0500
committerLinus Torvalds <torvalds@g5.osdl.org>2006-02-04 19:43:15 -0500
commit0c3749c41f5eee0da36bbf92b2793338b4d8574f (patch)
tree3af004c5533ab04297004f5ec40da4c6801b8fda /arch/x86_64/kernel/apic.c
parent099f318b8d97490e58c595eb1b6d3415da5ccc03 (diff)
[PATCH] x86_64: Calibrate APIC timer using PM timer
On some broken motherboards (at least one NForce3 based AMD64 laptop) the PIT timer runs at a incorrect frequency. This patch adds a new option "apicpmtimer" that allows to use the APIC timer and calibrate it using the PMTimer. It requires the earlier patch that allows to run the main timer from the APIC. Specifying apicpmtimer implies apicmaintimer. The option defaults to off for now. I tested it on a few systems and the resulting APIC timer frequencies were usually a bit off, but always <1%, which should be tolerable. TBD figure out heuristic to enable this automatically on the affected systems TBD perhaps do it on all NForce3s or using DMI? Signed-off-by: Andi Kleen <ak@suse.de> Signed-off-by: Linus Torvalds <torvalds@osdl.org>
Diffstat (limited to 'arch/x86_64/kernel/apic.c')
-rw-r--r--arch/x86_64/kernel/apic.c33
1 files changed, 27 insertions, 6 deletions
diff --git a/arch/x86_64/kernel/apic.c b/arch/x86_64/kernel/apic.c
index 673a2fe9923..c02218b3ae2 100644
--- a/arch/x86_64/kernel/apic.c
+++ b/arch/x86_64/kernel/apic.c
@@ -40,6 +40,7 @@
40 40
41int apic_verbosity; 41int apic_verbosity;
42int apic_runs_main_timer; 42int apic_runs_main_timer;
43int apic_calibrate_pmtmr __initdata;
43 44
44int disable_apic_timer __initdata; 45int disable_apic_timer __initdata;
45 46
@@ -746,14 +747,27 @@ static int __init calibrate_APIC_clock(void)
746 __setup_APIC_LVTT(1000000000); 747 __setup_APIC_LVTT(1000000000);
747 748
748 apic_start = apic_read(APIC_TMCCT); 749 apic_start = apic_read(APIC_TMCCT);
749 rdtscl(tsc_start); 750#ifdef CONFIG_X86_PM_TIMER
750 751 if (apic_calibrate_pmtmr && pmtmr_ioport) {
751 do { 752 pmtimer_wait(5000); /* 5ms wait */
752 apic = apic_read(APIC_TMCCT); 753 apic = apic_read(APIC_TMCCT);
753 rdtscl(tsc); 754 result = (apic_start - apic) * 1000L / 5;
754 } while ((tsc - tsc_start) < TICK_COUNT && (apic - apic_start) < TICK_COUNT); 755 } else
756#endif
757 {
758 rdtscl(tsc_start);
759
760 do {
761 apic = apic_read(APIC_TMCCT);
762 rdtscl(tsc);
763 } while ((tsc - tsc_start) < TICK_COUNT &&
764 (apic - apic_start) < TICK_COUNT);
765
766 result = (apic_start - apic) * 1000L * cpu_khz /
767 (tsc - tsc_start);
768 }
769 printk("result %d\n", result);
755 770
756 result = (apic_start - apic) * 1000L * cpu_khz / (tsc - tsc_start);
757 771
758 printk(KERN_INFO "Detected %d.%03d MHz APIC timer.\n", 772 printk(KERN_INFO "Detected %d.%03d MHz APIC timer.\n",
759 result / 1000 / 1000, result / 1000 % 1000); 773 result / 1000 / 1000, result / 1000 % 1000);
@@ -1115,6 +1129,13 @@ static __init int setup_noapicmaintimer(char *str)
1115} 1129}
1116__setup("noapicmaintimer", setup_noapicmaintimer); 1130__setup("noapicmaintimer", setup_noapicmaintimer);
1117 1131
1132static __init int setup_apicpmtimer(char *s)
1133{
1134 apic_calibrate_pmtmr = 1;
1135 return setup_apicmaintimer(NULL);
1136}
1137__setup("apicpmtimer", setup_apicpmtimer);
1138
1118/* dummy parsing: see setup.c */ 1139/* dummy parsing: see setup.c */
1119 1140
1120__setup("disableapic", setup_disableapic); 1141__setup("disableapic", setup_disableapic);