forked from Minki/linux
x86: merge tsc calibration
Merge the tsc calibration code for the 32bit and 64bit kernel. The paravirtualized calculate_cpu_khz for 64bit now points to the correct tsc_calibrate code as in 32bit. Original native_calculate_cpu_khz for 64 bit is now called as calibrate_cpu. Also moved the recalibrate_cpu_khz function in the common file. Note that this function is called only from powernow K7 cpu freq driver. 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>
This commit is contained in:
parent
0ef9553332
commit
bfc0f5947a
@ -56,7 +56,7 @@ static irqreturn_t timer_event_interrupt(int irq, void *dev_id)
|
||||
/* calibrate_cpu is used on systems with fixed rate TSCs to determine
|
||||
* processor frequency */
|
||||
#define TICK_COUNT 100000000
|
||||
unsigned long __init native_calculate_cpu_khz(void)
|
||||
static unsigned long __init calibrate_cpu(void)
|
||||
{
|
||||
int tsc_start, tsc_now;
|
||||
int i, no_ctr_free;
|
||||
@ -114,14 +114,18 @@ void __init hpet_time_init(void)
|
||||
setup_irq(0, &irq0);
|
||||
}
|
||||
|
||||
extern void set_cyc2ns_scale(unsigned long cpu_khz, int cpu);
|
||||
|
||||
void __init time_init(void)
|
||||
{
|
||||
tsc_calibrate();
|
||||
int cpu;
|
||||
|
||||
cpu_khz = calculate_cpu_khz();
|
||||
tsc_khz = cpu_khz;
|
||||
|
||||
cpu_khz = tsc_khz;
|
||||
if (cpu_has(&boot_cpu_data, X86_FEATURE_CONSTANT_TSC) &&
|
||||
(boot_cpu_data.x86_vendor == X86_VENDOR_AMD))
|
||||
cpu_khz = calculate_cpu_khz();
|
||||
cpu_khz = calibrate_cpu();
|
||||
|
||||
lpj_fine = ((unsigned long)tsc_khz * 1000)/HZ;
|
||||
|
||||
@ -135,6 +139,16 @@ void __init time_init(void)
|
||||
|
||||
printk(KERN_INFO "time.c: Detected %d.%03d MHz processor.\n",
|
||||
cpu_khz / 1000, cpu_khz % 1000);
|
||||
|
||||
/*
|
||||
* Secondary CPUs do not run through tsc_init(), so set up
|
||||
* all the scale factors for all CPUs, assuming the same
|
||||
* speed as the bootup CPU. (cpufreq notifiers will fix this
|
||||
* up if their speed diverges)
|
||||
*/
|
||||
for_each_possible_cpu(cpu)
|
||||
set_cyc2ns_scale(cpu_khz, cpu);
|
||||
|
||||
init_tsc_clocksource();
|
||||
late_time_init = choose_time_init();
|
||||
}
|
||||
|
@ -1,7 +1,11 @@
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/timer.h>
|
||||
#include <linux/acpi_pmtmr.h>
|
||||
|
||||
#include <asm/hpet.h>
|
||||
|
||||
unsigned int cpu_khz; /* TSC clocks / usec, not used here */
|
||||
EXPORT_SYMBOL(cpu_khz);
|
||||
@ -84,3 +88,130 @@ int __init notsc_setup(char *str)
|
||||
#endif
|
||||
|
||||
__setup("notsc", notsc_setup);
|
||||
|
||||
#define MAX_RETRIES 5
|
||||
#define SMI_TRESHOLD 50000
|
||||
|
||||
/*
|
||||
* Read TSC and the reference counters. Take care of SMI disturbance
|
||||
*/
|
||||
static u64 __init tsc_read_refs(u64 *pm, u64 *hpet)
|
||||
{
|
||||
u64 t1, t2;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < MAX_RETRIES; i++) {
|
||||
t1 = get_cycles();
|
||||
if (hpet)
|
||||
*hpet = hpet_readl(HPET_COUNTER) & 0xFFFFFFFF;
|
||||
else
|
||||
*pm = acpi_pm_read_early();
|
||||
t2 = get_cycles();
|
||||
if ((t2 - t1) < SMI_TRESHOLD)
|
||||
return t2;
|
||||
}
|
||||
return ULLONG_MAX;
|
||||
}
|
||||
|
||||
/**
|
||||
* tsc_calibrate - calibrate the tsc on boot
|
||||
*/
|
||||
static unsigned int __init tsc_calibrate(void)
|
||||
{
|
||||
unsigned long flags;
|
||||
u64 tsc1, tsc2, tr1, tr2, delta, pm1, pm2, hpet1, hpet2;
|
||||
int hpet = is_hpet_enabled();
|
||||
unsigned int tsc_khz_val = 0;
|
||||
|
||||
local_irq_save(flags);
|
||||
|
||||
tsc1 = tsc_read_refs(&pm1, hpet ? &hpet1 : NULL);
|
||||
|
||||
outb((inb(0x61) & ~0x02) | 0x01, 0x61);
|
||||
|
||||
outb(0xb0, 0x43);
|
||||
outb((CLOCK_TICK_RATE / (1000 / 50)) & 0xff, 0x42);
|
||||
outb((CLOCK_TICK_RATE / (1000 / 50)) >> 8, 0x42);
|
||||
tr1 = get_cycles();
|
||||
while ((inb(0x61) & 0x20) == 0);
|
||||
tr2 = get_cycles();
|
||||
|
||||
tsc2 = tsc_read_refs(&pm2, hpet ? &hpet2 : NULL);
|
||||
|
||||
local_irq_restore(flags);
|
||||
|
||||
/*
|
||||
* Preset the result with the raw and inaccurate PIT
|
||||
* calibration value
|
||||
*/
|
||||
delta = (tr2 - tr1);
|
||||
do_div(delta, 50);
|
||||
tsc_khz_val = delta;
|
||||
|
||||
/* hpet or pmtimer available ? */
|
||||
if (!hpet && !pm1 && !pm2) {
|
||||
printk(KERN_INFO "TSC calibrated against PIT\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Check, whether the sampling was disturbed by an SMI */
|
||||
if (tsc1 == ULLONG_MAX || tsc2 == ULLONG_MAX) {
|
||||
printk(KERN_WARNING "TSC calibration disturbed by SMI, "
|
||||
"using PIT calibration result\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
tsc2 = (tsc2 - tsc1) * 1000000LL;
|
||||
|
||||
if (hpet) {
|
||||
printk(KERN_INFO "TSC calibrated against HPET\n");
|
||||
if (hpet2 < hpet1)
|
||||
hpet2 += 0x100000000ULL;
|
||||
hpet2 -= hpet1;
|
||||
tsc1 = ((u64)hpet2 * hpet_readl(HPET_PERIOD));
|
||||
do_div(tsc1, 1000000);
|
||||
} else {
|
||||
printk(KERN_INFO "TSC calibrated against PM_TIMER\n");
|
||||
if (pm2 < pm1)
|
||||
pm2 += (u64)ACPI_PM_OVRRUN;
|
||||
pm2 -= pm1;
|
||||
tsc1 = pm2 * 1000000000LL;
|
||||
do_div(tsc1, PMTMR_TICKS_PER_SEC);
|
||||
}
|
||||
|
||||
do_div(tsc2, tsc1);
|
||||
tsc_khz_val = tsc2;
|
||||
|
||||
out:
|
||||
return tsc_khz_val;
|
||||
}
|
||||
|
||||
unsigned long native_calculate_cpu_khz(void)
|
||||
{
|
||||
return tsc_calibrate();
|
||||
}
|
||||
|
||||
#ifdef CONFIG_X86_32
|
||||
/* Only called from the Powernow K7 cpu freq driver */
|
||||
int recalibrate_cpu_khz(void)
|
||||
{
|
||||
#ifndef CONFIG_SMP
|
||||
unsigned long cpu_khz_old = cpu_khz;
|
||||
|
||||
if (cpu_has_tsc) {
|
||||
cpu_khz = calculate_cpu_khz();
|
||||
tsc_khz = cpu_khz;
|
||||
cpu_data(0).loops_per_jiffy =
|
||||
cpufreq_scale(cpu_data(0).loops_per_jiffy,
|
||||
cpu_khz_old, cpu_khz);
|
||||
return 0;
|
||||
} else
|
||||
return -ENODEV;
|
||||
#else
|
||||
return -ENODEV;
|
||||
#endif
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL(recalibrate_cpu_khz);
|
||||
|
||||
#endif /* CONFIG_X86_32 */
|
||||
|
@ -42,7 +42,7 @@ extern int tsc_disabled;
|
||||
|
||||
DEFINE_PER_CPU(unsigned long, cyc2ns);
|
||||
|
||||
static void set_cyc2ns_scale(unsigned long cpu_khz, int cpu)
|
||||
void set_cyc2ns_scale(unsigned long cpu_khz, int cpu)
|
||||
{
|
||||
unsigned long long tsc_now, ns_now;
|
||||
unsigned long flags, *scale;
|
||||
@ -65,78 +65,6 @@ static void set_cyc2ns_scale(unsigned long cpu_khz, int cpu)
|
||||
local_irq_restore(flags);
|
||||
}
|
||||
|
||||
unsigned long native_calculate_cpu_khz(void)
|
||||
{
|
||||
unsigned long long start, end;
|
||||
unsigned long count;
|
||||
u64 delta64 = (u64)ULLONG_MAX;
|
||||
int i;
|
||||
unsigned long flags;
|
||||
|
||||
local_irq_save(flags);
|
||||
|
||||
/* run 3 times to ensure the cache is warm and to get an accurate reading */
|
||||
for (i = 0; i < 3; i++) {
|
||||
mach_prepare_counter();
|
||||
rdtscll(start);
|
||||
mach_countup(&count);
|
||||
rdtscll(end);
|
||||
|
||||
/*
|
||||
* Error: ECTCNEVERSET
|
||||
* The CTC wasn't reliable: we got a hit on the very first read,
|
||||
* or the CPU was so fast/slow that the quotient wouldn't fit in
|
||||
* 32 bits..
|
||||
*/
|
||||
if (count <= 1)
|
||||
continue;
|
||||
|
||||
/* cpu freq too slow: */
|
||||
if ((end - start) <= CALIBRATE_TIME_MSEC)
|
||||
continue;
|
||||
|
||||
/*
|
||||
* We want the minimum time of all runs in case one of them
|
||||
* is inaccurate due to SMI or other delay
|
||||
*/
|
||||
delta64 = min(delta64, (end - start));
|
||||
}
|
||||
|
||||
/* cpu freq too fast (or every run was bad): */
|
||||
if (delta64 > (1ULL<<32))
|
||||
goto err;
|
||||
|
||||
delta64 += CALIBRATE_TIME_MSEC/2; /* round for do_div */
|
||||
do_div(delta64,CALIBRATE_TIME_MSEC);
|
||||
|
||||
local_irq_restore(flags);
|
||||
return (unsigned long)delta64;
|
||||
err:
|
||||
local_irq_restore(flags);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int recalibrate_cpu_khz(void)
|
||||
{
|
||||
#ifndef CONFIG_SMP
|
||||
unsigned long cpu_khz_old = cpu_khz;
|
||||
|
||||
if (cpu_has_tsc) {
|
||||
cpu_khz = calculate_cpu_khz();
|
||||
tsc_khz = cpu_khz;
|
||||
cpu_data(0).loops_per_jiffy =
|
||||
cpufreq_scale(cpu_data(0).loops_per_jiffy,
|
||||
cpu_khz_old, cpu_khz);
|
||||
return 0;
|
||||
} else
|
||||
return -ENODEV;
|
||||
#else
|
||||
return -ENODEV;
|
||||
#endif
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL(recalibrate_cpu_khz);
|
||||
|
||||
#ifdef CONFIG_CPU_FREQ
|
||||
|
||||
/*
|
||||
|
@ -40,7 +40,7 @@ extern int tsc_disabled;
|
||||
|
||||
DEFINE_PER_CPU(unsigned long, cyc2ns);
|
||||
|
||||
static void set_cyc2ns_scale(unsigned long cpu_khz, int cpu)
|
||||
void set_cyc2ns_scale(unsigned long cpu_khz, int cpu)
|
||||
{
|
||||
unsigned long long tsc_now, ns_now;
|
||||
unsigned long flags, *scale;
|
||||
@ -130,98 +130,6 @@ core_initcall(cpufreq_tsc);
|
||||
|
||||
#endif
|
||||
|
||||
#define MAX_RETRIES 5
|
||||
#define SMI_TRESHOLD 50000
|
||||
|
||||
/*
|
||||
* Read TSC and the reference counters. Take care of SMI disturbance
|
||||
*/
|
||||
static unsigned long __init tsc_read_refs(unsigned long *pm,
|
||||
unsigned long *hpet)
|
||||
{
|
||||
unsigned long t1, t2;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < MAX_RETRIES; i++) {
|
||||
t1 = get_cycles();
|
||||
if (hpet)
|
||||
*hpet = hpet_readl(HPET_COUNTER) & 0xFFFFFFFF;
|
||||
else
|
||||
*pm = acpi_pm_read_early();
|
||||
t2 = get_cycles();
|
||||
if ((t2 - t1) < SMI_TRESHOLD)
|
||||
return t2;
|
||||
}
|
||||
return ULONG_MAX;
|
||||
}
|
||||
|
||||
/**
|
||||
* tsc_calibrate - calibrate the tsc on boot
|
||||
*/
|
||||
void __init tsc_calibrate(void)
|
||||
{
|
||||
unsigned long flags, tsc1, tsc2, tr1, tr2, pm1, pm2, hpet1, hpet2;
|
||||
int hpet = is_hpet_enabled(), cpu;
|
||||
|
||||
local_irq_save(flags);
|
||||
|
||||
tsc1 = tsc_read_refs(&pm1, hpet ? &hpet1 : NULL);
|
||||
|
||||
outb((inb(0x61) & ~0x02) | 0x01, 0x61);
|
||||
|
||||
outb(0xb0, 0x43);
|
||||
outb((CLOCK_TICK_RATE / (1000 / 50)) & 0xff, 0x42);
|
||||
outb((CLOCK_TICK_RATE / (1000 / 50)) >> 8, 0x42);
|
||||
tr1 = get_cycles();
|
||||
while ((inb(0x61) & 0x20) == 0);
|
||||
tr2 = get_cycles();
|
||||
|
||||
tsc2 = tsc_read_refs(&pm2, hpet ? &hpet2 : NULL);
|
||||
|
||||
local_irq_restore(flags);
|
||||
|
||||
/*
|
||||
* Preset the result with the raw and inaccurate PIT
|
||||
* calibration value
|
||||
*/
|
||||
tsc_khz = (tr2 - tr1) / 50;
|
||||
|
||||
/* hpet or pmtimer available ? */
|
||||
if (!hpet && !pm1 && !pm2) {
|
||||
printk(KERN_INFO "TSC calibrated against PIT\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Check, whether the sampling was disturbed by an SMI */
|
||||
if (tsc1 == ULONG_MAX || tsc2 == ULONG_MAX) {
|
||||
printk(KERN_WARNING "TSC calibration disturbed by SMI, "
|
||||
"using PIT calibration result\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
tsc2 = (tsc2 - tsc1) * 1000000L;
|
||||
|
||||
if (hpet) {
|
||||
printk(KERN_INFO "TSC calibrated against HPET\n");
|
||||
if (hpet2 < hpet1)
|
||||
hpet2 += 0x100000000UL;
|
||||
hpet2 -= hpet1;
|
||||
tsc1 = (hpet2 * hpet_readl(HPET_PERIOD)) / 1000000;
|
||||
} else {
|
||||
printk(KERN_INFO "TSC calibrated against PM_TIMER\n");
|
||||
if (pm2 < pm1)
|
||||
pm2 += ACPI_PM_OVRRUN;
|
||||
pm2 -= pm1;
|
||||
tsc1 = (pm2 * 1000000000) / PMTMR_TICKS_PER_SEC;
|
||||
}
|
||||
|
||||
tsc_khz = tsc2 / tsc1;
|
||||
|
||||
out:
|
||||
for_each_possible_cpu(cpu)
|
||||
set_cyc2ns_scale(tsc_khz, cpu);
|
||||
}
|
||||
|
||||
/*
|
||||
* Make an educated guess if the TSC is trustworthy and synchronized
|
||||
* over all CPUs.
|
||||
|
@ -86,8 +86,8 @@ extern void hpet_unregister_irq_handler(rtc_irq_handler handler);
|
||||
#else /* CONFIG_HPET_TIMER */
|
||||
|
||||
static inline int hpet_enable(void) { return 0; }
|
||||
static inline unsigned long hpet_readl(unsigned long a) { return 0; }
|
||||
static inline int is_hpet_enabled(void) { return 0; }
|
||||
#define hpet_readl(a) 0
|
||||
|
||||
#endif
|
||||
#endif /* ASM_X86_HPET_H */
|
||||
|
@ -58,7 +58,6 @@ int check_tsc_unstable(void);
|
||||
extern void check_tsc_sync_source(int cpu);
|
||||
extern void check_tsc_sync_target(void);
|
||||
|
||||
extern void tsc_calibrate(void);
|
||||
extern int notsc_setup(char *);
|
||||
|
||||
#endif
|
||||
|
Loading…
Reference in New Issue
Block a user