Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit a68becd1 authored by Linus Walleij's avatar Linus Walleij Committed by Russell King
Browse files

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: default avatarPeter Chen <peter.chen@freescale.com>
Reviewed-by: default avatarSantosh Shilimkar <santosh.shilimkar@ti.com>
Tested-by: default avatarShawn Guo <shawn.guo@linaro.org>
Signed-off-by: default avatarLinus Walleij <linus.walleij@linaro.org>
Signed-off-by: default avatarRussell King <rmk+kernel@arm.linux.org.uk>
parent 2577cf24
Loading
Loading
Loading
Loading
+37 −5
Original line number Original line Diff line number Diff line
@@ -31,6 +31,8 @@ static void __iomem *twd_base;


static struct clk *twd_clk;
static struct clk *twd_clk;
static unsigned long twd_timer_rate;
static unsigned long twd_timer_rate;
static bool common_setup_called;
static DEFINE_PER_CPU(bool, percpu_setup_called);


static struct clock_event_device __percpu **twd_evt;
static struct clock_event_device __percpu **twd_evt;
static int twd_ppi;
static int twd_ppi;
@@ -264,15 +266,45 @@ static struct clk *twd_get_clock(void)
static int __cpuinit twd_timer_setup(struct clock_event_device *clk)
static int __cpuinit twd_timer_setup(struct clock_event_device *clk)
{
{
	struct clock_event_device **this_cpu_clk;
	struct clock_event_device **this_cpu_clk;
	int cpu = smp_processor_id();


	if (!twd_clk)
	/*
	 * If the basic setup for this CPU has been done before don't
	 * bother with the below.
	 */
	if (per_cpu(percpu_setup_called, cpu)) {
		__raw_writel(0, twd_base + TWD_TIMER_CONTROL);
		clockevents_register_device(*__this_cpu_ptr(twd_evt));
		enable_percpu_irq(clk->irq, 0);
		return 0;
	}
	per_cpu(percpu_setup_called, cpu) = true;

	/*
	 * This stuff only need to be done once for the entire TWD cluster
	 * during the runtime of the system.
	 */
	if (!common_setup_called) {
		twd_clk = twd_get_clock();
		twd_clk = twd_get_clock();


		/*
		 * We use IS_ERR_OR_NULL() here, because if the clock stubs
		 * are active we will get a valid clk reference which is
		 * however NULL and will return the rate 0. In that case we
		 * need to calibrate the rate instead.
		 */
		if (!IS_ERR_OR_NULL(twd_clk))
		if (!IS_ERR_OR_NULL(twd_clk))
			twd_timer_rate = clk_get_rate(twd_clk);
			twd_timer_rate = clk_get_rate(twd_clk);
		else
		else
			twd_calibrate_rate();
			twd_calibrate_rate();


		common_setup_called = true;
	}

	/*
	 * The following is done once per CPU the first time .setup() is
	 * called.
	 */
	__raw_writel(0, twd_base + TWD_TIMER_CONTROL);
	__raw_writel(0, twd_base + TWD_TIMER_CONTROL);


	clk->name = "local_timer";
	clk->name = "local_timer";