ARM: 7538/1: delay: add registration mechanism for delay timer sources
authorJonathan Austin <Jonathan.Austin@arm.com>
Fri, 21 Sep 2012 17:51:44 +0000 (18:51 +0100)
committerRussell King <rmk+kernel@arm.linux.org.uk>
Wed, 26 Sep 2012 21:57:52 +0000 (22:57 +0100)
The current timer-based delay loop relies on the architected timer to
initiate the switch away from the polling-based implementation. This is
unfortunate for platforms without the architected timers but with a
suitable delay source (that is, constant frequency, always powered-up
and ticking as long as the CPUs are online).

This patch introduces a registration mechanism for the delay timer
(which provides an unconditional read_current_timer implementation) and
updates the architected timer code to use the new interface.

Reviewed-by: Stephen Boyd <sboyd@codeaurora.org>
Signed-off-by: Jonathan Austin <jonathan.austin@arm.com>
Signed-off-by: Will Deacon <will.deacon@arm.com>
Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
arch/arm/include/asm/arch_timer.h
arch/arm/include/asm/delay.h
arch/arm/include/asm/timex.h
arch/arm/kernel/arch_timer.c
arch/arm/lib/delay.c

index ad9b155..d40229d 100644 (file)
@@ -5,7 +5,6 @@
 #include <linux/clocksource.h>
 
 #ifdef CONFIG_ARM_ARCH_TIMER
-#define ARCH_HAS_READ_CURRENT_TIMER
 int arch_timer_of_register(void);
 int arch_timer_sched_clock_init(void);
 struct timecounter *arch_timer_get_timecounter(void);
index dc61451..ab98fdd 100644 (file)
 
 #ifndef __ASSEMBLY__
 
+struct delay_timer {
+       unsigned long (*read_current_timer)(void);
+       unsigned long freq;
+};
+
 extern struct arm_delay_ops {
        void (*delay)(unsigned long);
        void (*const_udelay)(unsigned long);
@@ -56,6 +61,10 @@ extern void __loop_delay(unsigned long loops);
 extern void __loop_udelay(unsigned long usecs);
 extern void __loop_const_udelay(unsigned long);
 
+/* Delay-loop timer registration. */
+#define ARCH_HAS_READ_CURRENT_TIMER
+extern void register_current_timer_delay(const struct delay_timer *timer);
+
 #endif /* __ASSEMBLY__ */
 
 #endif /* defined(_ARM_DELAY_H) */
index ce11944..9acc135 100644 (file)
 #ifndef _ASMARM_TIMEX_H
 #define _ASMARM_TIMEX_H
 
-#include <asm/arch_timer.h>
 #include <mach/timex.h>
 
 typedef unsigned long cycles_t;
-
-#ifdef ARCH_HAS_READ_CURRENT_TIMER
 #define get_cycles()   ({ cycles_t c; read_current_timer(&c) ? 0 : c; })
-#else
-#define get_cycles()   (0)
-#endif
 
 #endif
index c04c2a6..c8ef207 100644 (file)
@@ -21,6 +21,7 @@
 #include <linux/io.h>
 
 #include <asm/cputype.h>
+#include <asm/delay.h>
 #include <asm/localtimer.h>
 #include <asm/arch_timer.h>
 #include <asm/system_info.h>
@@ -39,8 +40,7 @@ enum ppi_nr {
 static int arch_timer_ppi[MAX_TIMER_PPI];
 
 static struct clock_event_device __percpu **arch_timer_evt;
-
-extern void init_current_timer_delay(unsigned long freq);
+static struct delay_timer arch_delay_timer;
 
 static bool arch_timer_use_virtual = true;
 
@@ -325,12 +325,9 @@ static cycle_t arch_counter_read(struct clocksource *cs)
        return arch_counter_get_cntpct();
 }
 
-int read_current_timer(unsigned long *timer_val)
+static unsigned long arch_timer_read_current_timer(void)
 {
-       if (!arch_timer_rate)
-               return -ENXIO;
-       *timer_val = arch_counter_get_cntpct();
-       return 0;
+       return arch_counter_get_cntpct();
 }
 
 static cycle_t arch_counter_read_cc(const struct cyclecounter *cc)
@@ -441,11 +438,13 @@ static int __init arch_timer_register(void)
                arch_timer_global_evt.cpumask = cpumask_of(0);
                err = arch_timer_setup(&arch_timer_global_evt);
        }
-
        if (err)
                goto out_free_irq;
 
-       init_current_timer_delay(arch_timer_rate);
+       /* Use the architected timer for the delay loop. */
+       arch_delay_timer.read_current_timer = &arch_timer_read_current_timer;
+       arch_delay_timer.freq = arch_timer_rate;
+       register_current_timer_delay(&arch_delay_timer);
        return 0;
 
 out_free_irq:
index d6dacc6..9d0a300 100644 (file)
@@ -34,7 +34,18 @@ struct arm_delay_ops arm_delay_ops = {
        .udelay         = __loop_udelay,
 };
 
-#ifdef ARCH_HAS_READ_CURRENT_TIMER
+static const struct delay_timer *delay_timer;
+static bool delay_calibrated;
+
+int read_current_timer(unsigned long *timer_val)
+{
+       if (!delay_timer)
+               return -ENXIO;
+
+       *timer_val = delay_timer->read_current_timer();
+       return 0;
+}
+
 static void __timer_delay(unsigned long cycles)
 {
        cycles_t start = get_cycles();
@@ -55,17 +66,24 @@ static void __timer_udelay(unsigned long usecs)
        __timer_const_udelay(usecs * UDELAY_MULT);
 }
 
-void __init init_current_timer_delay(unsigned long freq)
+void __init register_current_timer_delay(const struct delay_timer *timer)
 {
-       pr_info("Switching to timer-based delay loop\n");
-       lpj_fine                        = freq / HZ;
-       arm_delay_ops.delay             = __timer_delay;
-       arm_delay_ops.const_udelay      = __timer_const_udelay;
-       arm_delay_ops.udelay            = __timer_udelay;
+       if (!delay_calibrated) {
+               pr_info("Switching to timer-based delay loop\n");
+               delay_timer                     = timer;
+               lpj_fine                        = timer->freq / HZ;
+               loops_per_jiffy                 = lpj_fine;
+               arm_delay_ops.delay             = __timer_delay;
+               arm_delay_ops.const_udelay      = __timer_const_udelay;
+               arm_delay_ops.udelay            = __timer_udelay;
+               delay_calibrated                = true;
+       } else {
+               pr_info("Ignoring duplicate/late registration of read_current_timer delay\n");
+       }
 }
 
 unsigned long __cpuinit calibrate_delay_is_known(void)
 {
+       delay_calibrated = true;
        return lpj_fine;
 }
-#endif