Blob Blame History Raw
From: =?UTF-8?q?Cl=C3=A9ment=20P=C3=A9ron?= <peron.clem@gmail.com>
Date: Mon, 13 Jan 2020 10:23:13 +0100
Subject: pwm: sun4i: Move pwm_calculate() out of spin_lock()
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Git-commit: 3e954d9626895d374704bb49a8fb538a974ebcf5
Patch-mainline: v5.6-rc1
References: git-fixes

pwm_calculate() calls clk_get_rate() while holding a spin_lock().

This create an issue as clk_get_rate() may sleep.

Move pwm_calculate() out of this spin_lock().

Fixes: c32c5c50d4fe ("pwm: sun4i: Switch to atomic PWM")
Reported-by: Alexander Finger <alex.mobigo@gmail.com>
Sugested-by: Vasily Khoruzhick <anarsoul@gmail.com>
Tested-by: Alexander Finger <alex.mobigo@gmail.com>
Reviewed-by: Uwe Kleine-König <u.kleine-koenig@pengutronix.de>
Signed-off-by: Clément Péron <peron.clem@gmail.com>
Signed-off-by: Thierry Reding <thierry.reding@gmail.com>
Acked-by: Nicolas Saenz Julienne <nsaenzjulienne@suse.de>
---
 drivers/pwm/pwm-sun4i.c |   14 ++++++++------
 1 file changed, 8 insertions(+), 6 deletions(-)

--- a/drivers/pwm/pwm-sun4i.c
+++ b/drivers/pwm/pwm-sun4i.c
@@ -205,6 +205,8 @@ static int sun4i_pwm_apply(struct pwm_ch
 			   struct pwm_state *state)
 {
 	struct sun4i_pwm_chip *sun4i_pwm = to_sun4i_pwm_chip(chip);
+	u32 period = 0, duty = 0, val = 0;
+	unsigned int prescaler = 0;
 	struct pwm_state cstate;
 	u32 ctrl;
 	int ret;
@@ -221,24 +223,24 @@ static int sun4i_pwm_apply(struct pwm_ch
 		}
 	}
 
-	spin_lock(&sun4i_pwm->ctrl_lock);
-	ctrl = sun4i_pwm_readl(sun4i_pwm, PWM_CTRL_REG);
-
 	if ((cstate.period != state->period) ||
 	    (cstate.duty_cycle != state->duty_cycle)) {
-		u32 period, duty, val;
-		unsigned int prescaler;
 
 		ret = sun4i_pwm_calculate(sun4i_pwm, state,
 					  &duty, &period, &prescaler);
 		if (ret) {
 			dev_err(chip->dev, "period exceeds the maximum value\n");
-			spin_unlock(&sun4i_pwm->ctrl_lock);
 			if (!cstate.enabled)
 				clk_disable_unprepare(sun4i_pwm->clk);
 			return ret;
 		}
+	}
 
+	spin_lock(&sun4i_pwm->ctrl_lock);
+	ctrl = sun4i_pwm_readl(sun4i_pwm, PWM_CTRL_REG);
+
+	if ((cstate.period != state->period) ||
+	    (cstate.duty_cycle != state->duty_cycle)) {
 		if (PWM_REG_PRESCAL(ctrl, pwm->hwpwm) != prescaler) {
 			/* Prescaler changed, the clock has to be gated */
 			ctrl &= ~BIT_CH(PWM_CLK_GATING, pwm->hwpwm);