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/x86_64 | |
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/x86_64')
-rw-r--r-- | arch/x86_64/kernel/hpet.c | 109 | ||||
-rw-r--r-- | arch/x86_64/kernel/time.c | 2 | ||||
-rw-r--r-- | arch/x86_64/kernel/tsc.c | 7 |
3 files changed, 48 insertions, 70 deletions
diff --git a/arch/x86_64/kernel/hpet.c b/arch/x86_64/kernel/hpet.c index 65a0edd71a17..8cf0b8a13778 100644 --- a/arch/x86_64/kernel/hpet.c +++ b/arch/x86_64/kernel/hpet.c | |||
@@ -12,6 +12,12 @@ | |||
12 | #include <asm/timex.h> | 12 | #include <asm/timex.h> |
13 | #include <asm/hpet.h> | 13 | #include <asm/hpet.h> |
14 | 14 | ||
15 | #define HPET_MASK 0xFFFFFFFF | ||
16 | #define HPET_SHIFT 22 | ||
17 | |||
18 | /* FSEC = 10^-15 NSEC = 10^-9 */ | ||
19 | #define FSEC_PER_NSEC 1000000 | ||
20 | |||
15 | int nohpet __initdata; | 21 | int nohpet __initdata; |
16 | 22 | ||
17 | unsigned long hpet_address; | 23 | unsigned long hpet_address; |
@@ -106,9 +112,31 @@ int hpet_timer_stop_set_go(unsigned long tick) | |||
106 | return 0; | 112 | return 0; |
107 | } | 113 | } |
108 | 114 | ||
115 | static cycle_t read_hpet(void) | ||
116 | { | ||
117 | return (cycle_t)hpet_readl(HPET_COUNTER); | ||
118 | } | ||
119 | |||
120 | static cycle_t __vsyscall_fn vread_hpet(void) | ||
121 | { | ||
122 | return readl((void __iomem *)fix_to_virt(VSYSCALL_HPET) + 0xf0); | ||
123 | } | ||
124 | |||
125 | struct clocksource clocksource_hpet = { | ||
126 | .name = "hpet", | ||
127 | .rating = 250, | ||
128 | .read = read_hpet, | ||
129 | .mask = (cycle_t)HPET_MASK, | ||
130 | .mult = 0, /* set below */ | ||
131 | .shift = HPET_SHIFT, | ||
132 | .flags = CLOCK_SOURCE_IS_CONTINUOUS, | ||
133 | .vread = vread_hpet, | ||
134 | }; | ||
135 | |||
109 | int hpet_arch_init(void) | 136 | int hpet_arch_init(void) |
110 | { | 137 | { |
111 | unsigned int id; | 138 | unsigned int id; |
139 | u64 tmp; | ||
112 | 140 | ||
113 | if (!hpet_address) | 141 | if (!hpet_address) |
114 | return -1; | 142 | return -1; |
@@ -132,6 +160,22 @@ int hpet_arch_init(void) | |||
132 | 160 | ||
133 | hpet_use_timer = (id & HPET_ID_LEGSUP); | 161 | hpet_use_timer = (id & HPET_ID_LEGSUP); |
134 | 162 | ||
163 | /* | ||
164 | * hpet period is in femto seconds per cycle | ||
165 | * so we need to convert this to ns/cyc units | ||
166 | * aproximated by mult/2^shift | ||
167 | * | ||
168 | * fsec/cyc * 1nsec/1000000fsec = nsec/cyc = mult/2^shift | ||
169 | * fsec/cyc * 1ns/1000000fsec * 2^shift = mult | ||
170 | * fsec/cyc * 2^shift * 1nsec/1000000fsec = mult | ||
171 | * (fsec/cyc << shift)/1000000 = mult | ||
172 | * (hpet_period << shift)/FSEC_PER_NSEC = mult | ||
173 | */ | ||
174 | tmp = (u64)hpet_period << HPET_SHIFT; | ||
175 | do_div(tmp, FSEC_PER_NSEC); | ||
176 | clocksource_hpet.mult = (u32)tmp; | ||
177 | clocksource_register(&clocksource_hpet); | ||
178 | |||
135 | return hpet_timer_stop_set_go(hpet_tick); | 179 | return hpet_timer_stop_set_go(hpet_tick); |
136 | } | 180 | } |
137 | 181 | ||
@@ -444,68 +488,3 @@ static int __init nohpet_setup(char *s) | |||
444 | } | 488 | } |
445 | 489 | ||
446 | __setup("nohpet", nohpet_setup); | 490 | __setup("nohpet", nohpet_setup); |
447 | |||
448 | #define HPET_MASK 0xFFFFFFFF | ||
449 | #define HPET_SHIFT 22 | ||
450 | |||
451 | /* FSEC = 10^-15 NSEC = 10^-9 */ | ||
452 | #define FSEC_PER_NSEC 1000000 | ||
453 | |||
454 | static void *hpet_ptr; | ||
455 | |||
456 | static cycle_t read_hpet(void) | ||
457 | { | ||
458 | return (cycle_t)readl(hpet_ptr); | ||
459 | } | ||
460 | |||
461 | static cycle_t __vsyscall_fn vread_hpet(void) | ||
462 | { | ||
463 | return readl((void __iomem *)fix_to_virt(VSYSCALL_HPET) + 0xf0); | ||
464 | } | ||
465 | |||
466 | struct clocksource clocksource_hpet = { | ||
467 | .name = "hpet", | ||
468 | .rating = 250, | ||
469 | .read = read_hpet, | ||
470 | .mask = (cycle_t)HPET_MASK, | ||
471 | .mult = 0, /* set below */ | ||
472 | .shift = HPET_SHIFT, | ||
473 | .flags = CLOCK_SOURCE_IS_CONTINUOUS, | ||
474 | .vread = vread_hpet, | ||
475 | }; | ||
476 | |||
477 | static int __init init_hpet_clocksource(void) | ||
478 | { | ||
479 | unsigned long hpet_period; | ||
480 | void __iomem *hpet_base; | ||
481 | u64 tmp; | ||
482 | |||
483 | if (!hpet_address) | ||
484 | return -ENODEV; | ||
485 | |||
486 | /* calculate the hpet address: */ | ||
487 | hpet_base = ioremap_nocache(hpet_address, HPET_MMAP_SIZE); | ||
488 | hpet_ptr = hpet_base + HPET_COUNTER; | ||
489 | |||
490 | /* calculate the frequency: */ | ||
491 | hpet_period = readl(hpet_base + HPET_PERIOD); | ||
492 | |||
493 | /* | ||
494 | * hpet period is in femto seconds per cycle | ||
495 | * so we need to convert this to ns/cyc units | ||
496 | * aproximated by mult/2^shift | ||
497 | * | ||
498 | * fsec/cyc * 1nsec/1000000fsec = nsec/cyc = mult/2^shift | ||
499 | * fsec/cyc * 1ns/1000000fsec * 2^shift = mult | ||
500 | * fsec/cyc * 2^shift * 1nsec/1000000fsec = mult | ||
501 | * (fsec/cyc << shift)/1000000 = mult | ||
502 | * (hpet_period << shift)/FSEC_PER_NSEC = mult | ||
503 | */ | ||
504 | tmp = (u64)hpet_period << HPET_SHIFT; | ||
505 | do_div(tmp, FSEC_PER_NSEC); | ||
506 | clocksource_hpet.mult = (u32)tmp; | ||
507 | |||
508 | return clocksource_register(&clocksource_hpet); | ||
509 | } | ||
510 | |||
511 | module_init(init_hpet_clocksource); | ||
diff --git a/arch/x86_64/kernel/time.c b/arch/x86_64/kernel/time.c index c9addcfb96dc..75d73a9aa9ff 100644 --- a/arch/x86_64/kernel/time.c +++ b/arch/x86_64/kernel/time.c | |||
@@ -358,6 +358,8 @@ void __init time_init(void) | |||
358 | set_cyc2ns_scale(cpu_khz); | 358 | set_cyc2ns_scale(cpu_khz); |
359 | printk(KERN_INFO "time.c: Detected %d.%03d MHz processor.\n", | 359 | printk(KERN_INFO "time.c: Detected %d.%03d MHz processor.\n", |
360 | cpu_khz / 1000, cpu_khz % 1000); | 360 | cpu_khz / 1000, cpu_khz % 1000); |
361 | init_tsc_clocksource(); | ||
362 | |||
361 | setup_irq(0, &irq0); | 363 | setup_irq(0, &irq0); |
362 | } | 364 | } |
363 | 365 | ||
diff --git a/arch/x86_64/kernel/tsc.c b/arch/x86_64/kernel/tsc.c index 895831865019..1a0edbbffaa0 100644 --- a/arch/x86_64/kernel/tsc.c +++ b/arch/x86_64/kernel/tsc.c | |||
@@ -210,7 +210,7 @@ void mark_tsc_unstable(void) | |||
210 | } | 210 | } |
211 | EXPORT_SYMBOL_GPL(mark_tsc_unstable); | 211 | EXPORT_SYMBOL_GPL(mark_tsc_unstable); |
212 | 212 | ||
213 | static int __init init_tsc_clocksource(void) | 213 | void __init init_tsc_clocksource(void) |
214 | { | 214 | { |
215 | if (!notsc) { | 215 | if (!notsc) { |
216 | clocksource_tsc.mult = clocksource_khz2mult(cpu_khz, | 216 | clocksource_tsc.mult = clocksource_khz2mult(cpu_khz, |
@@ -218,9 +218,6 @@ static int __init init_tsc_clocksource(void) | |||
218 | if (check_tsc_unstable()) | 218 | if (check_tsc_unstable()) |
219 | clocksource_tsc.rating = 0; | 219 | clocksource_tsc.rating = 0; |
220 | 220 | ||
221 | return clocksource_register(&clocksource_tsc); | 221 | clocksource_register(&clocksource_tsc); |
222 | } | 222 | } |
223 | return 0; | ||
224 | } | 223 | } |
225 | |||
226 | module_init(init_tsc_clocksource); | ||