diff options
| author | Ingo Molnar <mingo@elte.hu> | 2008-09-04 07:02:35 -0400 |
|---|---|---|
| committer | Ingo Molnar <mingo@elte.hu> | 2008-09-04 07:02:35 -0400 |
| commit | 42390cdec5f6e6e2ee54f308474a6ef7dd16aa5c (patch) | |
| tree | e9684c84f53272319a5acd4b9c86503f30274a51 /arch/x86/kernel/tsc.c | |
| parent | 11c231a962c740b3216eb6565149ae5a7944cba7 (diff) | |
| parent | d210baf53b699fc61aa891c177b71d7082d3b957 (diff) | |
Merge branch 'linus' into x86/x2apic
Conflicts:
arch/x86/kernel/cpu/cyrix.c
include/asm-x86/cpufeature.h
Signed-off-by: Ingo Molnar <mingo@elte.hu>
Diffstat (limited to 'arch/x86/kernel/tsc.c')
| -rw-r--r-- | arch/x86/kernel/tsc.c | 243 |
1 files changed, 192 insertions, 51 deletions
diff --git a/arch/x86/kernel/tsc.c b/arch/x86/kernel/tsc.c index 7603c0553909..346cae5ac423 100644 --- a/arch/x86/kernel/tsc.c +++ b/arch/x86/kernel/tsc.c | |||
| @@ -104,7 +104,7 @@ __setup("notsc", notsc_setup); | |||
| 104 | /* | 104 | /* |
| 105 | * Read TSC and the reference counters. Take care of SMI disturbance | 105 | * Read TSC and the reference counters. Take care of SMI disturbance |
| 106 | */ | 106 | */ |
| 107 | static u64 __init tsc_read_refs(u64 *pm, u64 *hpet) | 107 | static u64 tsc_read_refs(u64 *pm, u64 *hpet) |
| 108 | { | 108 | { |
| 109 | u64 t1, t2; | 109 | u64 t1, t2; |
| 110 | int i; | 110 | int i; |
| @@ -122,80 +122,217 @@ static u64 __init tsc_read_refs(u64 *pm, u64 *hpet) | |||
| 122 | return ULLONG_MAX; | 122 | return ULLONG_MAX; |
| 123 | } | 123 | } |
| 124 | 124 | ||
| 125 | /** | 125 | /* |
| 126 | * native_calibrate_tsc - calibrate the tsc on boot | 126 | * Try to calibrate the TSC against the Programmable |
| 127 | * Interrupt Timer and return the frequency of the TSC | ||
| 128 | * in kHz. | ||
| 129 | * | ||
| 130 | * Return ULONG_MAX on failure to calibrate. | ||
| 127 | */ | 131 | */ |
| 128 | unsigned long native_calibrate_tsc(void) | 132 | static unsigned long pit_calibrate_tsc(void) |
| 129 | { | 133 | { |
| 130 | unsigned long flags; | 134 | u64 tsc, t1, t2, delta; |
| 131 | u64 tsc1, tsc2, tr1, tr2, delta, pm1, pm2, hpet1, hpet2; | 135 | unsigned long tscmin, tscmax; |
| 132 | int hpet = is_hpet_enabled(); | 136 | int pitcnt; |
| 133 | unsigned int tsc_khz_val = 0; | ||
| 134 | |||
| 135 | local_irq_save(flags); | ||
| 136 | |||
| 137 | tsc1 = tsc_read_refs(&pm1, hpet ? &hpet1 : NULL); | ||
| 138 | 137 | ||
| 138 | /* Set the Gate high, disable speaker */ | ||
| 139 | outb((inb(0x61) & ~0x02) | 0x01, 0x61); | 139 | outb((inb(0x61) & ~0x02) | 0x01, 0x61); |
| 140 | 140 | ||
| 141 | /* | ||
| 142 | * Setup CTC channel 2* for mode 0, (interrupt on terminal | ||
| 143 | * count mode), binary count. Set the latch register to 50ms | ||
| 144 | * (LSB then MSB) to begin countdown. | ||
| 145 | */ | ||
| 141 | outb(0xb0, 0x43); | 146 | outb(0xb0, 0x43); |
| 142 | outb((CLOCK_TICK_RATE / (1000 / 50)) & 0xff, 0x42); | 147 | outb((CLOCK_TICK_RATE / (1000 / 50)) & 0xff, 0x42); |
| 143 | outb((CLOCK_TICK_RATE / (1000 / 50)) >> 8, 0x42); | 148 | outb((CLOCK_TICK_RATE / (1000 / 50)) >> 8, 0x42); |
| 144 | tr1 = get_cycles(); | ||
| 145 | while ((inb(0x61) & 0x20) == 0); | ||
| 146 | tr2 = get_cycles(); | ||
| 147 | 149 | ||
| 148 | tsc2 = tsc_read_refs(&pm2, hpet ? &hpet2 : NULL); | 150 | tsc = t1 = t2 = get_cycles(); |
| 149 | 151 | ||
| 150 | local_irq_restore(flags); | 152 | pitcnt = 0; |
| 153 | tscmax = 0; | ||
| 154 | tscmin = ULONG_MAX; | ||
| 155 | while ((inb(0x61) & 0x20) == 0) { | ||
| 156 | t2 = get_cycles(); | ||
| 157 | delta = t2 - tsc; | ||
| 158 | tsc = t2; | ||
| 159 | if ((unsigned long) delta < tscmin) | ||
| 160 | tscmin = (unsigned int) delta; | ||
| 161 | if ((unsigned long) delta > tscmax) | ||
| 162 | tscmax = (unsigned int) delta; | ||
| 163 | pitcnt++; | ||
| 164 | } | ||
| 151 | 165 | ||
| 152 | /* | 166 | /* |
| 153 | * Preset the result with the raw and inaccurate PIT | 167 | * Sanity checks: |
| 154 | * calibration value | 168 | * |
| 169 | * If we were not able to read the PIT more than 5000 | ||
| 170 | * times, then we have been hit by a massive SMI | ||
| 171 | * | ||
| 172 | * If the maximum is 10 times larger than the minimum, | ||
| 173 | * then we got hit by an SMI as well. | ||
| 155 | */ | 174 | */ |
| 156 | delta = (tr2 - tr1); | 175 | if (pitcnt < 5000 || tscmax > 10 * tscmin) |
| 176 | return ULONG_MAX; | ||
| 177 | |||
| 178 | /* Calculate the PIT value */ | ||
| 179 | delta = t2 - t1; | ||
| 157 | do_div(delta, 50); | 180 | do_div(delta, 50); |
| 158 | tsc_khz_val = delta; | 181 | return delta; |
| 182 | } | ||
| 183 | |||
| 184 | |||
| 185 | /** | ||
| 186 | * native_calibrate_tsc - calibrate the tsc on boot | ||
| 187 | */ | ||
| 188 | unsigned long native_calibrate_tsc(void) | ||
| 189 | { | ||
| 190 | u64 tsc1, tsc2, delta, pm1, pm2, hpet1, hpet2; | ||
| 191 | unsigned long tsc_pit_min = ULONG_MAX, tsc_ref_min = ULONG_MAX; | ||
| 192 | unsigned long flags; | ||
| 193 | int hpet = is_hpet_enabled(), i; | ||
| 194 | |||
| 195 | /* | ||
| 196 | * Run 5 calibration loops to get the lowest frequency value | ||
| 197 | * (the best estimate). We use two different calibration modes | ||
| 198 | * here: | ||
| 199 | * | ||
| 200 | * 1) PIT loop. We set the PIT Channel 2 to oneshot mode and | ||
| 201 | * load a timeout of 50ms. We read the time right after we | ||
| 202 | * started the timer and wait until the PIT count down reaches | ||
| 203 | * zero. In each wait loop iteration we read the TSC and check | ||
| 204 | * the delta to the previous read. We keep track of the min | ||
| 205 | * and max values of that delta. The delta is mostly defined | ||
| 206 | * by the IO time of the PIT access, so we can detect when a | ||
| 207 | * SMI/SMM disturbance happend between the two reads. If the | ||
| 208 | * maximum time is significantly larger than the minimum time, | ||
| 209 | * then we discard the result and have another try. | ||
| 210 | * | ||
| 211 | * 2) Reference counter. If available we use the HPET or the | ||
| 212 | * PMTIMER as a reference to check the sanity of that value. | ||
| 213 | * We use separate TSC readouts and check inside of the | ||
| 214 | * reference read for a SMI/SMM disturbance. We dicard | ||
| 215 | * disturbed values here as well. We do that around the PIT | ||
| 216 | * calibration delay loop as we have to wait for a certain | ||
| 217 | * amount of time anyway. | ||
| 218 | */ | ||
| 219 | for (i = 0; i < 5; i++) { | ||
| 220 | unsigned long tsc_pit_khz; | ||
| 221 | |||
| 222 | /* | ||
| 223 | * Read the start value and the reference count of | ||
| 224 | * hpet/pmtimer when available. Then do the PIT | ||
| 225 | * calibration, which will take at least 50ms, and | ||
| 226 | * read the end value. | ||
| 227 | */ | ||
| 228 | local_irq_save(flags); | ||
| 229 | tsc1 = tsc_read_refs(&pm1, hpet ? &hpet1 : NULL); | ||
| 230 | tsc_pit_khz = pit_calibrate_tsc(); | ||
| 231 | tsc2 = tsc_read_refs(&pm2, hpet ? &hpet2 : NULL); | ||
| 232 | local_irq_restore(flags); | ||
| 233 | |||
| 234 | /* Pick the lowest PIT TSC calibration so far */ | ||
| 235 | tsc_pit_min = min(tsc_pit_min, tsc_pit_khz); | ||
| 236 | |||
| 237 | /* hpet or pmtimer available ? */ | ||
| 238 | if (!hpet && !pm1 && !pm2) | ||
| 239 | continue; | ||
| 240 | |||
| 241 | /* Check, whether the sampling was disturbed by an SMI */ | ||
| 242 | if (tsc1 == ULLONG_MAX || tsc2 == ULLONG_MAX) | ||
| 243 | continue; | ||
| 244 | |||
| 245 | tsc2 = (tsc2 - tsc1) * 1000000LL; | ||
| 246 | |||
| 247 | if (hpet) { | ||
| 248 | if (hpet2 < hpet1) | ||
| 249 | hpet2 += 0x100000000ULL; | ||
| 250 | hpet2 -= hpet1; | ||
| 251 | tsc1 = ((u64)hpet2 * hpet_readl(HPET_PERIOD)); | ||
| 252 | do_div(tsc1, 1000000); | ||
| 253 | } else { | ||
| 254 | if (pm2 < pm1) | ||
| 255 | pm2 += (u64)ACPI_PM_OVRRUN; | ||
| 256 | pm2 -= pm1; | ||
| 257 | tsc1 = pm2 * 1000000000LL; | ||
| 258 | do_div(tsc1, PMTMR_TICKS_PER_SEC); | ||
| 259 | } | ||
| 260 | |||
| 261 | do_div(tsc2, tsc1); | ||
| 262 | tsc_ref_min = min(tsc_ref_min, (unsigned long) tsc2); | ||
| 263 | } | ||
| 264 | |||
| 265 | /* | ||
| 266 | * Now check the results. | ||
| 267 | */ | ||
| 268 | if (tsc_pit_min == ULONG_MAX) { | ||
| 269 | /* PIT gave no useful value */ | ||
| 270 | printk(KERN_WARNING "TSC: PIT calibration failed due to " | ||
| 271 | "SMI disturbance.\n"); | ||
| 272 | |||
| 273 | /* We don't have an alternative source, disable TSC */ | ||
| 274 | if (!hpet && !pm1 && !pm2) { | ||
| 275 | printk("TSC: No reference (HPET/PMTIMER) available\n"); | ||
| 276 | return 0; | ||
| 277 | } | ||
| 278 | |||
| 279 | /* The alternative source failed as well, disable TSC */ | ||
| 280 | if (tsc_ref_min == ULONG_MAX) { | ||
| 281 | printk(KERN_WARNING "TSC: HPET/PMTIMER calibration " | ||
| 282 | "failed due to SMI disturbance.\n"); | ||
| 283 | return 0; | ||
| 284 | } | ||
| 285 | |||
| 286 | /* Use the alternative source */ | ||
| 287 | printk(KERN_INFO "TSC: using %s reference calibration\n", | ||
| 288 | hpet ? "HPET" : "PMTIMER"); | ||
| 289 | |||
| 290 | return tsc_ref_min; | ||
| 291 | } | ||
| 159 | 292 | ||
| 160 | /* hpet or pmtimer available ? */ | 293 | /* We don't have an alternative source, use the PIT calibration value */ |
| 161 | if (!hpet && !pm1 && !pm2) { | 294 | if (!hpet && !pm1 && !pm2) { |
| 162 | printk(KERN_INFO "TSC calibrated against PIT\n"); | 295 | printk(KERN_INFO "TSC: Using PIT calibration value\n"); |
| 163 | goto out; | 296 | return tsc_pit_min; |
| 164 | } | 297 | } |
| 165 | 298 | ||
| 166 | /* Check, whether the sampling was disturbed by an SMI */ | 299 | /* The alternative source failed, use the PIT calibration value */ |
| 167 | if (tsc1 == ULLONG_MAX || tsc2 == ULLONG_MAX) { | 300 | if (tsc_ref_min == ULONG_MAX) { |
| 168 | printk(KERN_WARNING "TSC calibration disturbed by SMI, " | 301 | printk(KERN_WARNING "TSC: HPET/PMTIMER calibration failed due " |
| 169 | "using PIT calibration result\n"); | 302 | "to SMI disturbance. Using PIT calibration\n"); |
| 170 | goto out; | 303 | return tsc_pit_min; |
| 171 | } | 304 | } |
| 172 | 305 | ||
| 173 | tsc2 = (tsc2 - tsc1) * 1000000LL; | 306 | /* Check the reference deviation */ |
| 174 | 307 | delta = ((u64) tsc_pit_min) * 100; | |
| 175 | if (hpet) { | 308 | do_div(delta, tsc_ref_min); |
| 176 | printk(KERN_INFO "TSC calibrated against HPET\n"); | 309 | |
| 177 | if (hpet2 < hpet1) | 310 | /* |
| 178 | hpet2 += 0x100000000ULL; | 311 | * If both calibration results are inside a 5% window, the we |
| 179 | hpet2 -= hpet1; | 312 | * use the lower frequency of those as it is probably the |
| 180 | tsc1 = ((u64)hpet2 * hpet_readl(HPET_PERIOD)); | 313 | * closest estimate. |
| 181 | do_div(tsc1, 1000000); | 314 | */ |
| 182 | } else { | 315 | if (delta >= 95 && delta <= 105) { |
| 183 | printk(KERN_INFO "TSC calibrated against PM_TIMER\n"); | 316 | printk(KERN_INFO "TSC: PIT calibration confirmed by %s.\n", |
| 184 | if (pm2 < pm1) | 317 | hpet ? "HPET" : "PMTIMER"); |
| 185 | pm2 += (u64)ACPI_PM_OVRRUN; | 318 | printk(KERN_INFO "TSC: using %s calibration value\n", |
| 186 | pm2 -= pm1; | 319 | tsc_pit_min <= tsc_ref_min ? "PIT" : |
| 187 | tsc1 = pm2 * 1000000000LL; | 320 | hpet ? "HPET" : "PMTIMER"); |
| 188 | do_div(tsc1, PMTMR_TICKS_PER_SEC); | 321 | return tsc_pit_min <= tsc_ref_min ? tsc_pit_min : tsc_ref_min; |
| 189 | } | 322 | } |
| 190 | 323 | ||
| 191 | do_div(tsc2, tsc1); | 324 | printk(KERN_WARNING "TSC: PIT calibration deviates from %s: %lu %lu.\n", |
| 192 | tsc_khz_val = tsc2; | 325 | hpet ? "HPET" : "PMTIMER", tsc_pit_min, tsc_ref_min); |
| 193 | 326 | ||
| 194 | out: | 327 | /* |
| 195 | return tsc_khz_val; | 328 | * The calibration values differ too much. In doubt, we use |
| 329 | * the PIT value as we know that there are PMTIMERs around | ||
| 330 | * running at double speed. | ||
| 331 | */ | ||
| 332 | printk(KERN_INFO "TSC: Using PIT calibration value\n"); | ||
| 333 | return tsc_pit_min; | ||
| 196 | } | 334 | } |
| 197 | 335 | ||
| 198 | |||
| 199 | #ifdef CONFIG_X86_32 | 336 | #ifdef CONFIG_X86_32 |
| 200 | /* Only called from the Powernow K7 cpu freq driver */ | 337 | /* Only called from the Powernow K7 cpu freq driver */ |
| 201 | int recalibrate_cpu_khz(void) | 338 | int recalibrate_cpu_khz(void) |
| @@ -314,7 +451,7 @@ static int time_cpufreq_notifier(struct notifier_block *nb, unsigned long val, | |||
| 314 | mark_tsc_unstable("cpufreq changes"); | 451 | mark_tsc_unstable("cpufreq changes"); |
| 315 | } | 452 | } |
| 316 | 453 | ||
| 317 | set_cyc2ns_scale(tsc_khz_ref, freq->cpu); | 454 | set_cyc2ns_scale(tsc_khz, freq->cpu); |
| 318 | 455 | ||
| 319 | return 0; | 456 | return 0; |
| 320 | } | 457 | } |
| @@ -325,6 +462,10 @@ static struct notifier_block time_cpufreq_notifier_block = { | |||
| 325 | 462 | ||
| 326 | static int __init cpufreq_tsc(void) | 463 | static int __init cpufreq_tsc(void) |
| 327 | { | 464 | { |
| 465 | if (!cpu_has_tsc) | ||
| 466 | return 0; | ||
| 467 | if (boot_cpu_has(X86_FEATURE_CONSTANT_TSC)) | ||
| 468 | return 0; | ||
| 328 | cpufreq_register_notifier(&time_cpufreq_notifier_block, | 469 | cpufreq_register_notifier(&time_cpufreq_notifier_block, |
| 329 | CPUFREQ_TRANSITION_NOTIFIER); | 470 | CPUFREQ_TRANSITION_NOTIFIER); |
| 330 | return 0; | 471 | return 0; |
