aboutsummaryrefslogtreecommitdiffstats
path: root/arch/x86/kernel/tsc_64.c
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/kernel/tsc_64.c
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/kernel/tsc_64.c')
-rw-r--r--arch/x86/kernel/tsc_64.c90
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
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.