diff options
author | john stultz <johnstul@us.ibm.com> | 2007-03-05 03:30:50 -0500 |
---|---|---|
committer | Linus Torvalds <torvalds@woody.linux-foundation.org> | 2007-03-05 10:57:53 -0500 |
commit | 6bb74df481223731af6c7e0ff3adb31f6442cfcd (patch) | |
tree | 2aa0b0cfa55cb4b9a9236bd94b723d83eb0bdaa8 /arch/i386 | |
parent | 4540768011352d38afb89d400eacb3e261507b70 (diff) |
[PATCH] clocksource init adjustments (fix bug #7426)
This patch resolves the issue found here:
http://bugme.osdl.org/show_bug.cgi?id=7426
The basic summary is:
Currently we register most of i386/x86_64 clocksources at module_init
time. Then we enable clocksource selection at late_initcall time. This
causes some problems for drivers that use gettimeofday for init
calibration routines (specifically the es1968 driver in this case),
where durring module_init, the only clocksource available is the low-res
jiffies clocksource. This may cause slight calibration errors, due to
the small sampling time used.
It should be noted that drivers that require fine grained time may not
function on architectures that do not have better then jiffies
resolution timekeeping (there are a few). However, this does not
discount the reasonable need for such fine-grained timekeeping at init
time.
Thus the solution here is to register clocksources earlier (ideally when
the hardware is being initialized), and then we enable clocksource
selection at fs_initcall (before device_initcall).
This patch should probably get some testing time in -mm, since
clocksource selection is one of the most important issues for correct
timekeeping, and I've only been able to test this on a few of my own
boxes.
Signed-off-by: John Stultz <johnstul@us.ibm.com>
Cc: Thomas Gleixner <tglx@linutronix.de>
Cc: Ingo Molnar <mingo@elte.hu>
Cc: "David S. Miller" <davem@davemloft.net>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'arch/i386')
-rw-r--r-- | arch/i386/kernel/hpet.c | 80 | ||||
-rw-r--r-- | arch/i386/kernel/i8253.c | 2 | ||||
-rw-r--r-- | arch/i386/kernel/setup.c | 1 | ||||
-rw-r--r-- | arch/i386/kernel/time.c | 1 | ||||
-rw-r--r-- | arch/i386/kernel/tsc.c | 83 |
5 files changed, 76 insertions, 91 deletions
diff --git a/arch/i386/kernel/hpet.c b/arch/i386/kernel/hpet.c index e1006b7acc9e..f3ab61ee7498 100644 --- a/arch/i386/kernel/hpet.c +++ b/arch/i386/kernel/hpet.c | |||
@@ -201,12 +201,30 @@ static int hpet_next_event(unsigned long delta, | |||
201 | } | 201 | } |
202 | 202 | ||
203 | /* | 203 | /* |
204 | * Clock source related code | ||
205 | */ | ||
206 | static cycle_t read_hpet(void) | ||
207 | { | ||
208 | return (cycle_t)hpet_readl(HPET_COUNTER); | ||
209 | } | ||
210 | |||
211 | static struct clocksource clocksource_hpet = { | ||
212 | .name = "hpet", | ||
213 | .rating = 250, | ||
214 | .read = read_hpet, | ||
215 | .mask = HPET_MASK, | ||
216 | .shift = HPET_SHIFT, | ||
217 | .flags = CLOCK_SOURCE_IS_CONTINUOUS, | ||
218 | }; | ||
219 | |||
220 | /* | ||
204 | * Try to setup the HPET timer | 221 | * Try to setup the HPET timer |
205 | */ | 222 | */ |
206 | int __init hpet_enable(void) | 223 | int __init hpet_enable(void) |
207 | { | 224 | { |
208 | unsigned long id; | 225 | unsigned long id; |
209 | uint64_t hpet_freq; | 226 | uint64_t hpet_freq; |
227 | u64 tmp; | ||
210 | 228 | ||
211 | if (!is_hpet_capable()) | 229 | if (!is_hpet_capable()) |
212 | return 0; | 230 | return 0; |
@@ -253,6 +271,25 @@ int __init hpet_enable(void) | |||
253 | /* Start the counter */ | 271 | /* Start the counter */ |
254 | hpet_start_counter(); | 272 | hpet_start_counter(); |
255 | 273 | ||
274 | /* Initialize and register HPET clocksource | ||
275 | * | ||
276 | * hpet period is in femto seconds per cycle | ||
277 | * so we need to convert this to ns/cyc units | ||
278 | * aproximated by mult/2^shift | ||
279 | * | ||
280 | * fsec/cyc * 1nsec/1000000fsec = nsec/cyc = mult/2^shift | ||
281 | * fsec/cyc * 1ns/1000000fsec * 2^shift = mult | ||
282 | * fsec/cyc * 2^shift * 1nsec/1000000fsec = mult | ||
283 | * (fsec/cyc << shift)/1000000 = mult | ||
284 | * (hpet_period << shift)/FSEC_PER_NSEC = mult | ||
285 | */ | ||
286 | tmp = (u64)hpet_period << HPET_SHIFT; | ||
287 | do_div(tmp, FSEC_PER_NSEC); | ||
288 | clocksource_hpet.mult = (u32)tmp; | ||
289 | |||
290 | clocksource_register(&clocksource_hpet); | ||
291 | |||
292 | |||
256 | if (id & HPET_ID_LEGSUP) { | 293 | if (id & HPET_ID_LEGSUP) { |
257 | hpet_enable_int(); | 294 | hpet_enable_int(); |
258 | hpet_reserve_platform_timers(id); | 295 | hpet_reserve_platform_timers(id); |
@@ -273,49 +310,6 @@ out_nohpet: | |||
273 | return 0; | 310 | return 0; |
274 | } | 311 | } |
275 | 312 | ||
276 | /* | ||
277 | * Clock source related code | ||
278 | */ | ||
279 | static cycle_t read_hpet(void) | ||
280 | { | ||
281 | return (cycle_t)hpet_readl(HPET_COUNTER); | ||
282 | } | ||
283 | |||
284 | static struct clocksource clocksource_hpet = { | ||
285 | .name = "hpet", | ||
286 | .rating = 250, | ||
287 | .read = read_hpet, | ||
288 | .mask = HPET_MASK, | ||
289 | .shift = HPET_SHIFT, | ||
290 | .flags = CLOCK_SOURCE_IS_CONTINUOUS, | ||
291 | }; | ||
292 | |||
293 | static int __init init_hpet_clocksource(void) | ||
294 | { | ||
295 | u64 tmp; | ||
296 | |||
297 | if (!hpet_virt_address) | ||
298 | return -ENODEV; | ||
299 | |||
300 | /* | ||
301 | * hpet period is in femto seconds per cycle | ||
302 | * so we need to convert this to ns/cyc units | ||
303 | * aproximated by mult/2^shift | ||
304 | * | ||
305 | * fsec/cyc * 1nsec/1000000fsec = nsec/cyc = mult/2^shift | ||
306 | * fsec/cyc * 1ns/1000000fsec * 2^shift = mult | ||
307 | * fsec/cyc * 2^shift * 1nsec/1000000fsec = mult | ||
308 | * (fsec/cyc << shift)/1000000 = mult | ||
309 | * (hpet_period << shift)/FSEC_PER_NSEC = mult | ||
310 | */ | ||
311 | tmp = (u64)hpet_period << HPET_SHIFT; | ||
312 | do_div(tmp, FSEC_PER_NSEC); | ||
313 | clocksource_hpet.mult = (u32)tmp; | ||
314 | |||
315 | return clocksource_register(&clocksource_hpet); | ||
316 | } | ||
317 | |||
318 | module_init(init_hpet_clocksource); | ||
319 | 313 | ||
320 | #ifdef CONFIG_HPET_EMULATE_RTC | 314 | #ifdef CONFIG_HPET_EMULATE_RTC |
321 | 315 | ||
diff --git a/arch/i386/kernel/i8253.c b/arch/i386/kernel/i8253.c index a6bc7bb38834..5cbb776b3089 100644 --- a/arch/i386/kernel/i8253.c +++ b/arch/i386/kernel/i8253.c | |||
@@ -195,4 +195,4 @@ static int __init init_pit_clocksource(void) | |||
195 | clocksource_pit.mult = clocksource_hz2mult(CLOCK_TICK_RATE, 20); | 195 | clocksource_pit.mult = clocksource_hz2mult(CLOCK_TICK_RATE, 20); |
196 | return clocksource_register(&clocksource_pit); | 196 | return clocksource_register(&clocksource_pit); |
197 | } | 197 | } |
198 | module_init(init_pit_clocksource); | 198 | arch_initcall(init_pit_clocksource); |
diff --git a/arch/i386/kernel/setup.c b/arch/i386/kernel/setup.c index 122623dcc6e1..698c24fe482e 100644 --- a/arch/i386/kernel/setup.c +++ b/arch/i386/kernel/setup.c | |||
@@ -657,5 +657,4 @@ void __init setup_arch(char **cmdline_p) | |||
657 | conswitchp = &dummy_con; | 657 | conswitchp = &dummy_con; |
658 | #endif | 658 | #endif |
659 | #endif | 659 | #endif |
660 | tsc_init(); | ||
661 | } | 660 | } |
diff --git a/arch/i386/kernel/time.c b/arch/i386/kernel/time.c index ccd3734edb8f..94e5cb091104 100644 --- a/arch/i386/kernel/time.c +++ b/arch/i386/kernel/time.c | |||
@@ -279,5 +279,6 @@ void __init hpet_time_init(void) | |||
279 | */ | 279 | */ |
280 | void __init time_init(void) | 280 | void __init time_init(void) |
281 | { | 281 | { |
282 | tsc_init(); | ||
282 | late_time_init = choose_time_init(); | 283 | late_time_init = choose_time_init(); |
283 | } | 284 | } |
diff --git a/arch/i386/kernel/tsc.c b/arch/i386/kernel/tsc.c index 59222a04234b..875d8a6ecc02 100644 --- a/arch/i386/kernel/tsc.c +++ b/arch/i386/kernel/tsc.c | |||
@@ -184,34 +184,6 @@ int recalibrate_cpu_khz(void) | |||
184 | 184 | ||
185 | EXPORT_SYMBOL(recalibrate_cpu_khz); | 185 | EXPORT_SYMBOL(recalibrate_cpu_khz); |
186 | 186 | ||
187 | void __init tsc_init(void) | ||
188 | { | ||
189 | if (!cpu_has_tsc || tsc_disable) | ||
190 | goto out_no_tsc; | ||
191 | |||
192 | cpu_khz = calculate_cpu_khz(); | ||
193 | tsc_khz = cpu_khz; | ||
194 | |||
195 | if (!cpu_khz) | ||
196 | goto out_no_tsc; | ||
197 | |||
198 | printk("Detected %lu.%03lu MHz processor.\n", | ||
199 | (unsigned long)cpu_khz / 1000, | ||
200 | (unsigned long)cpu_khz % 1000); | ||
201 | |||
202 | set_cyc2ns_scale(cpu_khz); | ||
203 | use_tsc_delay(); | ||
204 | return; | ||
205 | |||
206 | out_no_tsc: | ||
207 | /* | ||
208 | * Set the tsc_disable flag if there's no TSC support, this | ||
209 | * makes it a fast flag for the kernel to see whether it | ||
210 | * should be using the TSC. | ||
211 | */ | ||
212 | tsc_disable = 1; | ||
213 | } | ||
214 | |||
215 | #ifdef CONFIG_CPU_FREQ | 187 | #ifdef CONFIG_CPU_FREQ |
216 | 188 | ||
217 | /* | 189 | /* |
@@ -381,28 +353,47 @@ static void __init check_geode_tsc_reliable(void) | |||
381 | static inline void check_geode_tsc_reliable(void) { } | 353 | static inline void check_geode_tsc_reliable(void) { } |
382 | #endif | 354 | #endif |
383 | 355 | ||
384 | static int __init init_tsc_clocksource(void) | 356 | |
357 | void __init tsc_init(void) | ||
385 | { | 358 | { |
359 | if (!cpu_has_tsc || tsc_disable) | ||
360 | goto out_no_tsc; | ||
386 | 361 | ||
387 | if (cpu_has_tsc && tsc_khz && !tsc_disable) { | 362 | cpu_khz = calculate_cpu_khz(); |
388 | /* check blacklist */ | 363 | tsc_khz = cpu_khz; |
389 | dmi_check_system(bad_tsc_dmi_table); | ||
390 | 364 | ||
391 | unsynchronized_tsc(); | 365 | if (!cpu_khz) |
392 | check_geode_tsc_reliable(); | 366 | goto out_no_tsc; |
393 | current_tsc_khz = tsc_khz; | ||
394 | clocksource_tsc.mult = clocksource_khz2mult(current_tsc_khz, | ||
395 | clocksource_tsc.shift); | ||
396 | /* lower the rating if we already know its unstable: */ | ||
397 | if (check_tsc_unstable()) { | ||
398 | clocksource_tsc.rating = 0; | ||
399 | clocksource_tsc.flags &= ~CLOCK_SOURCE_IS_CONTINUOUS; | ||
400 | } | ||
401 | 367 | ||
402 | return clocksource_register(&clocksource_tsc); | 368 | printk("Detected %lu.%03lu MHz processor.\n", |
369 | (unsigned long)cpu_khz / 1000, | ||
370 | (unsigned long)cpu_khz % 1000); | ||
371 | |||
372 | set_cyc2ns_scale(cpu_khz); | ||
373 | use_tsc_delay(); | ||
374 | |||
375 | /* Check and install the TSC clocksource */ | ||
376 | dmi_check_system(bad_tsc_dmi_table); | ||
377 | |||
378 | unsynchronized_tsc(); | ||
379 | check_geode_tsc_reliable(); | ||
380 | current_tsc_khz = tsc_khz; | ||
381 | clocksource_tsc.mult = clocksource_khz2mult(current_tsc_khz, | ||
382 | clocksource_tsc.shift); | ||
383 | /* lower the rating if we already know its unstable: */ | ||
384 | if (check_tsc_unstable()) { | ||
385 | clocksource_tsc.rating = 0; | ||
386 | clocksource_tsc.flags &= ~CLOCK_SOURCE_IS_CONTINUOUS; | ||
403 | } | 387 | } |
388 | clocksource_register(&clocksource_tsc); | ||
404 | 389 | ||
405 | return 0; | 390 | return; |
406 | } | ||
407 | 391 | ||
408 | module_init(init_tsc_clocksource); | 392 | out_no_tsc: |
393 | /* | ||
394 | * Set the tsc_disable flag if there's no TSC support, this | ||
395 | * makes it a fast flag for the kernel to see whether it | ||
396 | * should be using the TSC. | ||
397 | */ | ||
398 | tsc_disable = 1; | ||
399 | } | ||