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 @@ ...@@ -2,7 +2,7 @@
# Makefile for x86 timers # 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_X86_CYCLONE_TIMER) += timer_cyclone.o
obj-$(CONFIG_HPET_TIMER) += timer_hpet.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 @@ ...@@ -19,7 +19,6 @@
#include <asm/fixmap.h> #include <asm/fixmap.h>
extern spinlock_t i8253_lock; extern spinlock_t i8253_lock;
extern unsigned long calibrate_tsc(void);
/* Number of usecs that the last interrupt was delayed */ /* Number of usecs that the last interrupt was delayed */
static int delay_at_last_interrupt; static int delay_at_last_interrupt;
......
...@@ -131,63 +131,6 @@ void delay_hpet(unsigned long loops) ...@@ -131,63 +131,6 @@ void delay_hpet(unsigned long loops)
} while ((hpet_end - hpet_start) < (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) static int __init init_hpet(char* override)
{ {
unsigned long result, remain; unsigned long result, remain;
...@@ -201,7 +144,7 @@ static int __init init_hpet(char* override) ...@@ -201,7 +144,7 @@ static int __init init_hpet(char* override)
printk("Using HPET for gettimeofday\n"); printk("Using HPET for gettimeofday\n");
if (cpu_has_tsc) { if (cpu_has_tsc) {
unsigned long tsc_quotient = calibrate_tsc(); unsigned long tsc_quotient = calibrate_tsc_hpet(&tsc_hpet_quotient);
if (tsc_quotient) { if (tsc_quotient) {
/* report CPU clock rate in Hz. /* report CPU clock rate in Hz.
* The formula is (10^6 * 2^32) / (2^32 * 1 / (clocks/us)) = * The formula is (10^6 * 2^32) / (2^32 * 1 / (clocks/us)) =
......
...@@ -230,67 +230,6 @@ static void delay_tsc(unsigned long loops) ...@@ -230,67 +230,6 @@ static void delay_tsc(unsigned long loops)
} while ((now-bclock) < 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 #ifdef CONFIG_HPET_TIMER
static void mark_offset_tsc_hpet(void) static void mark_offset_tsc_hpet(void)
{ {
...@@ -339,58 +278,6 @@ 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, ASM_MUL64_REG(temp, delay_at_last_interrupt,
hpet_usec_quotient, 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 #endif
#ifdef CONFIG_CPU_FREQ #ifdef CONFIG_CPU_FREQ
...@@ -491,7 +378,7 @@ static int __init init_tsc(char* override) ...@@ -491,7 +378,7 @@ static int __init init_tsc(char* override)
if (is_hpet_enabled()){ if (is_hpet_enabled()){
unsigned long result, remain; unsigned long result, remain;
printk("Using TSC for gettimeofday\n"); 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; timer_tsc.mark_offset = &mark_offset_tsc_hpet;
/* /*
* Math to calculate hpet to usec multiplier * Math to calculate hpet to usec multiplier
......
...@@ -38,8 +38,10 @@ extern struct timer_opts timer_tsc; ...@@ -38,8 +38,10 @@ extern struct timer_opts timer_tsc;
extern struct timer_opts timer_cyclone; extern struct timer_opts timer_cyclone;
#endif #endif
extern unsigned long calibrate_tsc(void);
#ifdef CONFIG_HPET_TIMER #ifdef CONFIG_HPET_TIMER
extern struct timer_opts timer_hpet; extern struct timer_opts timer_hpet;
extern unsigned long calibrate_tsc_hpet(unsigned long *tsc_hpet_quotient_ptr);
#endif #endif
#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