Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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>

authored by

Linus Walleij and committed by
Russell King
a68becd1 2577cf24

+37 -5
+37 -5
arch/arm/kernel/smp_twd.c
··· 31 31 32 32 static struct clk *twd_clk; 33 33 static unsigned long twd_timer_rate; 34 + static bool common_setup_called; 35 + static DEFINE_PER_CPU(bool, percpu_setup_called); 34 36 35 37 static struct clock_event_device __percpu **twd_evt; 36 38 static int twd_ppi; ··· 266 264 static int __cpuinit twd_timer_setup(struct clock_event_device *clk) 267 265 { 268 266 struct clock_event_device **this_cpu_clk; 267 + int cpu = smp_processor_id(); 269 268 270 - if (!twd_clk) 269 + /* 270 + * If the basic setup for this CPU has been done before don't 271 + * bother with the below. 272 + */ 273 + if (per_cpu(percpu_setup_called, cpu)) { 274 + __raw_writel(0, twd_base + TWD_TIMER_CONTROL); 275 + clockevents_register_device(*__this_cpu_ptr(twd_evt)); 276 + enable_percpu_irq(clk->irq, 0); 277 + return 0; 278 + } 279 + per_cpu(percpu_setup_called, cpu) = true; 280 + 281 + /* 282 + * This stuff only need to be done once for the entire TWD cluster 283 + * during the runtime of the system. 284 + */ 285 + if (!common_setup_called) { 271 286 twd_clk = twd_get_clock(); 272 287 273 - if (!IS_ERR_OR_NULL(twd_clk)) 274 - twd_timer_rate = clk_get_rate(twd_clk); 275 - else 276 - twd_calibrate_rate(); 288 + /* 289 + * We use IS_ERR_OR_NULL() here, because if the clock stubs 290 + * are active we will get a valid clk reference which is 291 + * however NULL and will return the rate 0. In that case we 292 + * need to calibrate the rate instead. 293 + */ 294 + if (!IS_ERR_OR_NULL(twd_clk)) 295 + twd_timer_rate = clk_get_rate(twd_clk); 296 + else 297 + twd_calibrate_rate(); 277 298 299 + common_setup_called = true; 300 + } 301 + 302 + /* 303 + * The following is done once per CPU the first time .setup() is 304 + * called. 305 + */ 278 306 __raw_writel(0, twd_base + TWD_TIMER_CONTROL); 279 307 280 308 clk->name = "local_timer";