aboutsummaryrefslogtreecommitdiffstats
path: root/arch/i386/kernel/hpet.c
diff options
context:
space:
mode:
authorjohn stultz <johnstul@us.ibm.com>2007-03-05 03:30:50 -0500
committerLinus Torvalds <torvalds@woody.linux-foundation.org>2007-03-05 10:57:53 -0500
commit6bb74df481223731af6c7e0ff3adb31f6442cfcd (patch)
tree2aa0b0cfa55cb4b9a9236bd94b723d83eb0bdaa8 /arch/i386/kernel/hpet.c
parent4540768011352d38afb89d400eacb3e261507b70 (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/kernel/hpet.c')
-rw-r--r--arch/i386/kernel/hpet.c80
1 files changed, 37 insertions, 43 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 */
206static cycle_t read_hpet(void)
207{
208 return (cycle_t)hpet_readl(HPET_COUNTER);
209}
210
211static 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 */
206int __init hpet_enable(void) 223int __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 */
279static cycle_t read_hpet(void)
280{
281 return (cycle_t)hpet_readl(HPET_COUNTER);
282}
283
284static 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
293static 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
318module_init(init_hpet_clocksource);
319 313
320#ifdef CONFIG_HPET_EMULATE_RTC 314#ifdef CONFIG_HPET_EMULATE_RTC
321 315