summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Documentation/x86_64/boot-options.txt5
-rw-r--r--arch/x86_64/kernel/apic.c33
-rw-r--r--arch/x86_64/kernel/pmtimer.c20
-rw-r--r--include/asm-x86_64/proto.h1
4 files changed, 53 insertions, 6 deletions
diff --git a/Documentation/x86_64/boot-options.txt b/Documentation/x86_64/boot-options.txt
index 654ea4fccff8..153740f460a6 100644
--- a/Documentation/x86_64/boot-options.txt
+++ b/Documentation/x86_64/boot-options.txt
@@ -47,6 +47,11 @@ APICs
noapicmaintimer Don't do time keeping using the APIC timer.
Useful when this option was auto selected, but doesn't work.
+ apicpmtimer
+ Do APIC timer calibration using the pmtimer. Implies
+ apicmaintimer. Useful when your PIT timer is totally
+ broken.
+
Early Console
syntax: earlyprintk=vga
diff --git a/arch/x86_64/kernel/apic.c b/arch/x86_64/kernel/apic.c
index 673a2fe9923c..c02218b3ae2b 100644
--- a/arch/x86_64/kernel/apic.c
+++ b/arch/x86_64/kernel/apic.c
@@ -40,6 +40,7 @@
int apic_verbosity;
int apic_runs_main_timer;
+int apic_calibrate_pmtmr __initdata;
int disable_apic_timer __initdata;
@@ -746,14 +747,27 @@ static int __init calibrate_APIC_clock(void)
__setup_APIC_LVTT(1000000000);
apic_start = apic_read(APIC_TMCCT);
- rdtscl(tsc_start);
-
- do {
+#ifdef CONFIG_X86_PM_TIMER
+ if (apic_calibrate_pmtmr && pmtmr_ioport) {
+ pmtimer_wait(5000); /* 5ms wait */
apic = apic_read(APIC_TMCCT);
- rdtscl(tsc);
- } while ((tsc - tsc_start) < TICK_COUNT && (apic - apic_start) < TICK_COUNT);
+ result = (apic_start - apic) * 1000L / 5;
+ } else
+#endif
+ {
+ rdtscl(tsc_start);
+
+ do {
+ apic = apic_read(APIC_TMCCT);
+ rdtscl(tsc);
+ } while ((tsc - tsc_start) < TICK_COUNT &&
+ (apic - apic_start) < TICK_COUNT);
+
+ result = (apic_start - apic) * 1000L * cpu_khz /
+ (tsc - tsc_start);
+ }
+ printk("result %d\n", result);
- result = (apic_start - apic) * 1000L * cpu_khz / (tsc - tsc_start);
printk(KERN_INFO "Detected %d.%03d MHz APIC timer.\n",
result / 1000 / 1000, result / 1000 % 1000);
@@ -1115,6 +1129,13 @@ static __init int setup_noapicmaintimer(char *str)
}
__setup("noapicmaintimer", setup_noapicmaintimer);
+static __init int setup_apicpmtimer(char *s)
+{
+ apic_calibrate_pmtmr = 1;
+ return setup_apicmaintimer(NULL);
+}
+__setup("apicpmtimer", setup_apicpmtimer);
+
/* dummy parsing: see setup.c */
__setup("disableapic", setup_disableapic);
diff --git a/arch/x86_64/kernel/pmtimer.c b/arch/x86_64/kernel/pmtimer.c
index 8b2655ae4e61..5c51d10408a6 100644
--- a/arch/x86_64/kernel/pmtimer.c
+++ b/arch/x86_64/kernel/pmtimer.c
@@ -80,6 +80,26 @@ int pmtimer_mark_offset(void)
return lost - 1;
}
+static unsigned pmtimer_wait_tick(void)
+{
+ u32 a, b;
+ for (a = b = inl(pmtmr_ioport) & ACPI_PM_MASK;
+ a == b;
+ b = inl(pmtmr_ioport) & ACPI_PM_MASK)
+ ;
+ return b;
+}
+
+/* note: wait time is rounded up to one tick */
+void pmtimer_wait(unsigned us)
+{
+ u32 a, b;
+ a = pmtimer_wait_tick();
+ do {
+ b = inl(pmtmr_ioport);
+ } while (cyc2us(b - a) < us);
+}
+
void pmtimer_resume(void)
{
last_pmtmr_tick = inl(pmtmr_ioport);
diff --git a/include/asm-x86_64/proto.h b/include/asm-x86_64/proto.h
index a6748b9568fe..c99832e7bf3f 100644
--- a/include/asm-x86_64/proto.h
+++ b/include/asm-x86_64/proto.h
@@ -42,6 +42,7 @@ extern void iommu_hole_init(void);
extern void time_init_gtod(void);
extern int pmtimer_mark_offset(void);
extern void pmtimer_resume(void);
+extern void pmtimer_wait(unsigned);
extern unsigned int do_gettimeoffset_pm(void);
#ifdef CONFIG_X86_PM_TIMER
extern u32 pmtmr_ioport;