aboutsummaryrefslogtreecommitdiffstats
path: root/arch/x86
diff options
context:
space:
mode:
authorThomas Gleixner <tglx@linutronix.de>2007-10-12 17:04:06 -0400
committerThomas Gleixner <tglx@inhelltoy.tec.linutronix.de>2007-10-12 17:04:06 -0400
commitd371698efd45c3664fd1726780c360f02e1f9580 (patch)
treef6c45126c49f35a62520b89d1bc74fca1a6f9ff9 /arch/x86
parent89b2bbd69b89b4c5efdc112a88d72419bdeb8dfc (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')
-rw-r--r--arch/x86/kernel/hpet_64.c49
-rw-r--r--arch/x86/kernel/time_64.c33
-rw-r--r--arch/x86/kernel/tsc_64.c90
3 files changed, 92 insertions, 80 deletions
diff --git a/arch/x86/kernel/hpet_64.c b/arch/x86/kernel/hpet_64.c
index e2d1b912e154..1ebce2ec7eaf 100644
--- a/arch/x86/kernel/hpet_64.c
+++ b/arch/x86/kernel/hpet_64.c
@@ -184,55 +184,6 @@ int hpet_reenable(void)
184 return hpet_timer_stop_set_go(hpet_tick); 184 return hpet_timer_stop_set_go(hpet_tick);
185} 185}
186 186
187/*
188 * calibrate_tsc() calibrates the processor TSC in a very simple way, comparing
189 * it to the HPET timer of known frequency.
190 */
191
192#define TICK_COUNT 100000000
193#define SMI_THRESHOLD 50000
194#define MAX_TRIES 5
195
196/*
197 * Some platforms take periodic SMI interrupts with 5ms duration. Make sure none
198 * occurs between the reads of the hpet & TSC.
199 */
200static void __init read_hpet_tsc(int *hpet, int *tsc)
201{
202 int tsc1, tsc2, hpet1, i;
203
204 for (i = 0; i < MAX_TRIES; i++) {
205 tsc1 = get_cycles_sync();
206 hpet1 = hpet_readl(HPET_COUNTER);
207 tsc2 = get_cycles_sync();
208 if ((tsc2 - tsc1) < SMI_THRESHOLD)
209 break;
210 }
211 *hpet = hpet1;
212 *tsc = tsc2;
213}
214
215unsigned int __init hpet_calibrate_tsc(void)
216{
217 int tsc_start, hpet_start;
218 int tsc_now, hpet_now;
219 unsigned long flags;
220
221 local_irq_save(flags);
222
223 read_hpet_tsc(&hpet_start, &tsc_start);
224
225 do {
226 local_irq_disable();
227 read_hpet_tsc(&hpet_now, &tsc_now);
228 local_irq_restore(flags);
229 } while ((tsc_now - tsc_start) < TICK_COUNT &&
230 (hpet_now - hpet_start) < TICK_COUNT);
231
232 return (tsc_now - tsc_start) * 1000000000L
233 / ((hpet_now - hpet_start) * hpet_period / 1000);
234}
235
236#ifdef CONFIG_HPET_EMULATE_RTC 187#ifdef CONFIG_HPET_EMULATE_RTC
237/* HPET in LegacyReplacement Mode eats up RTC interrupt line. When, HPET 188/* HPET in LegacyReplacement Mode eats up RTC interrupt line. When, HPET
238 * is enabled, we support RTC interrupt functionality in software. 189 * is enabled, we support RTC interrupt functionality in software.
diff --git a/arch/x86/kernel/time_64.c b/arch/x86/kernel/time_64.c
index 2db4d698a2d3..e97a3ee13551 100644
--- a/arch/x86/kernel/time_64.c
+++ b/arch/x86/kernel/time_64.c
@@ -292,35 +292,6 @@ static unsigned int __init tsc_calibrate_cpu_khz(void)
292 return pmc_now * tsc_khz / (tsc_now - tsc_start); 292 return pmc_now * tsc_khz / (tsc_now - tsc_start);
293} 293}
294 294
295/*
296 * pit_calibrate_tsc() uses the speaker output (channel 2) of
297 * the PIT. This is better than using the timer interrupt output,
298 * because we can read the value of the speaker with just one inb(),
299 * where we need three i/o operations for the interrupt channel.
300 * We count how many ticks the TSC does in 50 ms.
301 */
302
303static unsigned int __init pit_calibrate_tsc(void)
304{
305 unsigned long start, end;
306 unsigned long flags;
307
308 spin_lock_irqsave(&i8253_lock, flags);
309
310 outb((inb(0x61) & ~0x02) | 0x01, 0x61);
311
312 outb(0xb0, 0x43);
313 outb((PIT_TICK_RATE / (1000 / 50)) & 0xff, 0x42);
314 outb((PIT_TICK_RATE / (1000 / 50)) >> 8, 0x42);
315 start = get_cycles_sync();
316 while ((inb(0x61) & 0x20) == 0);
317 end = get_cycles_sync();
318
319 spin_unlock_irqrestore(&i8253_lock, flags);
320
321 return (end - start) / 50;
322}
323
324#define PIT_MODE 0x43 295#define PIT_MODE 0x43
325#define PIT_CH0 0x40 296#define PIT_CH0 0x40
326 297
@@ -376,14 +347,14 @@ void __init time_init(void)
376 if (hpet_use_timer) { 347 if (hpet_use_timer) {
377 /* set tick_nsec to use the proper rate for HPET */ 348 /* set tick_nsec to use the proper rate for HPET */
378 tick_nsec = TICK_NSEC_HPET; 349 tick_nsec = TICK_NSEC_HPET;
379 tsc_khz = hpet_calibrate_tsc();
380 timename = "HPET"; 350 timename = "HPET";
381 } else { 351 } else {
382 pit_init(); 352 pit_init();
383 tsc_khz = pit_calibrate_tsc();
384 timename = "PIT"; 353 timename = "PIT";
385 } 354 }
386 355
356 tsc_calibrate();
357
387 cpu_khz = tsc_khz; 358 cpu_khz = tsc_khz;
388 if (cpu_has(&boot_cpu_data, X86_FEATURE_CONSTANT_TSC) && 359 if (cpu_has(&boot_cpu_data, X86_FEATURE_CONSTANT_TSC) &&
389 boot_cpu_data.x86_vendor == X86_VENDOR_AMD && 360 boot_cpu_data.x86_vendor == X86_VENDOR_AMD &&
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
12static int notsc __initdata = 0; 14static 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 */
129static 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 */
151void __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.