summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChristoph Lameter <clameter@sgi.com>2004-10-31 22:30:03 -0800
committerLinus Torvalds <torvalds@ppc970.osdl.org>2004-10-31 22:30:03 -0800
commit6f0c33591ddb035677b2003097deb2a06b4f9708 (patch)
treef3e1dd22429f40618036a735131cb9bedf397eeb
parent6f1780569c67765e1fef8754fc1812714b85e4d0 (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.txt23
-rw-r--r--arch/ia64/kernel/asm-offsets.c1
-rw-r--r--arch/ia64/kernel/cyclone.c5
-rw-r--r--arch/ia64/kernel/fsys.S8
-rw-r--r--arch/ia64/kernel/time.c9
-rw-r--r--arch/ia64/sn/kernel/sn2/timer.c12
-rw-r--r--arch/sparc64/kernel/time.c1
-rw-r--r--drivers/char/hpet.c3
-rw-r--r--include/linux/timex.h21
-rw-r--r--kernel/timer.c46
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)) {