Use architected timers for delay loop
Subsystems (3)
cover Use architected timers for delay loop
Hello,
This patch series reworks our delay loop implementation to allow the busy-loop to be replaced with the physical counter included as part of the architected timer on recent CPUs. The polling implementation is still maintained to support cores without the timers and we use the same maths and upper delay bound of 2ms.
Russell - I think my maths is correct, but why can't we increase the
precision by using a magic constant of 4398046U and a right shift of 31?
As usual, all feedback is welcome,
Will
Will Deacon (2):
ARM: arch timer: implement read_current_timer and get_cycles
ARM: delay: allow timer-based delay implementation to be selected
arch/arm/include/asm/arch_timer.h | 1 +
arch/arm/include/asm/delay.h | 19 +++++++--
arch/arm/include/asm/timex.h | 10 +++--
arch/arm/kernel/arch_timer.c | 11 +++++
arch/arm/kernel/armksyms.c | 3 +-
arch/arm/lib/Makefile | 2 +-
arch/arm/lib/{delay.S => delay-loop.S} | 16 +++----
arch/arm/lib/delay.c | 66 ++++++++++++++++++++++++++++++++
arch/arm/mach-sa1100/sleep.S | 8 ++--
9 files changed, 112 insertions(+), 24 deletions(-)
rename arch/arm/lib/{delay.S => delay-loop.S} (85%)
create mode 100644 arch/arm/lib/delay.cSignature · 1 line
1.7.4.1
PATCH 1/2 ARM: arch timer: implement read_current_timer and get_cycles
This patch implements read_current_timer using the architected timers when they are selected via CONFIG_ARM_ARCH_TIMER. If they are detected not to be usable at runtime, we return -ENXIO to the caller.
Furthermore, if read_current_timer is exported then we can implement get_cycles in terms of it for use as both an entropy source and for implementing __udelay and friends.
--- arch/arm/include/asm/arch_timer.h | 1 + arch/arm/include/asm/timex.h | 10 ++++++---- arch/arm/kernel/arch_timer.c | 8 ++++++++ 3 files changed, 15 insertions(+), 4 deletions(-)
arch/arm/include/asm/arch_timer.h +1 -0
@@ -2,6 +2,7 @@#define __ASMARM_ARCH_TIMER_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 );#else
arch/arm/include/asm/timex.h +6 -4
@@ -12,13 +12,15 @@#ifndef _ASMARM_TIMEX_H#define _ASMARM_TIMEX_H +#include <asm/arch_timer.h> #include <mach/timex.h> typedef unsigned long cycles_t ; -static inline cycles_t get_cycles (void ) -{ -return 0 ; -} +#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
arch/arm/kernel/arch_timer.c +8 -0
@@ -223,6 +223,14 @@ static cycle_t arch_counter_read(struct clocksource *cs)return arch_counter_get_cntpct (); } +int read_current_timer (unsigned long *timer_val) +{ +if (!arch_timer_rate) +return -ENXIO ; + *timer_val =arch_counter_get_cntpct (); +return 0 ; +} +static struct clocksource clocksource_counter = { .name ="arch_sys_counter" , .rating =400 ,
Signature · 1 line
1.7.4.1
PATCH 1/2 ARM: arch timer: implement read_current_timer and get_cycles
Quoted 7 lines · Will Deacon
This patch implements
read_current_timerusing the architected timers when they are selected viaCONFIG_ARM_ARCH_TIMER. If they are detected not to be usable at runtime, we return-ENXIOto the caller.Furthermore, if
read_current_timeris exported then we can implementget_cyclesin terms of it for use as both an entropy source and for implementing__udelayand friends.
Neat.
arch/arm/include/asm/timex.h line 15
Quoted 9 lines
arch/arm/include/asm/timex.h +1
-0@@ -12,13 +12,15 @@#ifndef _ASMARM_TIMEX_H#define _ASMARM_TIMEX_H +#include <asm/arch_timer.h>
This seems to cause compilation failures if you aren't building with architected timers enabled.
In file included from arch/arm/include/asm/timex.h:15,
from include/linux/timex.h:174,
from include/linux/sched.h:57,
from arch/arm/kernel/asm-offsets.c:13:arch/arm/include/asm/arch_timer.h: In function 'arch_timer_of_register':arch/arm/include/asm/arch_timer.h:11: error: 'ENXIO' undeclared (first use in this function)
Quoted 20 lines
arch/arm/include/asm/timex.h +5
-4#include <mach/timex.h> typedef unsigned long cycles_t ; -static inline cycles_t get_cycles (void ) -{ -return 0 ; -} +#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 arch/arm/kernel/arch_timer.c +0
-0
arch/arm/kernel/arch_timer.c static cycle_t arch_counter_read(struct clocksource *cs) · line 229
Quoted 7 lines
arch/arm/kernel/arch_timer.c +4
-0@@ -223,6 +223,14 @@ static cycle_t arch_counter_read(struct clocksource *cs)return arch_counter_get_cntpct (); } +int read_current_timer (unsigned long *timer_val) +{ +if (!arch_timer_rate) +return -ENXIO ;
Shouldn't this be returning 0? Otherwise get_cycles() up there will evaluate to -ENXIO?
Signature · 2 lines
Sent by an employee of the Qualcomm Innovation Center, Inc. The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum.
PATCH 1/2 ARM: arch timer: implement read_current_timer and get_cycles
Quoted 14 lines · Stephen Boyd
arch/arm/include/asm/timex.h line 15
Quoted 9 lines · Will Deacon
arch/arm/include/asm/timex.h +1
-0@@ -12,13 +12,15 @@#ifndef _ASMARM_TIMEX_H#define _ASMARM_TIMEX_H +#include <asm/arch_timer.h> This seems to cause compilation failures if you aren't building with architected timers enabled.
Quoted 6 lines
In file included from
arch/arm/include/asm/timex.h:15,from include/linux/timex.h:174, from include/linux/sched.h:57, from arch/arm/kernel/asm-offsets.c:13:
arch/arm/include/asm/arch_timer.h: In function 'arch_timer_of_register':arch/arm/include/asm/arch_timer.h:11: error: 'ENXIO' undeclared (first use in this function)
Thanks, will fix for v2. I tested on a platform without the timers but I must've left them compiled in.
Quoted 24 lines
Quoted 24 lines
arch/arm/include/asm/timex.h +5
-4#include <mach/timex.h> typedef unsigned long cycles_t ; -static inline cycles_t get_cycles (void ) -{ -return 0 ; -} +#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 arch/arm/kernel/arch_timer.c +0
-0@@ -223,6 +223,14 @@ static cycle_t arch_counter_read(struct clocksource *cs)return arch_counter_get_cntpct (); }
Quoted 7 lines
arch/arm/kernel/arch_timer.c static cycle_t arch_counter_read(struct clocksource *cs) · line 229
Quoted 4 lines
arch/arm/kernel/arch_timer.c +4
-0+int read_current_timer (unsigned long *timer_val) +{ +if (!arch_timer_rate) +return -ENXIO ;Shouldn't this be returning 0? Otherwise
get_cycles()up there will evaluate to-ENXIO?
I don't think so. The cycle count is returned via the timer_val parameter and the get_cycles code checks the return value, so will give 0 if read_current_timer returns anything other than 0. The core calibration code (not that we call it) also expects a return value < 0 to indicate failure.
Will
Quoted 5 lines
Signature · 2 lines
Sent by an employee of the Qualcomm Innovation Center, Inc. The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum.
PATCH 1/2 ARM: arch timer: implement read_current_timer and get_cycles
Quoted 11 lines · Will Deacon
Quoted 6 lines · Stephen Boyd
arch/arm/kernel/arch_timer.c static cycle_t arch_counter_read(struct clocksource *cs) · line 229
Quoted 4 lines
arch/arm/kernel/arch_timer.c +4
-0+int read_current_timer (unsigned long *timer_val) +{ +if (!arch_timer_rate) +return -ENXIO ;Shouldn't this be returning 0? Otherwise
get_cycles()up there will evaluate to-ENXIO?I don't think so. The cycle count is returned via the
timer_valparameter and theget_cyclescode checks the return value, so will give 0 ifread_current_timerreturns anything other than 0. The core calibration code (not that we call it) also expects a return value < 0 to indicate failure.
Ah you're right. More coffee in the morning would do me some good.
Signature · 2 lines
Sent by an employee of the Qualcomm Innovation Center, Inc. The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum.
PATCH 2/2 ARM: delay: allow timer-based delay implementation to be selected
In reply to Will DeaconThis 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.
---
arch/arm/include/asm/delay.h | 19 +++++++--
arch/arm/kernel/arch_timer.c | 3 +
arch/arm/kernel/armksyms.c | 3 +-
arch/arm/lib/Makefile | 2 +-
arch/arm/lib/{delay.S => delay-loop.S} | 16 +++----
arch/arm/lib/delay.c | 66 ++++++++++++++++++++++++++++++++
arch/arm/mach-sa1100/sleep.S | 8 ++--
7 files changed, 97 insertions(+), 20 deletions(-)
rename arch/arm/lib/{delay.S => delay-loop.S} (85%)
create mode 100644 arch/arm/lib/delay.carch/arm/include/asm/delay.h +15 -4
@@ -8,7 +8,13 @@#include <asm/param.h> /* HZ */ -extern void __delay (int loops); +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,14 +29,14 @@ 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 __udelay (n) arm_delay_ops.udelay(n) +#define __const_udelay (n) arm_delay_ops.const_udelay(n)#define MAX_UDELAY_MS 2
@@ -40,5 +46,10 @@ extern void __const_udelay(unsigned long); __const_udelay((n) * ((2199023U*HZ)>>11))) : \ __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 /* defined(_ARM_DELAY_H) */
arch/arm/kernel/arch_timer.c +3 -0
@@ -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:
arch/arm/kernel/armksyms.c +1 -2
@@ -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);
arch/arm/lib/Makefile +1 -1
@@ -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 \
arch/arm/lib/delay-loop.S renamed from arch/arm/lib/delay.S +7 -9
@@ -21,10 +21,10 @@ * HZ <= 1000 */ -ENTRY(__udelay) +ENTRY(__loop_udelay) ldr r2, .LC1 mul r0, r2, r0 -ENTRY(__const_udelay) @ 0 <= r0 <= 0x7fffff06 +ENTRY(__loop_const_udelay) @ 0 <= r0 <= 0x7fffff06 mov r1, #-1 ldr r2, .LC0 ldr r2, [r2] @ max = 0x01ffffff
@@ -39,12 +39,10 @@ ENTRY(__const_udelay) @ 0 <= r0 <= 0x7fffff06 /* * loops = r0 * HZ * loops_per_jiffy / 1000000 - * - * Oh, if only we had a cycle counter... */ @ Delay routine -ENTRY(__delay) +ENTRY(__loop_delay) subs r0, r0, #1 #if 0 movls pc, lr
@@ -62,8 +60,8 @@ ENTRY(__delay) movls pc, lr subs r0, r0, #1 #endif - bhi __delay + bhi __loop_delay mov pc, lr -ENDPROC(__udelay) -ENDPROC(__const_udelay) -ENDPROC(__delay) +ENDPROC(__loop_udelay) +ENDPROC(__loop_const_udelay) +ENDPROC(__loop_delay)
arch/arm/lib/delay.c new file +66 -0
66 lines not on this page · raw message
arch/arm/mach-sa1100/sleep.S +4 -4
@@ -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:
Signature · 1 line
1.7.4.1
PATCH 2/2 ARM: delay: allow timer-based delay implementation to be selected
arch/arm/kernel/arch_timer.c static int arch_timer_ppi2; · line 35
Quoted 9 lines · Will Deacon
arch/arm/kernel/arch_timer.c +1
-0@@ -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);
Can we find a home for this in some header file?
arch/arm/kernel/arch_timer.c static int arch_timer_ppi2;
Quoted 6 lines
arch/arm/kernel/arch_timer.c +6
-0+static void __timer_const_udelay (unsigned long xloops) +{ +unsigned long long loops = xloops; + loops *= loops_per_jiffy; +__timer_delay (loops >>30 ); +}
Is it ok to have a 64 bit multiply here? It seems the assembly version tries to keep it all 32 bit math.
arch/arm/kernel/arch_timer.c
Quoted 5 lines
arch/arm/kernel/arch_timer.c +5
-0+ +static void __timer_udelay (unsigned long usecs) +{ +__timer_const_udelay (usecs * ((2199023U *HZ ) >>11 )); +}
It's unfortunate that we have to duplicate the same code and constants in both C and assembly. With my approach we convert delay.S into C and avoid the duplication.
arch/arm/kernel/arch_timer.c
Quoted 4 lines
arch/arm/kernel/arch_timer.c +4
-0+ +void __init init_current_timer_delay (unsigned long freq) +{ +pr_info ("Switching to timer-based delay loop\n" );
Might be worth printing the frequency here too.
Signature · 2 lines
Sent by an employee of the Qualcomm Innovation Center, Inc. The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum.
PATCH 2/2 ARM: delay: allow timer-based delay implementation to be selected
Quoted 7 lines · Stephen Boyd
Quoted 6 lines · Will Deacon
arch/arm/kernel/arch_timer.c +0
-0@@ -32,6 +32,8 @@ static int arch_timer_ppi2;
Quoted 5 lines
arch/arm/kernel/arch_timer.c static int arch_timer_ppi2; · line 35
Quoted 3 lines
arch/arm/kernel/arch_timer.c +1
-0static struct clock_event_device __percpu **arch_timer_evt; +extern void init_current_timer_delay (unsigned long freq);Can we find a home for this in some header file?
I wondered about that...
The reason I didn't add it to a header file is that we really don't want it to be called willy-nilly across the kernel. In fact, it must be called exactly once by the clocksource backing read_current_timer when it knows that the timer is live and ticking.
I suppose I could allow the function to fail if it's called after we've calibrated. What do you reckon?
Quoted 9 lines
arch/arm/kernel/arch_timer.c static int arch_timer_ppi2;
Quoted 6 lines
arch/arm/kernel/arch_timer.c +6
-0+static void __timer_const_udelay (unsigned long xloops) +{ +unsigned long long loops = xloops; + loops *= loops_per_jiffy; +__timer_delay (loops >>30 ); +}Is it ok to have a 64 bit multiply here? It seems the assembly version tries to keep it all 32 bit math.
It's actually a 32-bit multiply with a 64-bit result, so it's just a umull:
00000050 <__timer_const_udelay>:
50: e3003000 movw r3, #0
54: e3403000 movt r3, #0
58: e5932000 ldr r2, [r3]
5c: e0832290 umull r2, r3, r0, r2
60: e1a00f22 lsr r0, r2, #30
64: e1800103 orr r0, r0, r3, lsl #2
68: eaffffe4 b 0 <__timer_delay>
Quoted 9 lines
arch/arm/kernel/arch_timer.c
Quoted 5 lines
arch/arm/kernel/arch_timer.c +5
-0+ +static void __timer_udelay (unsigned long usecs) +{ +__timer_const_udelay (usecs * ((2199023U *HZ ) >>11 )); +}It's unfortunate that we have to duplicate the same code and constants in both C and assembly. With my approach we convert
delay.Sinto C and avoid the duplication.
It's probably easy enough to have a #define for the multiplier, I can do that for v2.
Quoted 6 lines
arch/arm/kernel/arch_timer.c
Quoted 4 lines
arch/arm/kernel/arch_timer.c +4
-0+ +void __init init_current_timer_delay (unsigned long freq) +{ +pr_info ("Switching to timer-based delay loop\n" );Might be worth printing the frequency here too.
Could do, but the timer itself probably prints that information already (at least it does the arch timer).
Cheers,
Will
PATCH 2/2 ARM: delay: allow timer-based delay implementation to be selected
Quoted 8 lines · Will Deacon
Quoted 4 lines · Stephen Boyd
It's unfortunate that we have to duplicate the same code and constants in both C and assembly. With my approach we convert
delay.Sinto C and avoid the duplication.It's probably easy enough to have a #define for the multiplier, I can do that for v2.
Looks like I spoke too soon :)
The reason this is slightly problematic is due to gas's inability to represent unsigned constants -- namely that the 'U' suffix causes it to barf. I can fix it with the diff below, but please let me know what you think.
Cheers,
Will
arch/arm/include/asm/delay.h +12 -3
@@ -8,6 +8,12 @@#include <asm/param.h> /* HZ */ +#define MAX_UDELAY_MS 2 +#define UDELAY_MULT ((UDELAY_MULT_HZ * HZ) >> 11) +#define UDELAY_SHIFT 30 + +#ifndef __ASSEMBLY__ + extern struct arm_delay_ops { void (*delay)(unsigned long); void (*const_udelay)(unsigned long);
@@ -38,12 +44,10 @@ extern void __bad_udelay(void);#define __udelay (n) arm_delay_ops.udelay(n)#define __const_udelay (n) arm_delay_ops.const_udelay(n) -#define MAX_UDELAY_MS 2 -#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. */
@@ -51,5 +55,10 @@ extern void __loop_delay(unsigned long loops);extern void __loop_udelay (unsigned long usecs);extern void __loop_const_udelay (unsigned long ); +#define UDELAY_MULT_HZ 2199023U +#else +#define UDELAY_MULT_HZ 2199023 +#endif /* __ASSEMBLY__ */ +#endif /* defined(_ARM_DELAY_H) */
arch/arm/lib/delay-loop.S +2 -2
@@ -9,11 +9,11 @@ */ #include <linux/linkage.h> #include <asm/assembler.h> -#include <asm/param.h> +#include <asm/delay.h> .text .LC0: .word loops_per_jiffy -.LC1: .word (2199023*HZ)>>11 +.LC1: .word UDELAY_MULT /* * r0 <= 2000
arch/arm/lib/delay.c +2 -2
@@ -47,12 +47,12 @@ static void __timer_const_udelay(unsigned long xloops) {unsigned long long loops = xloops; loops *= loops_per_jiffy; -__timer_delay (loops >>30 ); +__timer_delay (loops >>UDELAY_SHIFT ); }static void __timer_udelay (unsigned long usecs) { -__timer_const_udelay (usecs * ((2199023U *HZ ) >>11 )); +__timer_const_udelay (usecs *UDELAY_MULT ); }void __init init_current_timer_delay (unsigned long freq)
PATCH 2/2 ARM: delay: allow timer-based delay implementation to be selected
Quoted 16 lines · Will Deacon
Quoted 8 lines · Will Deacon
Quoted 4 lines · Stephen Boyd
It's unfortunate that we have to duplicate the same code and constants in both C and assembly. With my approach we convert
delay.Sinto C and avoid the duplication.It's probably easy enough to have a #define for the multiplier, I can do that for v2.
Looks like I spoke too soon :)
The reason this is slightly problematic is due to gas's inability to represent unsigned constants -- namely that the 'U' suffix causes it to barf. I can fix it with the diff below, but please let me know what you think.
[snip]
arch/arm/include/asm/delay.h extern void __loop_delay(unsigned long loops); · line 60
Quoted 7 lines
arch/arm/include/asm/delay.h +5
-0+#ifndef __ASSEMBLY__ + +#define UDELAY_MULT_HZ 2199023U +#else +#define UDELAY_MULT_HZ 2199023
Doesn't UL() macro do what you need?
Rob
Quoted 46 lines
arch/arm/include/asm/delay.h +2
-0+#endif /* __ASSEMBLY__ */ +#endif /* defined(_ARM_DELAY_H) */ arch/arm/lib/delay-loop.S +2
-2@@ -9,11 +9,11 @@ */ #include <linux/linkage.h> #include <asm/assembler.h> -#include <asm/param.h> +#include <asm/delay.h> .text .LC0: .word loops_per_jiffy -.LC1: .word (2199023*HZ)>>11 +.LC1: .word UDELAY_MULT /* * r0 <= 2000arch/arm/lib/delay.c +2
-2@@ -47,12 +47,12 @@ static void __timer_const_udelay(unsigned long xloops) {unsigned long long loops = xloops; loops *= loops_per_jiffy; -__timer_delay (loops >>30 ); +__timer_delay (loops >>UDELAY_SHIFT ); }static void __timer_udelay (unsigned long usecs) { -__timer_const_udelay (usecs * ((2199023U *HZ ) >>11 )); +__timer_const_udelay (usecs *UDELAY_MULT ); }void __init init_current_timer_delay (unsigned long freq)_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel at lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
PATCH 2/2 ARM: delay: allow timer-based delay implementation to be selected
Quoted 12 lines · Rob Herring
Quoted 4 lines · Will Deacon
The reason this is slightly problematic is due to gas's inability to represent unsigned constants -- namely that the 'U' suffix causes it to barf. I can fix it with the diff below, but please let me know what you think.
[snip]
Quoted 2 lines
arch/arm/include/asm/delay.h +2
-0+#ifndef __ASSEMBLY__ +
Quoted 5 lines
arch/arm/include/asm/delay.h extern void __loop_delay(unsigned long loops); · line 60
Quoted 3 lines
arch/arm/include/asm/delay.h +3
-0+#define UDELAY_MULT_HZ 2199023U +#else +#define UDELAY_MULT_HZ 2199023Doesn't
UL()macro do what you need?
Indeed, thanks for pointing that out as I'd failed to spot it. I'll include this change for v2.
Will
PATCH 2/2 ARM: delay: allow timer-based delay implementation to be selected
In reply to Will DeaconQuoted 14 lines · Will Deacon
Quoted 11 lines · Stephen Boyd
arch/arm/kernel/arch_timer.c static int arch_timer_ppi2; · line 35
Quoted 9 lines · Will Deacon
arch/arm/kernel/arch_timer.c +1
-0@@ -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);Can we find a home for this in some header file?
I wondered about that...
Quoted 8 lines
The reason I didn't add it to a header file is that we really don't want it to be called willy-nilly across the kernel. In fact, it must be called exactly once by the clocksource backing
read_current_timerwhen it knows that the timer is live and ticking.I suppose I could allow the function to fail if it's called after we've calibrated. What do you reckon?
Fair enough. Would anything actually go wrong if you called it twice? I would think everything would be assigned to what it already is but I haven't thought deeply about it. I don't really care to make the function more complicated for a case that should never happen.
Quoted 10 lines
It's actually a 32-bit multiply with a 64-bit result, so it's just a umull:
00000050 <
__timer_const_udelay>:
50:e3003000movw r3, #0
54:e3403000movt r3, #0
58:e5932000ldr r2, [r3]
5c:e0832290umull r2, r3, r0, r2
60:e1a00f22lsr r0, r2, #30
64:e1800103orr r0, r0, r3, lsl #2
68:eaffffe4b 0 <__timer_delay>
Ok. Maybe Russell can comment further. Or maybe it doesn't matter to save some cycles after Linus said that udelay() doesn't need to be that accurate.
Quoted 5 lines
Quoted 3 lines
It's unfortunate that we have to duplicate the same code and constants in both C and assembly. With my approach we convert
delay.Sinto C and avoid the duplication.It's probably easy enough to have a #define for the multiplier, I can do that for v2.
I look forward to seeing how v2 works out.
Quoted 8 lines
Quoted 6 lines
arch/arm/kernel/arch_timer.c static int arch_timer_ppi2; · line 39
Quoted 5 lines
arch/arm/kernel/arch_timer.c +4
-0+ +void __init init_current_timer_delay (unsigned long freq) +{ +pr_info ("Switching to timer-based delay loop\n" );Might be worth printing the frequency here too.
Could do, but the timer itself probably prints that information already (at least it does the arch timer).
Sure. Thinking more about it I don't like my suggestion.
Signature · 2 lines
Sent by an employee of the Qualcomm Innovation Center, Inc. The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum.
PATCH 2/2 ARM: delay: allow timer-based delay implementation to be selected
Quoted 9 lines · Stephen Boyd
Quoted 3 lines · Will Deacon
I suppose I could allow the function to fail if it's called after we've calibrated. What do you reckon?
Fair enough. Would anything actually go wrong if you called it twice? I would think everything would be assigned to what it already is but I haven't thought deeply about it. I don't really care to make the function more complicated for a case that should never happen.
If it's called once before calibration and again after calibration then that could cause problems if a different frequency is passed. However, I think we can probably leave the function as-is until we have the capacity to support multiple read_current_timer implementations in the same kernel. At that point, we'd probably have a way to register the current_timer which could also setup the delay loop.
Will
cover Use architected timers for delay loop
In reply to Will DeaconQuoted 11 lines · Will Deacon
Hello,
This patch series reworks our delay loop implementation to allow the busy-loop to be replaced with the physical counter included as part of the architected timer on recent CPUs. The polling implementation is still maintained to support cores without the timers and we use the same maths and upper delay bound of 2ms.
Russell - I think my maths is correct, but why can't we increase the
precision by using a magic constant of 4398046U and a right shift of 31?
It's been soo long since I looked at that code that I've forgotten the maths behind it.
What I can say is that the maths in the assembly code version is carefully chosen to avoid overflows during the various stages of computation without loosing too much precision along the way.
The assembly is also designed to round up any lost precision, so we try to produce a longer delay by way of the lost precision in the maths.
I forget what kind of precision we do get, but precision is not really a major concern here. We are only after a "rough" estimate of the delay (hell, we don't even guarantee according to Linus that you will get a minimum of the delay you asked for - you might get a shorter delay!) You _will_ get shorter delays because of the way the loop is calibrated (think: the jiffy code tries to find how many loops fit in a single jiffy, but a jiffy _also_ includes the overhead of running the timer interrupt, which artificially reduces loops_per_jiffy.)
Provided we get a delay "in the ballpark" of what was requested, that's good enough.
cover Use architected timers for delay loop
Hi Russell,
Thanks for the clarification.
Quoted 7 lines · Russell King - ARM Linux
Quoted 3 lines · Will Deacon
Russell - I think my maths is correct, but why can't we increase the
precision by using a magic constant of 4398046U and a right shift of 31?It's been soo long since I looked at that code that I've forgotten the maths behind it.
No problem. I managed to convince myself of the maths but I don't want to touch the assembly code in delay.S if I don't have to!
Quoted 7 lines
What I can say is that the maths in the assembly code version is carefully chosen to avoid overflows during the various stages of computation without loosing too much precision along the way.
The assembly is also designed to round up any lost precision, so we try to produce a longer delay by way of the lost precision in the maths.
Right, that probably explains why I was having difficulty following it.
Quoted 8 lines
I forget what kind of precision we do get, but precision is not really a major concern here. We are only after a "rough" estimate of the delay (hell, we don't even guarantee according to Linus that you will get a minimum of the delay you asked for - you might get a shorter delay!) You _will_ get shorter delays because of the way the loop is calibrated (think: the jiffy code tries to find how many loops fit in a single jiffy, but a jiffy _also_ includes the overhead of running the timer interrupt, which artificially reduces
loops_per_jiffy.)
We avoid the calibration step for the timer-based loop and use the counter frequency directly so I think we probably always delay for slightly longer in that case. But yes, I see that precision isn't the be-all and end-all here!
Quoted 2 lines
Provided we get a delay "in the ballpark" of what was requested, that's good enough.
In which case, let's leave the maths like it is. The current values work fine with both the polling loop and the timer loop up to 2ms.
Cheers,
Will
cover Use architected timers for delay loop
In reply to Will DeaconQuoted 8 lines · Will Deacon
Hello,
This patch series reworks our delay loop implementation to allow the busy-loop to be replaced with the physical counter included as part of the architected timer on recent CPUs. The polling implementation is still maintained to support cores without the timers and we use the same maths and upper delay bound of 2ms.
Quoted 5 lines
Russell - I think my maths is correct, but why can't we increase the
precision by using a magic constant of 4398046U and a right shift of 31?As usual, all feedback is welcome,
I have posted a read_current_timer implementation to the list a couple times but had no success in getting it merged. The patches are still in the patch tracker but I haven't really pushed them to get merged.
http://www.arm.linux.org.uk/developer/patches/viewpatch.php?id=6874/1 http://www.arm.linux.org.uk/developer/patches/viewpatch.php?id=6873/1 http://www.arm.linux.org.uk/developer/patches/viewpatch.php?id=6875/1
So far we've just been carrying them internally. I suppose ARM needs this now because of big little?
Signature · 2 lines
Sent by an employee of the Qualcomm Innovation Center, Inc. The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum.
cover Use architected timers for delay loop
Hi Stephen,
Thanks for taking a look at the series!
Quoted 7 lines · Stephen Boyd
Quoted 5 lines · Will Deacon
This patch series reworks our delay loop implementation to allow the busy-loop to be replaced with the physical counter included as part of the architected timer on recent CPUs. The polling implementation is still maintained to support cores without the timers and we use the same maths and upper delay bound of 2ms.
Quoted 7 lines
I have posted a
read_current_timerimplementation to the list a couple times but had no success in getting it merged. The patches are still in the patch tracker but I haven't really pushed them to get merged.http://www.arm.linux.org.uk/developer/patches/viewpatch.php?id=6874/1 http://www.arm.linux.org.uk/developer/patches/viewpatch.php?id=6873/1 http://www.arm.linux.org.uk/developer/patches/viewpatch.php?id=6875/1
I took a look at these but I really shy away from using memory-mapped clocksources for delay -- it feels like we're asking for problems if we go down that route. Maybe it could work, but switching from the polling loop to the clocksource would surely require some recalibration?
Quoted 2 lines
So far we've just been carrying them internally. I suppose ARM needs this now because of big little?
To be honest, I just implemented this because it allows us to skip the calibration and I thought the solution fell out quite neatly. It does also remove the need for re-calibration when scaling the core clock, so yes it would be useful whenever that occurs.
Will
cover Use architected timers for delay loop
Quoted 5 lines · Will Deacon
Quoted 4 lines · Stephen Boyd
I have posted a
read_current_timerimplementation to the list a couple times but had no success in getting it merged. The patches are still in the patch tracker but I haven't really pushed them to get merged.
Quoted 7 lines
Quoted 3 lines
http://www.arm.linux.org.uk/developer/patches/viewpatch.php?id=6874/1 http://www.arm.linux.org.uk/developer/patches/viewpatch.php?id=6873/1 http://www.arm.linux.org.uk/developer/patches/viewpatch.php?id=6875/1I took a look at these but I really shy away from using memory-mapped clocksources for delay -- it feels like we're asking for problems if we go down that route. Maybe it could work, but switching from the polling loop to the clocksource would surely require some recalibration?
We never switch from polling to clocksource (or vice versa) after the calibration is done in calibrate_delay(). The lpj calculation is always based on the clocksource using calibrate_delay_direct(). This looks to be pretty much like what your patches are doing but you skip the calibration step by setting lpj_fine, which is even better.
Even then, I don't understand why the series scares you that much. You could just define read_current_timer() for architected timers like you already have and then the series would work just the same with coprocessor accesses. The benefit is no code duplication. I like how you've implemented get_cycle(), but that's a minor difference.
Signature · 2 lines
Sent by an employee of the Qualcomm Innovation Center, Inc. The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum.