aboutsummaryrefslogtreecommitdiffstats
path: root/arch/arm/kernel/smp_twd.c
diff options
context:
space:
mode:
authorLinus Walleij <linus.walleij@linaro.org>2012-10-23 03:29:48 -0400
committerRussell King <rmk+kernel@arm.linux.org.uk>2012-11-04 05:31:02 -0500
commita68becd1dcda55b467dcabaff136cadc10abb761 (patch)
tree5340a006f3a058b5aa1f40c81bbadcb50c762295 /arch/arm/kernel/smp_twd.c
parent2577cf246233b1e4e38576f28a5ec05c9c6a6c2a (diff)
ARM: 7563/1: SMP_TWD: make setup()/stop() reentrant
It has been brought to my knowledge that the .setup()/.stop() function pair in the SMP TWD is going to be called from atomic contexts for CPUs coming and going, and then the clk_prepare()/clk_unprepare() calls cannot be called on subsequent .setup()/.stop() iterations. This is however just the tip of an iceberg as the function pair is not designed to be reentrant at all. This change makes the SMP_TWD clock .setup()/.stop() pair reentrant by splitting the .setup() function in three parts: - One COMMON part that is executed the first time the first CPU in the TWD cluster is initialized. This will fetch the TWD clk for the cluster and prepare+enable it. If no clk is available it will calibrate the rate instead. - One part that is executed the FIRST TIME a certain CPU is brought on-line. This initializes and sets up the clock event for a certain CPU. - One part that is executed on every subsequent .setup() call. This will re-initialize the clock event. This is augmented to call the clk_enable()/clk_disable() pair properly. Cc: Shawn Guo <shawn.guo@linaro.org> Reported-by: Peter Chen <peter.chen@freescale.com> Reviewed-by: Santosh Shilimkar <santosh.shilimkar@ti.com> Tested-by: Shawn Guo <shawn.guo@linaro.org> Signed-off-by: Linus Walleij <linus.walleij@linaro.org> Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
Diffstat (limited to 'arch/arm/kernel/smp_twd.c')
-rw-r--r--arch/arm/kernel/smp_twd.c42
1 files changed, 37 insertions, 5 deletions
diff --git a/arch/arm/kernel/smp_twd.c b/arch/arm/kernel/smp_twd.c
index 780b05706364..a2e74375945a 100644
--- a/arch/arm/kernel/smp_twd.c
+++ b/arch/arm/kernel/smp_twd.c
@@ -31,6 +31,8 @@ static void __iomem *twd_base;
31 31
32static struct clk *twd_clk; 32static struct clk *twd_clk;
33static unsigned long twd_timer_rate; 33static unsigned long twd_timer_rate;
34static bool common_setup_called;
35static DEFINE_PER_CPU(bool, percpu_setup_called);
34 36
35static struct clock_event_device __percpu **twd_evt; 37static struct clock_event_device __percpu **twd_evt;
36static int twd_ppi; 38static int twd_ppi;
@@ -264,15 +266,45 @@ static struct clk *twd_get_clock(void)
264static int __cpuinit twd_timer_setup(struct clock_event_device *clk) 266static int __cpuinit twd_timer_setup(struct clock_event_device *clk)
265{ 267{
266 struct clock_event_device **this_cpu_clk; 268 struct clock_event_device **this_cpu_clk;
269 int cpu = smp_processor_id();
267 270
268 if (!twd_clk) 271 /*
272 * If the basic setup for this CPU has been done before don't
273 * bother with the below.
274 */
275 if (per_cpu(percpu_setup_called, cpu)) {
276 __raw_writel(0, twd_base + TWD_TIMER_CONTROL);
277 clockevents_register_device(*__this_cpu_ptr(twd_evt));
278 enable_percpu_irq(clk->irq, 0);
279 return 0;
280 }
281 per_cpu(percpu_setup_called, cpu) = true;
282
283 /*
284 * This stuff only need to be done once for the entire TWD cluster
285 * during the runtime of the system.
286 */
287 if (!common_setup_called) {
269 twd_clk = twd_get_clock(); 288 twd_clk = twd_get_clock();
270 289
271 if (!IS_ERR_OR_NULL(twd_clk)) 290 /*
272 twd_timer_rate = clk_get_rate(twd_clk); 291 * We use IS_ERR_OR_NULL() here, because if the clock stubs
273 else 292 * are active we will get a valid clk reference which is
274 twd_calibrate_rate(); 293 * however NULL and will return the rate 0. In that case we
294 * need to calibrate the rate instead.
295 */
296 if (!IS_ERR_OR_NULL(twd_clk))
297 twd_timer_rate = clk_get_rate(twd_clk);
298 else
299 twd_calibrate_rate();
300
301 common_setup_called = true;
302 }
275 303
304 /*
305 * The following is done once per CPU the first time .setup() is
306 * called.
307 */
276 __raw_writel(0, twd_base + TWD_TIMER_CONTROL); 308 __raw_writel(0, twd_base + TWD_TIMER_CONTROL);
277 309
278 clk->name = "local_timer"; 310 clk->name = "local_timer";