Commit 6f0c3359 authored by Christoph Lameter's avatar Christoph Lameter Committed by Linus Torvalds

[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: default avatarChristoph Lameter <clameter@sgi.com>
Signed-off-by: default avatarLinus Torvalds <torvalds@osdl.org>
parent 6f178056
...@@ -9,32 +9,33 @@ The architecture specific code typically provides gettimeofday and ...@@ -9,32 +9,33 @@ The architecture specific code typically provides gettimeofday and
settimeofday under Linux. The time interpolator provides both if an arch settimeofday under Linux. The time interpolator provides both if an arch
defines CONFIG_TIME_INTERPOLATION. The arch still must set up timer tick defines CONFIG_TIME_INTERPOLATION. The arch still must set up timer tick
operations and call the necessary functions to advance the clock. operations and call the necessary functions to advance the clock.
With the time interpolator a standardized interface exists for time With the time interpolator a standardized interface exists for time
interpolation between ticks which also allows the determination interpolation between ticks. The provided logic is highly scalable
of time in a hardware independent way. The provided logic is highly scalable
and has been tested in SMP situations of up to 512 CPUs. and has been tested in SMP situations of up to 512 CPUs.
If CONFIG_TIME_INTERPOLATION is defined then the architecture specific code 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: 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) void time_init(void)
{ {
.... ....
/* Initialization of the timer *. /* Initialization of the timer *.
my_interpolator.frequency = MY_FREQUENCY;
my_interpolator.source = TIME_SOURCE_MMIO32;
my_interpolator.address = &my_timer; 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); 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
...@@ -217,6 +217,7 @@ void foo(void) ...@@ -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_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_LAST_COUNTER_OFFSET, offsetof (struct time_interpolator, last_counter));
DEFINE(IA64_TIME_INTERPOLATOR_JITTER_OFFSET, offsetof (struct time_interpolator, jitter)); 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_CPU, TIME_SOURCE_CPU);
DEFINE(IA64_TIME_SOURCE_MMIO64, TIME_SOURCE_MMIO64); DEFINE(IA64_TIME_SOURCE_MMIO64, TIME_SOURCE_MMIO64);
DEFINE(IA64_TIME_SOURCE_MMIO32, TIME_SOURCE_MMIO32); DEFINE(IA64_TIME_SOURCE_MMIO32, TIME_SOURCE_MMIO32);
......
...@@ -19,10 +19,11 @@ void __init cyclone_setup(void) ...@@ -19,10 +19,11 @@ void __init cyclone_setup(void)
struct time_interpolator cyclone_interpolator = { struct time_interpolator cyclone_interpolator = {
.source = TIME_SOURCE_MMIO32, .source = TIME_SOURCE_MMIO64,
.shift = 32, .shift = 16,
.frequency = CYCLONE_TIMER_FREQ, .frequency = CYCLONE_TIMER_FREQ,
.drift = -100, .drift = -100,
.mask = (1LL << 40) - 1
}; };
int __init init_cyclone_clock(void) int __init init_cyclone_clock(void)
......
...@@ -177,7 +177,7 @@ ENTRY(fsys_gettimeofday) ...@@ -177,7 +177,7 @@ ENTRY(fsys_gettimeofday)
// r11 = preserved: saved ar.pfs // r11 = preserved: saved ar.pfs
// r12 = preserved: memory stack // r12 = preserved: memory stack
// r13 = preserved: thread pointer // r13 = preserved: thread pointer
// r14 = debug pointer / usable // r14 = address of mask / mask
// r15 = preserved: system call number // r15 = preserved: system call number
// r16 = preserved: current task pointer // r16 = preserved: current task pointer
// r17 = wall to monotonic use // r17 = wall to monotonic use
...@@ -226,7 +226,6 @@ ENTRY(fsys_gettimeofday) ...@@ -226,7 +226,6 @@ ENTRY(fsys_gettimeofday)
add r10 = IA64_TIME_INTERPOLATOR_ADDRESS_OFFSET,r20 add r10 = IA64_TIME_INTERPOLATOR_ADDRESS_OFFSET,r20
extr r3 = r21,32,32 // time_interpolator->nsec_per_cyc extr r3 = r21,32,32 // time_interpolator->nsec_per_cyc
extr r8 = r21,0,16 // time_interpolator->source extr r8 = r21,0,16 // time_interpolator->source
nop.i 123
cmp.ne p6, p0 = 0, r2 // Fallback if work is scheduled cmp.ne p6, p0 = 0, r2 // Fallback if work is scheduled
(p6) br.cond.spnt.many fsys_fallback_syscall (p6) br.cond.spnt.many fsys_fallback_syscall
;; ;;
...@@ -257,17 +256,20 @@ ENTRY(fsys_gettimeofday) ...@@ -257,17 +256,20 @@ ENTRY(fsys_gettimeofday)
add r24 = IA64_TIME_INTERPOLATOR_OFFSET_OFFSET,r20 add r24 = IA64_TIME_INTERPOLATOR_OFFSET_OFFSET,r20
(p15) ld8 r17 = [r19],IA64_TIMESPEC_TV_NSEC_OFFSET (p15) ld8 r17 = [r19],IA64_TIMESPEC_TV_NSEC_OFFSET
ld8 r9 = [r27],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 r18 = [r24] // time_interpolator->offset
ld8 r8 = [r27],-IA64_TIMESPEC_TV_NSEC_OFFSET // xtime.tv_nsec ld8 r8 = [r27],-IA64_TIMESPEC_TV_NSEC_OFFSET // xtime.tv_nsec
(p13) sub r3 = r25,r2 // Diff needed before comparison (thanks davidm) (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 (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 sub r10 = r2,r26 // current_counter - last_counter
;; ;;
(p6) sub r10 = r25,r26 // time we got was less than last_cycle (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 (p7) mov ar.ccv = r25 // more than last_cycle. Prep for cmpxchg
;;
and r10 = r10,r14 // Apply mask
;; ;;
setf.sig f8 = r10 setf.sig f8 = r10
nop.i 123 nop.i 123
......
...@@ -45,7 +45,11 @@ EXPORT_SYMBOL(last_cli_ip); ...@@ -45,7 +45,11 @@ EXPORT_SYMBOL(last_cli_ip);
#endif #endif
static struct time_interpolator itc_interpolator; static struct time_interpolator itc_interpolator = {
.shift = 16,
.mask = 0xffffffffffffffffLL,
.source = TIME_SOURCE_CPU
};
static irqreturn_t static irqreturn_t
timer_interrupt (int irq, void *dev_id, struct pt_regs *regs) timer_interrupt (int irq, void *dev_id, struct pt_regs *regs)
...@@ -206,9 +210,7 @@ ia64_init_itm (void) ...@@ -206,9 +210,7 @@ ia64_init_itm (void)
if (!(sal_platform_features & IA64_SAL_PLATFORM_FEATURE_ITC_DRIFT)) { if (!(sal_platform_features & IA64_SAL_PLATFORM_FEATURE_ITC_DRIFT)) {
itc_interpolator.frequency = local_cpu_data->itc_freq; itc_interpolator.frequency = local_cpu_data->itc_freq;
itc_interpolator.shift = 16;
itc_interpolator.drift = itc_drift; itc_interpolator.drift = itc_drift;
itc_interpolator.source = TIME_SOURCE_CPU;
#ifdef CONFIG_SMP #ifdef CONFIG_SMP
/* On IA64 in an SMP configuration ITCs are never accurately synchronized. /* On IA64 in an SMP configuration ITCs are never accurately synchronized.
* Jitter compensation requires a cmpxchg which may limit * Jitter compensation requires a cmpxchg which may limit
...@@ -222,7 +224,6 @@ ia64_init_itm (void) ...@@ -222,7 +224,6 @@ ia64_init_itm (void)
*/ */
if (!nojitter) itc_interpolator.jitter = 1; if (!nojitter) itc_interpolator.jitter = 1;
#endif #endif
itc_interpolator.addr = NULL;
register_time_interpolator(&itc_interpolator); register_time_interpolator(&itc_interpolator);
} }
......
...@@ -22,14 +22,16 @@ ...@@ -22,14 +22,16 @@
extern unsigned long sn_rtc_cycles_per_second; 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) void __init sn_timer_init(void)
{ {
sn2_interpolator.frequency = sn_rtc_cycles_per_second; 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); register_time_interpolator(&sn2_interpolator);
} }
...@@ -1046,6 +1046,7 @@ static struct notifier_block sparc64_cpufreq_notifier_block = { ...@@ -1046,6 +1046,7 @@ static struct notifier_block sparc64_cpufreq_notifier_block = {
static struct time_interpolator sparc64_cpu_interpolator = { static struct time_interpolator sparc64_cpu_interpolator = {
.source = TIME_SOURCE_CPU, .source = TIME_SOURCE_CPU,
.shift = 16, .shift = 16,
.mask = 0xffffffffffffffffLL
}; };
/* The quotient formula is taken from the IA64 port. */ /* The quotient formula is taken from the IA64 port. */
......
...@@ -664,7 +664,8 @@ int hpet_control(struct hpet_task *tp, unsigned int cmd, unsigned long arg) ...@@ -664,7 +664,8 @@ int hpet_control(struct hpet_task *tp, unsigned int cmd, unsigned long arg)
static struct time_interpolator hpet_interpolator = { static struct time_interpolator hpet_interpolator = {
.source = TIME_SOURCE_MMIO64, .source = TIME_SOURCE_MMIO64,
.shift = 10 .shift = 10,
.mask = 0xffffffffffffffffLL
}; };
#endif #endif
......
...@@ -285,20 +285,18 @@ extern long pps_stbcnt; /* stability limit exceeded */ ...@@ -285,20 +285,18 @@ extern long pps_stbcnt; /* stability limit exceeded */
* for the compensation is that the timer routines are not as scalable anymore. * 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 { struct time_interpolator {
unsigned short source; /* time source flags */ u16 source; /* time source flags */
unsigned char shift; /* increases accuracy of multiply by shifting. */ u8 shift; /* increases accuracy of multiply by shifting. */
/* Note that bits may be lost if shift is set too high */ /* Note that bits may be lost if shift is set too high */
unsigned char jitter; /* if set compensate for fluctuations */ u8 jitter; /* if set compensate for fluctuations */
unsigned nsec_per_cyc; /* set by register_time_interpolator() */ u32 nsec_per_cyc; /* set by register_time_interpolator() */
void *addr; /* address of counter or function */ 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 offset; /* nsec offset at last update of interpolator */
unsigned long last_counter; /* counter value in units of the counter at last update */ u64 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 */ u64 last_cycle; /* Last timer value if TIME_SOURCE_JITTER is set */
unsigned long frequency; /* frequency in counts/second */ u64 frequency; /* frequency in counts/second */
long drift; /* drift in parts-per-million (or -1) */ long drift; /* drift in parts-per-million (or -1) */
unsigned long skips; /* skips forward */ unsigned long skips; /* skips forward */
unsigned long ns_skipped; /* nanoseconds skipped */ unsigned long ns_skipped; /* nanoseconds skipped */
...@@ -308,7 +306,6 @@ struct time_interpolator { ...@@ -308,7 +306,6 @@ struct time_interpolator {
extern void register_time_interpolator(struct time_interpolator *); extern void register_time_interpolator(struct time_interpolator *);
extern void unregister_time_interpolator(struct time_interpolator *); extern void unregister_time_interpolator(struct time_interpolator *);
extern void time_interpolator_reset(void); extern void time_interpolator_reset(void);
extern unsigned long time_interpolator_resolution(void);
extern unsigned long time_interpolator_get_offset(void); extern unsigned long time_interpolator_get_offset(void);
#else /* !CONFIG_TIME_INTERPOLATION */ #else /* !CONFIG_TIME_INTERPOLATION */
......
...@@ -1438,7 +1438,7 @@ struct time_interpolator *time_interpolator; ...@@ -1438,7 +1438,7 @@ struct time_interpolator *time_interpolator;
static struct time_interpolator *time_interpolator_list; static struct time_interpolator *time_interpolator_list;
static spinlock_t time_interpolator_lock = SPIN_LOCK_UNLOCKED; 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); unsigned long (*x)(void);
...@@ -1453,23 +1453,25 @@ static inline unsigned long time_interpolator_get_cycles(unsigned int src) ...@@ -1453,23 +1453,25 @@ static inline unsigned long time_interpolator_get_cycles(unsigned int src)
case TIME_SOURCE_MMIO32 : case TIME_SOURCE_MMIO32 :
return readl(time_interpolator->addr); return readl(time_interpolator->addr);
default: return get_cycles(); 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; unsigned int src = time_interpolator->source;
if (time_interpolator->jitter) if (time_interpolator->jitter)
{ {
unsigned long lcycle; u64 lcycle;
unsigned long now; u64 now;
do { do {
lcycle = time_interpolator->last_cycle; lcycle = time_interpolator->last_cycle;
now = time_interpolator_get_cycles(src); 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 /* Keep track of the last timer value returned. The use of cmpxchg here
* will cause contention in an SMP environment. * will cause contention in an SMP environment.
*/ */
...@@ -1486,26 +1488,29 @@ void time_interpolator_reset(void) ...@@ -1486,26 +1488,29 @@ void time_interpolator_reset(void)
time_interpolator->last_counter = time_interpolator_get_counter(); time_interpolator->last_counter = time_interpolator_get_counter();
} }
unsigned long time_interpolator_resolution(void) #define GET_TI_NSECS(count,i) (((((count) - i->last_counter) & (i)->mask) * (i)->nsec_per_cyc) >> (i)->shift)
{
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)
unsigned long time_interpolator_get_offset(void) 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 + return time_interpolator->offset +
GET_TI_NSECS(time_interpolator_get_counter(), time_interpolator); 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) static void time_interpolator_update(long delta_nsec)
{ {
unsigned long counter = time_interpolator_get_counter(); u64 counter;
unsigned long offset = time_interpolator->offset + GET_TI_NSECS(counter, time_interpolator); 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 interpolator compensates for late ticks by accumulating
* the late time in time_interpolator->offset. A tick earlier than * the late time in time_interpolator->offset. A tick earlier than
...@@ -1515,6 +1520,9 @@ static void time_interpolator_update(long delta_nsec) ...@@ -1515,6 +1520,9 @@ static void time_interpolator_update(long delta_nsec)
* and the tuning logic insures that. * 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) if (delta_nsec < 0 || (unsigned long) delta_nsec < offset)
time_interpolator->offset = offset - delta_nsec; time_interpolator->offset = offset - delta_nsec;
else { else {
...@@ -1553,7 +1561,11 @@ register_time_interpolator(struct time_interpolator *ti) ...@@ -1553,7 +1561,11 @@ register_time_interpolator(struct time_interpolator *ti)
{ {
unsigned long flags; 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); spin_lock(&time_interpolator_lock);
write_seqlock_irqsave(&xtime_lock, flags); write_seqlock_irqsave(&xtime_lock, flags);
if (is_better_time_interpolator(ti)) { if (is_better_time_interpolator(ti)) {
......
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