diff options
author | Thomas Gleixner <tglx@linutronix.de> | 2007-10-12 17:04:06 -0400 |
---|---|---|
committer | Thomas Gleixner <tglx@inhelltoy.tec.linutronix.de> | 2007-10-12 17:04:06 -0400 |
commit | d371698efd45c3664fd1726780c360f02e1f9580 (patch) | |
tree | f6c45126c49f35a62520b89d1bc74fca1a6f9ff9 /arch/x86/kernel/tsc_64.c | |
parent | 89b2bbd69b89b4c5efdc112a88d72419bdeb8dfc (diff) |
x86_64: Consolidate tsc calibration
Move the TSC calibration code to tsc.c. Reimplement it so the
pm timer can be used as a reference as well.
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Signed-off-by: Chris Wright <chrisw@sous-sol.org>
Signed-off-by: Ingo Molnar <mingo@elte.hu>
Signed-off-by: Arjan van de Ven <arjan@linux.intel.com>
Diffstat (limited to 'arch/x86/kernel/tsc_64.c')
-rw-r--r-- | arch/x86/kernel/tsc_64.c | 90 |
1 files changed, 90 insertions, 0 deletions
diff --git a/arch/x86/kernel/tsc_64.c b/arch/x86/kernel/tsc_64.c index 2a59bde663f2..59baecd135ab 100644 --- a/arch/x86/kernel/tsc_64.c +++ b/arch/x86/kernel/tsc_64.c | |||
@@ -6,7 +6,9 @@ | |||
6 | #include <linux/time.h> | 6 | #include <linux/time.h> |
7 | #include <linux/acpi.h> | 7 | #include <linux/acpi.h> |
8 | #include <linux/cpufreq.h> | 8 | #include <linux/cpufreq.h> |
9 | #include <linux/acpi_pmtmr.h> | ||
9 | 10 | ||
11 | #include <asm/hpet.h> | ||
10 | #include <asm/timex.h> | 12 | #include <asm/timex.h> |
11 | 13 | ||
12 | static int notsc __initdata = 0; | 14 | static int notsc __initdata = 0; |
@@ -118,6 +120,94 @@ core_initcall(cpufreq_tsc); | |||
118 | 120 | ||
119 | #endif | 121 | #endif |
120 | 122 | ||
123 | #define MAX_RETRIES 5 | ||
124 | #define SMI_TRESHOLD 50000 | ||
125 | |||
126 | /* | ||
127 | * Read TSC and the reference counters. Take care of SMI disturbance | ||
128 | */ | ||
129 | static unsigned long __init tsc_read_refs(unsigned long *pm, | ||
130 | unsigned long *hpet) | ||
131 | { | ||
132 | unsigned long t1, t2; | ||
133 | int i; | ||
134 | |||
135 | for (i = 0; i < MAX_RETRIES; i++) { | ||
136 | t1 = get_cycles_sync(); | ||
137 | if (hpet) | ||
138 | *hpet = hpet_readl(HPET_COUNTER) & 0xFFFFFFFF; | ||
139 | else | ||
140 | *pm = acpi_pm_read_early(); | ||
141 | t2 = get_cycles_sync(); | ||
142 | if ((t2 - t1) < SMI_TRESHOLD) | ||
143 | return t2; | ||
144 | } | ||
145 | return ULONG_MAX; | ||
146 | } | ||
147 | |||
148 | /** | ||
149 | * tsc_calibrate - calibrate the tsc on boot | ||
150 | */ | ||
151 | void __init tsc_calibrate(void) | ||
152 | { | ||
153 | unsigned long flags, tsc1, tsc2, tr1, tr2, pm1, pm2, hpet1, hpet2; | ||
154 | int hpet = is_hpet_enabled(); | ||
155 | |||
156 | local_irq_save(flags); | ||
157 | |||
158 | tsc1 = tsc_read_refs(&pm1, hpet ? &hpet1 : NULL); | ||
159 | |||
160 | outb((inb(0x61) & ~0x02) | 0x01, 0x61); | ||
161 | |||
162 | outb(0xb0, 0x43); | ||
163 | outb((CLOCK_TICK_RATE / (1000 / 50)) & 0xff, 0x42); | ||
164 | outb((CLOCK_TICK_RATE / (1000 / 50)) >> 8, 0x42); | ||
165 | tr1 = get_cycles_sync(); | ||
166 | while ((inb(0x61) & 0x20) == 0); | ||
167 | tr2 = get_cycles_sync(); | ||
168 | |||
169 | tsc2 = tsc_read_refs(&pm2, hpet ? &hpet2 : NULL); | ||
170 | |||
171 | local_irq_restore(flags); | ||
172 | |||
173 | /* | ||
174 | * Preset the result with the raw and inaccurate PIT | ||
175 | * calibration value | ||
176 | */ | ||
177 | tsc_khz = (tr2 - tr1) / 50; | ||
178 | |||
179 | /* hpet or pmtimer available ? */ | ||
180 | if (!hpet && !pm1 && !pm2) { | ||
181 | printk(KERN_INFO "TSC calibrated against PIT\n"); | ||
182 | return; | ||
183 | } | ||
184 | |||
185 | /* Check, whether the sampling was disturbed by an SMI */ | ||
186 | if (tsc1 == ULONG_MAX || tsc2 == ULONG_MAX) { | ||
187 | printk(KERN_WARNING "TSC calibration disturbed by SMI, " | ||
188 | "using PIT calibration result\n"); | ||
189 | return; | ||
190 | } | ||
191 | |||
192 | tsc2 = (tsc2 - tsc1) * 1000000L; | ||
193 | |||
194 | if (hpet) { | ||
195 | printk(KERN_INFO "TSC calibrated against HPET\n"); | ||
196 | if (hpet2 < hpet1) | ||
197 | hpet2 += 0x100000000; | ||
198 | hpet2 -= hpet1; | ||
199 | tsc1 = (hpet2 * hpet_readl(HPET_PERIOD)) / 1000000; | ||
200 | } else { | ||
201 | printk(KERN_INFO "TSC calibrated against PM_TIMER\n"); | ||
202 | if (pm2 < pm1) | ||
203 | pm2 += ACPI_PM_OVRRUN; | ||
204 | pm2 -= pm1; | ||
205 | tsc1 = (pm2 * 1000000000) / PMTMR_TICKS_PER_SEC; | ||
206 | } | ||
207 | |||
208 | tsc_khz = tsc2 / tsc1; | ||
209 | } | ||
210 | |||
121 | /* | 211 | /* |
122 | * Make an educated guess if the TSC is trustworthy and synchronized | 212 | * Make an educated guess if the TSC is trustworthy and synchronized |
123 | * over all CPUs. | 213 | * over all CPUs. |