ARM: 7452/1: delay: allow timer-based delay implementation to be selected
authorWill Deacon <will.deacon@arm.com>
Fri, 6 Jul 2012 14:47:17 +0000 (15:47 +0100)
committerRussell King <rmk+kernel@arm.linux.org.uk>
Mon, 9 Jul 2012 16:42:23 +0000 (17:42 +0100)
This patch allows a timer-based delay implementation to be selected by
switching the delay routines over to use get_cycles, which is
implemented in terms of read_current_timer. This further allows us to
skip the loop calibration and have a consistent delay function in the
face of core frequency scaling.

To avoid the pain of dealing with memory-mapped counters, this
implementation uses the co-processor interface to the architected timers
when they are available. The previous loop-based implementation is
kept around for CPUs without the architected timers and we retain both
the maximum delay (2ms) and the corresponding conversion factors for
determining the number of loops required for a given interval. Since the
indirection of the timer routines will only work when called from C,
the sa1100 sleep routines are modified to branch to the loop-based delay
functions directly.

Tested-by: Shinya Kuribayashi <shinya.kuribayashi.px@renesas.com>
Reviewed-by: Stephen Boyd <sboyd@codeaurora.org>
Signed-off-by: Will Deacon <will.deacon@arm.com>
Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
arch/arm/include/asm/delay.h
arch/arm/kernel/arch_timer.c
arch/arm/kernel/armksyms.c
arch/arm/lib/Makefile
arch/arm/lib/delay-loop.S [new file with mode: 0644]
arch/arm/lib/delay.S [deleted file]
arch/arm/lib/delay.c [new file with mode: 0644]
arch/arm/mach-sa1100/sleep.S

index b2deda1815496d83ab3dde5c9eb2c4f5194c7b1e..dc6145120de32d8e1d583f45bbef878365a752e5 100644 (file)
@@ -6,9 +6,22 @@
 #ifndef __ASM_ARM_DELAY_H
 #define __ASM_ARM_DELAY_H
 
+#include <asm/memory.h>
 #include <asm/param.h> /* HZ */
 
-extern void __delay(int loops);
+#define MAX_UDELAY_MS  2
+#define UDELAY_MULT    ((UL(2199023) * HZ) >> 11)
+#define UDELAY_SHIFT   30
+
+#ifndef __ASSEMBLY__
+
+extern struct arm_delay_ops {
+       void (*delay)(unsigned long);
+       void (*const_udelay)(unsigned long);
+       void (*udelay)(unsigned long);
+} arm_delay_ops;
+
+#define __delay(n)             arm_delay_ops.delay(n)
 
 /*
  * This function intentionally does not exist; if you see references to
@@ -23,22 +36,27 @@ extern void __bad_udelay(void);
  * division by multiplication: you don't have to worry about
  * loss of precision.
  *
- * Use only for very small delays ( < 1 msec).  Should probably use a
+ * Use only for very small delays ( < 2 msec).  Should probably use a
  * lookup table, really, as the multiplications take much too long with
  * short delays.  This is a "reasonable" implementation, though (and the
  * first constant multiplications gets optimized away if the delay is
  * a constant)
  */
-extern void __udelay(unsigned long usecs);
-extern void __const_udelay(unsigned long);
-
-#define MAX_UDELAY_MS 2
+#define __udelay(n)            arm_delay_ops.udelay(n)
+#define __const_udelay(n)      arm_delay_ops.const_udelay(n)
 
 #define udelay(n)                                                      \
        (__builtin_constant_p(n) ?                                      \
          ((n) > (MAX_UDELAY_MS * 1000) ? __bad_udelay() :              \
-                       __const_udelay((n) * ((2199023U*HZ)>>11))) :    \
+                       __const_udelay((n) * UDELAY_MULT)) :            \
          __udelay(n))
 
+/* Loop-based definitions for assembly code. */
+extern void __loop_delay(unsigned long loops);
+extern void __loop_udelay(unsigned long usecs);
+extern void __loop_const_udelay(unsigned long);
+
+#endif /* __ASSEMBLY__ */
+
 #endif /* defined(_ARM_DELAY_H) */
 
index dbbeec4f06e2e5f0aaf0ba71c249baa313367fba..675cee09c014be0aca0b2ee8bc2c8941ee5903d5 100644 (file)
@@ -32,6 +32,8 @@ static int arch_timer_ppi2;
 
 static struct clock_event_device __percpu **arch_timer_evt;
 
