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);