diff options
| author | Christoph Lameter <clameter@sgi.com> | 2004-10-31 22:30:03 -0800 |
|---|---|---|
| committer | Linus Torvalds <torvalds@ppc970.osdl.org> | 2004-10-31 22:30:03 -0800 |
| commit | 6f0c33591ddb035677b2003097deb2a06b4f9708 (patch) | |
| tree | f3e1dd22429f40618036a735131cb9bedf397eeb | |
| parent | 6f1780569c67765e1fef8754fc1812714b85e4d0 (diff) | |
[PATCH] fix IBM cyclone clock and some cleanup
- fix broken IBM cyclone time interpolator support
- add support for cyclic timers through an addition of a mask
in the timer interpolator structure
- Allow time_interpolator_update() and time_interpolator_get_offset()
to be invoked without an active time interpolator
(necessary since the cyclone clock is initialized late in ACPI
processing)
- remove obsolete function time_interpolator_resolution()
- add a mask to all struct time_interpolator setups in the
kernel
- Make time interpolators work on 32bit platforms
Signed-off-by: Christoph Lameter <clameter@sgi.com>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
| -rw-r--r-- | Documentation/time_interpolators.txt | 23 | ||||
| -rw-r--r-- | arch/ia64/kernel/asm-offsets.c | 1 | ||||
| -rw-r--r-- | arch/ia64/kernel/cyclone.c | 5 | ||||
| -rw-r--r-- | arch/ia64/kernel/fsys.S | 8 | ||||
| -rw-r--r-- | arch/ia64/kernel/time.c | 9 | ||||
| -rw-r--r-- | arch/ia64/sn/kernel/sn2/timer.c | 12 | ||||
| -rw-r--r-- | arch/sparc64/kernel/time.c | 1 | ||||
| -rw-r--r-- | drivers/char/hpet.c | 3 | ||||
| -rw-r--r-- | include/linux/timex.h | 21 | ||||
| -rw-r--r-- | kernel/timer.c | 46 |
10 files changed, 74 insertions, 55 deletions
diff --git a/Documentation/time_interpolators.txt b/Documentation/time_interpolators.txt index d9fefd5f4bd0..e3b60854fbc2 100644 --- a/Documentation/time_interpolators.txt +++ b/Documentation/time_interpolators.txt @@ -9,32 +9,33 @@ The architecture specific code typically provides gettimeofday and settimeofday under Linux. The time interpolator provides both if an arch defines CONFIG_TIME_INTERPOLATION. The arch still must set up timer tick operations and call the necessary functions to advance the clock. + With the time interpolator a standardized interface exists for time -interpolation between ticks which also allows the determination -of time in a hardware independent way. The provided logic is highly scalable +interpolation between ticks. The provided logic is highly scalable and has been tested in SMP situations of up to 512 CPUs. If CONFIG_TIME_INTERPOLATION is defined then the architecture specific code -(or the device drivers - like HPET) must register time interpolators. +(or the device drivers - like HPET) may register time interpolators. These are typically defined in the following way: -static struct time_interpolator my_interpolator; +static struct time_interpolator my_interpolator { + .frequency = MY_FREQUENCY, + .source = TIME_SOURCE_MMIO32, + .shift = 8, /* scaling for higher accuracy */ + .drift = -1, /* Unknown drift */ + .jitter = 0 /* time source is stable */ +}; void time_init(void) { .... /* Initialization of the timer *. - my_interpolator.frequency = MY_FREQUENCY; - my_interpolator.source = TIME_SOURCE_MMIO32; my_interpolator.address = &my_timer; - my_interpolator.shift = 32; /* increase accuracy of scaling */ - my_interpolator.drift = -1; /* Unknown */ - my_interpolator.jitter = 0; /* A stable time source */ register_time_interpolator(&my_interpolator); .... } -For more details see include/linux/timex.h. +For more details see include/linux/timex.h and kernel/timer.c. -Christoph Lameter <christoph@lameter.com>, September 8, 2004 +Christoph Lameter <christoph@lameter.com>, October 31, 2004 diff --git a/arch/ia64/kernel/asm-offsets.c b/arch/ia64/kernel/asm-offsets.c index b1ab994b26ac..e0a50bf62913 100644 --- a/arch/ia64/kernel/asm-offsets.c +++ b/arch/ia64/kernel/asm-offsets.c @@ -217,6 +217,7 @@ void foo(void) DEFINE(IA64_TIME_INTERPOLATOR_LAST_CYCLE_OFFSET, offsetof (struct time_interpolator, last_cycle)); DEFINE(IA64_TIME_INTERPOLATOR_LAST_COUNTER_OFFSET, offsetof (struct time_interpolator, last_counter)); DEFINE(IA64_TIME_INTERPOLATOR_JITTER_OFFSET, offsetof (struct time_interpolator, jitter)); + DEFINE(IA64_TIME_INTERPOLATOR_MASK_OFFSET, offsetof (struct time_interpolator, mask)); DEFINE(IA64_TIME_SOURCE_CPU, TIME_SOURCE_CPU); DEFINE(IA64_TIME_SOURCE_MMIO64, TIME_SOURCE_MMIO64); DEFINE(IA64_TIME_SOURCE_MMIO32, TIME_SOURCE_MMIO32); diff --git a/arch/ia64/kernel/cyclone.c b/arch/ia64/kernel/cyclone.c index f8261d3a9145..768c7e46957c 100644 --- a/arch/ia64/kernel/cyclone.c +++ b/arch/ia64/kernel/cyclone.c @@ -19,10 +19,11 @@ void __init cyclone_setup(void) struct time_interpolator cyclone_interpolator = { - .source = TIME_SOURCE_MMIO32, - .shift = 32, + .source = TIME_SOURCE_MMIO64, + .shift = 16, .frequency = CYCLONE_TIMER_FREQ, .drift = -100, + .mask = (1LL << 40) - 1 }; int __init init_cyclone_clock(void) diff --git a/arch/ia64/kernel/fsys.S b/arch/ia64/kernel/fsys.S index 4895559ee807..b48f5c3aff34 100644 --- a/arch/ia64/kernel/fsys.S +++ b/arch/ia64/kernel/fsys.S @@ -177,7 +177,7 @@ ENTRY(fsys_gettimeofday) // r11 = preserved: saved ar.pfs // r12 = preserved: memory stack // r13 = preserved: thread pointer - // r14 = debug pointer / usable + // r14 = address of mask / mask // r15 = preserved: system call number // r16 = preserved: current task pointer // r17 = wall to monotonic use @@ -226,7 +226,6 @@ ENTRY(fsys_gettimeofday) add r10 = IA64_TIME_INTERPOLATOR_ADDRESS_OFFSET,r20 extr r3 = r21,32,32 // time_interpolator->nsec_per_cyc extr r8 = r21,0,16 // time_interpolator->source - nop.i 123 cmp.ne p6, p0 = 0, r2 // Fallback if work is scheduled (p6) br.cond.spnt.many fsys_fallback_syscall ;; @@ -257,18 +256,21 @@ ENTRY(fsys_gettimeofday) add r24 = IA64_TIME_INTERPOLATOR_OFFSET_OFFSET,r20 (p15) ld8 r17 = [r19],IA64_TIMESPEC_TV_NSEC_OFFSET ld8 r9 = [r27],IA64_TIMESPEC_TV_NSEC_OFFSET - nop.i 123 + add r14 = IA64_TIME_INTERPOLATOR_MASK_OFFSET, r20 ;; ld8 r18 = [r24] // time_interpolator->offset ld8 r8 = [r27],-IA64_TIMESPEC_TV_NSEC_OFFSET // xtime.tv_nsec (p13) sub r3 = r25,r2 // Diff needed before comparison (thanks davidm) ;; + ld8 r14 = [r14] // time_interpolator->mask (p13) cmp.gt.unc p6,p7 = r3,r0 // check if it is less than last. p6,p7 cleared sub r10 = r2,r26 // current_counter - last_counter ;; (p6) sub r10 = r25,r26 // time we got was less than last_cycle (p7) mov ar.ccv = r25 // more than last_cycle. Prep for cmpxchg ;; + and r10 = r10,r14 // Apply mask + ;; setf.sig f8 = r10 nop.i 123 ;; diff --git a/arch/ia64/kernel/time.c b/arch/ia64/kernel/time.c index 992854fa4b4c..c3205b657b0b 100644 --- a/arch/ia64/kernel/time.c +++ b/arch/ia64/kernel/time.c @@ -45,7 +45,11 @@ EXPORT_SYMBOL(last_cli_ip); #endif -static struct time_interpolator itc_interpolator; +static struct time_interpolator itc_interpolator = { + .shift = 16, + .mask = 0xffffffffffffffffLL, + .source = TIME_SOURCE_CPU +}; static irqreturn_t timer_interrupt (int irq, void *dev_id, struct pt_regs *regs) @@ -206,9 +210,7 @@ ia64_init_itm (void) if (!(sal_platform_features & IA64_SAL_PLATFORM_FEATURE_ITC_DRIFT)) { itc_interpolator.frequency = local_cpu_data->itc_freq; - itc_interpolator.shift = 16; itc_interpolator.drift = itc_drift; - itc_interpolator.source = TIME_SOURCE_CPU; #ifdef CONFIG_SMP /* On IA64 in an SMP configuration ITCs are never accurately synchronized. * Jitter compensation requires a cmpxchg which may limit @@ -222,7 +224,6 @@ ia64_init_itm (void) */ if (!nojitter) itc_interpolator.jitter = 1; #endif - itc_interpolator.addr = NULL; register_time_interpolator(&itc_interpolator); } diff --git a/arch/ia64/sn/kernel/sn2/timer.c b/arch/ia64/sn/kernel/sn2/timer.c index 9a21135978f4..8f6d49b455d0 100644 --- a/arch/ia64/sn/kernel/sn2/timer.c +++ b/arch/ia64/sn/kernel/sn2/timer.c @@ -22,14 +22,16 @@ extern unsigned long sn_rtc_cycles_per_second; -static struct time_interpolator sn2_interpolator; +static struct time_interpolator sn2_interpolator = { + .drift = -1, + .shift = 10, + .mask = (1LL << 55) - 1, + .source = TIME_SOURCE_MMIO64, + .addr = RTC_COUNTER_ADDR +}; void __init sn_timer_init(void) { sn2_interpolator.frequency = sn_rtc_cycles_per_second; - sn2_interpolator.drift = -1; /* unknown */ - sn2_interpolator.shift = 10; /* RTC is 54 bits maximum shift is 10 */ - sn2_interpolator.addr = RTC_COUNTER_ADDR; - sn2_interpolator.source = TIME_SOURCE_MMIO64; register_time_interpolator(&sn2_interpolator); } diff --git a/arch/sparc64/kernel/time.c b/arch/sparc64/kernel/time.c index 96d7cba29679..0a93354f16a6 100644 --- a/arch/sparc64/kernel/time.c +++ b/arch/sparc64/kernel/time.c @@ -1046,6 +1046,7 @@ static struct notifier_block sparc64_cpufreq_notifier_block = { static struct time_interpolator sparc64_cpu_interpolator = { .source = TIME_SOURCE_CPU, .shift = 16, + .mask = 0xffffffffffffffffLL }; /* The quotient formula is taken from the IA64 port. */ diff --git a/drivers/char/hpet.c b/drivers/char/hpet.c index 7ee8a1d70ee8..bc71333eda4a 100644 --- a/drivers/char/hpet.c +++ b/drivers/char/hpet.c @@ -664,7 +664,8 @@ int hpet_control(struct hpet_task *tp, unsigned int cmd, unsigned long arg) static struct time_interpolator hpet_interpolator = { .source = TIME_SOURCE_MMIO64, - .shift = 10 + .shift = 10, + .mask = 0xffffffffffffffffLL }; #endif diff --git a/include/linux/timex.h b/include/linux/timex.h index 438645f2fedc..b54c288dd6b8 100644 --- a/include/linux/timex.h +++ b/include/linux/timex.h @@ -285,20 +285,18 @@ extern long pps_stbcnt; /* stability limit exceeded */ * for the compensation is that the timer routines are not as scalable anymore. */ -#define INTERPOLATOR_ADJUST 65536 -#define INTERPOLATOR_MAX_SKIP 10*INTERPOLATOR_ADJUST - struct time_interpolator { - unsigned short source; /* time source flags */ - unsigned char shift; /* increases accuracy of multiply by shifting. */ - /* Note that bits may be lost if shift is set too high */ - unsigned char jitter; /* if set compensate for fluctuations */ - unsigned nsec_per_cyc; /* set by register_time_interpolator() */ + u16 source; /* time source flags */ + u8 shift; /* increases accuracy of multiply by shifting. */ + /* Note that bits may be lost if shift is set too high */ + u8 jitter; /* if set compensate for fluctuations */ + u32 nsec_per_cyc; /* set by register_time_interpolator() */ void *addr; /* address of counter or function */ + u64 mask; /* mask the valid bits of the counter */ unsigned long offset; /* nsec offset at last update of interpolator */ - unsigned long last_counter; /* counter value in units of the counter at last update */ - unsigned long last_cycle; /* Last timer value if TIME_SOURCE_JITTER is set */ - unsigned long frequency; /* frequency in counts/second */ + u64 last_counter; /* counter value in units of the counter at last update */ + u64 last_cycle; /* Last timer value if TIME_SOURCE_JITTER is set */ + u64 frequency; /* frequency in counts/second */ long drift; /* drift in parts-per-million (or -1) */ unsigned long skips; /* skips forward */ unsigned long ns_skipped; /* nanoseconds skipped */ @@ -308,7 +306,6 @@ struct time_interpolator { extern void register_time_interpolator(struct time_interpolator *); extern void unregister_time_interpolator(struct time_interpolator *); extern void time_interpolator_reset(void); -extern unsigned long time_interpolator_resolution(void); extern unsigned long time_interpolator_get_offset(void); #else /* !CONFIG_TIME_INTERPOLATION */ diff --git a/kernel/timer.c b/kernel/timer.c index 3027f7d36e98..5a8fc3a4519c 100644 --- a/kernel/timer.c +++ b/kernel/timer.c @@ -1438,7 +1438,7 @@ struct time_interpolator *time_interpolator; static struct time_interpolator *time_interpolator_list; static spinlock_t time_interpolator_lock = SPIN_LOCK_UNLOCKED; -static inline unsigned long time_interpolator_get_cycles(unsigned int src) +static inline u64 time_interpolator_get_cycles(unsigned int src) { unsigned long (*x)(void); @@ -1453,23 +1453,25 @@ static inline unsigned long time_interpolator_get_cycles(unsigned int src) case TIME_SOURCE_MMIO32 : return readl(time_interpolator->addr); + default: return get_cycles(); } } -static inline unsigned long time_interpolator_get_counter(void) +static inline u64 time_interpolator_get_counter(void) { unsigned int src = time_interpolator->source; if (time_interpolator->jitter) { - unsigned long lcycle; - unsigned long now; + u64 lcycle; + u64 now; do { lcycle = time_interpolator->last_cycle; now = time_interpolator_get_cycles(src); - if (lcycle && time_after(lcycle, now)) return lcycle; + if (lcycle && time_after(lcycle, now)) + return lcycle; /* Keep track of the last timer value returned. The use of cmpxchg here * will cause contention in an SMP environment. */ @@ -1486,26 +1488,29 @@ void time_interpolator_reset(void) time_interpolator->last_counter = time_interpolator_get_counter(); } -unsigned long time_interpolator_resolution(void) -{ - if (time_interpolator->frequency < NSEC_PER_SEC) - return NSEC_PER_SEC / time_interpolator->frequency; - else - return 1; -} - -#define GET_TI_NSECS(count,i) ((((count) - i->last_counter) * i->nsec_per_cyc) >> i->shift) +#define GET_TI_NSECS(count,i) (((((count) - i->last_counter) & (i)->mask) * (i)->nsec_per_cyc) >> (i)->shift) unsigned long time_interpolator_get_offset(void) { + /* If we do not have a time interpolator set up then just return zero */ + if (!time_interpolator) + return 0; + return time_interpolator->offset + GET_TI_NSECS(time_interpolator_get_counter(), time_interpolator); } +#define INTERPOLATOR_ADJUST 65536 +#define INTERPOLATOR_MAX_SKIP 10*INTERPOLATOR_ADJUST + static void time_interpolator_update(long delta_nsec) { - unsigned long counter = time_interpolator_get_counter(); - unsigned long offset = time_interpolator->offset + GET_TI_NSECS(counter, time_interpolator); + u64 counter; + unsigned long offset; + + /* If there is no time interpolator set up then do nothing */ + if (!time_interpolator) + return; /* The interpolator compensates for late ticks by accumulating * the late time in time_interpolator->offset. A tick earlier than @@ -1515,6 +1520,9 @@ static void time_interpolator_update(long delta_nsec) * and the tuning logic insures that. */ + counter = time_interpolator_get_counter(); + offset = time_interpolator->offset + GET_TI_NSECS(counter, time_interpolator); + if (delta_nsec < 0 || (unsigned long) delta_nsec < offset) time_interpolator->offset = offset - delta_nsec; else { @@ -1553,7 +1561,11 @@ register_time_interpolator(struct time_interpolator *ti) { unsigned long flags; - ti->nsec_per_cyc = (NSEC_PER_SEC << ti->shift) / ti->frequency; + /* Sanity check */ + if (ti->frequency == 0 || ti->mask == 0) + BUG(); + + ti->nsec_per_cyc = ((u64)NSEC_PER_SEC << ti->shift) / ti->frequency; spin_lock(&time_interpolator_lock); write_seqlock_irqsave(&xtime_lock, flags); if (is_better_time_interpolator(ti)) { |
