diff options
author | Jonathan Austin <Jonathan.Austin@arm.com> | 2012-09-21 13:51:44 -0400 |
---|---|---|
committer | Russell King <rmk+kernel@arm.linux.org.uk> | 2012-09-26 17:57:52 -0400 |
commit | 56942fec06efa0e17df0f4c3b438332c923b9014 (patch) | |
tree | 89b604c987b7f6a0eaaa16af7a9d8f45711349a7 | |
parent | a1b2dde70419ae947fd7c9c8fcad7da005dc600e (diff) |
ARM: 7538/1: delay: add registration mechanism for delay timer sources
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>
-rw-r--r-- | arch/arm/include/asm/arch_timer.h | 1 | ||||
-rw-r--r-- | arch/arm/include/asm/delay.h | 9 | ||||
-rw-r--r-- | arch/arm/include/asm/timex.h | 6 | ||||
-rw-r--r-- | arch/arm/kernel/arch_timer.c | 17 | ||||
-rw-r--r-- | arch/arm/lib/delay.c | 34 |
5 files changed, 43 insertions, 24 deletions
diff --git a/arch/arm/include/asm/arch_timer.h b/arch/arm/include/asm/arch_timer.h index ad9b155c8262..d40229d9a1c9 100644 --- a/arch/arm/include/asm/arch_timer.h +++ b/arch/arm/include/asm/arch_timer.h | |||
@@ -5,7 +5,6 @@ | |||
5 | #include <linux/clocksource.h> | 5 | #include <linux/clocksource.h> |
6 | 6 | ||
7 | #ifdef CONFIG_ARM_ARCH_TIMER | 7 | #ifdef CONFIG_ARM_ARCH_TIMER |
8 | #define ARCH_HAS_READ_CURRENT_TIMER | ||
9 | int arch_timer_of_register(void); | 8 | int arch_timer_of_register(void); |
10 | int arch_timer_sched_clock_init(void); | 9 | int arch_timer_sched_clock_init(void); |
11 | struct timecounter *arch_timer_get_timecounter(void); | 10 | struct timecounter *arch_timer_get_timecounter(void); |
diff --git a/arch/arm/include/asm/delay.h b/arch/arm/include/asm/delay.h index dc6145120de3..ab98fdd083bd 100644 --- a/arch/arm/include/asm/delay.h +++ b/arch/arm/include/asm/delay.h | |||
@@ -15,6 +15,11 @@ | |||
15 | 15 | ||
16 | #ifndef __ASSEMBLY__ | 16 | #ifndef __ASSEMBLY__ |
17 | 17 | ||
18 | struct delay_timer { | ||
19 | unsigned long (*read_current_timer)(void); | ||
20 | unsigned long freq; | ||
21 | }; | ||
22 | |||
18 | extern struct arm_delay_ops { | 23 | extern struct arm_delay_ops { |
19 | void (*delay)(unsigned long); | 24 | void (*delay)(unsigned long); |
20 | void (*const_udelay)(unsigned long); | 25 | void (*const_udelay)(unsigned long); |
@@ -56,6 +61,10 @@ extern void __loop_delay(unsigned long loops); | |||
56 | extern void __loop_udelay(unsigned long usecs); | 61 | extern void __loop_udelay(unsigned long usecs); |
57 | extern void __loop_const_udelay(unsigned long); | 62 | extern void __loop_const_udelay(unsigned long); |
58 | 63 | ||
64 | /* Delay-loop timer registration. */ | ||
65 | #define ARCH_HAS_READ_CURRENT_TIMER | ||
66 | extern void register_current_timer_delay(const struct delay_timer *timer); | ||
67 | |||
59 | #endif /* __ASSEMBLY__ */ | 68 | #endif /* __ASSEMBLY__ */ |
60 | 69 | ||
61 | #endif /* defined(_ARM_DELAY_H) */ | 70 | #endif /* defined(_ARM_DELAY_H) */ |
diff --git a/arch/arm/include/asm/timex.h b/arch/arm/include/asm/timex.h index ce119442277c..9acc135dad94 100644 --- a/arch/arm/include/asm/timex.h +++ b/arch/arm/include/asm/timex.h | |||
@@ -12,15 +12,9 @@ | |||
12 | #ifndef _ASMARM_TIMEX_H | 12 | #ifndef _ASMARM_TIMEX_H |
13 | #define _ASMARM_TIMEX_H | 13 | #define _ASMARM_TIMEX_H |
14 | 14 | ||
15 | #include <asm/arch_timer.h> | ||
16 | #include <mach/timex.h> | 15 | #include <mach/timex.h> |
17 | 16 | ||
18 | typedef unsigned long cycles_t; | 17 | typedef unsigned long cycles_t; |
19 | |||
20 | #ifdef ARCH_HAS_READ_CURRENT_TIMER | ||
21 | #define get_cycles() ({ cycles_t c; read_current_timer(&c) ? 0 : c; }) | 18 | #define get_cycles() ({ cycles_t c; read_current_timer(&c) ? 0 : c; }) |
22 | #else | ||
23 | #define get_cycles() (0) | ||
24 | #endif | ||
25 | 19 | ||
26 | #endif | 20 | #endif |
diff --git a/arch/arm/kernel/arch_timer.c b/arch/arm/kernel/arch_timer.c index c04c2a6da470..c8ef20747ee7 100644 --- a/arch/arm/kernel/arch_timer.c +++ b/arch/arm/kernel/arch_timer.c | |||
@@ -21,6 +21,7 @@ | |||
21 | #include <linux/io.h> | 21 | #include <linux/io.h> |
22 | 22 | ||
23 | #include <asm/cputype.h> | 23 | #include <asm/cputype.h> |
24 | #include <asm/delay.h> | ||
24 | #include <asm/localtimer.h> | 25 | #include <asm/localtimer.h> |
25 | #include <asm/arch_timer.h> | 26 | #include <asm/arch_timer.h> |
26 | #include <asm/system_info.h> | 27 | #include <asm/system_info.h> |
@@ -39,8 +40,7 @@ enum ppi_nr { | |||
39 | static int arch_timer_ppi[MAX_TIMER_PPI]; | 40 | static int arch_timer_ppi[MAX_TIMER_PPI]; |
40 | 41 | ||
41 | static struct clock_event_device __percpu **arch_timer_evt; | 42 | static struct clock_event_device __percpu **arch_timer_evt; |
42 | 43 | static struct delay_timer arch_delay_timer; | |
43 | extern void init_current_timer_delay(unsigned long freq); | ||
44 | 44 | ||
45 | static bool arch_timer_use_virtual = true; | 45 | static bool arch_timer_use_virtual = true; |
46 | 46 | ||
@@ -325,12 +325,9 @@ static cycle_t arch_counter_read(struct clocksource *cs) | |||
325 | return arch_counter_get_cntpct(); | 325 | return arch_counter_get_cntpct(); |
326 | } | 326 | } |
327 | 327 | ||
328 | int read_current_timer(unsigned long *timer_val) | 328 | static unsigned long arch_timer_read_current_timer(void) |
329 | { | 329 | { |
330 | if (!arch_timer_rate) | 330 | return arch_counter_get_cntpct(); |
331 | return -ENXIO; | ||
332 | *timer_val = arch_counter_get_cntpct(); | ||
333 | return 0; | ||
334 | } | 331 | } |
335 | 332 | ||
336 | static cycle_t arch_counter_read_cc(const struct cyclecounter *cc) | 333 | static cycle_t arch_counter_read_cc(const struct cyclecounter *cc) |
@@ -441,11 +438,13 @@ static int __init arch_timer_register(void) | |||
441 | arch_timer_global_evt.cpumask = cpumask_of(0); | 438 | arch_timer_global_evt.cpumask = cpumask_of(0); |
442 | err = arch_timer_setup(&arch_timer_global_evt); | 439 | err = arch_timer_setup(&arch_timer_global_evt); |
443 | } | 440 | } |
444 | |||
445 | if (err) | 441 | if (err) |
446 | goto out_free_irq; | 442 | goto out_free_irq; |
447 | 443 | ||
448 | init_current_timer_delay(arch_timer_rate); | 444 | /* Use the architected timer for the delay loop. */ |
445 | arch_delay_timer.read_current_timer = &arch_timer_read_current_timer; | ||
446 | arch_delay_timer.freq = arch_timer_rate; | ||
447 | register_current_timer_delay(&arch_delay_timer); | ||
449 | return 0; | 448 | return 0; |
450 | 449 | ||
451 | out_free_irq: | 450 | out_free_irq: |
diff --git a/arch/arm/lib/delay.c b/arch/arm/lib/delay.c index d6dacc69254e..9d0a30032d7f 100644 --- a/arch/arm/lib/delay.c +++ b/arch/arm/lib/delay.c | |||
@@ -34,7 +34,18 @@ struct arm_delay_ops arm_delay_ops = { | |||
34 | .udelay = __loop_udelay, | 34 | .udelay = __loop_udelay, |
35 | }; | 35 | }; |
36 | 36 | ||
37 | #ifdef ARCH_HAS_READ_CURRENT_TIMER | 37 | static const struct delay_timer *delay_timer; |
38 | static bool delay_calibrated; | ||
39 | |||
40 | int read_current_timer(unsigned long *timer_val) | ||
41 | { | ||
42 | if (!delay_timer) | ||
43 | return -ENXIO; | ||
44 | |||
45 | *timer_val = delay_timer->read_current_timer(); | ||
46 | return 0; | ||
47 | } | ||
48 | |||
38 | static void __timer_delay(unsigned long cycles) | 49 | static void __timer_delay(unsigned long cycles) |
39 | { | 50 | { |
40 | cycles_t start = get_cycles(); | 51 | cycles_t start = get_cycles(); |
@@ -55,17 +66,24 @@ static void __timer_udelay(unsigned long usecs) | |||
55 | __timer_const_udelay(usecs * UDELAY_MULT); | 66 | __timer_const_udelay(usecs * UDELAY_MULT); |
56 | } | 67 | } |
57 | 68 | ||
58 | void __init init_current_timer_delay(unsigned long freq) | 69 | void __init register_current_timer_delay(const struct delay_timer *timer) |
59 | { | 70 | { |
60 | pr_info("Switching to timer-based delay loop\n"); | 71 | if (!delay_calibrated) { |
61 | lpj_fine = freq / HZ; | 72 | pr_info("Switching to timer-based delay loop\n"); |
62 | arm_delay_ops.delay = __timer_delay; | 73 | delay_timer = timer; |
63 | arm_delay_ops.const_udelay = __timer_const_udelay; | 74 | lpj_fine = timer->freq / HZ; |
64 | arm_delay_ops.udelay = __timer_udelay; | 75 | loops_per_jiffy = lpj_fine; |
76 | arm_delay_ops.delay = __timer_delay; | ||
77 | arm_delay_ops.const_udelay = __timer_const_udelay; | ||
78 | arm_delay_ops.udelay = __timer_udelay; | ||
79 | delay_calibrated = true; | ||
80 | } else { | ||
81 | pr_info("Ignoring duplicate/late registration of read_current_timer delay\n"); | ||
82 | } | ||
65 | } | 83 | } |
66 | 84 | ||
67 | unsigned long __cpuinit calibrate_delay_is_known(void) | 85 | unsigned long __cpuinit calibrate_delay_is_known(void) |
68 | { | 86 | { |
87 | delay_calibrated = true; | ||
69 | return lpj_fine; | 88 | return lpj_fine; |
70 | } | 89 | } |
71 | #endif | ||