diff options
Diffstat (limited to 'arch/x86_64/kernel/time.c')
| -rw-r--r-- | arch/x86_64/kernel/time.c | 71 |
1 files changed, 60 insertions, 11 deletions
diff --git a/arch/x86_64/kernel/time.c b/arch/x86_64/kernel/time.c index 75d73a9aa9ff..0652e173813b 100644 --- a/arch/x86_64/kernel/time.c +++ b/arch/x86_64/kernel/time.c | |||
| @@ -39,13 +39,11 @@ | |||
| 39 | #include <asm/proto.h> | 39 | #include <asm/proto.h> |
| 40 | #include <asm/hpet.h> | 40 | #include <asm/hpet.h> |
| 41 | #include <asm/sections.h> | 41 | #include <asm/sections.h> |
| 42 | #include <linux/cpufreq.h> | ||
| 43 | #include <linux/hpet.h> | 42 | #include <linux/hpet.h> |
| 44 | #include <asm/apic.h> | 43 | #include <asm/apic.h> |
| 45 | #include <asm/hpet.h> | 44 | #include <asm/hpet.h> |
| 46 | 45 | #include <asm/mpspec.h> | |
| 47 | extern void i8254_timer_resume(void); | 46 | #include <asm/nmi.h> |
| 48 | extern int using_apic_timer; | ||
| 49 | 47 | ||
| 50 | static char *timename = NULL; | 48 | static char *timename = NULL; |
| 51 | 49 | ||
| @@ -252,6 +250,51 @@ static unsigned long get_cmos_time(void) | |||
| 252 | return mktime(year, mon, day, hour, min, sec); | 250 | return mktime(year, mon, day, hour, min, sec); |
| 253 | } | 251 | } |
| 254 | 252 | ||
| 253 | /* calibrate_cpu is used on systems with fixed rate TSCs to determine | ||
| 254 | * processor frequency */ | ||
| 255 | #define TICK_COUNT 100000000 | ||
| 256 | static unsigned int __init tsc_calibrate_cpu_khz(void) | ||
| 257 | { | ||
| 258 | int tsc_start, tsc_now; | ||
| 259 | int i, no_ctr_free; | ||
| 260 | unsigned long evntsel3 = 0, pmc3 = 0, pmc_now = 0; | ||
| 261 | unsigned long flags; | ||
| 262 | |||
| 263 | for (i = 0; i < 4; i++) | ||
| 264 | if (avail_to_resrv_perfctr_nmi_bit(i)) | ||
| 265 | break; | ||
| 266 | no_ctr_free = (i == 4); | ||
| 267 | if (no_ctr_free) { | ||
| 268 | i = 3; | ||
| 269 | rdmsrl(MSR_K7_EVNTSEL3, evntsel3); | ||
| 270 | wrmsrl(MSR_K7_EVNTSEL3, 0); | ||
| 271 | rdmsrl(MSR_K7_PERFCTR3, pmc3); | ||
| 272 | } else { | ||
| 273 | reserve_perfctr_nmi(MSR_K7_PERFCTR0 + i); | ||
| 274 | reserve_evntsel_nmi(MSR_K7_EVNTSEL0 + i); | ||
| 275 | } | ||
| 276 | local_irq_save(flags); | ||
| 277 | /* start meauring cycles, incrementing from 0 */ | ||
| 278 | wrmsrl(MSR_K7_PERFCTR0 + i, 0); | ||
| 279 | wrmsrl(MSR_K7_EVNTSEL0 + i, 1 << 22 | 3 << 16 | 0x76); | ||
| 280 | rdtscl(tsc_start); | ||
| 281 | do { | ||
| 282 | rdmsrl(MSR_K7_PERFCTR0 + i, pmc_now); | ||
| 283 | tsc_now = get_cycles_sync(); | ||
| 284 | } while ((tsc_now - tsc_start) < TICK_COUNT); | ||
| 285 | |||
| 286 | local_irq_restore(flags); | ||
| 287 | if (no_ctr_free) { | ||
| 288 | wrmsrl(MSR_K7_EVNTSEL3, 0); | ||
| 289 | wrmsrl(MSR_K7_PERFCTR3, pmc3); | ||
| 290 | wrmsrl(MSR_K7_EVNTSEL3, evntsel3); | ||
| 291 | } else { | ||
| 292 | release_perfctr_nmi(MSR_K7_PERFCTR0 + i); | ||
| 293 | release_evntsel_nmi(MSR_K7_EVNTSEL0 + i); | ||
| 294 | } | ||
| 295 | |||
| 296 | return pmc_now * tsc_khz / (tsc_now - tsc_start); | ||
| 297 | } | ||
| 255 | 298 | ||
| 256 | /* | 299 | /* |
| 257 | * pit_calibrate_tsc() uses the speaker output (channel 2) of | 300 | * pit_calibrate_tsc() uses the speaker output (channel 2) of |
| @@ -285,7 +328,7 @@ static unsigned int __init pit_calibrate_tsc(void) | |||
| 285 | #define PIT_MODE 0x43 | 328 | #define PIT_MODE 0x43 |
| 286 | #define PIT_CH0 0x40 | 329 | #define PIT_CH0 0x40 |
| 287 | 330 | ||
| 288 | static void __init __pit_init(int val, u8 mode) | 331 | static void __pit_init(int val, u8 mode) |
| 289 | { | 332 | { |
| 290 | unsigned long flags; | 333 | unsigned long flags; |
| 291 | 334 | ||
| @@ -301,12 +344,12 @@ void __init pit_init(void) | |||
| 301 | __pit_init(LATCH, 0x34); /* binary, mode 2, LSB/MSB, ch 0 */ | 344 | __pit_init(LATCH, 0x34); /* binary, mode 2, LSB/MSB, ch 0 */ |
| 302 | } | 345 | } |
| 303 | 346 | ||
| 304 | void __init pit_stop_interrupt(void) | 347 | void pit_stop_interrupt(void) |
| 305 | { | 348 | { |
| 306 | __pit_init(0, 0x30); /* mode 0 */ | 349 | __pit_init(0, 0x30); /* mode 0 */ |
| 307 | } | 350 | } |
| 308 | 351 | ||
| 309 | void __init stop_timer_interrupt(void) | 352 | void stop_timer_interrupt(void) |
| 310 | { | 353 | { |
| 311 | char *name; | 354 | char *name; |
| 312 | if (hpet_address) { | 355 | if (hpet_address) { |
| @@ -339,23 +382,29 @@ void __init time_init(void) | |||
| 339 | if (hpet_use_timer) { | 382 | if (hpet_use_timer) { |
| 340 | /* set tick_nsec to use the proper rate for HPET */ | 383 | /* set tick_nsec to use the proper rate for HPET */ |
| 341 | tick_nsec = TICK_NSEC_HPET; | 384 | tick_nsec = TICK_NSEC_HPET; |
| 342 | cpu_khz = hpet_calibrate_tsc(); | 385 | tsc_khz = hpet_calibrate_tsc(); |
| 343 | timename = "HPET"; | 386 | timename = "HPET"; |
| 344 | } else { | 387 | } else { |
| 345 | pit_init(); | 388 | pit_init(); |
| 346 | cpu_khz = pit_calibrate_tsc(); | 389 | tsc_khz = pit_calibrate_tsc(); |
| 347 | timename = "PIT"; | 390 | timename = "PIT"; |
| 348 | } | 391 | } |
| 349 | 392 | ||
| 393 | cpu_khz = tsc_khz; | ||
| 394 | if (cpu_has(&boot_cpu_data, X86_FEATURE_CONSTANT_TSC) && | ||
| 395 | boot_cpu_data.x86_vendor == X86_VENDOR_AMD && | ||
| 396 | boot_cpu_data.x86 == 16) | ||
| 397 | cpu_khz = tsc_calibrate_cpu_khz(); | ||
| 398 | |||
| 350 | if (unsynchronized_tsc()) | 399 | if (unsynchronized_tsc()) |
| 351 | mark_tsc_unstable(); | 400 | mark_tsc_unstable("TSCs unsynchronized"); |
| 352 | 401 | ||
| 353 | if (cpu_has(&boot_cpu_data, X86_FEATURE_RDTSCP)) | 402 | if (cpu_has(&boot_cpu_data, X86_FEATURE_RDTSCP)) |
| 354 | vgetcpu_mode = VGETCPU_RDTSCP; | 403 | vgetcpu_mode = VGETCPU_RDTSCP; |
| 355 | else | 404 | else |
| 356 | vgetcpu_mode = VGETCPU_LSL; | 405 | vgetcpu_mode = VGETCPU_LSL; |
| 357 | 406 | ||
| 358 | set_cyc2ns_scale(cpu_khz); | 407 | set_cyc2ns_scale(tsc_khz); |
| 359 | printk(KERN_INFO "time.c: Detected %d.%03d MHz processor.\n", | 408 | printk(KERN_INFO "time.c: Detected %d.%03d MHz processor.\n", |
| 360 | cpu_khz / 1000, cpu_khz % 1000); | 409 | cpu_khz / 1000, cpu_khz % 1000); |
| 361 | init_tsc_clocksource(); | 410 | init_tsc_clocksource(); |
