|
@@ -43,6 +43,7 @@
|
|
|
#include <asm/apic.h>
|
|
|
#include <asm/hpet.h>
|
|
|
#include <asm/mpspec.h>
|
|
|
+#include <asm/nmi.h>
|
|
|
|
|
|
static char *timename = NULL;
|
|
|
|
|
@@ -249,6 +250,51 @@ static unsigned long get_cmos_time(void)
|
|
|
return mktime(year, mon, day, hour, min, sec);
|
|
|
}
|
|
|
|
|
|
+/* calibrate_cpu is used on systems with fixed rate TSCs to determine
|
|
|
+ * processor frequency */
|
|
|
+#define TICK_COUNT 100000000
|
|
|
+static unsigned int __init tsc_calibrate_cpu_khz(void)
|
|
|
+{
|
|
|
+ int tsc_start, tsc_now;
|
|
|
+ int i, no_ctr_free;
|
|
|
+ unsigned long evntsel3 = 0, pmc3 = 0, pmc_now = 0;
|
|
|
+ unsigned long flags;
|
|
|
+
|
|
|
+ for (i = 0; i < 4; i++)
|
|
|
+ if (avail_to_resrv_perfctr_nmi_bit(i))
|
|
|
+ break;
|
|
|
+ no_ctr_free = (i == 4);
|
|
|
+ if (no_ctr_free) {
|
|
|
+ i = 3;
|
|
|
+ rdmsrl(MSR_K7_EVNTSEL3, evntsel3);
|
|
|
+ wrmsrl(MSR_K7_EVNTSEL3, 0);
|
|
|
+ rdmsrl(MSR_K7_PERFCTR3, pmc3);
|
|
|
+ } else {
|
|
|
+ reserve_perfctr_nmi(MSR_K7_PERFCTR0 + i);
|
|
|
+ reserve_evntsel_nmi(MSR_K7_EVNTSEL0 + i);
|
|
|
+ }
|
|
|
+ local_irq_save(flags);
|
|
|
+ /* start meauring cycles, incrementing from 0 */
|
|
|
+ wrmsrl(MSR_K7_PERFCTR0 + i, 0);
|
|
|
+ wrmsrl(MSR_K7_EVNTSEL0 + i, 1 << 22 | 3 << 16 | 0x76);
|
|
|
+ rdtscl(tsc_start);
|
|
|
+ do {
|
|
|
+ rdmsrl(MSR_K7_PERFCTR0 + i, pmc_now);
|
|
|
+ tsc_now = get_cycles_sync();
|
|
|
+ } while ((tsc_now - tsc_start) < TICK_COUNT);
|
|
|
+
|
|
|
+ local_irq_restore(flags);
|
|
|
+ if (no_ctr_free) {
|
|
|
+ wrmsrl(MSR_K7_EVNTSEL3, 0);
|
|
|
+ wrmsrl(MSR_K7_PERFCTR3, pmc3);
|
|
|
+ wrmsrl(MSR_K7_EVNTSEL3, evntsel3);
|
|
|
+ } else {
|
|
|
+ release_perfctr_nmi(MSR_K7_PERFCTR0 + i);
|
|
|
+ release_evntsel_nmi(MSR_K7_EVNTSEL0 + i);
|
|
|
+ }
|
|
|
+
|
|
|
+ return pmc_now * tsc_khz / (tsc_now - tsc_start);
|
|
|
+}
|
|
|
|
|
|
/*
|
|
|
* pit_calibrate_tsc() uses the speaker output (channel 2) of
|
|
@@ -336,14 +382,20 @@ void __init time_init(void)
|
|
|
if (hpet_use_timer) {
|
|
|
/* set tick_nsec to use the proper rate for HPET */
|
|
|
tick_nsec = TICK_NSEC_HPET;
|
|
|
- cpu_khz = hpet_calibrate_tsc();
|
|
|
+ tsc_khz = hpet_calibrate_tsc();
|
|
|
timename = "HPET";
|
|
|
} else {
|
|
|
pit_init();
|
|
|
- cpu_khz = pit_calibrate_tsc();
|
|
|
+ tsc_khz = pit_calibrate_tsc();
|
|
|
timename = "PIT";
|
|
|
}
|
|
|
|
|
|
+ cpu_khz = tsc_khz;
|
|
|
+ if (cpu_has(&boot_cpu_data, X86_FEATURE_CONSTANT_TSC) &&
|
|
|
+ boot_cpu_data.x86_vendor == X86_VENDOR_AMD &&
|
|
|
+ boot_cpu_data.x86 == 16)
|
|
|
+ cpu_khz = tsc_calibrate_cpu_khz();
|
|
|
+
|
|
|
if (unsynchronized_tsc())
|
|
|
mark_tsc_unstable();
|
|
|
|
|
@@ -352,7 +404,7 @@ void __init time_init(void)
|
|
|
else
|
|
|
vgetcpu_mode = VGETCPU_LSL;
|
|
|
|
|
|
- set_cyc2ns_scale(cpu_khz);
|
|
|
+ set_cyc2ns_scale(tsc_khz);
|
|
|
printk(KERN_INFO "time.c: Detected %d.%03d MHz processor.\n",
|
|
|
cpu_khz / 1000, cpu_khz % 1000);
|
|
|
init_tsc_clocksource();
|