From 62cff15fbf2b12ebbb0b01ca720030c2bc12aed1 Mon Sep 17 00:00:00 2001 From: Ruoyu Wang Date: Fri, 14 Aug 2026 21:41:43 +0800 Subject: [PATCH] pwm: renesas-tpu: Fix runtime PM reference leak MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit tpu_pwm_timer_start() used pm_runtime_get_sync() without checking its return value before touching the TPU clock. A failed runtime resume was therefore ignored, and the callback continued into register access. Use pm_runtime_resume_and_get() and propagate a failed resume. Keep the matching put when clock preparation fails after a successful resume. tpu_pwm_disable() also needs to propagate a failed start so that the PWM apply callback does not access registers while runtime PM is inactive. This issue was found by a static analysis checker and confirmed by manual source review. Fixes: 99b82abb0a35 ("pwm: Add Renesas TPU PWM driver") Signed-off-by: Ruoyu Wang Link: https://patch.msgid.link/20260814134143.1388120-1-ruoyuw560@gmail.com Signed-off-by: Uwe Kleine-König --- drivers/pwm/pwm-renesas-tpu.c | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/drivers/pwm/pwm-renesas-tpu.c b/drivers/pwm/pwm-renesas-tpu.c index 140fd0bcb93f..e35f2e14777d 100644 --- a/drivers/pwm/pwm-renesas-tpu.c +++ b/drivers/pwm/pwm-renesas-tpu.c @@ -149,10 +149,14 @@ static int tpu_pwm_timer_start(struct tpu_pwm_device *tpd) if (!tpd->timer_on) { /* Wake up device and enable clock. */ - pm_runtime_get_sync(&tpd->tpu->pdev->dev); + ret = pm_runtime_resume_and_get(&tpd->tpu->pdev->dev); + if (ret < 0) + return ret; + ret = clk_prepare_enable(tpd->tpu->clk); if (ret) { dev_err(&tpd->tpu->pdev->dev, "cannot enable clock\n"); + pm_runtime_put(&tpd->tpu->pdev->dev); return ret; } tpd->timer_on = true; @@ -382,15 +386,21 @@ static int tpu_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm) return 0; } -static void tpu_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm) +static int tpu_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm) { struct tpu_device *tpu = to_tpu_device(chip); struct tpu_pwm_device *tpd = &tpu->tpd[pwm->hwpwm]; + int ret; /* The timer must be running to modify the pin output configuration. */ - tpu_pwm_timer_start(tpd); + ret = tpu_pwm_timer_start(tpd); + if (ret < 0) + return ret; + tpu_pwm_set_pin(tpd, TPU_PIN_INACTIVE); tpu_pwm_timer_stop(tpd); + + return 0; } static int tpu_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm, @@ -401,7 +411,10 @@ static int tpu_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm, if (state->polarity != pwm->state.polarity) { if (enabled) { - tpu_pwm_disable(chip, pwm); + err = tpu_pwm_disable(chip, pwm); + if (err) + return err; + enabled = false; } @@ -412,7 +425,7 @@ static int tpu_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm, if (!state->enabled) { if (enabled) - tpu_pwm_disable(chip, pwm); + return tpu_pwm_disable(chip, pwm); return 0; }