+extern void init_current_timer_delay(unsigned long freq);
+
 /*
  * Architected system timer support.
  */
@@ -304,6 +306,7 @@ static int __init arch_timer_register(void)
        if (err)
                goto out_free_irq;
 
+       init_current_timer_delay(arch_timer_rate);
        return 0;
 
 out_free_irq:
index b57c75e0b01f975defe734de3cce7c6ecfb34dda..71962284d288719176023c0da5ed22fe36f6ba37 100644 (file)
@@ -49,8 +49,7 @@ extern void __aeabi_ulcmp(void);
 extern void fpundefinstr(void);
 
        /* platform dependent support */
-EXPORT_SYMBOL(__udelay);
-EXPORT_SYMBOL(__const_udelay);
+EXPORT_SYMBOL(arm_delay_ops);
 
        /* networking */
 EXPORT_SYMBOL(csum_partial);
index 992769ae2599f1f5c050847311fabd789d308c52..b621114644fdeeb06e87f6bad1a08dfb0c26fdd1 100644 (file)
@@ -6,7 +6,7 @@
 
 lib-y          := backtrace.o changebit.o csumipv6.o csumpartial.o   \
                   csumpartialcopy.o csumpartialcopyuser.o clearbit.o \
-                  delay.o findbit.o memchr.o memcpy.o                \
+                  delay.o delay-loop.o findbit.o memchr.o memcpy.o   \
                   memmove.o memset.o memzero.o setbit.o              \
                   strncpy_from_user.o strnlen_user.o                 \
                   strchr.o strrchr.o                                 \
