Commit ceffc556 authored by Andrew Morton's avatar Andrew Morton Committed by Linus Torvalds

[PATCH] calibrate_tsc() fix and consolidation

From: "Pallipadi, Venkatesh" <venkatesh.pallipadi@intel.com>

CONFIG_TIMER_CYCLONE doesn't build at present because calibrate_tsc() was
made static.

The patch fixes that up and moves all calibrate_tsc functions into a common
file, avoiding the current code duplication.
parent 58807f83
......@@ -2,7 +2,7 @@
# Makefile for x86 timers
#
obj-y := timer.o timer_none.o timer_tsc.o timer_pit.o
obj-y := timer.o timer_none.o timer_tsc.o timer_pit.o common.o
obj-$(CONFIG_X86_CYCLONE_TIMER) += timer_cyclone.o
obj-$(CONFIG_HPET_TIMER) += timer_hpet.o
/*
* Common functions used across the timers go here
*/
#include <linux/init.h>
#include <linux/timex.h>
#include <linux/errno.h>
#include <asm/io.h>
#include <asm/timer.h>
#include <asm/hpet.h>
#include "mach_timer.h"
/* ------ Calibrate the TSC -------
* Return 2^32 * (1 / (TSC clocks per usec)) for do_fast_gettimeoffset().
* Too much 64-bit arithmetic here to do this cleanly in C, and for
* accuracy's sake we want to keep the overhead on the CTC speaker (channel 2)
* output busy loop as low as possible. We avoid reading the CTC registers
* directly because of the awkward 8-bit access mechanism of the 82C54
* device.
*/
#define CALIBRATE_TIME (5 * 1000020/HZ)
unsigned long __init calibrate_tsc(void)
{
mach_prepare_counter();
{
unsigned long startlow, starthigh;
unsigned long endlow, endhigh;
unsigned long count;
rdtsc(startlow,starthigh);
mach_countup(&count);
rdtsc(endlow,endhigh);
/* Error: ECTCNEVERSET */
if (count <= 1)
goto bad_ctc;
/* 64-bit subtract - gcc just messes up with long longs */
__asm__("subl %2,%0\n\t"
"sbbl %3,%1"
:"=a" (endlow), "=d" (endhigh)
:"g" (startlow), "g" (starthigh),
"0" (endlow), "1" (endhigh));
/* Error: ECPUTOOFAST */
if (endhigh)
goto bad_ctc;
/* Error: ECPUTOOSLOW */
if (endlow <= CALIBRATE_TIME)
goto bad_ctc;
__asm__("divl %2"
:"=a" (endlow), "=d" (endhigh)
:"r" (endlow), "0" (0), "1" (CALIBRATE_TIME));
return endlow;
}
/*
* 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..
*/
bad_ctc:
return 0;
}
#ifdef CONFIG_HPET_TIMER
/* ------ Calibrate the TSC using HPET -------
* Return 2^32 * (1 / (TSC clocks per usec)) for getting the CPU freq.
* Second output is parameter 1 (when non NULL)
* Set 2^32 * (1 / (tsc per HPET clk)) for delay_hpet().
* calibrate_tsc() calibrates the processor TSC by comparing
* it to the HPET timer of known frequency.
* Too much 64-bit arithmetic here to do this cleanly in C
*/
#define CALIBRATE_CNT_HPET (5 * hpet_tick)
#define CALIBRATE_TIME_HPET (5 * KERNEL_TICK_USEC)
unsigned long __init calibrate_tsc_hpet(unsigned long *tsc_hpet_quotient_ptr)
{
unsigned long tsc_startlow, tsc_starthigh;
unsigned long tsc_endlow, tsc_endhigh;
unsigned long hpet_start, hpet_end;
unsigned long result, remain;
hpet_start = hpet_readl(HPET_COUNTER);
rdtsc(tsc_startlow, tsc_starthigh);
do {
hpet_end = hpet_readl(HPET_COUNTER);
} while ((hpet_end - hpet_start) < CALIBRATE_CNT_HPET);
rdtsc(tsc_endlow, tsc_endhigh);
/* 64-bit subtract - gcc just messes up with long longs */
__asm__("subl %2,%0\n\t"
"sbbl %3,%1"
:"=a" (tsc_endlow), "=d" (tsc_endhigh)
:"g" (tsc_startlow), "g" (tsc_starthigh),
"0" (tsc_endlow), "1" (tsc_endhigh));
/* Error: ECPUTOOFAST */
if (tsc_endhigh)
goto bad_calibration;
/* Error: ECPUTOOSLOW */
if (tsc_endlow <= CALIBRATE_TIME_HPET)
goto bad_calibration;
ASM_DIV64_REG(result, remain, tsc_endlow, 0, CALIBRATE_TIME_HPET);
if (remain > (tsc_endlow >> 1))
result++; /* rounding the result */
if (tsc_hpet_quotient_ptr) {
unsigned long tsc_hpet_quotient;
ASM_DIV64_REG(tsc_hpet_quotient, remain, tsc_endlow, 0,
CALIBRATE_CNT_HPET);
if (remain > (tsc_endlow >> 1))
tsc_hpet_quotient++; /* rounding the result */
*tsc_hpet_quotient_ptr = tsc_hpet_quotient;
}
return result;
bad_calibration:
/*
* the CPU was so fast/slow that the quotient wouldn't fit in
* 32 bits..
*/
return 0;
}
#endif
......@@ -19,7 +19,6 @@
#include <asm/fixmap.h>
extern spinlock_t i8253_lock;
extern unsigned long calibrate_tsc(void);
/* Number of usecs that the last interrupt was delayed */
static int delay_at_last_interrupt;
......
......@@ -131,63 +131,6 @@ void delay_hpet(unsigned long loops)
} while ((hpet_end - hpet_start) < (loops));
}
/* ------ Calibrate the TSC -------
* Return 2^32 * (1 / (TSC clocks per usec)) for getting the CPU freq.
* Set 2^32 * (1 / (tsc per HPET clk)) for delay_hpet().
* calibrate_tsc() calibrates the processor TSC by comparing
* it to the HPET timer of known frequency.
* Too much 64-bit arithmetic here to do this cleanly in C
*/
#define CALIBRATE_CNT_HPET (5 * hpet_tick)
#define CALIBRATE_TIME_HPET (5 * KERNEL_TICK_USEC)
static unsigned long __init calibrate_tsc(void)
{
unsigned long tsc_startlow, tsc_starthigh;
unsigned long tsc_endlow, tsc_endhigh;
unsigned long hpet_start, hpet_end;
unsigned long result, remain;
hpet_start = hpet_readl(HPET_COUNTER);
rdtsc(tsc_startlow, tsc_starthigh);
do {
hpet_end = hpet_readl(HPET_COUNTER);
} while ((hpet_end - hpet_start) < CALIBRATE_CNT_HPET);
rdtsc(tsc_endlow, tsc_endhigh);
/* 64-bit subtract - gcc just messes up with long longs */
__asm__("subl %2,%0\n\t"
"sbbl %3,%1"
:"=a" (tsc_endlow), "=d" (tsc_endhigh)
:"g" (tsc_startlow), "g" (tsc_starthigh),
"0" (tsc_endlow), "1" (tsc_endhigh));
/* Error: ECPUTOOFAST */
if (tsc_endhigh)
goto bad_calibration;
/* Error: ECPUTOOSLOW */
if (tsc_endlow <= CALIBRATE_TIME_HPET)
goto bad_calibration;
ASM_DIV64_REG(result, remain, tsc_endlow, 0, CALIBRATE_TIME_HPET);
if (remain > (tsc_endlow >> 1))
result++; /* rounding the result */
ASM_DIV64_REG(tsc_hpet_quotient, remain, tsc_endlow, 0,
CALIBRATE_CNT_HPET);
if (remain > (tsc_endlow >> 1))
tsc_hpet_quotient++; /* rounding the result */
return result;
bad_calibration:
/*
* the CPU was so fast/slow that the quotient wouldn't fit in
* 32 bits..
*/
return 0;
}
static int __init init_hpet(char* override)
{
unsigned long result, remain;
......@@ -201,7 +144,7 @@ static int __init init_hpet(char* override)
printk("Using HPET for gettimeofday\n");
if (cpu_has_tsc) {
unsigned long tsc_quotient = calibrate_tsc();
unsigned long tsc_quotient = calibrate_tsc_hpet(&tsc_hpet_quotient);
if (tsc_quotient) {
/* report CPU clock rate in Hz.
* The formula is (10^6 * 2^32) / (2^32 * 1 / (clocks/us)) =
......
......@@ -230,67 +230,6 @@ static void delay_tsc(unsigned long loops)
} while ((now-bclock) < loops);
}
/* ------ Calibrate the TSC -------
* Return 2^32 * (1 / (TSC clocks per usec)) for do_fast_gettimeoffset().
* Too much 64-bit arithmetic here to do this cleanly in C, and for
* accuracy's sake we want to keep the overhead on the CTC speaker (channel 2)
* output busy loop as low as possible. We avoid reading the CTC registers
* directly because of the awkward 8-bit access mechanism of the 82C54
* device.
*/
#define CALIBRATE_TIME (5 * 1000020/HZ)
static unsigned long __init calibrate_tsc(void)
{
mach_prepare_counter();
{
unsigned long startlow, starthigh;
unsigned long endlow, endhigh;
unsigned long count;
rdtsc(startlow,starthigh);
mach_countup(&count);
rdtsc(endlow,endhigh);
last_tsc_low = endlow;
/* Error: ECTCNEVERSET */
if (count <= 1)
goto bad_ctc;
/* 64-bit subtract - gcc just messes up with long longs */
__asm__("subl %2,%0\n\t"
"sbbl %3,%1"
:"=a" (endlow), "=d" (endhigh)
:"g" (startlow), "g" (starthigh),
"0" (endlow), "1" (endhigh));
/* Error: ECPUTOOFAST */
if (endhigh)
goto bad_ctc;
/* Error: ECPUTOOSLOW */
if (endlow <= CALIBRATE_TIME)
goto bad_ctc;
__asm__("divl %2"
:"=a" (endlow), "=d" (endhigh)
:"r" (endlow), "0" (0), "1" (CALIBRATE_TIME));
return endlow;
}
/*
* 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..
*/
bad_ctc:
return 0;
}
#ifdef CONFIG_HPET_TIMER
static void mark_offset_tsc_hpet(void)
{
......@@ -339,58 +278,6 @@ static void mark_offset_tsc_hpet(void)
ASM_MUL64_REG(temp, delay_at_last_interrupt,
hpet_usec_quotient, delay_at_last_interrupt);
}
/* ------ Calibrate the TSC based on HPET timer -------
* Return 2^32 * (1 / (TSC clocks per usec)) for do_fast_gettimeoffset().
* calibrate_tsc() calibrates the processor TSC by comparing
* it to the HPET timer of known frequency.
* Too much 64-bit arithmetic here to do this cleanly in C
*/
#define CALIBRATE_CNT_HPET (5 * hpet_tick)
#define CALIBRATE_TIME_HPET (5 * KERNEL_TICK_USEC)
unsigned long __init calibrate_tsc_hpet(void)
{
unsigned long tsc_startlow, tsc_starthigh;
unsigned long tsc_endlow, tsc_endhigh;
unsigned long hpet_start, hpet_end;
unsigned long result, remain;
hpet_start = hpet_readl(HPET_COUNTER);
rdtsc(tsc_startlow, tsc_starthigh);
do {
hpet_end = hpet_readl(HPET_COUNTER);
} while ((hpet_end - hpet_start) < CALIBRATE_CNT_HPET);
rdtsc(tsc_endlow, tsc_endhigh);
/* 64-bit subtract - gcc just messes up with long longs */
__asm__("subl %2,%0\n\t"
"sbbl %3,%1"
:"=a" (tsc_endlow), "=d" (tsc_endhigh)
:"g" (tsc_startlow), "g" (tsc_starthigh),
"0" (tsc_endlow), "1" (tsc_endhigh));
/* Error: ECPUTOOFAST */
if (tsc_endhigh)
goto bad_calibration;
/* Error: ECPUTOOSLOW */
if (tsc_endlow <= CALIBRATE_TIME_HPET)
goto bad_calibration;
ASM_DIV64_REG(result, remain, tsc_endlow, 0, CALIBRATE_TIME_HPET);
if (remain > (tsc_endlow >> 1))
result++; /* rounding the result */
return result;
bad_calibration:
/*
* the CPU was so fast/slow that the quotient wouldn't fit in
* 32 bits..
*/
return 0;
}
#endif
#ifdef CONFIG_CPU_FREQ
......@@ -491,7 +378,7 @@ static int __init init_tsc(char* override)
if (is_hpet_enabled()){
unsigned long result, remain;
printk("Using TSC for gettimeofday\n");
tsc_quotient = calibrate_tsc_hpet();
tsc_quotient = calibrate_tsc_hpet(NULL);
timer_tsc.mark_offset = &mark_offset_tsc_hpet;
/*
* Math to calculate hpet to usec multiplier
......
......@@ -38,8 +38,10 @@ extern struct timer_opts timer_tsc;
extern struct timer_opts timer_cyclone;
#endif
extern unsigned long calibrate_tsc(void);
#ifdef CONFIG_HPET_TIMER
extern struct timer_opts timer_hpet;
extern unsigned long calibrate_tsc_hpet(unsigned long *tsc_hpet_quotient_ptr);
#endif
#endif
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