aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--arch/ia64/kernel/time.c39
-rw-r--r--arch/ia64/sn/kernel/sn2/timer.c19
-rw-r--r--include/asm-ia64/timex.h2
3 files changed, 38 insertions, 22 deletions
diff --git a/arch/ia64/kernel/time.c b/arch/ia64/kernel/time.c
index a094ec49ccf..307d01e15b2 100644
--- a/arch/ia64/kernel/time.c
+++ b/arch/ia64/kernel/time.c
@@ -250,32 +250,27 @@ time_init (void)
set_normalized_timespec(&wall_to_monotonic, -xtime.tv_sec, -xtime.tv_nsec);
}
-#define SMALLUSECS 100
-
-void
-udelay (unsigned long usecs)
+/*
+ * Generic udelay assumes that if preemption is allowed and the thread
+ * migrates to another CPU, that the ITC values are synchronized across
+ * all CPUs.
+ */
+static void
+ia64_itc_udelay (unsigned long usecs)
{
- unsigned long start;
- unsigned long cycles;
- unsigned long smallusecs;
+ unsigned long start = ia64_get_itc();
+ unsigned long end = start + usecs*local_cpu_data->cyc_per_usec;
- /*
- * Execute the non-preemptible delay loop (because the ITC might
- * not be synchronized between CPUS) in relatively short time
- * chunks, allowing preemption between the chunks.
- */
- while (usecs > 0) {
- smallusecs = (usecs > SMALLUSECS) ? SMALLUSECS : usecs;
- preempt_disable();
- cycles = smallusecs*local_cpu_data->cyc_per_usec;
- start = ia64_get_itc();
+ while (time_before(ia64_get_itc(), end))
+ cpu_relax();
+}
- while (ia64_get_itc() - start < cycles)
- cpu_relax();
+void (*ia64_udelay)(unsigned long usecs) = &ia64_itc_udelay;
- preempt_enable();
- usecs -= smallusecs;
- }
+void
+udelay (unsigned long usecs)
+{
+ (*ia64_udelay)(usecs);
}
EXPORT_SYMBOL(udelay);
diff --git a/arch/ia64/sn/kernel/sn2/timer.c b/arch/ia64/sn/kernel/sn2/timer.c
index deb9baf4d47..56a88b6df4b 100644
--- a/arch/ia64/sn/kernel/sn2/timer.c
+++ b/arch/ia64/sn/kernel/sn2/timer.c
@@ -14,6 +14,7 @@
#include <asm/hw_irq.h>
#include <asm/system.h>
+#include <asm/timex.h>
#include <asm/sn/leds.h>
#include <asm/sn/shub_mmr.h>
@@ -28,9 +29,27 @@ static struct time_interpolator sn2_interpolator = {
.source = TIME_SOURCE_MMIO64
};
+/*
+ * sn udelay uses the RTC instead of the ITC because the ITC is not
+ * synchronized across all CPUs, and the thread may migrate to another CPU
+ * if preemption is enabled.
+ */
+static void
+ia64_sn_udelay (unsigned long usecs)
+{
+ unsigned long start = rtc_time();
+ unsigned long end = start +
+ usecs * sn_rtc_cycles_per_second / 1000000;
+
+ while (time_before((unsigned long)rtc_time(), end))
+ cpu_relax();
+}
+
void __init sn_timer_init(void)
{
sn2_interpolator.frequency = sn_rtc_cycles_per_second;
sn2_interpolator.addr = RTC_COUNTER_ADDR;
register_time_interpolator(&sn2_interpolator);
+
+ ia64_udelay = &ia64_sn_udelay;
}
diff --git a/include/asm-ia64/timex.h b/include/asm-ia64/timex.h
index 414aae06044..05a6baf8a47 100644
--- a/include/asm-ia64/timex.h
+++ b/include/asm-ia64/timex.h
@@ -15,6 +15,8 @@
typedef unsigned long cycles_t;
+extern void (*ia64_udelay)(unsigned long usecs);
+
/*
* For performance reasons, we don't want to define CLOCK_TICK_TRATE as
* local_cpu_data->itc_rate. Fortunately, we don't have to, either: according to George