diff --git a/arch/arm/lib/delay-loop.S b/arch/arm/lib/delay-loop.S
new file mode 100644 (file)
index 0000000..36b668d
--- /dev/null
@@ -0,0 +1,67 @@
+/*
+ *  linux/arch/arm/lib/delay.S
+ *
+ *  Copyright (C) 1995, 1996 Russell King
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#include <linux/linkage.h>
+#include <asm/assembler.h>
+#include <asm/delay.h>
+               .text
+
+.LC0:          .word   loops_per_jiffy
+.LC1:          .word   UDELAY_MULT
+
+/*
+ * r0  <= 2000
+ * lpj <= 0x01ffffff (max. 3355 bogomips)
+ * HZ  <= 1000
+ */
+
+ENTRY(__loop_udelay)
+               ldr     r2, .LC1
+               mul     r0, r2, r0
+ENTRY(__loop_const_udelay)                     @ 0 <= r0 <= 0x7fffff06
+               mov     r1, #-1
+               ldr     r2, .LC0
+               ldr     r2, [r2]                @ max = 0x01ffffff
+               add     r0, r0, r1, lsr #32-14
+               mov     r0, r0, lsr #14         @ max = 0x0001ffff
+               add     r2, r2, r1, lsr #32-10
+               mov     r2, r2, lsr #10         @ max = 0x00007fff
+               mul     r0, r2, r0              @ max = 2^32-1
+               add     r0, r0, r1, lsr #32-6
+               movs    r0, r0, lsr #6
+               moveq   pc, lr
+
+/*
+ * loops = r0 * HZ * loops_per_jiffy / 1000000
+ */
+
+@ Delay routine
+ENTRY(__loop_delay)
+               subs    r0, r0, #1
+#if 0
+               movls   pc, lr
+               subs    r0, r0, #1
+               movls   pc, lr
+               subs    r0, r0, #1
+               movls   pc, lr
+               subs    r0, r0, #1
+               movls   pc, lr
+               subs    r0, r0, #1
+               movls   pc, lr
+               subs    r0, r0, #1
+               movls   pc, lr
+               subs    r0, r0, #1
+               movls   pc, lr
+               subs    r0, r0, #1
+#endif
+               bhi     __loop_delay
+               mov     pc, lr
+ENDPROC(__loop_udelay)
+ENDPROC(__loop_const_udelay)
+ENDPROC(__loop_delay)
diff --git a/arch/arm/lib/delay.S b/arch/arm/lib/delay.S
deleted file mode 100644 (file)
index 3c9a05c..0000000
+++ /dev/null
@@ -1,69 +0,0 @@
-/*
- *  linux/arch/arm/lib/delay.S
- *
- *  Copyright (C) 1995, 1996 Russell King
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- */
-#include <linux/linkage.h>
-#include <asm/assembler.h>
-#include <asm/param.h>
-               .text
-
-.LC0:          .word   loops_per_jiffy
-.LC1:          .word   (2199023*HZ)>>11
-
-/*
- * r0  <= 2000
- * lpj <= 0x01ffffff (max. 3355 bogomips)
- * HZ  <= 1000
- */
-
-ENTRY(__udelay)
-               ldr     r2, .LC1
-               mul     r0, r2, r0
-ENTRY(__const_udelay)                          @ 0 <= r0 <= 0x7fffff06
-               mov     r1, #-1
-               ldr     r2, .LC0
-               ldr     r2, [r2]                @ max = 0x01ffffff
-               add     r0, r0, r1, lsr #32-14
-               mov     r0, r0, lsr #14         @ max = 0x0001ffff
-               add     r2, r2, r1, lsr #32-10
-               mov     r2, r2, lsr #10         @ max = 0x00007fff
-               mul     r0, r2, r0              @ max = 2^32-1
-               add     r0, r0, r1, lsr #32-6
-               movs    r0, r0, lsr #6
-               moveq   pc, lr
-
-/*
- * loops = r0 * HZ * loops_per_jiffy / 1000000
- *
- * Oh, if only we had a cycle counter...
- */
-
-@ Delay routine
-ENTRY(__delay)
-               subs    r0, r0, #1
-#if 0
-               movls   pc, lr
-               subs    r0, r0, #1
-               movls   pc, lr
-               subs    r0, r0, #1
-               movls   pc, lr
-               subs    r0, r0, #1
-               movls   pc, lr
-               subs    r0, r0, #1
-               movls   pc, lr
-               subs    r0, r0, #1
-               movls   pc, lr
-               subs    r0, r0, #1
-               movls   pc, lr
-               subs    r0, r0, #1
-#endif
-               bhi     __delay
-               mov     pc, lr
-ENDPROC(__udelay)
-ENDPROC(__const_udelay)
-ENDPROC(__delay)
diff --git a/arch/arm/lib/delay.c b/arch/arm/lib/delay.c
new file mode 100644 (file)
index 0000000..d6dacc6
--- /dev/null
@@ -0,0 +1,71 @@
+/*
+ * Delay loops based on the OpenRISC implementation.
+ *
+ * Copyright (C) 2012 ARM Limited
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Author: Will Deacon <will.deacon@arm.com>
+ */
+
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/timex.h>
+
+/*
+ * Default to the loop-based delay implementation.
+ */
+struct arm_delay_ops arm_delay_ops = {
+       .delay          = __loop_delay,
+       .const_udelay   = __loop_const_udelay,
+       .udelay         = __loop_udelay,
+};
+
+#ifdef ARCH_HAS_READ_CURRENT_TIMER
+static void __timer_delay(unsigned long cycles)
+{
+       cycles_t start = get_cycles();
+
+       while ((get_cycles() - start) < cycles)
+               cpu_relax();
+}
+
+static void __timer_const_udelay(unsigned long xloops)
+{
+       unsigned long long loops = xloops;
+       loops *= loops_per_jiffy;
+       __timer_delay(loops >> UDELAY_SHIFT);
+}
+
+static void __timer_udelay(unsigned long usecs)
+{
+       __timer_const_udelay(usecs * UDELAY_MULT);
+}
+
+void __init init_current_timer_delay(unsigned long freq)
+{
+       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;
+}
+
+unsigned long __cpuinit calibrate_delay_is_known(void)
+{
+       return lpj_fine;
+}
+#endif
index 30cc6721665bf69821efaaacf92dcc78d220245e..85863741ef8bb6d5b6c0fa9199036557ac3aa734 100644 (file)
@@ -38,9 +38,9 @@ ENTRY(sa1100_finish_suspend)
        orr     r4, r4, #MDREFR_K1DB2
        ldr     r5, =PPCR
 
-       @ Pre-load __udelay into the I-cache
+       @ Pre-load __loop_udelay into the I-cache
        mov     r0, #1
-       bl      __udelay
+       bl      __loop_udelay
        mov     r0, r0
 
        @ The following must all exist in a single cache line to
@@ -53,11 +53,11 @@ ENTRY(sa1100_finish_suspend)
        @ delay 90us and set CPU PLL to lowest speed
        @ fixes resume problem on high speed SA1110
        mov     r0, #90
-       bl      __udelay
+       bl      __loop_udelay
        mov     r1, #0
        str     r1, [r5]
        mov     r0, #90
-       bl      __udelay
+       bl      __loop_udelay
 
        /*
         * SA1110 SDRAM controller workaround.  register values: