lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<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

Powered by Openwall GNU/*/Linux Powered by OpenVZ