aboutsummaryrefslogtreecommitdiffstats
path: root/arch
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
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')
-rw-r--r--arch/i386/kernel/hpet.c80
-rw-r--r--arch/i386/kernel/i8253.c2
-rw-r--r--arch/i386/kernel/setup.c1
-rw-r--r--arch/i386/kernel/time.c1
-rw-r--r--arch/i386/kernel/tsc.c83
-rw-r--r--arch/x86_64/kernel/hpet.c109
-rw-r--r--arch/x86_64/kernel/time.c2
-rw-r--r--arch/x86_64/kernel/tsc.c7
8 files changed, 124 insertions, 161 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
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}
198module_init(init_pit_clocksource); 198arch_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 */
280void __init time_init(void) 280void __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
185EXPORT_SYMBOL(recalibrate_cpu_khz); 185EXPORT_SYMBOL(recalibrate_cpu_khz);
186 186
187void __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
206out_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)
381static inline void check_geode_tsc_reliable(void) { } 353static inline void check_geode_tsc_reliable(void) { }
382#endif 354#endif
383 355
384static int __init init_tsc_clocksource(void) 356
357void __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
408module_init(init_tsc_clocksource); 392out_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}
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
15int nohpet __initdata; 21int nohpet __initdata;
16 22
17unsigned long hpet_address; 23unsigned 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
115static cycle_t read_hpet(void)
116{
117 return (cycle_t)hpet_readl(HPET_COUNTER);
118}
119
120static cycle_t __vsyscall_fn vread_hpet(void)
121{
122 return readl((void __iomem *)fix_to_virt(VSYSCALL_HPET) + 0xf0);
123}
124
125struct 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
109int hpet_arch_init(void) 136int 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
454static void *hpet_ptr;
455
456static cycle_t read_hpet(void)
457{
458 return (cycle_t)readl(hpet_ptr);
459}
460
461static cycle_t __vsyscall_fn vread_hpet(void)
462{
463 return readl((void __iomem *)fix_to_virt(VSYSCALL_HPET) + 0xf0);
464}
465
466struct 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
477static 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
511module_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}
211EXPORT_SYMBOL_GPL(mark_tsc_unstable); 211EXPORT_SYMBOL_GPL(mark_tsc_unstable);
212 212
213static int __init init_tsc_clocksource(void) 213void __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
226module_init(init_tsc_clocksource);