diff options
Diffstat (limited to 'arch/x86/kernel/tsc.c')
-rw-r--r-- | arch/x86/kernel/tsc.c | 135 |
1 files changed, 72 insertions, 63 deletions
diff --git a/arch/x86/kernel/tsc.c b/arch/x86/kernel/tsc.c index ac79bd143da8..8f98e9de1b82 100644 --- a/arch/x86/kernel/tsc.c +++ b/arch/x86/kernel/tsc.c | |||
@@ -122,15 +122,75 @@ static u64 tsc_read_refs(u64 *pm, u64 *hpet) | |||
122 | return ULLONG_MAX; | 122 | return ULLONG_MAX; |
123 | } | 123 | } |
124 | 124 | ||
125 | /* | ||
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. | ||
131 | */ | ||
132 | static unsigned long pit_calibrate_tsc(void) | ||
133 | { | ||
134 | u64 tsc, t1, t2, delta; | ||
135 | unsigned long tscmin, tscmax; | ||
136 | int pitcnt; | ||
137 | |||
138 | /* Set the Gate high, disable speaker */ | ||
139 | outb((inb(0x61) & ~0x02) | 0x01, 0x61); | ||
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 | */ | ||
146 | outb(0xb0, 0x43); | ||
147 | outb((CLOCK_TICK_RATE / (1000 / 50)) & 0xff, 0x42); | ||
148 | outb((CLOCK_TICK_RATE / (1000 / 50)) >> 8, 0x42); | ||
149 | |||
150 | tsc = t1 = t2 = get_cycles(); | ||
151 | |||
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 | } | ||
165 | |||
166 | /* | ||
167 | * Sanity checks: | ||
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. | ||
174 | */ | ||
175 | if (pitcnt < 5000 || tscmax > 10 * tscmin) | ||
176 | return ULONG_MAX; | ||
177 | |||
178 | /* Calculate the PIT value */ | ||
179 | delta = t2 - t1; | ||
180 | do_div(delta, 50); | ||
181 | return delta; | ||
182 | } | ||
183 | |||
184 | |||
125 | /** | 185 | /** |
126 | * native_calibrate_tsc - calibrate the tsc on boot | 186 | * native_calibrate_tsc - calibrate the tsc on boot |
127 | */ | 187 | */ |
128 | unsigned long native_calibrate_tsc(void) | 188 | unsigned long native_calibrate_tsc(void) |
129 | { | 189 | { |
130 | u64 tsc1, tsc2, tr1, tr2, tsc, delta, pm1, pm2, hpet1, hpet2; | 190 | u64 tsc1, tsc2, delta, pm1, pm2, hpet1, hpet2; |
131 | unsigned long tsc_pit_min = ULONG_MAX, tsc_ref_min = ULONG_MAX; | 191 | unsigned long tsc_pit_min = ULONG_MAX, tsc_ref_min = ULONG_MAX; |
132 | unsigned long flags, tscmin, tscmax; | 192 | unsigned long flags; |
133 | int hpet = is_hpet_enabled(), pitcnt, i; | 193 | int hpet = is_hpet_enabled(), i; |
134 | 194 | ||
135 | /* | 195 | /* |
136 | * Run 5 calibration loops to get the lowest frequency value | 196 | * Run 5 calibration loops to get the lowest frequency value |
@@ -157,72 +217,22 @@ unsigned long native_calibrate_tsc(void) | |||
157 | * amount of time anyway. | 217 | * amount of time anyway. |
158 | */ | 218 | */ |
159 | for (i = 0; i < 5; i++) { | 219 | for (i = 0; i < 5; i++) { |
160 | 220 | unsigned long tsc_pit_khz; | |
161 | tscmin = ULONG_MAX; | ||
162 | tscmax = 0; | ||
163 | pitcnt = 0; | ||
164 | |||
165 | local_irq_save(flags); | ||
166 | 221 | ||
167 | /* | 222 | /* |
168 | * Read the start value and the reference count of | 223 | * Read the start value and the reference count of |
169 | * hpet/pmtimer when available: | 224 | * hpet/pmtimer when available. Then do the PIT |
225 | * calibration, which will take at least 50ms, and | ||
226 | * read the end value. | ||
170 | */ | 227 | */ |
228 | local_irq_save(flags); | ||
171 | tsc1 = tsc_read_refs(&pm1, hpet ? &hpet1 : NULL); | 229 | tsc1 = tsc_read_refs(&pm1, hpet ? &hpet1 : NULL); |
172 | 230 | tsc_pit_khz = pit_calibrate_tsc(); | |
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); | 231 | tsc2 = tsc_read_refs(&pm2, hpet ? &hpet2 : NULL); |
205 | |||
206 | local_irq_restore(flags); | 232 | local_irq_restore(flags); |
207 | 233 | ||
208 | /* | 234 | /* Pick the lowest PIT TSC calibration so far */ |
209 | * Sanity checks: | 235 | tsc_pit_min = min(tsc_pit_min, tsc_pit_khz); |
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 | 236 | ||
227 | /* hpet or pmtimer available ? */ | 237 | /* hpet or pmtimer available ? */ |
228 | if (!hpet && !pm1 && !pm2) | 238 | if (!hpet && !pm1 && !pm2) |
@@ -257,8 +267,7 @@ unsigned long native_calibrate_tsc(void) | |||
257 | */ | 267 | */ |
258 | if (tsc_pit_min == ULONG_MAX) { | 268 | if (tsc_pit_min == ULONG_MAX) { |
259 | /* PIT gave no useful value */ | 269 | /* PIT gave no useful value */ |
260 | printk(KERN_WARNING "TSC: PIT calibration failed due to " | 270 | printk(KERN_WARNING "TSC: Unable to calibrate against PIT\n"); |
261 | "SMI disturbance.\n"); | ||
262 | 271 | ||
263 | /* We don't have an alternative source, disable TSC */ | 272 | /* We don't have an alternative source, disable TSC */ |
264 | if (!hpet && !pm1 && !pm2) { | 273 | if (!hpet && !pm1 && !pm2) { |