[PATCH v6 6/9] irqchip/imx-irqsteer: Let devres own the clock
From: Zhipeng . wang_1
Date: Thu Oct 08 2026 - 05:06:58 EST
From: Zhipeng Wang <zhipeng.wang_1@xxxxxxx>
Let the driver core own the clock lifetime so that the probe() error
path and remove() do not have to hand-balance it:
- acquire the clock with devm_clk_get_enabled() instead of a bare
devm_clk_get() followed by a manual clk_prepare_enable(), so it is
prepared/enabled for the device lifetime and released on unbind;
- keep only clk_enable()/clk_disable() in the runtime PM callbacks,
since prepare/unprepare is now handled once by devres.
Signed-off-by: Zhipeng Wang <zhipeng.wang_1@xxxxxxx>
---
drivers/irqchip/irq-imx-irqsteer.c | 37 +++++++++++++++---------------
1 file changed, 19 insertions(+), 18 deletions(-)
diff --git a/drivers/irqchip/irq-imx-irqsteer.c b/drivers/irqchip/irq-imx-irqsteer.c
index fa233de9bc2b..15ee50f65d54 100644
--- a/drivers/irqchip/irq-imx-irqsteer.c
+++ b/drivers/irqchip/irq-imx-irqsteer.c
@@ -194,7 +194,7 @@ static int imx_irqsteer_probe(struct platform_device *pdev)
return PTR_ERR(data->regs);
}
- data->ipg_clk = devm_clk_get(&pdev->dev, "ipg");
+ data->ipg_clk = devm_clk_get_enabled(&pdev->dev, "ipg");
if (IS_ERR(data->ipg_clk))
return dev_err_probe(&pdev->dev, PTR_ERR(data->ipg_clk),
"failed to get ipg clk\n");
@@ -229,12 +229,6 @@ static int imx_irqsteer_probe(struct platform_device *pdev)
return -ENOMEM;
}
- ret = clk_prepare_enable(data->ipg_clk);
- if (ret) {
- dev_err(&pdev->dev, "failed to enable ipg clk: %d\n", ret);
- return ret;
- }
-
/* steer all IRQs into configured channel */
if (irqsteer_has_chanctrl(data->devtype_data))
writel_relaxed(BIT(data->channel), data->regs + CHANCTRL);
@@ -249,14 +243,13 @@ static int imx_irqsteer_probe(struct platform_device *pdev)
ret = devm_pm_runtime_set_active_enabled(&pdev->dev);
if (ret)
- goto out;
+ return ret;
data->domain = irq_domain_create_linear(dev_fwnode(&pdev->dev), data->reg_num * 32,
&imx_irqsteer_domain_ops, data);
if (!data->domain) {
dev_err(&pdev->dev, "failed to create IRQ domain\n");
- ret = -ENOMEM;
- goto out;
+ return -ENOMEM;
}
irq_domain_set_pm_device(data->domain, &pdev->dev);
@@ -273,15 +266,23 @@ static int imx_irqsteer_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, data);
return 0;
-out:
- clk_disable_unprepare(data->ipg_clk);
- return ret;
}
static void imx_irqsteer_remove(struct platform_device *pdev)
{
struct irqsteer_data *irqsteer_data = platform_get_drvdata(pdev);
- int i;
+ int i, ret;
+
+ /*
+ * The device may be runtime-suspended here, in which case the runtime
+ * suspend callback has already dropped the clock enable count. Resume
+ * it so the devres clk_disable_unprepare(), which runs after remove(),
+ * finds the clock enabled and stays balanced. On success drop the
+ * usage count again with pm_runtime_put_noidle(): it must not trigger a
+ * suspend (which would re-disable the clock) and must not leak to the
+ * next probe of this persistent device.
+ */
+ ret = pm_runtime_resume_and_get(&pdev->dev);
for (i = 0; i < irqsteer_data->irq_count; i++) {
if (!irqsteer_data->irq[i])
@@ -294,8 +295,8 @@ static void imx_irqsteer_remove(struct platform_device *pdev)
irq_domain_remove(irqsteer_data->domain);
- if (!pm_runtime_status_suspended(&pdev->dev))
- clk_disable_unprepare(irqsteer_data->ipg_clk);
+ if (ret >= 0)
+ pm_runtime_put_noidle(&pdev->dev);
}
#ifdef CONFIG_PM
@@ -325,7 +326,7 @@ static int imx_irqsteer_suspend(struct device *dev)
struct irqsteer_data *irqsteer_data = dev_get_drvdata(dev);
imx_irqsteer_save_regs(irqsteer_data);
- clk_disable_unprepare(irqsteer_data->ipg_clk);
+ clk_disable(irqsteer_data->ipg_clk);
return 0;
}
@@ -335,7 +336,7 @@ static int imx_irqsteer_resume(struct device *dev)
struct irqsteer_data *irqsteer_data = dev_get_drvdata(dev);
int ret;
- ret = clk_prepare_enable(irqsteer_data->ipg_clk);
+ ret = clk_enable(irqsteer_data->ipg_clk);
if (ret) {
dev_err(dev, "failed to enable ipg clk: %d\n", ret);
return ret;
--
2.34.1