* [BK PATCH] i386 timer changes for 2.5.41 @ 2002-10-10 18:26 Greg KH 2002-10-10 18:27 ` [PATCH] " Greg KH 2002-10-10 18:33 ` [BK PATCH] " Linus Torvalds 0 siblings, 2 replies; 8+ messages in thread From: Greg KH @ 2002-10-10 18:26 UTC (permalink / raw) To: torvalds; +Cc: linux-kernel, johnstul Hi Linus, I've taken the i386 timer.c patches that John Stultz has been working on for a while, made some minor tweaks, added them to a bk tree, and tested them on all the boxes that I have access too. Here's the resulting changesets: Please pull from bk://lsm.bkbits.net/timer-2.5 These split up the time.c code to handle different interrupt time sources, moving the code into a new arck/i386/kernel/timers directory. This is going to get more important as new timer sources become available (like IBM's Summit chipset), and removes a lot of #ifdefs from the existing code. The differences from John's last patches are: - use bk to show the history of the time.c file moves - added proper documentation for struct timer_opts - init() in timer_opts now returns 0 for success - timer array is now static, and NULL terminated to make adding new sources an easier patch. thanks, greg k-h arch/i386/Makefile | 2 arch/i386/kernel/time.c | 374 ----------------------------------- arch/i386/kernel/timers/Makefile | 10 arch/i386/kernel/timers/timer.c | 39 +++ arch/i386/kernel/timers/timer_pit.c | 132 ++++++++++++ arch/i386/kernel/timers/timer_tsc.c | 382 +++++++++++++++++++++++++++++++----- include/asm-i386/timer.h | 22 ++ 7 files changed, 538 insertions(+), 423 deletions(-) ----- ChangeSet@1.751, 2002-10-10 01:10:45-07:00, johnstul@us.ibm.com i386 timer core: intergrate the new timer code to use the two different timer files. arch/i386/Makefile | 2 arch/i386/kernel/time.c | 23 ++---- arch/i386/kernel/timers/Makefile | 10 +++ arch/i386/kernel/timers/timer.c | 8 +- arch/i386/kernel/timers/timer_pit.c | 35 +++++++++- arch/i386/kernel/timers/timer_tsc.c | 120 ++++++++++++++++++++---------------- include/asm-i386/timer.h | 2 7 files changed, 128 insertions(+), 72 deletions(-) ------ ChangeSet@1.750, 2002-10-10 00:08:03-07:00, johnstul@us.ibm.com i386 timer core: move code out of time.c into timers/timer_pit.c and timers/timer_tsc.c arch/i386/kernel/time.c | 351 ------------------------------------ arch/i386/kernel/timers/timer_pit.c | 97 +++++++++ arch/i386/kernel/timers/timer_tsc.c | 262 ++++++++++++++++++++++++++ 3 files changed, 359 insertions(+), 351 deletions(-) ------ ChangeSet@1.749, 2002-10-09 23:57:56-07:00, johnstul@us.ibm.com i386 timer core: introduce struct timer_ops provides the infrastructure needed via the timer_ops structure, as well as the select_timer() function for choosing the best available timer arch/i386/kernel/timers/timer.c | 31 +++++++++++++++++++++++++++++++ include/asm-i386/timer.h | 20 ++++++++++++++++++++ 2 files changed, 51 insertions(+) ------ ^ permalink raw reply [flat|nested] 8+ messages in thread
* [PATCH] i386 timer changes for 2.5.41 2002-10-10 18:26 [BK PATCH] i386 timer changes for 2.5.41 Greg KH @ 2002-10-10 18:27 ` Greg KH 2002-10-10 18:29 ` Greg KH 2002-10-10 18:33 ` [BK PATCH] " Linus Torvalds 1 sibling, 1 reply; 8+ messages in thread From: Greg KH @ 2002-10-10 18:27 UTC (permalink / raw) To: linux-kernel; +Cc: johnstul # This is a BitKeeper generated patch for the following project: # Project Name: Linux kernel tree # This patch format is intended for GNU patch command version 2.5 or higher. # This patch includes the following deltas: # ChangeSet 1.748 -> 1.749 # (new) -> 1.1 include/asm-i386/timer.h # (new) -> 1.1 arch/i386/kernel/timers/timer.c # # The following is the BitKeeper ChangeSet Log # -------------------------------------------- # 02/10/09 johnstul@us.ibm.com 1.749 # i386 timer core: introduce struct timer_ops # # provides the infrastructure needed via the timer_ops structure, # as well as the select_timer() function for choosing the best # available timer # -------------------------------------------- # diff -Nru a/arch/i386/kernel/timers/timer.c b/arch/i386/kernel/timers/timer.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/i386/kernel/timers/timer.c Thu Oct 10 11:21:16 2002 @@ -0,0 +1,31 @@ +#include <linux/kernel.h> +#include <asm/timer.h> + +/* list of externed timers */ +/* eg: extern struct timer_opts timer_XXX*/; + +/* list of timers, ordered by preference, NULL terminated */ +static struct timer_opts* timers[] = { + /* eg: &timer_XXX */ + NULL, +}; + + +/* iterates through the list of timers, returning the first + * one that initializes successfully. + */ +struct timer_opts* select_timer(void) +{ + int i = 0; + + /* find most preferred working timer */ + while (timers[i]) { + if (timers[i]->init) + if (timers[i]->init() == 0) + return timers[i]; + ++i; + } + + panic("select_timer: Cannot find a suitable timer\n"); + return NULL; +} diff -Nru a/include/asm-i386/timer.h b/include/asm-i386/timer.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/asm-i386/timer.h Thu Oct 10 11:21:16 2002 @@ -0,0 +1,20 @@ +#ifndef _ASMi386_TIMER_H +#define _ASMi386_TIMER_H + +/** + * struct timer_ops - used to define a timer source + * + * @init: Probes and initializes the timer. Returns 0 on success, anything + * else on failure. + * @mark_offset: called by the timer interrupt + * @get_offset: called by gettimeofday(). Returns the number of ms since the + * last timer intruupt. + */ +struct timer_opts{ + int (*init)(void); + void (*mark_offset)(void); + unsigned long (*get_offset)(void); +}; + +extern struct timer_opts* select_timer(void); +#endif ^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH] i386 timer changes for 2.5.41 2002-10-10 18:27 ` [PATCH] " Greg KH @ 2002-10-10 18:29 ` Greg KH 2002-10-10 18:30 ` Greg KH 0 siblings, 1 reply; 8+ messages in thread From: Greg KH @ 2002-10-10 18:29 UTC (permalink / raw) To: linux-kernel; +Cc: johnstul Note, this patch is messy. If you look at the bk changeset you will see that no new code is added, but time.c is copied to timer_pit.c and timer_tsc.c, and then code is taken away from those files. # This is a BitKeeper generated patch for the following project: # Project Name: Linux kernel tree # This patch format is intended for GNU patch command version 2.5 or higher. # This patch includes the following deltas: # ChangeSet 1.749 -> 1.750 # arch/i386/kernel/time.c 1.16 -> 1.17 # (new) -> 1.18 arch/i386/kernel/timers/timer_pit.c # (new) -> 1.18 arch/i386/kernel/timers/timer_tsc.c # # The following is the BitKeeper ChangeSet Log # -------------------------------------------- # 02/10/10 johnstul@us.ibm.com 1.750 # i386 timer core: move code out of time.c into timers/timer_pit.c and timers/timer_tsc.c # -------------------------------------------- # diff -Nru a/arch/i386/kernel/time.c b/arch/i386/kernel/time.c --- a/arch/i386/kernel/time.c Thu Oct 10 11:21:13 2002 +++ b/arch/i386/kernel/time.c Thu Oct 10 11:21:13 2002 @@ -73,51 +73,11 @@ unsigned long cpu_khz; /* Detected as we calibrate the TSC */ -/* Number of usecs that the last interrupt was delayed */ -static int delay_at_last_interrupt; - -static unsigned long last_tsc_low; /* lsb 32 bits of Time Stamp Counter */ - -/* Cached *multiplier* to convert TSC counts to microseconds. - * (see the equation below). - * Equal to 2^32 * (1 / (clocks per usec) ). - * Initialized in time_init. - */ -unsigned long fast_gettimeoffset_quotient; - extern rwlock_t xtime_lock; extern unsigned long wall_jiffies; spinlock_t rtc_lock = SPIN_LOCK_UNLOCKED; -static inline unsigned long do_fast_gettimeoffset(void) -{ - register unsigned long eax, edx; - - /* Read the Time Stamp Counter */ - - rdtsc(eax,edx); - - /* .. relative to previous jiffy (32 bits is enough) */ - eax -= last_tsc_low; /* tsc_low delta */ - - /* - * Time offset = (tsc_low delta) * fast_gettimeoffset_quotient - * = (tsc_low delta) * (usecs_per_clock) - * = (tsc_low delta) * (usecs_per_jiffy / clocks_per_jiffy) - * - * Using a mull instead of a divl saves up to 31 clock cycles - * in the critical path. - */ - - __asm__("mull %2" - :"=a" (eax), "=d" (edx) - :"rm" (fast_gettimeoffset_quotient), - "0" (eax)); - - /* our adjusted time offset in microseconds */ - return delay_at_last_interrupt + edx; -} #define TICK_SIZE (tick_nsec / 1000) @@ -125,104 +85,6 @@ EXPORT_SYMBOL(i8253_lock); #ifndef CONFIG_X86_TSC - -/* This function must be called with interrupts disabled - * It was inspired by Steve McCanne's microtime-i386 for BSD. -- jrs - * - * However, the pc-audio speaker driver changes the divisor so that - * it gets interrupted rather more often - it loads 64 into the - * counter rather than 11932! This has an adverse impact on - * do_gettimeoffset() -- it stops working! What is also not - * good is that the interval that our timer function gets called - * is no longer 10.0002 ms, but 9.9767 ms. To get around this - * would require using a different timing source. Maybe someone - * could use the RTC - I know that this can interrupt at frequencies - * ranging from 8192Hz to 2Hz. If I had the energy, I'd somehow fix - * it so that at startup, the timer code in sched.c would select - * using either the RTC or the 8253 timer. The decision would be - * based on whether there was any other device around that needed - * to trample on the 8253. I'd set up the RTC to interrupt at 1024 Hz, - * and then do some jiggery to have a version of do_timer that - * advanced the clock by 1/1024 s. Every time that reached over 1/100 - * of a second, then do all the old code. If the time was kept correct - * then do_gettimeoffset could just return 0 - there is no low order - * divider that can be accessed. - * - * Ideally, you would be able to use the RTC for the speaker driver, - * but it appears that the speaker driver really needs interrupt more - * often than every 120 us or so. - * - * Anyway, this needs more thought.... pjsg (1993-08-28) - * - * If you are really that interested, you should be reading - * comp.protocols.time.ntp! - */ - -static unsigned long do_slow_gettimeoffset(void) -{ - int count; - - static int count_p = LATCH; /* for the first call after boot */ - static unsigned long jiffies_p = 0; - - /* - * cache volatile jiffies temporarily; we have IRQs turned off. - */ - unsigned long jiffies_t; - - /* gets recalled with irq locally disabled */ - spin_lock(&i8253_lock); - /* timer count may underflow right here */ - outb_p(0x00, 0x43); /* latch the count ASAP */ - - count = inb_p(0x40); /* read the latched count */ - - /* - * We do this guaranteed double memory access instead of a _p - * postfix in the previous port access. Wheee, hackady hack - */ - jiffies_t = jiffies; - - count |= inb_p(0x40) << 8; - - /* VIA686a test code... reset the latch if count > max + 1 */ - if (count > LATCH) { - outb_p(0x34, 0x43); - outb_p(LATCH & 0xff, 0x40); - outb(LATCH >> 8, 0x40); - count = LATCH - 1; - } - - spin_unlock(&i8253_lock); - - /* - * avoiding timer inconsistencies (they are rare, but they happen)... - * there are two kinds of problems that must be avoided here: - * 1. the timer counter underflows - * 2. hardware problem with the timer, not giving us continuous time, - * the counter does small "jumps" upwards on some Pentium systems, - * (see c't 95/10 page 335 for Neptun bug.) - */ - - - if( jiffies_t == jiffies_p ) { - if( count > count_p ) { - /* the nutcase */ - count = do_timer_overflow(count); - } - } else - jiffies_p = jiffies_t; - - count_p = count; - - count = ((LATCH-1) - count) * TICK_SIZE; - count = (count + LATCH/2) / LATCH; - - return count; -} - -static unsigned long (*do_gettimeoffset)(void) = do_slow_gettimeoffset; - #else #define do_gettimeoffset() do_fast_gettimeoffset() @@ -433,34 +295,7 @@ */ write_lock(&xtime_lock); - if (use_tsc) - { - /* - * 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 */ - - rdtscl(last_tsc_low); - - spin_lock(&i8253_lock); - outb_p(0x00, 0x43); /* latch the count ASAP */ - - count = inb_p(0x40); /* read the latched count */ - count |= inb(0x40) << 8; - spin_unlock(&i8253_lock); - - count = ((LATCH-1) - count) * TICK_SIZE; - delay_at_last_interrupt = (count + LATCH/2) / LATCH; - } do_timer_interrupt(irq, NULL, regs); @@ -510,85 +345,6 @@ return mktime(year, mon, day, hour, min, sec); } -/* ------ 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_LATCH (5 * LATCH) -#define CALIBRATE_TIME (5 * 1000020/HZ) - -#ifdef CONFIG_X86_TSC -static unsigned long __init calibrate_tsc(void) -{ - /* Set the Gate high, disable speaker */ - outb((inb(0x61) & ~0x02) | 0x01, 0x61); - - /* - * Now let's take care of CTC channel 2 - * - * Set the Gate high, program CTC channel 2 for mode 0, - * (interrupt on terminal count mode), binary count, - * load 5 * LATCH count, (LSB and MSB) to begin countdown. - */ - outb(0xb0, 0x43); /* binary, mode 0, LSB/MSB, Ch 2 */ - outb(CALIBRATE_LATCH & 0xff, 0x42); /* LSB of count */ - outb(CALIBRATE_LATCH >> 8, 0x42); /* MSB of count */ - - { - unsigned long startlow, starthigh; - unsigned long endlow, endhigh; - unsigned long count; - - rdtsc(startlow,starthigh); - count = 0; - do { - count++; - } while ((inb(0x61) & 0x20) == 0); - 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; -} -#endif /* CONFIG_X86_TSC */ - static struct sys_device device_i8253 = { .name = "rtc", .id = 0, @@ -605,119 +361,12 @@ device_initcall(time_init_device); -#ifdef CONFIG_CPU_FREQ - -static int -time_cpufreq_notifier(struct notifier_block *nb, unsigned long val, - void *data) -{ - struct cpufreq_freqs *freq = data; - unsigned int i; - - if (!cpu_has_tsc) - return 0; - - switch (val) { - case CPUFREQ_PRECHANGE: - if ((freq->old < freq->new) && - ((freq->cpu == CPUFREQ_ALL_CPUS) || (freq->cpu == 0))) { - cpu_khz = cpufreq_scale(cpu_khz, freq->old, freq->new); - fast_gettimeoffset_quotient = cpufreq_scale(fast_gettimeoffset_quotient, freq->new, freq->old); - } - for (i=0; i<NR_CPUS; i++) - if ((freq->cpu == CPUFREQ_ALL_CPUS) || (freq->cpu == i)) - cpu_data[i].loops_per_jiffy = cpufreq_scale(cpu_data[i].loops_per_jiffy, freq->old, freq->new); - break; - - case CPUFREQ_POSTCHANGE: - if ((freq->new < freq->old) && - ((freq->cpu == CPUFREQ_ALL_CPUS) || (freq->cpu == 0))) { - cpu_khz = cpufreq_scale(cpu_khz, freq->old, freq->new); - fast_gettimeoffset_quotient = cpufreq_scale(fast_gettimeoffset_quotient, freq->new, freq->old); - } - for (i=0; i<NR_CPUS; i++) - if ((freq->cpu == CPUFREQ_ALL_CPUS) || (freq->cpu == i)) - cpu_data[i].loops_per_jiffy = cpufreq_scale(cpu_data[i].loops_per_jiffy, freq->old, freq->new); - break; - } - - return 0; -} - -static struct notifier_block time_cpufreq_notifier_block = { - notifier_call: time_cpufreq_notifier -}; -#endif - - void __init time_init(void) { -#ifdef CONFIG_X86_TSC - extern int x86_udelay_tsc; -#endif xtime.tv_sec = get_cmos_time(); xtime.tv_nsec = 0; -/* - * 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 - * 'real time' but nothing will break. - * The most frequent case is that the CPU is "woken" from a halt - * state by the timer interrupt itself, so we get 0 error. In the - * rare cases where a driver would "wake" the CPU and request a - * timestamp, the maximum error is < 1 jiffy. But timestamps are - * still perfectly ordered. - * 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 - * a potentially buggy TSC. At this point we haven't run - * the ident/bugs checks so we must run this hook as it - * may turn off the TSC flag. - * - * NOTE: this doesnt yet handle SMP 486 machines where only - * some CPU's have a TSC. Thats never worked and nobody has - * moaned if you have the only one in the world - you fix it! - */ - - dodgy_tsc(); - - if (cpu_has_tsc) { - unsigned long tsc_quotient = calibrate_tsc(); - if (tsc_quotient) { - fast_gettimeoffset_quotient = tsc_quotient; - use_tsc = 1; - /* - * We could be more selective here I suspect - * and just enable this for the next intel chips ? - */ - x86_udelay_tsc = 1; -#ifndef do_gettimeoffset - do_gettimeoffset = do_fast_gettimeoffset; -#endif - - /* report CPU clock rate in Hz. - * The formula is (10^6 * 2^32) / (2^32 * 1 / (clocks/us)) = - * clock/second. Our precision is about 100 ppm. - */ - { unsigned long eax=0, edx=1000; - __asm__("divl %2" - :"=a" (cpu_khz), "=d" (edx) - :"r" (tsc_quotient), - "0" (eax), "1" (edx)); - printk("Detected %lu.%03lu MHz processor.\n", cpu_khz / 1000, cpu_khz % 1000); - } -#ifdef CONFIG_CPU_FREQ - cpufreq_register_notifier(&time_cpufreq_notifier_block, CPUFREQ_TRANSITION_NOTIFIER); -#endif - } - } -#endif /* CONFIG_X86_TSC */ time_init_hook(); } diff -Nru a/arch/i386/kernel/timers/timer_pit.c b/arch/i386/kernel/timers/timer_pit.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/i386/kernel/timers/timer_pit.c Thu Oct 10 11:21:13 2002 @@ -0,0 +1,97 @@ + +/* This function must be called with interrupts disabled + * It was inspired by Steve McCanne's microtime-i386 for BSD. -- jrs + * + * However, the pc-audio speaker driver changes the divisor so that + * it gets interrupted rather more often - it loads 64 into the + * counter rather than 11932! This has an adverse impact on + * do_gettimeoffset() -- it stops working! What is also not + * good is that the interval that our timer function gets called + * is no longer 10.0002 ms, but 9.9767 ms. To get around this + * would require using a different timing source. Maybe someone + * could use the RTC - I know that this can interrupt at frequencies + * ranging from 8192Hz to 2Hz. If I had the energy, I'd somehow fix + * it so that at startup, the timer code in sched.c would select + * using either the RTC or the 8253 timer. The decision would be + * based on whether there was any other device around that needed + * to trample on the 8253. I'd set up the RTC to interrupt at 1024 Hz, + * and then do some jiggery to have a version of do_timer that + * advanced the clock by 1/1024 s. Every time that reached over 1/100 + * of a second, then do all the old code. If the time was kept correct + * then do_gettimeoffset could just return 0 - there is no low order + * divider that can be accessed. + * + * Ideally, you would be able to use the RTC for the speaker driver, + * but it appears that the speaker driver really needs interrupt more + * often than every 120 us or so. + * + * Anyway, this needs more thought.... pjsg (1993-08-28) + * + * If you are really that interested, you should be reading + * comp.protocols.time.ntp! + */ + +static unsigned long do_slow_gettimeoffset(void) +{ + int count; + + static int count_p = LATCH; /* for the first call after boot */ + static unsigned long jiffies_p = 0; + + /* + * cache volatile jiffies temporarily; we have IRQs turned off. + */ + unsigned long jiffies_t; + + /* gets recalled with irq locally disabled */ + spin_lock(&i8253_lock); + /* timer count may underflow right here */ + outb_p(0x00, 0x43); /* latch the count ASAP */ + + count = inb_p(0x40); /* read the latched count */ + + /* + * We do this guaranteed double memory access instead of a _p + * postfix in the previous port access. Wheee, hackady hack + */ + jiffies_t = jiffies; + + count |= inb_p(0x40) << 8; + + /* VIA686a test code... reset the latch if count > max + 1 */ + if (count > LATCH) { + outb_p(0x34, 0x43); + outb_p(LATCH & 0xff, 0x40); + outb(LATCH >> 8, 0x40); + count = LATCH - 1; + } + + spin_unlock(&i8253_lock); + + /* + * avoiding timer inconsistencies (they are rare, but they happen)... + * there are two kinds of problems that must be avoided here: + * 1. the timer counter underflows + * 2. hardware problem with the timer, not giving us continuous time, + * the counter does small "jumps" upwards on some Pentium systems, + * (see c't 95/10 page 335 for Neptun bug.) + */ + + + if( jiffies_t == jiffies_p ) { + if( count > count_p ) { + /* the nutcase */ + count = do_timer_overflow(count); + } + } else + jiffies_p = jiffies_t; + + count_p = count; + + count = ((LATCH-1) - count) * TICK_SIZE; + count = (count + LATCH/2) / LATCH; + + return count; +} + +static unsigned long (*do_gettimeoffset)(void) = do_slow_gettimeoffset; diff -Nru a/arch/i386/kernel/timers/timer_tsc.c b/arch/i386/kernel/timers/timer_tsc.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/i386/kernel/timers/timer_tsc.c Thu Oct 10 11:21:13 2002 @@ -0,0 +1,262 @@ +/* Number of usecs that the last interrupt was delayed */ +static int delay_at_last_interrupt; + +static unsigned long last_tsc_low; /* lsb 32 bits of Time Stamp Counter */ + +/* Cached *multiplier* to convert TSC counts to microseconds. + * (see the equation below). + * Equal to 2^32 * (1 / (clocks per usec) ). + * Initialized in time_init. + */ +unsigned long fast_gettimeoffset_quotient; + +static inline unsigned long do_fast_gettimeoffset(void) +{ + register unsigned long eax, edx; + + /* Read the Time Stamp Counter */ + + rdtsc(eax,edx); + + /* .. relative to previous jiffy (32 bits is enough) */ + eax -= last_tsc_low; /* tsc_low delta */ + + /* + * Time offset = (tsc_low delta) * fast_gettimeoffset_quotient + * = (tsc_low delta) * (usecs_per_clock) + * = (tsc_low delta) * (usecs_per_jiffy / clocks_per_jiffy) + * + * Using a mull instead of a divl saves up to 31 clock cycles + * in the critical path. + */ + + __asm__("mull %2" + :"=a" (eax), "=d" (edx) + :"rm" (fast_gettimeoffset_quotient), + "0" (eax)); + + /* our adjusted time offset in microseconds */ + return delay_at_last_interrupt + edx; +} + + + +if (use_tsc) + { + /* + * 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 */ + + rdtscl(last_tsc_low); + + spin_lock(&i8253_lock); + outb_p(0x00, 0x43); /* latch the count ASAP */ + + count = inb_p(0x40); /* read the latched count */ + count |= inb(0x40) << 8; + spin_unlock(&i8253_lock); + + count = ((LATCH-1) - count) * TICK_SIZE; + delay_at_last_interrupt = (count + LATCH/2) / LATCH; + } + + +/* ------ 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_LATCH (5 * LATCH) +#define CALIBRATE_TIME (5 * 1000020/HZ) + +#ifdef CONFIG_X86_TSC +static unsigned long __init calibrate_tsc(void) +{ + /* Set the Gate high, disable speaker */ + outb((inb(0x61) & ~0x02) | 0x01, 0x61); + + /* + * Now let's take care of CTC channel 2 + * + * Set the Gate high, program CTC channel 2 for mode 0, + * (interrupt on terminal count mode), binary count, + * load 5 * LATCH count, (LSB and MSB) to begin countdown. + */ + outb(0xb0, 0x43); /* binary, mode 0, LSB/MSB, Ch 2 */ + outb(CALIBRATE_LATCH & 0xff, 0x42); /* LSB of count */ + outb(CALIBRATE_LATCH >> 8, 0x42); /* MSB of count */ + + { + unsigned long startlow, starthigh; + unsigned long endlow, endhigh; + unsigned long count; + + rdtsc(startlow,starthigh); + count = 0; + do { + count++; + } while ((inb(0x61) & 0x20) == 0); + 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; +} +#endif /* CONFIG_X86_TSC */ + + +#ifdef CONFIG_CPU_FREQ + +static int +time_cpufreq_notifier(struct notifier_block *nb, unsigned long val, + void *data) +{ + struct cpufreq_freqs *freq = data; + unsigned int i; + + if (!cpu_has_tsc) + return 0; + + switch (val) { + case CPUFREQ_PRECHANGE: + if ((freq->old < freq->new) && + ((freq->cpu == CPUFREQ_ALL_CPUS) || (freq->cpu == 0))) { + cpu_khz = cpufreq_scale(cpu_khz, freq->old, freq->new); + fast_gettimeoffset_quotient = cpufreq_scale(fast_gettimeoffset_quotient, freq->new, freq->old); + } + for (i=0; i<NR_CPUS; i++) + if ((freq->cpu == CPUFREQ_ALL_CPUS) || (freq->cpu == i)) + cpu_data[i].loops_per_jiffy = cpufreq_scale(cpu_data[i].loops_per_jiffy, freq->old, freq->new); + break; + + case CPUFREQ_POSTCHANGE: + if ((freq->new < freq->old) && + ((freq->cpu == CPUFREQ_ALL_CPUS) || (freq->cpu == 0))) { + cpu_khz = cpufreq_scale(cpu_khz, freq->old, freq->new); + fast_gettimeoffset_quotient = cpufreq_scale(fast_gettimeoffset_quotient, freq->new, freq->old); + } + for (i=0; i<NR_CPUS; i++) + if ((freq->cpu == CPUFREQ_ALL_CPUS) || (freq->cpu == i)) + cpu_data[i].loops_per_jiffy = cpufreq_scale(cpu_data[i].loops_per_jiffy, freq->old, freq->new); + break; + } + + return 0; +} + +static struct notifier_block time_cpufreq_notifier_block = { + notifier_call: time_cpufreq_notifier +}; +#endif + + + +#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 + * 'real time' but nothing will break. + * The most frequent case is that the CPU is "woken" from a halt + * state by the timer interrupt itself, so we get 0 error. In the + * rare cases where a driver would "wake" the CPU and request a + * timestamp, the maximum error is < 1 jiffy. But timestamps are + * still perfectly ordered. + * 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 + * a potentially buggy TSC. At this point we haven't run + * the ident/bugs checks so we must run this hook as it + * may turn off the TSC flag. + * + * NOTE: this doesnt yet handle SMP 486 machines where only + * some CPU's have a TSC. Thats never worked and nobody has + * moaned if you have the only one in the world - you fix it! + */ + + dodgy_tsc(); + + if (cpu_has_tsc) { + unsigned long tsc_quotient = calibrate_tsc(); + if (tsc_quotient) { + fast_gettimeoffset_quotient = tsc_quotient; + use_tsc = 1; + /* + * We could be more selective here I suspect + * and just enable this for the next intel chips ? + */ + x86_udelay_tsc = 1; +#ifndef do_gettimeoffset + do_gettimeoffset = do_fast_gettimeoffset; +#endif + + /* report CPU clock rate in Hz. + * The formula is (10^6 * 2^32) / (2^32 * 1 / (clocks/us)) = + * clock/second. Our precision is about 100 ppm. + */ + { unsigned long eax=0, edx=1000; + __asm__("divl %2" + :"=a" (cpu_khz), "=d" (edx) + :"r" (tsc_quotient), + "0" (eax), "1" (edx)); + printk("Detected %lu.%03lu MHz processor.\n", cpu_khz / 1000, cpu_khz % 1000); + } +#ifdef CONFIG_CPU_FREQ + cpufreq_register_notifier(&time_cpufreq_notifier_block, CPUFREQ_TRANSITION_NOTIFIER); +#endif + } + } +#endif /* CONFIG_X86_TSC */ ^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH] i386 timer changes for 2.5.41 2002-10-10 18:29 ` Greg KH @ 2002-10-10 18:30 ` Greg KH 0 siblings, 0 replies; 8+ messages in thread From: Greg KH @ 2002-10-10 18:30 UTC (permalink / raw) To: linux-kernel; +Cc: johnstul # This is a BitKeeper generated patch for the following project: # Project Name: Linux kernel tree # This patch format is intended for GNU patch command version 2.5 or higher. # This patch includes the following deltas: # ChangeSet 1.750 -> 1.751 # include/asm-i386/timer.h 1.1 -> 1.2 # arch/i386/kernel/timers/timer.c 1.1 -> 1.2 # arch/i386/Makefile 1.19 -> 1.20 # arch/i386/kernel/time.c 1.17 -> 1.18 # arch/i386/kernel/timers/timer_pit.c 1.18 -> 1.19 # arch/i386/kernel/timers/timer_tsc.c 1.18 -> 1.19 # (new) -> 1.1 arch/i386/kernel/timers/Makefile # # The following is the BitKeeper ChangeSet Log # -------------------------------------------- # 02/10/10 johnstul@us.ibm.com 1.751 # i386 timer core: intergrate the new timer code to use the two different timer files. # -------------------------------------------- # diff -Nru a/arch/i386/Makefile b/arch/i386/Makefile --- a/arch/i386/Makefile Thu Oct 10 11:21:10 2002 +++ b/arch/i386/Makefile Thu Oct 10 11:21:10 2002 @@ -53,7 +53,7 @@ libs-y += arch/i386/lib/ 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_PCI) += arch/i386/pci/ diff -Nru a/arch/i386/kernel/time.c b/arch/i386/kernel/time.c --- a/arch/i386/kernel/time.c Thu Oct 10 11:21:10 2002 +++ b/arch/i386/kernel/time.c Thu Oct 10 11:21:10 2002 @@ -53,6 +53,7 @@ #include <asm/mpspec.h> #include <asm/uaccess.h> #include <asm/processor.h> +#include <asm/timer.h> #include <linux/mc146818rtc.h> #include <linux/timex.h> @@ -78,18 +79,10 @@ spinlock_t rtc_lock = SPIN_LOCK_UNLOCKED; - -#define TICK_SIZE (tick_nsec / 1000) - spinlock_t i8253_lock = SPIN_LOCK_UNLOCKED; EXPORT_SYMBOL(i8253_lock); -#ifndef CONFIG_X86_TSC -#else - -#define do_gettimeoffset() do_fast_gettimeoffset() - -#endif +struct timer_opts* timer; /* * This version of gettimeofday has microsecond resolution @@ -101,7 +94,7 @@ unsigned long usec, sec; read_lock_irqsave(&xtime_lock, flags); - usec = do_gettimeoffset(); + usec = timer->get_offset(); { unsigned long lost = jiffies - wall_jiffies; if (lost) @@ -129,7 +122,7 @@ * wall time. Discover what correction gettimeofday() would have * made, and then undo it! */ - tv->tv_usec -= do_gettimeoffset(); + tv->tv_usec -= timer->get_offset(); tv->tv_usec -= (jiffies - wall_jiffies) * (1000000 / HZ); while (tv->tv_usec < 0) { @@ -275,8 +268,6 @@ #endif } -static int use_tsc; - /* * This is the same as the above, except we _also_ save the current * Time Stamp Counter value at the time of the timer interrupt, so that @@ -284,8 +275,6 @@ */ void timer_interrupt(int irq, void *dev_id, struct pt_regs *regs) { - int count; - /* * Here we are in the timer irq handler. We just have irqs locally * disabled but we don't know if the timer_bh is running on the other @@ -295,7 +284,7 @@ */ write_lock(&xtime_lock); - + timer->mark_offset(); do_timer_interrupt(irq, NULL, regs); @@ -345,6 +334,7 @@ return mktime(year, mon, day, hour, min, sec); } +/* XXX this driverfs stuff should probably go elsewhere later -john */ static struct sys_device device_i8253 = { .name = "rtc", .id = 0, @@ -368,5 +358,6 @@ xtime.tv_nsec = 0; + timer = select_timer(); time_init_hook(); } diff -Nru a/arch/i386/kernel/timers/Makefile b/arch/i386/kernel/timers/Makefile --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/i386/kernel/timers/Makefile Thu Oct 10 11:21:10 2002 @@ -0,0 +1,10 @@ +# +# Makefile for x86 timers +# + +obj-y := timer.o + +obj-y += timer_tsc.o +obj-y += timer_pit.o + +include $(TOPDIR)/Rules.make diff -Nru a/arch/i386/kernel/timers/timer.c b/arch/i386/kernel/timers/timer.c --- a/arch/i386/kernel/timers/timer.c Thu Oct 10 11:21:10 2002 +++ b/arch/i386/kernel/timers/timer.c Thu Oct 10 11:21:10 2002 @@ -2,11 +2,15 @@ #include <asm/timer.h> /* list of externed timers */ -/* eg: extern struct timer_opts timer_XXX*/; +extern struct timer_opts timer_pit; +extern struct timer_opts timer_tsc; /* list of timers, ordered by preference, NULL terminated */ static struct timer_opts* timers[] = { - /* eg: &timer_XXX */ + &timer_tsc, +#ifndef CONFIG_X86_TSC + &timer_pit, +#endif NULL, }; diff -Nru a/arch/i386/kernel/timers/timer_pit.c b/arch/i386/kernel/timers/timer_pit.c --- a/arch/i386/kernel/timers/timer_pit.c Thu Oct 10 11:21:10 2002 +++ b/arch/i386/kernel/timers/timer_pit.c Thu Oct 10 11:21:10 2002 @@ -1,3 +1,28 @@ +/* + * 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 0; +} + +static void mark_offset_pit(void) +{ + /* nothing needed */ +} + /* This function must be called with interrupts disabled * It was inspired by Steve McCanne's microtime-i386 for BSD. -- jrs @@ -31,7 +56,7 @@ * comp.protocols.time.ntp! */ -static unsigned long do_slow_gettimeoffset(void) +static unsigned long get_offset_pit(void) { int count; @@ -94,4 +119,10 @@ 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, +}; diff -Nru a/arch/i386/kernel/timers/timer_tsc.c b/arch/i386/kernel/timers/timer_tsc.c --- a/arch/i386/kernel/timers/timer_tsc.c Thu Oct 10 11:21:10 2002 +++ b/arch/i386/kernel/timers/timer_tsc.c Thu Oct 10 11:21:10 2002 @@ -1,3 +1,20 @@ +/* + * 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 <linux/errno.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 */ static int delay_at_last_interrupt; @@ -10,7 +27,7 @@ */ 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; @@ -39,36 +56,35 @@ 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) - { - /* - * 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 - */ + /* + * 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); - outb_p(0x00, 0x43); /* latch the count ASAP */ + spin_lock(&i8253_lock); + outb_p(0x00, 0x43); /* latch the count ASAP */ - count = inb_p(0x40); /* read the latched count */ - count |= inb(0x40) << 8; - spin_unlock(&i8253_lock); + count = inb_p(0x40); /* read the latched count */ + count |= inb(0x40) << 8; + spin_unlock(&i8253_lock); - count = ((LATCH-1) - count) * TICK_SIZE; - delay_at_last_interrupt = (count + LATCH/2) / LATCH; - } + count = ((LATCH-1) - count) * TICK_SIZE; + delay_at_last_interrupt = (count + LATCH/2) / LATCH; +} /* ------ Calibrate the TSC ------- @@ -83,7 +99,6 @@ #define CALIBRATE_LATCH (5 * LATCH) #define CALIBRATE_TIME (5 * 1000020/HZ) -#ifdef CONFIG_X86_TSC static unsigned long __init calibrate_tsc(void) { /* Set the Gate high, disable speaker */ @@ -148,7 +163,6 @@ bad_ctc: return 0; } -#endif /* CONFIG_X86_TSC */ #ifdef CONFIG_CPU_FREQ @@ -196,26 +210,22 @@ #endif - -#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 - * 'real time' but nothing will break. - * The most frequent case is that the CPU is "woken" from a halt - * state by the timer interrupt itself, so we get 0 error. In the - * rare cases where a driver would "wake" the CPU and request a - * timestamp, the maximum error is < 1 jiffy. But timestamps are - * still perfectly ordered. - * 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 +static int init_tsc(void) +{ + /* + * 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 + * 'real time' but nothing will break. + * The most frequent case is that the CPU is "woken" from a halt + * state by the timer interrupt itself, so we get 0 error. In the + * rare cases where a driver would "wake" the CPU and request a + * timestamp, the maximum error is < 1 jiffy. But timestamps are + * still perfectly ordered. + * 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. + */ /* * Firstly we have to do a CPU check for chips with * a potentially buggy TSC. At this point we haven't run @@ -239,9 +249,6 @@ * and just enable this for the next intel chips ? */ x86_udelay_tsc = 1; -#ifndef do_gettimeoffset - do_gettimeoffset = do_fast_gettimeoffset; -#endif /* report CPU clock rate in Hz. * The formula is (10^6 * 2^32) / (2^32 * 1 / (clocks/us)) = @@ -257,6 +264,17 @@ #ifdef CONFIG_CPU_FREQ cpufreq_register_notifier(&time_cpufreq_notifier_block, CPUFREQ_TRANSITION_NOTIFIER); #endif + return 0; } } -#endif /* CONFIG_X86_TSC */ + return -ENODEV; +} + +/************************************************************/ + +/* tsc timer_opts struct */ +struct timer_opts timer_tsc = { + .init = init_tsc, + .mark_offset = mark_offset_tsc, + .get_offset = get_offset_tsc, +}; diff -Nru a/include/asm-i386/timer.h b/include/asm-i386/timer.h --- a/include/asm-i386/timer.h Thu Oct 10 11:21:10 2002 +++ b/include/asm-i386/timer.h Thu Oct 10 11:21:10 2002 @@ -16,5 +16,7 @@ unsigned long (*get_offset)(void); }; +#define TICK_SIZE (tick_nsec / 1000) + extern struct timer_opts* select_timer(void); #endif ^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [BK PATCH] i386 timer changes for 2.5.41 2002-10-10 18:26 [BK PATCH] i386 timer changes for 2.5.41 Greg KH 2002-10-10 18:27 ` [PATCH] " Greg KH @ 2002-10-10 18:33 ` Linus Torvalds 2002-10-10 18:36 ` Greg KH 2002-10-10 18:38 ` Linus Torvalds 1 sibling, 2 replies; 8+ messages in thread From: Linus Torvalds @ 2002-10-10 18:33 UTC (permalink / raw) To: Greg KH; +Cc: linux-kernel, johnstul On Thu, 10 Oct 2002, Greg KH wrote: > > I've taken the i386 timer.c patches that John Stultz has been working on > for a while Hmm, I was getting to them anyway, this would make it easier. Except for the fact that I get [torvalds@penguin linux]$ bk pull bk://lsm.bkbits.net/timer-2.5 ERROR-cannot cd to timer-2.5 (illegal, nonexistant, or not package root) when I try to check it out.. Linus ^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [BK PATCH] i386 timer changes for 2.5.41 2002-10-10 18:33 ` [BK PATCH] " Linus Torvalds @ 2002-10-10 18:36 ` Greg KH 2002-10-10 18:38 ` Linus Torvalds 1 sibling, 0 replies; 8+ messages in thread From: Greg KH @ 2002-10-10 18:36 UTC (permalink / raw) To: Linus Torvalds; +Cc: linux-kernel, johnstul On Thu, Oct 10, 2002 at 11:33:38AM -0700, Linus Torvalds wrote: > > On Thu, 10 Oct 2002, Greg KH wrote: > > > > I've taken the i386 timer.c patches that John Stultz has been working on > > for a while > > Hmm, I was getting to them anyway, this would make it easier. > > Except for the fact that I get > > [torvalds@penguin linux]$ bk pull bk://lsm.bkbits.net/timer-2.5 > ERROR-cannot cd to timer-2.5 (illegal, nonexistant, or not package root) > > when I try to check it out.. Argh, wrong repo, try this: bk://linuxusb.bkbits.net/timer-2.5 Sorry about that. greg k-h (drowning in different bk trees...) ^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [BK PATCH] i386 timer changes for 2.5.41 2002-10-10 18:33 ` [BK PATCH] " Linus Torvalds 2002-10-10 18:36 ` Greg KH @ 2002-10-10 18:38 ` Linus Torvalds 2002-10-10 19:27 ` [PATCH] minor " Greg KH 1 sibling, 1 reply; 8+ messages in thread From: Linus Torvalds @ 2002-10-10 18:38 UTC (permalink / raw) To: Greg KH; +Cc: linux-kernel, johnstul I ended up just importing it from the patches I had pending anyway. Including the Cyclone code, although I removed the Config.in entry to make sure nobody even tries to enable it until the rest of the summit code is there. Linus ^ permalink raw reply [flat|nested] 8+ messages in thread
* [PATCH] minor i386 timer changes for 2.5.41 2002-10-10 18:38 ` Linus Torvalds @ 2002-10-10 19:27 ` Greg KH 0 siblings, 0 replies; 8+ messages in thread From: Greg KH @ 2002-10-10 19:27 UTC (permalink / raw) To: Linus Torvalds; +Cc: linux-kernel, johnstul On Thu, Oct 10, 2002 at 11:38:29AM -0700, Linus Torvalds wrote: > > I ended up just importing it from the patches I had pending anyway. > Including the Cyclone code, although I removed the Config.in entry to make > sure nobody even tries to enable it until the rest of the summit code is > there. Thanks for doing this. Here's a patch against your latest bk tree, that contains the cleanups I did to John's patches. It does the following: - removes a few compiler warnings from time.c - uses C99 initializers - makes the timer list static - adds better documentation to the timer function structure - makes the timer init function return 0 on success - NULL terminates the list of timers to make further patches easier. I can put this in a bk tree for you to pull, but I thought a simple patch would be easier. thanks, greg k-h diff -Naur -X ../dontdiff timer2-2.5/arch/i386/kernel/time.c timer-2.5/arch/i386/kernel/time.c --- timer2-2.5/arch/i386/kernel/time.c Thu Oct 10 12:06:27 2002 +++ timer-2.5/arch/i386/kernel/time.c Thu Oct 10 08:59:22 2002 @@ -268,8 +268,6 @@ #endif } -static int use_tsc; - /* * This is the same as the above, except we _also_ save the current * Time Stamp Counter value at the time of the timer interrupt, so that @@ -277,8 +275,6 @@ */ void timer_interrupt(int irq, void *dev_id, struct pt_regs *regs) { - int count; - /* * Here we are in the timer irq handler. We just have irqs locally * disabled but we don't know if the timer_bh is running on the other diff -Naur -X ../dontdiff timer2-2.5/arch/i386/kernel/timers/timer.c timer-2.5/arch/i386/kernel/timers/timer.c --- timer2-2.5/arch/i386/kernel/timers/timer.c Thu Oct 10 12:06:28 2002 +++ timer-2.5/arch/i386/kernel/timers/timer.c Thu Oct 10 08:59:23 2002 @@ -2,31 +2,34 @@ #include <asm/timer.h> /* list of externed timers */ -#ifndef CONFIG_X86_TSC extern struct timer_opts timer_pit; -#endif extern struct timer_opts timer_tsc; -/* list of timers, ordered by preference */ -struct timer_opts* timers[] = { - &timer_tsc +/* list of timers, ordered by preference, NULL terminated */ +static struct timer_opts* timers[] = { + &timer_tsc, #ifndef CONFIG_X86_TSC - ,&timer_pit + &timer_pit, #endif + NULL, }; -#define NR_TIMERS (sizeof(timers)/sizeof(timers[0])) /* iterates through the list of timers, returning the first * one that initializes successfully. */ struct timer_opts* select_timer(void) { - int i; + int i = 0; + /* find most preferred working timer */ - for(i=0; i < NR_TIMERS; i++) - if(timers[i]->init()) - return timers[i]; + while (timers[i]) { + if (timers[i]->init) + if (timers[i]->init() == 0) + return timers[i]; + ++i; + } + panic("select_timer: Cannot find a suitable timer\n"); - return 0; + return NULL; } diff -Naur -X ../dontdiff timer2-2.5/arch/i386/kernel/timers/timer_pit.c timer-2.5/arch/i386/kernel/timers/timer_pit.c --- timer2-2.5/arch/i386/kernel/timers/timer_pit.c Thu Oct 10 12:06:28 2002 +++ timer-2.5/arch/i386/kernel/timers/timer_pit.c Thu Oct 10 08:59:23 2002 @@ -15,7 +15,7 @@ static int init_pit(void) { - return 1; + return 0; } static void mark_offset_pit(void) @@ -122,7 +122,7 @@ /* tsc timer_opts struct */ struct timer_opts timer_pit = { - init: init_pit, - mark_offset: mark_offset_pit, - get_offset: get_offset_pit + .init = init_pit, + .mark_offset = mark_offset_pit, + .get_offset = get_offset_pit, }; diff -Naur -X ../dontdiff timer2-2.5/arch/i386/kernel/timers/timer_tsc.c timer-2.5/arch/i386/kernel/timers/timer_tsc.c --- timer2-2.5/arch/i386/kernel/timers/timer_tsc.c Thu Oct 10 12:06:28 2002 +++ timer-2.5/arch/i386/kernel/timers/timer_tsc.c Thu Oct 10 08:59:23 2002 @@ -6,6 +6,7 @@ #include <linux/spinlock.h> #include <linux/init.h> #include <linux/timex.h> +#include <linux/errno.h> #include <asm/timer.h> #include <asm/io.h> @@ -263,17 +264,17 @@ #ifdef CONFIG_CPU_FREQ cpufreq_register_notifier(&time_cpufreq_notifier_block, CPUFREQ_TRANSITION_NOTIFIER); #endif - return 1; + return 0; } } - return 0; + return -ENODEV; } /************************************************************/ /* tsc timer_opts struct */ struct timer_opts timer_tsc = { - init: init_tsc, - mark_offset: mark_offset_tsc, - get_offset: get_offset_tsc + .init = init_tsc, + .mark_offset = mark_offset_tsc, + .get_offset = get_offset_tsc, }; diff -Naur -X ../dontdiff timer2-2.5/include/asm-i386/timer.h timer-2.5/include/asm-i386/timer.h --- timer2-2.5/include/asm-i386/timer.h Thu Oct 10 12:07:43 2002 +++ timer-2.5/include/asm-i386/timer.h Thu Oct 10 09:00:17 2002 @@ -1,14 +1,22 @@ #ifndef _ASMi386_TIMER_H #define _ASMi386_TIMER_H +/** + * struct timer_ops - used to define a timer source + * + * @init: Probes and initializes the timer. Returns 0 on success, anything + * else on failure. + * @mark_offset: called by the timer interrupt + * @get_offset: called by gettimeofday(). Returns the number of ms since the + * last timer intruupt. + */ struct timer_opts{ - /* probes and initializes timer. returns 1 on sucess, 0 on failure */ int (*init)(void); - /* called by the timer interrupt */ void (*mark_offset)(void); - /* called by gettimeofday. returns # ms since the last timer interrupt */ unsigned long (*get_offset)(void); }; + #define TICK_SIZE (tick_nsec / 1000) -struct timer_opts* select_timer(void); + +extern struct timer_opts* select_timer(void); #endif ^ permalink raw reply [flat|nested] 8+ messages in thread
end of thread, other threads:[~2002-10-10 19:26 UTC | newest] Thread overview: 8+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2002-10-10 18:26 [BK PATCH] i386 timer changes for 2.5.41 Greg KH 2002-10-10 18:27 ` [PATCH] " Greg KH 2002-10-10 18:29 ` Greg KH 2002-10-10 18:30 ` Greg KH 2002-10-10 18:33 ` [BK PATCH] " Linus Torvalds 2002-10-10 18:36 ` Greg KH 2002-10-10 18:38 ` Linus Torvalds 2002-10-10 19:27 ` [PATCH] minor " Greg KH
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox