diff options
Diffstat (limited to 'arch/x86/kernel/tsc.c')
-rw-r--r-- | arch/x86/kernel/tsc.c | 235 |
1 files changed, 181 insertions, 54 deletions
diff --git a/arch/x86/kernel/tsc.c b/arch/x86/kernel/tsc.c index 8e786b0d665a..ac79bd143da8 100644 --- a/arch/x86/kernel/tsc.c +++ b/arch/x86/kernel/tsc.c | |||
@@ -127,75 +127,202 @@ static u64 tsc_read_refs(u64 *pm, u64 *hpet) | |||
127 | */ | 127 | */ |
128 | unsigned long native_calibrate_tsc(void) | 128 | unsigned long native_calibrate_tsc(void) |
129 | { | 129 | { |
130 | unsigned long flags; | 130 | u64 tsc1, tsc2, tr1, tr2, tsc, delta, pm1, pm2, hpet1, hpet2; |
131 | u64 tsc1, tsc2, tr1, tr2, delta, pm1, pm2, hpet1, hpet2; | 131 | unsigned long tsc_pit_min = ULONG_MAX, tsc_ref_min = ULONG_MAX; |
132 | int hpet = is_hpet_enabled(); | 132 | unsigned long flags, tscmin, tscmax; |
133 | unsigned int tsc_khz_val = 0; | 133 | int hpet = is_hpet_enabled(), pitcnt, i; |
134 | 134 | ||
135 | local_irq_save(flags); | 135 | /* |
136 | 136 | * Run 5 calibration loops to get the lowest frequency value | |
137 | tsc1 = tsc_read_refs(&pm1, hpet ? &hpet1 : NULL); | 137 | * (the best estimate). We use two different calibration modes |
138 | 138 | * here: | |
139 | outb((inb(0x61) & ~0x02) | 0x01, 0x61); | 139 | * |
140 | 140 | * 1) PIT loop. We set the PIT Channel 2 to oneshot mode and | |
141 | outb(0xb0, 0x43); | 141 | * load a timeout of 50ms. We read the time right after we |
142 | outb((CLOCK_TICK_RATE / (1000 / 50)) & 0xff, 0x42); | 142 | * started the timer and wait until the PIT count down reaches |
143 | outb((CLOCK_TICK_RATE / (1000 / 50)) >> 8, 0x42); | 143 | * zero. In each wait loop iteration we read the TSC and check |
144 | tr1 = get_cycles(); | 144 | * the delta to the previous read. We keep track of the min |
145 | while ((inb(0x61) & 0x20) == 0); | 145 | * and max values of that delta. The delta is mostly defined |
146 | tr2 = get_cycles(); | 146 | * by the IO time of the PIT access, so we can detect when a |
147 | 147 | * SMI/SMM disturbance happend between the two reads. If the | |
148 | tsc2 = tsc_read_refs(&pm2, hpet ? &hpet2 : NULL); | 148 | * maximum time is significantly larger than the minimum time, |
149 | 149 | * then we discard the result and have another try. | |
150 | local_irq_restore(flags); | 150 | * |
151 | * 2) Reference counter. If available we use the HPET or the | ||
152 | * PMTIMER as a reference to check the sanity of that value. | ||
153 | * We use separate TSC readouts and check inside of the | ||
154 | * reference read for a SMI/SMM disturbance. We dicard | ||
155 | * disturbed values here as well. We do that around the PIT | ||
156 | * calibration delay loop as we have to wait for a certain | ||
157 | * amount of time anyway. | ||
158 | */ | ||
159 | for (i = 0; i < 5; i++) { | ||
160 | |||
161 | tscmin = ULONG_MAX; | ||
162 | tscmax = 0; | ||
163 | pitcnt = 0; | ||
164 | |||
165 | local_irq_save(flags); | ||
166 | |||
167 | /* | ||
168 | * Read the start value and the reference count of | ||
169 | * hpet/pmtimer when available: | ||
170 | */ | ||
171 | tsc1 = tsc_read_refs(&pm1, hpet ? &hpet1 : NULL); | ||
172 | |||
173 | /* Set the Gate high, disable speaker */ | ||
174 | outb((inb(0x61) & ~0x02) | 0x01, 0x61); | ||
175 | |||
176 | /* | ||
177 | * Setup CTC channel 2* for mode 0, (interrupt on terminal | ||
178 | * count mode), binary count. Set the latch register to 50ms | ||
179 | * (LSB then MSB) to begin countdown. | ||
180 | * | ||
181 | * Some devices need a delay here. | ||
182 | */ | ||
183 | outb(0xb0, 0x43); | ||
184 | outb((CLOCK_TICK_RATE / (1000 / 50)) & 0xff, 0x42); | ||
185 | outb((CLOCK_TICK_RATE / (1000 / 50)) >> 8, 0x42); | ||
186 | |||
187 | tsc = tr1 = tr2 = get_cycles(); | ||
188 | |||
189 | while ((inb(0x61) & 0x20) == 0) { | ||
190 | tr2 = get_cycles(); | ||
191 | delta = tr2 - tsc; | ||
192 | tsc = tr2; | ||
193 | if ((unsigned int) delta < tscmin) | ||
194 | tscmin = (unsigned int) delta; | ||
195 | if ((unsigned int) delta > tscmax) | ||
196 | tscmax = (unsigned int) delta; | ||
197 | pitcnt++; | ||
198 | } | ||
199 | |||
200 | /* | ||
201 | * We waited at least 50ms above. Now read | ||
202 | * pmtimer/hpet reference again | ||
203 | */ | ||
204 | tsc2 = tsc_read_refs(&pm2, hpet ? &hpet2 : NULL); | ||
205 | |||
206 | local_irq_restore(flags); | ||
207 | |||
208 | /* | ||
209 | * Sanity checks: | ||
210 | * | ||
211 | * If we were not able to read the PIT more than 5000 | ||
212 | * times, then we have been hit by a massive SMI | ||
213 | * | ||
214 | * If the maximum is 10 times larger than the minimum, | ||
215 | * then we got hit by an SMI as well. | ||
216 | */ | ||
217 | if (pitcnt > 5000 && tscmax < 10 * tscmin) { | ||
218 | |||
219 | /* Calculate the PIT value */ | ||
220 | delta = tr2 - tr1; | ||
221 | do_div(delta, 50); | ||
222 | |||
223 | /* We take the smallest value into account */ | ||
224 | tsc_pit_min = min(tsc_pit_min, (unsigned long) delta); | ||
225 | } | ||
226 | |||
227 | /* hpet or pmtimer available ? */ | ||
228 | if (!hpet && !pm1 && !pm2) | ||
229 | continue; | ||
230 | |||
231 | /* Check, whether the sampling was disturbed by an SMI */ | ||
232 | if (tsc1 == ULLONG_MAX || tsc2 == ULLONG_MAX) | ||
233 | continue; | ||
234 | |||
235 | tsc2 = (tsc2 - tsc1) * 1000000LL; | ||
236 | |||
237 | if (hpet) { | ||
238 | if (hpet2 < hpet1) | ||
239 | hpet2 += 0x100000000ULL; | ||
240 | hpet2 -= hpet1; | ||
241 | tsc1 = ((u64)hpet2 * hpet_readl(HPET_PERIOD)); | ||
242 | do_div(tsc1, 1000000); | ||
243 | } else { | ||
244 | if (pm2 < pm1) | ||
245 | pm2 += (u64)ACPI_PM_OVRRUN; | ||
246 | pm2 -= pm1; | ||
247 | tsc1 = pm2 * 1000000000LL; | ||
248 | do_div(tsc1, PMTMR_TICKS_PER_SEC); | ||
249 | } | ||
250 | |||
251 | do_div(tsc2, tsc1); | ||
252 | tsc_ref_min = min(tsc_ref_min, (unsigned long) tsc2); | ||
253 | } | ||
151 | 254 | ||
152 | /* | 255 | /* |
153 | * Preset the result with the raw and inaccurate PIT | 256 | * Now check the results. |
154 | * calibration value | ||
155 | */ | 257 | */ |
156 | delta = (tr2 - tr1); | 258 | if (tsc_pit_min == ULONG_MAX) { |
157 | do_div(delta, 50); | 259 | /* PIT gave no useful value */ |
158 | tsc_khz_val = delta; | 260 | printk(KERN_WARNING "TSC: PIT calibration failed due to " |
261 | "SMI disturbance.\n"); | ||
262 | |||
263 | /* We don't have an alternative source, disable TSC */ | ||
264 | if (!hpet && !pm1 && !pm2) { | ||
265 | printk("TSC: No reference (HPET/PMTIMER) available\n"); | ||
266 | return 0; | ||
267 | } | ||
268 | |||
269 | /* The alternative source failed as well, disable TSC */ | ||
270 | if (tsc_ref_min == ULONG_MAX) { | ||
271 | printk(KERN_WARNING "TSC: HPET/PMTIMER calibration " | ||
272 | "failed due to SMI disturbance.\n"); | ||
273 | return 0; | ||
274 | } | ||
275 | |||
276 | /* Use the alternative source */ | ||
277 | printk(KERN_INFO "TSC: using %s reference calibration\n", | ||
278 | hpet ? "HPET" : "PMTIMER"); | ||
279 | |||
280 | return tsc_ref_min; | ||
281 | } | ||
159 | 282 | ||
160 | /* hpet or pmtimer available ? */ | 283 | /* We don't have an alternative source, use the PIT calibration value */ |
161 | if (!hpet && !pm1 && !pm2) { | 284 | if (!hpet && !pm1 && !pm2) { |
162 | printk(KERN_INFO "TSC calibrated against PIT\n"); | 285 | printk(KERN_INFO "TSC: Using PIT calibration value\n"); |
163 | goto out; | 286 | return tsc_pit_min; |
164 | } | 287 | } |
165 | 288 | ||
166 | /* Check, whether the sampling was disturbed by an SMI */ | 289 | /* The alternative source failed, use the PIT calibration value */ |
167 | if (tsc1 == ULLONG_MAX || tsc2 == ULLONG_MAX) { | 290 | if (tsc_ref_min == ULONG_MAX) { |
168 | printk(KERN_WARNING "TSC calibration disturbed by SMI, " | 291 | printk(KERN_WARNING "TSC: HPET/PMTIMER calibration failed due " |
169 | "using PIT calibration result\n"); | 292 | "to SMI disturbance. Using PIT calibration\n"); |
170 | goto out; | 293 | return tsc_pit_min; |
171 | } | 294 | } |
172 | 295 | ||
173 | tsc2 = (tsc2 - tsc1) * 1000000LL; | 296 | /* Check the reference deviation */ |
174 | 297 | delta = ((u64) tsc_pit_min) * 100; | |
175 | if (hpet) { | 298 | do_div(delta, tsc_ref_min); |
176 | printk(KERN_INFO "TSC calibrated against HPET\n"); | 299 | |
177 | if (hpet2 < hpet1) | 300 | /* |
178 | hpet2 += 0x100000000ULL; | 301 | * If both calibration results are inside a 5% window, the we |
179 | hpet2 -= hpet1; | 302 | * use the lower frequency of those as it is probably the |
180 | tsc1 = ((u64)hpet2 * hpet_readl(HPET_PERIOD)); | 303 | * closest estimate. |
181 | do_div(tsc1, 1000000); | 304 | */ |
182 | } else { | 305 | if (delta >= 95 && delta <= 105) { |
183 | printk(KERN_INFO "TSC calibrated against PM_TIMER\n"); | 306 | printk(KERN_INFO "TSC: PIT calibration confirmed by %s.\n", |
184 | if (pm2 < pm1) | 307 | hpet ? "HPET" : "PMTIMER"); |
185 | pm2 += (u64)ACPI_PM_OVRRUN; | 308 | printk(KERN_INFO "TSC: using %s calibration value\n", |
186 | pm2 -= pm1; | 309 | tsc_pit_min <= tsc_ref_min ? "PIT" : |
187 | tsc1 = pm2 * 1000000000LL; | 310 | hpet ? "HPET" : "PMTIMER"); |
188 | do_div(tsc1, PMTMR_TICKS_PER_SEC); | 311 | return tsc_pit_min <= tsc_ref_min ? tsc_pit_min : tsc_ref_min; |
189 | } | 312 | } |
190 | 313 | ||
191 | do_div(tsc2, tsc1); | 314 | printk(KERN_WARNING "TSC: PIT calibration deviates from %s: %lu %lu.\n", |
192 | tsc_khz_val = tsc2; | 315 | hpet ? "HPET" : "PMTIMER", tsc_pit_min, tsc_ref_min); |
193 | 316 | ||
194 | out: | 317 | /* |
195 | return tsc_khz_val; | 318 | * The calibration values differ too much. In doubt, we use |
319 | * the PIT value as we know that there are PMTIMERs around | ||
320 | * running at double speed. | ||
321 | */ | ||
322 | printk(KERN_INFO "TSC: Using PIT calibration value\n"); | ||
323 | return tsc_pit_min; | ||
196 | } | 324 | } |
197 | 325 | ||
198 | |||
199 | #ifdef CONFIG_X86_32 | 326 | #ifdef CONFIG_X86_32 |
200 | /* Only called from the Powernow K7 cpu freq driver */ | 327 | /* Only called from the Powernow K7 cpu freq driver */ |
201 | int recalibrate_cpu_khz(void) | 328 | int recalibrate_cpu_khz(void) |