Commit 6b37f5a2 authored by Joerg Roedel's avatar Joerg Roedel Committed by Andi Kleen

[PATCH] x86-64: fix cpu MHz reporting on constant_tsc cpus

This patch fixes the reporting of cpu_mhz in /proc/cpuinfo on CPUs with
a constant TSC rate and a kernel with disabled cpufreq.
Signed-off-by: default avatarMark Langsdorf <mark.langsdorf@amd.com>
Signed-off-by: default avatarJoerg Roedel <joerg.roedel@amd.com>
Signed-off-by: default avatarAndi Kleen <ak@suse.de>

 arch/x86_64/kernel/apic.c     |    2 -
 arch/x86_64/kernel/time.c     |   58 +++++++++++++++++++++++++++++++++++++++---
 arch/x86_64/kernel/tsc.c      |   12 +++++---
 arch/x86_64/kernel/tsc_sync.c |    2 -
 include/asm-x86_64/proto.h    |    1
 5 files changed, 65 insertions(+), 10 deletions(-)
parent fbc16f2c
...@@ -843,7 +843,7 @@ static int __init calibrate_APIC_clock(void) ...@@ -843,7 +843,7 @@ static int __init calibrate_APIC_clock(void)
} while ((tsc - tsc_start) < TICK_COUNT && } while ((tsc - tsc_start) < TICK_COUNT &&
(apic - apic_start) < TICK_COUNT); (apic - apic_start) < TICK_COUNT);
result = (apic_start - apic) * 1000L * cpu_khz / result = (apic_start - apic) * 1000L * tsc_khz /
(tsc - tsc_start); (tsc - tsc_start);
} }
printk("result %d\n", result); printk("result %d\n", result);
......
...@@ -43,6 +43,7 @@ ...@@ -43,6 +43,7 @@
#include <asm/apic.h> #include <asm/apic.h>
#include <asm/hpet.h> #include <asm/hpet.h>
#include <asm/mpspec.h> #include <asm/mpspec.h>
#include <asm/nmi.h>
static char *timename = NULL; static char *timename = NULL;
...@@ -249,6 +250,51 @@ static unsigned long get_cmos_time(void) ...@@ -249,6 +250,51 @@ static unsigned long get_cmos_time(void)
return mktime(year, mon, day, hour, min, sec); 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 * pit_calibrate_tsc() uses the speaker output (channel 2) of
...@@ -336,14 +382,20 @@ void __init time_init(void) ...@@ -336,14 +382,20 @@ void __init time_init(void)
if (hpet_use_timer) { if (hpet_use_timer) {
/* set tick_nsec to use the proper rate for HPET */ /* set tick_nsec to use the proper rate for HPET */
tick_nsec = TICK_NSEC_HPET; tick_nsec = TICK_NSEC_HPET;
cpu_khz = hpet_calibrate_tsc(); tsc_khz = hpet_calibrate_tsc();
timename = "HPET"; timename = "HPET";
} else { } else {
pit_init(); pit_init();
cpu_khz = pit_calibrate_tsc(); tsc_khz = pit_calibrate_tsc();
timename = "PIT"; 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()) if (unsynchronized_tsc())
mark_tsc_unstable(); mark_tsc_unstable();
...@@ -352,7 +404,7 @@ void __init time_init(void) ...@@ -352,7 +404,7 @@ void __init time_init(void)
else else
vgetcpu_mode = VGETCPU_LSL; 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", printk(KERN_INFO "time.c: Detected %d.%03d MHz processor.\n",
cpu_khz / 1000, cpu_khz % 1000); cpu_khz / 1000, cpu_khz % 1000);
init_tsc_clocksource(); init_tsc_clocksource();
......
...@@ -13,6 +13,8 @@ static int notsc __initdata = 0; ...@@ -13,6 +13,8 @@ static int notsc __initdata = 0;
unsigned int cpu_khz; /* TSC clocks / usec, not used here */ unsigned int cpu_khz; /* TSC clocks / usec, not used here */
EXPORT_SYMBOL(cpu_khz); EXPORT_SYMBOL(cpu_khz);
unsigned int tsc_khz;
EXPORT_SYMBOL(tsc_khz);
static unsigned int cyc2ns_scale __read_mostly; static unsigned int cyc2ns_scale __read_mostly;
...@@ -77,7 +79,7 @@ static void handle_cpufreq_delayed_get(struct work_struct *v) ...@@ -77,7 +79,7 @@ static void handle_cpufreq_delayed_get(struct work_struct *v)
static unsigned int ref_freq = 0; static unsigned int ref_freq = 0;
static unsigned long loops_per_jiffy_ref = 0; static unsigned long loops_per_jiffy_ref = 0;
static unsigned long cpu_khz_ref = 0; static unsigned long tsc_khz_ref = 0;
static int time_cpufreq_notifier(struct notifier_block *nb, unsigned long val, static int time_cpufreq_notifier(struct notifier_block *nb, unsigned long val,
void *data) void *data)
...@@ -99,7 +101,7 @@ static int time_cpufreq_notifier(struct notifier_block *nb, unsigned long val, ...@@ -99,7 +101,7 @@ static int time_cpufreq_notifier(struct notifier_block *nb, unsigned long val,
if (!ref_freq) { if (!ref_freq) {
ref_freq = freq->old; ref_freq = freq->old;
loops_per_jiffy_ref = *lpj; loops_per_jiffy_ref = *lpj;
cpu_khz_ref = cpu_khz; tsc_khz_ref = tsc_khz;
} }
if ((val == CPUFREQ_PRECHANGE && freq->old < freq->new) || if ((val == CPUFREQ_PRECHANGE && freq->old < freq->new) ||
(val == CPUFREQ_POSTCHANGE && freq->old > freq->new) || (val == CPUFREQ_POSTCHANGE && freq->old > freq->new) ||
...@@ -107,12 +109,12 @@ static int time_cpufreq_notifier(struct notifier_block *nb, unsigned long val, ...@@ -107,12 +109,12 @@ static int time_cpufreq_notifier(struct notifier_block *nb, unsigned long val,
*lpj = *lpj =
cpufreq_scale(loops_per_jiffy_ref, ref_freq, freq->new); cpufreq_scale(loops_per_jiffy_ref, ref_freq, freq->new);
cpu_khz = cpufreq_scale(cpu_khz_ref, ref_freq, freq->new); tsc_khz = cpufreq_scale(tsc_khz_ref, ref_freq, freq->new);
if (!(freq->flags & CPUFREQ_CONST_LOOPS)) if (!(freq->flags & CPUFREQ_CONST_LOOPS))
mark_tsc_unstable(); mark_tsc_unstable();
} }
set_cyc2ns_scale(cpu_khz_ref); set_cyc2ns_scale(tsc_khz_ref);
return 0; return 0;
} }
...@@ -213,7 +215,7 @@ EXPORT_SYMBOL_GPL(mark_tsc_unstable); ...@@ -213,7 +215,7 @@ EXPORT_SYMBOL_GPL(mark_tsc_unstable);
void __init init_tsc_clocksource(void) void __init init_tsc_clocksource(void)
{ {
if (!notsc) { if (!notsc) {
clocksource_tsc.mult = clocksource_khz2mult(cpu_khz, clocksource_tsc.mult = clocksource_khz2mult(tsc_khz,
clocksource_tsc.shift); clocksource_tsc.shift);
if (check_tsc_unstable()) if (check_tsc_unstable())
clocksource_tsc.rating = 0; clocksource_tsc.rating = 0;
......
...@@ -50,7 +50,7 @@ static __cpuinit void check_tsc_warp(void) ...@@ -50,7 +50,7 @@ static __cpuinit void check_tsc_warp(void)
/* /*
* The measurement runs for 20 msecs: * The measurement runs for 20 msecs:
*/ */
end = start + cpu_khz * 20ULL; end = start + tsc_khz * 20ULL;
now = start; now = start;
for (i = 0; ; i++) { for (i = 0; ; i++) {
......
...@@ -92,6 +92,7 @@ extern unsigned long table_start, table_end; ...@@ -92,6 +92,7 @@ extern unsigned long table_start, table_end;
extern int exception_trace; extern int exception_trace;
extern unsigned cpu_khz; extern unsigned cpu_khz;
extern unsigned tsc_khz;
extern void no_iommu_init(void); extern void no_iommu_init(void);
extern int force_iommu, no_iommu; extern int force_iommu, no_iommu;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment