Skip to content

Commit

Permalink
---
Browse files Browse the repository at this point in the history
yaml
---
r: 100017
b: refs/heads/master
c: bfc0f59
h: refs/heads/master
i:
  100015: 529127b
v: v3
  • Loading branch information
Alok Kataria authored and Ingo Molnar committed Jul 9, 2008
1 parent 6a47d0c commit f91b642
Show file tree
Hide file tree
Showing 7 changed files with 155 additions and 175 deletions.
2 changes: 1 addition & 1 deletion [refs]
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
---
refs/heads/master: 0ef95533326a7b37d16025af9edc0c18e644b346
refs/heads/master: bfc0f5947afa5e3a13e55867f4478c8a92c11dca
26 changes: 20 additions & 6 deletions trunk/arch/x86/kernel/time_64.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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();
(boot_cpu_data.x86_vendor == X86_VENDOR_AMD))
cpu_khz = calibrate_cpu();

lpj_fine = ((unsigned long)tsc_khz * 1000)/HZ;

Expand All @@ -134,7 +138,17 @@ void __init time_init(void)
vgetcpu_mode = VGETCPU_LSL;

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();
late_time_init = choose_time_init();
}
131 changes: 131 additions & 0 deletions trunk/arch/x86/kernel/tsc.c
Original file line number Diff line number Diff line change
@@ -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);
Expand Down Expand Up @@ -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 */
74 changes: 1 addition & 73 deletions trunk/arch/x86/kernel/tsc_32.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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

/*
Expand Down
94 changes: 1 addition & 93 deletions trunk/arch/x86/kernel/tsc_64.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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.
Expand Down
Loading

0 comments on commit f91b642

Please sign in to comment.