diff options
-rw-r--r-- | arch/x86/kernel/tsc.c | 95 |
1 files changed, 58 insertions, 37 deletions
diff --git a/arch/x86/kernel/tsc.c b/arch/x86/kernel/tsc.c index 52284d31fc9..da033b5b3e1 100644 --- a/arch/x86/kernel/tsc.c +++ b/arch/x86/kernel/tsc.c | |||
@@ -159,9 +159,14 @@ static unsigned long calc_pmtimer_ref(u64 deltatsc, u64 pm1, u64 pm2) | |||
159 | return (unsigned long) deltatsc; | 159 | return (unsigned long) deltatsc; |
160 | } | 160 | } |
161 | 161 | ||
162 | #define CAL_MS 50 | 162 | #define CAL_MS 10 |
163 | #define CAL_LATCH (CLOCK_TICK_RATE / (1000 / CAL_MS)) | 163 | #define CAL_LATCH (CLOCK_TICK_RATE / (1000 / CAL_MS)) |
164 | #define CAL_PIT_LOOPS 5000 | 164 | #define CAL_PIT_LOOPS 1000 |
165 | |||
166 | #define CAL2_MS 50 | ||
167 | #define CAL2_LATCH (CLOCK_TICK_RATE / (1000 / CAL2_MS)) | ||
168 | #define CAL2_PIT_LOOPS 5000 | ||
169 | |||
165 | 170 | ||
166 | /* | 171 | /* |
167 | * Try to calibrate the TSC against the Programmable | 172 | * Try to calibrate the TSC against the Programmable |
@@ -170,7 +175,7 @@ static unsigned long calc_pmtimer_ref(u64 deltatsc, u64 pm1, u64 pm2) | |||
170 | * | 175 | * |
171 | * Return ULONG_MAX on failure to calibrate. | 176 | * Return ULONG_MAX on failure to calibrate. |
172 | */ | 177 | */ |
173 | static unsigned long pit_calibrate_tsc(void) | 178 | static unsigned long pit_calibrate_tsc(u32 latch, unsigned long ms, int loopmin) |
174 | { | 179 | { |
175 | u64 tsc, t1, t2, delta; | 180 | u64 tsc, t1, t2, delta; |
176 | unsigned long tscmin, tscmax; | 181 | unsigned long tscmin, tscmax; |
@@ -185,8 +190,8 @@ static unsigned long pit_calibrate_tsc(void) | |||
185 | * (LSB then MSB) to begin countdown. | 190 | * (LSB then MSB) to begin countdown. |
186 | */ | 191 | */ |
187 | outb(0xb0, 0x43); | 192 | outb(0xb0, 0x43); |
188 | outb(CAL_LATCH & 0xff, 0x42); | 193 | outb(latch & 0xff, 0x42); |
189 | outb(CAL_LATCH >> 8, 0x42); | 194 | outb(latch >> 8, 0x42); |
190 | 195 | ||
191 | tsc = t1 = t2 = get_cycles(); | 196 | tsc = t1 = t2 = get_cycles(); |
192 | 197 | ||
@@ -207,18 +212,18 @@ static unsigned long pit_calibrate_tsc(void) | |||
207 | /* | 212 | /* |
208 | * Sanity checks: | 213 | * Sanity checks: |
209 | * | 214 | * |
210 | * If we were not able to read the PIT more than PIT_MIN_LOOPS | 215 | * If we were not able to read the PIT more than loopmin |
211 | * times, then we have been hit by a massive SMI | 216 | * times, then we have been hit by a massive SMI |
212 | * | 217 | * |
213 | * If the maximum is 10 times larger than the minimum, | 218 | * If the maximum is 10 times larger than the minimum, |
214 | * then we got hit by an SMI as well. | 219 | * then we got hit by an SMI as well. |
215 | */ | 220 | */ |
216 | if (pitcnt < CAL_PIT_LOOPS || tscmax > 10 * tscmin) | 221 | if (pitcnt < loopmin || tscmax > 10 * tscmin) |
217 | return ULONG_MAX; | 222 | return ULONG_MAX; |
218 | 223 | ||
219 | /* Calculate the PIT value */ | 224 | /* Calculate the PIT value */ |
220 | delta = t2 - t1; | 225 | delta = t2 - t1; |
221 | do_div(delta, CAL_MS); | 226 | do_div(delta, ms); |
222 | return delta; | 227 | return delta; |
223 | } | 228 | } |
224 | 229 | ||
@@ -230,8 +235,8 @@ unsigned long native_calibrate_tsc(void) | |||
230 | { | 235 | { |
231 | u64 tsc1, tsc2, delta, ref1, ref2; | 236 | u64 tsc1, tsc2, delta, ref1, ref2; |
232 | unsigned long tsc_pit_min = ULONG_MAX, tsc_ref_min = ULONG_MAX; | 237 | unsigned long tsc_pit_min = ULONG_MAX, tsc_ref_min = ULONG_MAX; |
233 | unsigned long flags; | 238 | unsigned long flags, latch, ms; |
234 | int hpet = is_hpet_enabled(), i; | 239 | int hpet = is_hpet_enabled(), i, loopmin; |
235 | 240 | ||
236 | /* | 241 | /* |
237 | * Run 5 calibration loops to get the lowest frequency value | 242 | * Run 5 calibration loops to get the lowest frequency value |
@@ -257,7 +262,13 @@ unsigned long native_calibrate_tsc(void) | |||
257 | * calibration delay loop as we have to wait for a certain | 262 | * calibration delay loop as we have to wait for a certain |
258 | * amount of time anyway. | 263 | * amount of time anyway. |
259 | */ | 264 | */ |
260 | for (i = 0; i < 5; i++) { | 265 | |
266 | /* Preset PIT loop values */ | ||
267 | latch = CAL_LATCH; | ||
268 | ms = CAL_MS; | ||
269 | loopmin = CAL_PIT_LOOPS; | ||
270 | |||
271 | for (i = 0; i < 3; i++) { | ||
261 | unsigned long tsc_pit_khz; | 272 | unsigned long tsc_pit_khz; |
262 | 273 | ||
263 | /* | 274 | /* |
@@ -268,7 +279,7 @@ unsigned long native_calibrate_tsc(void) | |||
268 | */ | 279 | */ |
269 | local_irq_save(flags); | 280 | local_irq_save(flags); |
270 | tsc1 = tsc_read_refs(&ref1, hpet); | 281 | tsc1 = tsc_read_refs(&ref1, hpet); |
271 | tsc_pit_khz = pit_calibrate_tsc(); | 282 | tsc_pit_khz = pit_calibrate_tsc(latch, ms, loopmin); |
272 | tsc2 = tsc_read_refs(&ref2, hpet); | 283 | tsc2 = tsc_read_refs(&ref2, hpet); |
273 | local_irq_restore(flags); | 284 | local_irq_restore(flags); |
274 | 285 | ||
@@ -290,6 +301,35 @@ unsigned long native_calibrate_tsc(void) | |||
290 | tsc2 = calc_pmtimer_ref(tsc2, ref1, ref2); | 301 | tsc2 = calc_pmtimer_ref(tsc2, ref1, ref2); |
291 | 302 | ||
292 | tsc_ref_min = min(tsc_ref_min, (unsigned long) tsc2); | 303 | tsc_ref_min = min(tsc_ref_min, (unsigned long) tsc2); |
304 | |||
305 | /* Check the reference deviation */ | ||
306 | delta = ((u64) tsc_pit_min) * 100; | ||
307 | do_div(delta, tsc_ref_min); | ||
308 | |||
309 | /* | ||
310 | * If both calibration results are inside a 10% window | ||
311 | * then we can be sure, that the calibration | ||
312 | * succeeded. We break out of the loop right away. We | ||
313 | * use the reference value, as it is more precise. | ||
314 | */ | ||
315 | if (delta >= 90 && delta <= 110) { | ||
316 | printk(KERN_INFO | ||
317 | "TSC: PIT calibration matches %s. %d loops\n", | ||
318 | hpet ? "HPET" : "PMTIMER", i + 1); | ||
319 | return tsc_ref_min; | ||
320 | } | ||
321 | |||
322 | /* | ||
323 | * Check whether PIT failed more than once. This | ||
324 | * happens in virtualized environments. We need to | ||
325 | * give the virtual PC a slightly longer timeframe for | ||
326 | * the HPET/PMTIMER to make the result precise. | ||
327 | */ | ||
328 | if (i == 1 && tsc_pit_min == ULONG_MAX) { | ||
329 | latch = CAL2_LATCH; | ||
330 | ms = CAL2_MS; | ||
331 | loopmin = CAL2_PIT_LOOPS; | ||
332 | } | ||
293 | } | 333 | } |
294 | 334 | ||
295 | /* | 335 | /* |
@@ -309,7 +349,7 @@ unsigned long native_calibrate_tsc(void) | |||
309 | /* The alternative source failed as well, disable TSC */ | 349 | /* The alternative source failed as well, disable TSC */ |
310 | if (tsc_ref_min == ULONG_MAX) { | 350 | if (tsc_ref_min == ULONG_MAX) { |
311 | printk(KERN_WARNING "TSC: HPET/PMTIMER calibration " | 351 | printk(KERN_WARNING "TSC: HPET/PMTIMER calibration " |
312 | "failed due to SMI disturbance.\n"); | 352 | "failed.\n"); |
313 | return 0; | 353 | return 0; |
314 | } | 354 | } |
315 | 355 | ||
@@ -328,37 +368,18 @@ unsigned long native_calibrate_tsc(void) | |||
328 | 368 | ||
329 | /* The alternative source failed, use the PIT calibration value */ | 369 | /* The alternative source failed, use the PIT calibration value */ |
330 | if (tsc_ref_min == ULONG_MAX) { | 370 | if (tsc_ref_min == ULONG_MAX) { |
331 | printk(KERN_WARNING "TSC: HPET/PMTIMER calibration failed due " | 371 | printk(KERN_WARNING "TSC: HPET/PMTIMER calibration failed. " |
332 | "to SMI disturbance. Using PIT calibration\n"); | 372 | "Using PIT calibration\n"); |
333 | return tsc_pit_min; | 373 | return tsc_pit_min; |
334 | } | 374 | } |
335 | 375 | ||
336 | /* Check the reference deviation */ | ||
337 | delta = ((u64) tsc_pit_min) * 100; | ||
338 | do_div(delta, tsc_ref_min); | ||
339 | |||
340 | /* | ||
341 | * If both calibration results are inside a 5% window, the we | ||
342 | * use the lower frequency of those as it is probably the | ||
343 | * closest estimate. | ||
344 | */ | ||
345 | if (delta >= 95 && delta <= 105) { | ||
346 | printk(KERN_INFO "TSC: PIT calibration confirmed by %s.\n", | ||
347 | hpet ? "HPET" : "PMTIMER"); | ||
348 | printk(KERN_INFO "TSC: using %s calibration value\n", | ||
349 | tsc_pit_min <= tsc_ref_min ? "PIT" : | ||
350 | hpet ? "HPET" : "PMTIMER"); | ||
351 | return tsc_pit_min <= tsc_ref_min ? tsc_pit_min : tsc_ref_min; | ||
352 | } | ||
353 | |||
354 | printk(KERN_WARNING "TSC: PIT calibration deviates from %s: %lu %lu.\n", | ||
355 | hpet ? "HPET" : "PMTIMER", tsc_pit_min, tsc_ref_min); | ||
356 | |||
357 | /* | 376 | /* |
358 | * The calibration values differ too much. In doubt, we use | 377 | * The calibration values differ too much. In doubt, we use |
359 | * the PIT value as we know that there are PMTIMERs around | 378 | * the PIT value as we know that there are PMTIMERs around |
360 | * running at double speed. | 379 | * running at double speed. At least we let the user know: |
361 | */ | 380 | */ |
381 | printk(KERN_WARNING "TSC: PIT calibration deviates from %s: %lu %lu.\n", | ||
382 | hpet ? "HPET" : "PMTIMER", tsc_pit_min, tsc_ref_min); | ||
362 | printk(KERN_INFO "TSC: Using PIT calibration value\n"); | 383 | printk(KERN_INFO "TSC: Using PIT calibration value\n"); |
363 | return tsc_pit_min; | 384 | return tsc_pit_min; |
364 | } | 385 | } |