Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit bfc0f594 authored by Alok Kataria's avatar Alok Kataria Committed by Ingo Molnar
Browse files

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: default avatarAlok N Kataria <akataria@vmware.com>
Signed-off-by: default avatarDan Hecht <dhecht@vmware.com>
Cc: Dan Hecht <dhecht@vmware.com>
Signed-off-by: default avatarIngo Molnar <mingo@elte.hu>
parent 0ef95533
Loading
Loading
Loading
Loading
+20 −6
Original line number Original line Diff line number Diff line
@@ -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
/* calibrate_cpu is used on systems with fixed rate TSCs to determine
 * processor frequency */
 * processor frequency */
#define TICK_COUNT 100000000
#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 tsc_start, tsc_now;
	int i, no_ctr_free;
	int i, no_ctr_free;
@@ -114,14 +114,18 @@ void __init hpet_time_init(void)
	setup_irq(0, &irq0);
	setup_irq(0, &irq0);
}
}


extern void set_cyc2ns_scale(unsigned long cpu_khz, int cpu);

void __init time_init(void)
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) &&
	if (cpu_has(&boot_cpu_data, X86_FEATURE_CONSTANT_TSC) &&
			(boot_cpu_data.x86_vendor == X86_VENDOR_AMD))
			(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;
	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",
	printk(KERN_INFO "time.c: Detected %d.%03d MHz processor.\n",
			cpu_khz / 1000, cpu_khz % 1000);
			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();
	init_tsc_clocksource();
	late_time_init = choose_time_init();
	late_time_init = choose_time_init();
}
}
+131 −0
Original line number Original line Diff line number Diff line
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/sched.h>
#include <linux/init.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/module.h>
#include <linux/timer.h>
#include <linux/timer.h>
#include <linux/acpi_pmtmr.h>

#include <asm/hpet.h>


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);
@@ -84,3 +88,130 @@ int __init notsc_setup(char *str)
#endif
#endif


__setup("notsc", notsc_setup);
__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 */
+1 −73
Original line number Original line Diff line number Diff line
@@ -42,7 +42,7 @@ extern int tsc_disabled;


DEFINE_PER_CPU(unsigned long, cyc2ns);
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 long tsc_now, ns_now;
	unsigned long flags, *scale;
	unsigned long flags, *scale;
@@ -65,78 +65,6 @@ static void set_cyc2ns_scale(unsigned long cpu_khz, int cpu)
	local_irq_restore(flags);
	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
#ifdef CONFIG_CPU_FREQ


/*
/*
+1 −93
Original line number Original line Diff line number Diff line
@@ -40,7 +40,7 @@ extern int tsc_disabled;


DEFINE_PER_CPU(unsigned long, cyc2ns);
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 long tsc_now, ns_now;
	unsigned long flags, *scale;
	unsigned long flags, *scale;
@@ -130,98 +130,6 @@ core_initcall(cpufreq_tsc);


#endif
#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
 * Make an educated guess if the TSC is trustworthy and synchronized
 * over all CPUs.
 * over all CPUs.
+1 −1
Original line number Original line Diff line number Diff line
@@ -86,8 +86,8 @@ extern void hpet_unregister_irq_handler(rtc_irq_handler handler);
#else /* CONFIG_HPET_TIMER */
#else /* CONFIG_HPET_TIMER */


static inline int hpet_enable(void) { return 0; }
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; }
static inline int is_hpet_enabled(void) { return 0; }
#define hpet_readl(a) 0


#endif
#endif
#endif /* ASM_X86_HPET_H */
#endif /* ASM_X86_HPET_H */
Loading