[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <20251123233349.2122-1-rafael.v.volkmer@gmail.com>
Date: Sun, 23 Nov 2025 20:33:49 -0300
From: "Rafael V. Volkmer" <rafael.v.volkmer@...il.com>
To: rafael.v.volkmer@...il.com
Cc: linux-kernel@...r.kernel.org,
linux-pwm@...r.kernel.org,
ukleinek@...nel.org
Subject: [PATCH v6 5/6] pwm: tiehrpwm: handle already-running channels at probe
Detect active A/B outputs from AQCTL(A/B) when they are not
software-forced in AQCSFRC, take one pm_runtime_get_sync() per active
channel, and enable tbclk only if at least one channel is active. This
keeps runtime PM usage counts and tbclk state consistent with the
actual hardware configuration.
Before, an eHRPWM instance left running by firmware or the bootloader
could have no runtime PM reference and no tbclk enable, leaving the
driver out of sync with hardware. After this change, PM and clock
state match the active channels observed at probe.
Signed-off-by: Rafael V. Volkmer <rafael.v.volkmer@...il.com>
---
drivers/pwm/pwm-tiehrpwm.c | 64 +++++++++++++++++++++++++++++++++-----
1 file changed, 57 insertions(+), 7 deletions(-)
diff --git a/drivers/pwm/pwm-tiehrpwm.c b/drivers/pwm/pwm-tiehrpwm.c
index cc525626b2f9..1a9be36fc7b2 100644
--- a/drivers/pwm/pwm-tiehrpwm.c
+++ b/drivers/pwm/pwm-tiehrpwm.c
@@ -554,13 +554,35 @@ static int ehrpwm_pwm_probe(struct platform_device *pdev)
struct ehrpwm_pwm_chip *pc;
struct pwm_chip *chip;
struct clk *clk;
- int ret;
+ int ret, ch_idx, ch_disable;
+ u16 aqcsfrc_reg, aqctla_reg, aqctlb_reg;
+ bool enabled_ch[NUM_PWM_CHANNEL] = { false, false };
chip = devm_pwmchip_alloc(&pdev->dev, NUM_PWM_CHANNEL, sizeof(*pc));
if (IS_ERR(chip))
return PTR_ERR(chip);
pc = to_ehrpwm_pwm_chip(chip);
+ pc->mmio_base = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(pc->mmio_base))
+ return PTR_ERR(pc->mmio_base);
+
+ /*
+ * Best-effort snapshot of AQCSFRC/AQCTLx before touching clocks or
+ * runtime PM. If the eHRPWM block is power-gated or its clock is
+ * disabled these registers are expected to read as 0, which we
+ * interpret as "channel not configured / disabled".
+ */
+ aqcsfrc_reg = readw(pc->mmio_base + AQCSFRC);
+ aqctla_reg = readw(pc->mmio_base + AQCTLA);
+ aqctlb_reg = readw(pc->mmio_base + AQCTLB);
+
+ if (aqctla_reg != 0 && !FIELD_GET(AQCSFRC_CSFA_MASK, aqcsfrc_reg))
+ enabled_ch[PWM_CHA] = true;
+
+ if (aqctlb_reg != 0 && !FIELD_GET(AQCSFRC_CSFB_MASK, aqcsfrc_reg))
+ enabled_ch[PWM_CHB] = true;
+
clk = devm_clk_get(&pdev->dev, "fck");
if (IS_ERR(clk)) {
if (of_device_is_compatible(np, "ti,am33xx-ecap")) {
@@ -580,10 +602,6 @@ static int ehrpwm_pwm_probe(struct platform_device *pdev)
chip->ops = &ehrpwm_pwm_ops;
- pc->mmio_base = devm_platform_ioremap_resource(pdev, 0);
- if (IS_ERR(pc->mmio_base))
- return PTR_ERR(pc->mmio_base);
-
/* Acquire tbclk for Time Base EHRPWM submodule */
pc->tbclk = devm_clk_get(&pdev->dev, "tbclk");
if (IS_ERR(pc->tbclk))
@@ -595,17 +613,49 @@ static int ehrpwm_pwm_probe(struct platform_device *pdev)
return ret;
}
+ if (enabled_ch[PWM_CHA] || enabled_ch[PWM_CHB]) {
+ ret = clk_enable(pc->tbclk);
+ if (ret) {
+ dev_err_probe(&pdev->dev, ret, "clk_enable(tbclk) failed\n");
+ goto err_clk_unprepare;
+ }
+ }
+
ret = pwmchip_add(chip);
if (ret < 0) {
dev_err(&pdev->dev, "pwmchip_add() failed: %d\n", ret);
- goto err_clk_unprepare;
+ goto err_disable_tbclk;
}
platform_set_drvdata(pdev, chip);
pm_runtime_enable(&pdev->dev);
- return 0;
+ /*
+ * Treat channels that were configured and not software-forced at probe
+ * time as "pre-enabled": take a runtime PM reference so the eHRPWM block
+ * stays powered while such channels exist. Consumers still get/put PM
+ * on top of this bias via pwm_enable()/pwm_disable().
+ */
+ for (ch_idx = 0; ch_idx < NUM_PWM_CHANNEL; ch_idx++) {
+ if (enabled_ch[ch_idx]) {
+ ret = pm_runtime_get_sync(&pdev->dev);
+ if (ret < 0) {
+ for (ch_disable = 0; ch_disable <= ch_idx; ch_disable++) {
+ if (enabled_ch[ch_disable])
+ pm_runtime_put_noidle(&pdev->dev);
+ }
+
+ pwmchip_remove(chip);
+ pm_runtime_disable(&pdev->dev);
+ goto err_disable_tbclk;
+ }
+ }
+ }
+ return 0;
+err_disable_tbclk:
+ if (enabled_ch[PWM_CHA] || enabled_ch[PWM_CHB])
+ clk_disable(pc->tbclk);
err_clk_unprepare:
clk_unprepare(pc->tbclk);
--
2.43.0
Powered by blists - more mailing lists