Commit f59a48cf authored by John Stultz's avatar John Stultz Committed by David S. Miller

[PATCH] linux-2.5.41_timer-changes_A4 (3/3 - integration)

        This is the final part 3 of 3 of my timer-change patch. Part 3
integrates the moved code (from part 2) into the new infrastructure
(from part 1).
parent e8498001
...@@ -53,7 +53,7 @@ HEAD := arch/i386/kernel/head.o arch/i386/kernel/init_task.o ...@@ -53,7 +53,7 @@ HEAD := arch/i386/kernel/head.o arch/i386/kernel/init_task.o
libs-y += arch/i386/lib/ libs-y += arch/i386/lib/
core-y += arch/i386/kernel/ arch/i386/mm/ \ core-y += arch/i386/kernel/ arch/i386/mm/ \
arch/i386/$(MACHINE)/ arch/i386/$(MACHINE)/ arch/i386/kernel/timers/
drivers-$(CONFIG_MATH_EMULATION) += arch/i386/math-emu/ drivers-$(CONFIG_MATH_EMULATION) += arch/i386/math-emu/
drivers-$(CONFIG_PCI) += arch/i386/pci/ drivers-$(CONFIG_PCI) += arch/i386/pci/
......
...@@ -53,6 +53,7 @@ ...@@ -53,6 +53,7 @@
#include <asm/mpspec.h> #include <asm/mpspec.h>
#include <asm/uaccess.h> #include <asm/uaccess.h>
#include <asm/processor.h> #include <asm/processor.h>
#include <asm/timer.h>
#include <linux/mc146818rtc.h> #include <linux/mc146818rtc.h>
#include <linux/timex.h> #include <linux/timex.h>
...@@ -78,18 +79,10 @@ extern unsigned long wall_jiffies; ...@@ -78,18 +79,10 @@ extern unsigned long wall_jiffies;
spinlock_t rtc_lock = SPIN_LOCK_UNLOCKED; spinlock_t rtc_lock = SPIN_LOCK_UNLOCKED;
#define TICK_SIZE (tick_nsec / 1000)
spinlock_t i8253_lock = SPIN_LOCK_UNLOCKED; spinlock_t i8253_lock = SPIN_LOCK_UNLOCKED;
EXPORT_SYMBOL(i8253_lock); EXPORT_SYMBOL(i8253_lock);
#ifndef CONFIG_X86_TSC struct timer_opts* timer;
#else
#define do_gettimeoffset() do_fast_gettimeoffset()
#endif
/* /*
* This version of gettimeofday has microsecond resolution * This version of gettimeofday has microsecond resolution
...@@ -101,7 +94,7 @@ void do_gettimeofday(struct timeval *tv) ...@@ -101,7 +94,7 @@ void do_gettimeofday(struct timeval *tv)
unsigned long usec, sec; unsigned long usec, sec;
read_lock_irqsave(&xtime_lock, flags); read_lock_irqsave(&xtime_lock, flags);
usec = do_gettimeoffset(); usec = timer->get_offset();
{ {
unsigned long lost = jiffies - wall_jiffies; unsigned long lost = jiffies - wall_jiffies;
if (lost) if (lost)
...@@ -129,7 +122,7 @@ void do_settimeofday(struct timeval *tv) ...@@ -129,7 +122,7 @@ void do_settimeofday(struct timeval *tv)
* wall time. Discover what correction gettimeofday() would have * wall time. Discover what correction gettimeofday() would have
* made, and then undo it! * made, and then undo it!
*/ */
tv->tv_usec -= do_gettimeoffset(); tv->tv_usec -= timer->get_offset();
tv->tv_usec -= (jiffies - wall_jiffies) * (1000000 / HZ); tv->tv_usec -= (jiffies - wall_jiffies) * (1000000 / HZ);
while (tv->tv_usec < 0) { while (tv->tv_usec < 0) {
...@@ -295,7 +288,7 @@ void timer_interrupt(int irq, void *dev_id, struct pt_regs *regs) ...@@ -295,7 +288,7 @@ void timer_interrupt(int irq, void *dev_id, struct pt_regs *regs)
*/ */
write_lock(&xtime_lock); write_lock(&xtime_lock);
timer->mark_offset();
do_timer_interrupt(irq, NULL, regs); do_timer_interrupt(irq, NULL, regs);
...@@ -345,6 +338,7 @@ unsigned long get_cmos_time(void) ...@@ -345,6 +338,7 @@ unsigned long get_cmos_time(void)
return mktime(year, mon, day, hour, min, sec); return mktime(year, mon, day, hour, min, sec);
} }
/* XXX this driverfs stuff should probably go elsewhere later -john */
static struct sys_device device_i8253 = { static struct sys_device device_i8253 = {
.name = "rtc", .name = "rtc",
.id = 0, .id = 0,
...@@ -368,5 +362,6 @@ void __init time_init(void) ...@@ -368,5 +362,6 @@ void __init time_init(void)
xtime.tv_nsec = 0; xtime.tv_nsec = 0;
timer = select_timer();
time_init_hook(); time_init_hook();
} }
#
# Makefile for x86 timers
#
obj-y := timer.o
obj-y += timer_tsc.o
obj-y += timer_pit.o
include $(TOPDIR)/Rules.make
...@@ -2,11 +2,17 @@ ...@@ -2,11 +2,17 @@
#include <asm/timer.h> #include <asm/timer.h>
/* list of externed timers */ /* list of externed timers */
/* eg: extern struct timer_opts timer_XXX*/; #ifndef CONFIG_X86_TSC
extern struct timer_opts timer_pit;
#endif
extern struct timer_opts timer_tsc;
/* list of timers, ordered by preference */ /* list of timers, ordered by preference */
struct timer_opts* timers[] = { struct timer_opts* timers[] = {
/* eg: &timer_XXX */ &timer_tsc
#ifndef CONFIG_X86_TSC
,&timer_pit
#endif
}; };
#define NR_TIMERS (sizeof(timers)/sizeof(timers[0])) #define NR_TIMERS (sizeof(timers)/sizeof(timers[0]))
......
/*
* This code largely moved from arch/i386/kernel/time.c.
* See comments there for proper credits.
*/
#include <linux/spinlock.h>
#include <linux/module.h>
#include <linux/device.h>
#include <asm/timer.h>
#include <asm/io.h>
extern spinlock_t i8259A_lock;
extern spinlock_t i8253_lock;
#include "do_timer.h"
static int init_pit(void)
{
return 1;
}
static void mark_offset_pit(void)
{
/* nothing needed */
}
/* This function must be called with interrupts disabled /* This function must be called with interrupts disabled
* It was inspired by Steve McCanne's microtime-i386 for BSD. -- jrs * It was inspired by Steve McCanne's microtime-i386 for BSD. -- jrs
...@@ -31,7 +56,7 @@ ...@@ -31,7 +56,7 @@
* comp.protocols.time.ntp! * comp.protocols.time.ntp!
*/ */
static unsigned long do_slow_gettimeoffset(void) static unsigned long get_offset_pit(void)
{ {
int count; int count;
...@@ -94,4 +119,10 @@ static unsigned long do_slow_gettimeoffset(void) ...@@ -94,4 +119,10 @@ static unsigned long do_slow_gettimeoffset(void)
return count; return count;
} }
static unsigned long (*do_gettimeoffset)(void) = do_slow_gettimeoffset;
/* tsc timer_opts struct */
struct timer_opts timer_pit = {
init: init_pit,
mark_offset: mark_offset_pit,
get_offset: get_offset_pit
};
/*
* This code largely moved from arch/i386/kernel/time.c.
* See comments there for proper credits.
*/
#include <linux/spinlock.h>
#include <linux/init.h>
#include <linux/timex.h>
#include <asm/timer.h>
#include <asm/io.h>
extern int x86_udelay_tsc;
extern spinlock_t i8253_lock;
static int use_tsc;
/* 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;
...@@ -10,7 +26,7 @@ static unsigned long last_tsc_low; /* lsb 32 bits of Time Stamp Counter */ ...@@ -10,7 +26,7 @@ static unsigned long last_tsc_low; /* lsb 32 bits of Time Stamp Counter */
*/ */
unsigned long fast_gettimeoffset_quotient; unsigned long fast_gettimeoffset_quotient;
static inline unsigned long do_fast_gettimeoffset(void) static unsigned long get_offset_tsc(void)
{ {
register unsigned long eax, edx; register unsigned long eax, edx;
...@@ -39,36 +55,35 @@ static inline unsigned long do_fast_gettimeoffset(void) ...@@ -39,36 +55,35 @@ static inline unsigned long do_fast_gettimeoffset(void)
return delay_at_last_interrupt + edx; return delay_at_last_interrupt + edx;
} }
static void mark_offset_tsc(void)
{
int count;
/*
* It is important that these two operations happen almost at
* the same time. We do the RDTSC stuff first, since it's
* faster. To avoid any inconsistencies, we need interrupts
* disabled locally.
*/
/*
if (use_tsc) * Interrupts are just disabled locally since the timer irq
{ * has the SA_INTERRUPT flag set. -arca
/* */
* It is important that these two operations happen almost at
* the same time. We do the RDTSC stuff first, since it's
* faster. To avoid any inconsistencies, we need interrupts
* disabled locally.
*/
/*
* Interrupts are just disabled locally since the timer irq
* has the SA_INTERRUPT flag set. -arca
*/
/* read Pentium cycle counter */ /* read Pentium cycle counter */
rdtscl(last_tsc_low); rdtscl(last_tsc_low);
spin_lock(&i8253_lock); spin_lock(&i8253_lock);
outb_p(0x00, 0x43); /* latch the count ASAP */ outb_p(0x00, 0x43); /* latch the count ASAP */
count = inb_p(0x40); /* read the latched count */ count = inb_p(0x40); /* read the latched count */
count |= inb(0x40) << 8; count |= inb(0x40) << 8;
spin_unlock(&i8253_lock); spin_unlock(&i8253_lock);
count = ((LATCH-1) - count) * TICK_SIZE; count = ((LATCH-1) - count) * TICK_SIZE;
delay_at_last_interrupt = (count + LATCH/2) / LATCH; delay_at_last_interrupt = (count + LATCH/2) / LATCH;
} }
/* ------ Calibrate the TSC ------- /* ------ Calibrate the TSC -------
...@@ -83,7 +98,6 @@ if (use_tsc) ...@@ -83,7 +98,6 @@ if (use_tsc)
#define CALIBRATE_LATCH (5 * LATCH) #define CALIBRATE_LATCH (5 * LATCH)
#define CALIBRATE_TIME (5 * 1000020/HZ) #define CALIBRATE_TIME (5 * 1000020/HZ)
#ifdef CONFIG_X86_TSC
static unsigned long __init calibrate_tsc(void) static unsigned long __init calibrate_tsc(void)
{ {
/* Set the Gate high, disable speaker */ /* Set the Gate high, disable speaker */
...@@ -148,7 +162,6 @@ static unsigned long __init calibrate_tsc(void) ...@@ -148,7 +162,6 @@ static unsigned long __init calibrate_tsc(void)
bad_ctc: bad_ctc:
return 0; return 0;
} }
#endif /* CONFIG_X86_TSC */
#ifdef CONFIG_CPU_FREQ #ifdef CONFIG_CPU_FREQ
...@@ -196,26 +209,22 @@ static struct notifier_block time_cpufreq_notifier_block = { ...@@ -196,26 +209,22 @@ static struct notifier_block time_cpufreq_notifier_block = {
#endif #endif
static int init_tsc(void)
#ifdef CONFIG_X86_TSC {
extern int x86_udelay_tsc; /*
#endif * If we have APM enabled or the CPU clock speed is variable
* (CPU stops clock on HLT or slows clock to save power)
/* * then the TSC timestamps may diverge by up to 1 jiffy from
* If we have APM enabled or the CPU clock speed is variable * 'real time' but nothing will break.
* (CPU stops clock on HLT or slows clock to save power) * The most frequent case is that the CPU is "woken" from a halt
* then the TSC timestamps may diverge by up to 1 jiffy from * state by the timer interrupt itself, so we get 0 error. In the
* 'real time' but nothing will break. * rare cases where a driver would "wake" the CPU and request a
* The most frequent case is that the CPU is "woken" from a halt * timestamp, the maximum error is < 1 jiffy. But timestamps are
* state by the timer interrupt itself, so we get 0 error. In the * still perfectly ordered.
* rare cases where a driver would "wake" the CPU and request a * Note that the TSC counter will be reset if APM suspends
* timestamp, the maximum error is < 1 jiffy. But timestamps are * to disk; this won't break the kernel, though, 'cuz we're
* still perfectly ordered. * smart. See arch/i386/kernel/apm.c.
* Note that the TSC counter will be reset if APM suspends */
* to disk; this won't break the kernel, though, 'cuz we're
* smart. See arch/i386/kernel/apm.c.
*/
#ifdef CONFIG_X86_TSC
/* /*
* Firstly we have to do a CPU check for chips with * Firstly we have to do a CPU check for chips with
* a potentially buggy TSC. At this point we haven't run * a potentially buggy TSC. At this point we haven't run
...@@ -239,9 +248,6 @@ static struct notifier_block time_cpufreq_notifier_block = { ...@@ -239,9 +248,6 @@ static struct notifier_block time_cpufreq_notifier_block = {
* and just enable this for the next intel chips ? * and just enable this for the next intel chips ?
*/ */
x86_udelay_tsc = 1; x86_udelay_tsc = 1;
#ifndef do_gettimeoffset
do_gettimeoffset = do_fast_gettimeoffset;
#endif
/* 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)) =
...@@ -257,6 +263,17 @@ static struct notifier_block time_cpufreq_notifier_block = { ...@@ -257,6 +263,17 @@ static struct notifier_block time_cpufreq_notifier_block = {
#ifdef CONFIG_CPU_FREQ #ifdef CONFIG_CPU_FREQ
cpufreq_register_notifier(&time_cpufreq_notifier_block, CPUFREQ_TRANSITION_NOTIFIER); cpufreq_register_notifier(&time_cpufreq_notifier_block, CPUFREQ_TRANSITION_NOTIFIER);
#endif #endif
return 1;
} }
} }
#endif /* CONFIG_X86_TSC */ return 0;
}
/************************************************************/
/* tsc timer_opts struct */
struct timer_opts timer_tsc = {
init: init_tsc,
mark_offset: mark_offset_tsc,
get_offset: get_offset_tsc
};
...@@ -9,6 +9,6 @@ struct timer_opts{ ...@@ -9,6 +9,6 @@ struct timer_opts{
/* called by gettimeofday. returns # ms since the last timer interrupt */ /* called by gettimeofday. returns # ms since the last timer interrupt */
unsigned long (*get_offset)(void); unsigned long (*get_offset)(void);
}; };
#define TICK_SIZE (tick_nsec / 1000)
struct timer_opts* select_timer(void); struct timer_opts* select_timer(void);
#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