diff options
author | Alok Kataria <akataria@vmware.com> | 2008-07-01 14:43:34 -0400 |
---|---|---|
committer | Ingo Molnar <mingo@elte.hu> | 2008-07-09 01:43:27 -0400 |
commit | 8fbbc4b45ce3e4c0eeb15004c79c72b6896a79c2 (patch) | |
tree | 3b7c8e4ee74ef5fec07f64ea56bff60afb433c2c /arch/x86/kernel/tsc.c | |
parent | 2dbe06faf37b39f9ecffc054dd173b2a1dc2adcd (diff) |
x86: merge tsc_init and clocksource code
Unify the clocksource code.
Unify the tsc_init code.
Signed-off-by: Alok N Kataria <akataria@vmware.com>
Signed-off-by: Dan Hecht <dhecht@vmware.com>
Cc: Dan Hecht <dhecht@vmware.com>
Signed-off-by: Ingo Molnar <mingo@elte.hu>
Diffstat (limited to 'arch/x86/kernel/tsc.c')
-rw-r--r-- | arch/x86/kernel/tsc.c | 212 |
1 files changed, 209 insertions, 3 deletions
diff --git a/arch/x86/kernel/tsc.c b/arch/x86/kernel/tsc.c index 595f78a22212..94c16bdd5696 100644 --- a/arch/x86/kernel/tsc.c +++ b/arch/x86/kernel/tsc.c | |||
@@ -5,8 +5,16 @@ | |||
5 | #include <linux/timer.h> | 5 | #include <linux/timer.h> |
6 | #include <linux/acpi_pmtmr.h> | 6 | #include <linux/acpi_pmtmr.h> |
7 | #include <linux/cpufreq.h> | 7 | #include <linux/cpufreq.h> |
8 | #include <linux/dmi.h> | ||
9 | #include <linux/delay.h> | ||
10 | #include <linux/clocksource.h> | ||
11 | #include <linux/percpu.h> | ||
8 | 12 | ||
9 | #include <asm/hpet.h> | 13 | #include <asm/hpet.h> |
14 | #include <asm/timer.h> | ||
15 | #include <asm/vgtod.h> | ||
16 | #include <asm/time.h> | ||
17 | #include <asm/delay.h> | ||
10 | 18 | ||
11 | unsigned int cpu_khz; /* TSC clocks / usec, not used here */ | 19 | unsigned int cpu_khz; /* TSC clocks / usec, not used here */ |
12 | EXPORT_SYMBOL(cpu_khz); | 20 | EXPORT_SYMBOL(cpu_khz); |
@@ -16,12 +24,12 @@ EXPORT_SYMBOL(tsc_khz); | |||
16 | /* | 24 | /* |
17 | * TSC can be unstable due to cpufreq or due to unsynced TSCs | 25 | * TSC can be unstable due to cpufreq or due to unsynced TSCs |
18 | */ | 26 | */ |
19 | int tsc_unstable; | 27 | static int tsc_unstable; |
20 | 28 | ||
21 | /* native_sched_clock() is called before tsc_init(), so | 29 | /* native_sched_clock() is called before tsc_init(), so |
22 | we must start with the TSC soft disabled to prevent | 30 | we must start with the TSC soft disabled to prevent |
23 | erroneous rdtsc usage on !cpu_has_tsc processors */ | 31 | erroneous rdtsc usage on !cpu_has_tsc processors */ |
24 | int tsc_disabled = -1; | 32 | static int tsc_disabled = -1; |
25 | 33 | ||
26 | /* | 34 | /* |
27 | * Scheduler clock - returns current time in nanosec units. | 35 | * Scheduler clock - returns current time in nanosec units. |
@@ -241,7 +249,7 @@ EXPORT_SYMBOL(recalibrate_cpu_khz); | |||
241 | 249 | ||
242 | DEFINE_PER_CPU(unsigned long, cyc2ns); | 250 | DEFINE_PER_CPU(unsigned long, cyc2ns); |
243 | 251 | ||
244 | void set_cyc2ns_scale(unsigned long cpu_khz, int cpu) | 252 | static void set_cyc2ns_scale(unsigned long cpu_khz, int cpu) |
245 | { | 253 | { |
246 | unsigned long long tsc_now, ns_now; | 254 | unsigned long long tsc_now, ns_now; |
247 | unsigned long flags, *scale; | 255 | unsigned long flags, *scale; |
@@ -329,3 +337,201 @@ static int __init cpufreq_tsc(void) | |||
329 | core_initcall(cpufreq_tsc); | 337 | core_initcall(cpufreq_tsc); |
330 | 338 | ||
331 | #endif /* CONFIG_CPU_FREQ */ | 339 | #endif /* CONFIG_CPU_FREQ */ |
340 | |||
341 | /* clocksource code */ | ||
342 | |||
343 | static struct clocksource clocksource_tsc; | ||
344 | |||
345 | /* | ||
346 | * We compare the TSC to the cycle_last value in the clocksource | ||
347 | * structure to avoid a nasty time-warp. This can be observed in a | ||
348 | * very small window right after one CPU updated cycle_last under | ||
349 | * xtime/vsyscall_gtod lock and the other CPU reads a TSC value which | ||
350 | * is smaller than the cycle_last reference value due to a TSC which | ||
351 | * is slighty behind. This delta is nowhere else observable, but in | ||
352 | * that case it results in a forward time jump in the range of hours | ||
353 | * due to the unsigned delta calculation of the time keeping core | ||
354 | * code, which is necessary to support wrapping clocksources like pm | ||
355 | * timer. | ||
356 | */ | ||
357 | static cycle_t read_tsc(void) | ||
358 | { | ||
359 | cycle_t ret = (cycle_t)get_cycles(); | ||
360 | |||
361 | return ret >= clocksource_tsc.cycle_last ? | ||
362 | ret : clocksource_tsc.cycle_last; | ||
363 | } | ||
364 | |||
365 | static cycle_t __vsyscall_fn vread_tsc(void) | ||
366 | { | ||
367 | cycle_t ret = (cycle_t)vget_cycles(); | ||
368 | |||
369 | return ret >= __vsyscall_gtod_data.clock.cycle_last ? | ||
370 | ret : __vsyscall_gtod_data.clock.cycle_last; | ||
371 | } | ||
372 | |||
373 | static struct clocksource clocksource_tsc = { | ||
374 | .name = "tsc", | ||
375 | .rating = 300, | ||
376 | .read = read_tsc, | ||
377 | .mask = CLOCKSOURCE_MASK(64), | ||
378 | .shift = 22, | ||
379 | .flags = CLOCK_SOURCE_IS_CONTINUOUS | | ||
380 | CLOCK_SOURCE_MUST_VERIFY, | ||
381 | #ifdef CONFIG_X86_64 | ||
382 | .vread = vread_tsc, | ||
383 | #endif | ||
384 | }; | ||
385 | |||
386 | void mark_tsc_unstable(char *reason) | ||
387 | { | ||
388 | if (!tsc_unstable) { | ||
389 | tsc_unstable = 1; | ||
390 | printk("Marking TSC unstable due to %s\n", reason); | ||
391 | /* Change only the rating, when not registered */ | ||
392 | if (clocksource_tsc.mult) | ||
393 | clocksource_change_rating(&clocksource_tsc, 0); | ||
394 | else | ||
395 | clocksource_tsc.rating = 0; | ||
396 | } | ||
397 | } | ||
398 | |||
399 | EXPORT_SYMBOL_GPL(mark_tsc_unstable); | ||
400 | |||
401 | static int __init dmi_mark_tsc_unstable(const struct dmi_system_id *d) | ||
402 | { | ||
403 | printk(KERN_NOTICE "%s detected: marking TSC unstable.\n", | ||
404 | d->ident); | ||
405 | tsc_unstable = 1; | ||
406 | return 0; | ||
407 | } | ||
408 | |||
409 | /* List of systems that have known TSC problems */ | ||
410 | static struct dmi_system_id __initdata bad_tsc_dmi_table[] = { | ||
411 | { | ||
412 | .callback = dmi_mark_tsc_unstable, | ||
413 | .ident = "IBM Thinkpad 380XD", | ||
414 | .matches = { | ||
415 | DMI_MATCH(DMI_BOARD_VENDOR, "IBM"), | ||
416 | DMI_MATCH(DMI_BOARD_NAME, "2635FA0"), | ||
417 | }, | ||
418 | }, | ||
419 | {} | ||
420 | }; | ||
421 | |||
422 | /* | ||
423 | * Geode_LX - the OLPC CPU has a possibly a very reliable TSC | ||
424 | */ | ||
425 | #ifdef CONFIG_MGEODE_LX | ||
426 | /* RTSC counts during suspend */ | ||
427 | #define RTSC_SUSP 0x100 | ||
428 | |||
429 | static void __init check_geode_tsc_reliable(void) | ||
430 | { | ||
431 | unsigned long res_low, res_high; | ||
432 | |||
433 | rdmsr_safe(MSR_GEODE_BUSCONT_CONF0, &res_low, &res_high); | ||
434 | if (res_low & RTSC_SUSP) | ||
435 | clocksource_tsc.flags &= ~CLOCK_SOURCE_MUST_VERIFY; | ||
436 | } | ||
437 | #else | ||
438 | static inline void check_geode_tsc_reliable(void) { } | ||
439 | #endif | ||
440 | |||
441 | /* | ||
442 | * Make an educated guess if the TSC is trustworthy and synchronized | ||
443 | * over all CPUs. | ||
444 | */ | ||
445 | __cpuinit int unsynchronized_tsc(void) | ||
446 | { | ||
447 | if (!cpu_has_tsc || tsc_unstable) | ||
448 | return 1; | ||
449 | |||
450 | #ifdef CONFIG_SMP | ||
451 | if (apic_is_clustered_box()) | ||
452 | return 1; | ||
453 | #endif | ||
454 | |||
455 | if (boot_cpu_has(X86_FEATURE_CONSTANT_TSC)) | ||
456 | return 0; | ||
457 | /* | ||
458 | * Intel systems are normally all synchronized. | ||
459 | * Exceptions must mark TSC as unstable: | ||
460 | */ | ||
461 | if (boot_cpu_data.x86_vendor != X86_VENDOR_INTEL) { | ||
462 | /* assume multi socket systems are not synchronized: */ | ||
463 | if (num_possible_cpus() > 1) | ||
464 | tsc_unstable = 1; | ||
465 | } | ||
466 | |||
467 | return tsc_unstable; | ||
468 | } | ||
469 | |||
470 | static void __init init_tsc_clocksource(void) | ||
471 | { | ||
472 | clocksource_tsc.mult = clocksource_khz2mult(tsc_khz, | ||
473 | clocksource_tsc.shift); | ||
474 | /* lower the rating if we already know its unstable: */ | ||
475 | if (check_tsc_unstable()) { | ||
476 | clocksource_tsc.rating = 0; | ||
477 | clocksource_tsc.flags &= ~CLOCK_SOURCE_IS_CONTINUOUS; | ||
478 | } | ||
479 | clocksource_register(&clocksource_tsc); | ||
480 | } | ||
481 | |||
482 | void __init tsc_init(void) | ||
483 | { | ||
484 | u64 lpj; | ||
485 | int cpu; | ||
486 | |||
487 | if (!cpu_has_tsc) | ||
488 | return; | ||
489 | |||
490 | cpu_khz = calculate_cpu_khz(); | ||
491 | tsc_khz = cpu_khz; | ||
492 | |||
493 | if (!cpu_khz) { | ||
494 | mark_tsc_unstable("could not calculate TSC khz"); | ||
495 | return; | ||
496 | } | ||
497 | |||
498 | #ifdef CONFIG_X86_64 | ||
499 | if (cpu_has(&boot_cpu_data, X86_FEATURE_CONSTANT_TSC) && | ||
500 | (boot_cpu_data.x86_vendor == X86_VENDOR_AMD)) | ||
501 | cpu_khz = calibrate_cpu(); | ||
502 | #endif | ||
503 | |||
504 | lpj = ((u64)tsc_khz * 1000); | ||
505 | do_div(lpj, HZ); | ||
506 | lpj_fine = lpj; | ||
507 | |||
508 | printk("Detected %lu.%03lu MHz processor.\n", | ||
509 | (unsigned long)cpu_khz / 1000, | ||
510 | (unsigned long)cpu_khz % 1000); | ||
511 | |||
512 | /* | ||
513 | * Secondary CPUs do not run through tsc_init(), so set up | ||
514 | * all the scale factors for all CPUs, assuming the same | ||
515 | * speed as the bootup CPU. (cpufreq notifiers will fix this | ||
516 | * up if their speed diverges) | ||
517 | */ | ||
518 | for_each_possible_cpu(cpu) | ||
519 | set_cyc2ns_scale(cpu_khz, cpu); | ||
520 | |||
521 | if (tsc_disabled > 0) | ||
522 | return; | ||
523 | |||
524 | /* now allow native_sched_clock() to use rdtsc */ | ||
525 | tsc_disabled = 0; | ||
526 | |||
527 | use_tsc_delay(); | ||
528 | /* Check and install the TSC clocksource */ | ||
529 | dmi_check_system(bad_tsc_dmi_table); | ||
530 | |||
531 | if (unsynchronized_tsc()) | ||
532 | mark_tsc_unstable("TSCs unsynchronized"); | ||
533 | |||
534 | check_geode_tsc_reliable(); | ||
535 | init_tsc_clocksource(); | ||
536 | } | ||
537 | |||