* [PATCH v6 1/9] irqchip/imx-irqsteer: Call chained_irq_exit() on the handler error path
2026-10-08 9:02 [PATCH v6 0/9] irqchip/imx-irqsteer: Allow building as module Zhipeng.wang_1
@ 2026-10-08 9:02 ` Zhipeng.wang_1
2026-10-08 9:02 ` [PATCH v6 2/9] irqchip/imx-irqsteer: Validate IRQ count before creating domain Zhipeng.wang_1
` (7 subsequent siblings)
8 siblings, 0 replies; 14+ messages in thread
From: Zhipeng.wang_1 @ 2026-10-08 9:02 UTC (permalink / raw)
To: Thomas Gleixner, Marc Zyngier, Frank Li
Cc: Radu Rendec, Sascha Hauer, Pengutronix Kernel Team,
Fabio Estevam, Jindong Yue, xuegang.liu, linux-kernel, imx,
linux-arm-kernel
From: Zhipeng Wang <zhipeng.wang_1@nxp.com>
A chained handler must pair every chained_irq_enter() with a
chained_irq_exit() before returning, so that the parent interrupt's flow
control is completed (EOI for fasteoi parents, unmask for level-triggered
parents). Skipping it leaves the parent interrupt unacknowledged, blocking
further interrupts multiplexed through that line.
When imx_irqsteer_get_hwirq_base() fails, the handler returned early
without calling chained_irq_exit(). Route the error path through the
existing chained_irq_exit() so the parent interrupt is always completed
before returning.
Fixes: 28528fca4908 ("irqchip/imx-irqsteer: Add multi output interrupts support")
Signed-off-by: Zhipeng Wang <zhipeng.wang_1@nxp.com>
Reviewed-by: Frank Li <Frank.Li@nxp.com>
Reviewed-by: Radu Rendec <radu@rendec.net>
---
drivers/irqchip/irq-imx-irqsteer.c | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/drivers/irqchip/irq-imx-irqsteer.c b/drivers/irqchip/irq-imx-irqsteer.c
index 87b07f517be3..1b8d0c8eedb9 100644
--- a/drivers/irqchip/irq-imx-irqsteer.c
+++ b/drivers/irqchip/irq-imx-irqsteer.c
@@ -154,7 +154,7 @@ static void imx_irqsteer_irq_handler(struct irq_desc *desc)
if (hwirq < 0) {
pr_warn("%s: unable to get hwirq base for irq %d\n",
__func__, irq);
- return;
+ goto out;
}
for (i = 0; i < 2; i++, hwirq += 32) {
@@ -172,6 +172,7 @@ static void imx_irqsteer_irq_handler(struct irq_desc *desc)
generic_handle_domain_irq(data->domain, pos + hwirq);
}
+out:
chained_irq_exit(irq_desc_get_chip(desc), desc);
}
--
2.34.1
^ permalink raw reply [flat|nested] 14+ messages in thread* [PATCH v6 2/9] irqchip/imx-irqsteer: Validate IRQ count before creating domain
2026-10-08 9:02 [PATCH v6 0/9] irqchip/imx-irqsteer: Allow building as module Zhipeng.wang_1
2026-10-08 9:02 ` [PATCH v6 1/9] irqchip/imx-irqsteer: Call chained_irq_exit() on the handler error path Zhipeng.wang_1
@ 2026-10-08 9:02 ` Zhipeng.wang_1
2026-10-08 20:36 ` Frank Li
2026-10-08 9:02 ` [PATCH v6 3/9] irqchip/imx-irqsteer: Dispose of parent IRQ mappings in remove() Zhipeng.wang_1
` (6 subsequent siblings)
8 siblings, 1 reply; 14+ messages in thread
From: Zhipeng.wang_1 @ 2026-10-08 9:02 UTC (permalink / raw)
To: Thomas Gleixner, Marc Zyngier, Frank Li
Cc: Radu Rendec, Sascha Hauer, Pengutronix Kernel Team,
Fabio Estevam, Jindong Yue, xuegang.liu, linux-kernel, imx,
linux-arm-kernel
From: Fabio Estevam <festevam@nabladev.com>
The IRQ count is validated after creating the IRQ domain. If it is
invalid, probe returns without removing the domain, leaving its host
data pointing at devm-managed memory that is freed on probe failure.
Validate the count before allocating resources to avoid the leak and
dangling pointer.
Fixes: 28528fca4908 ("irqchip/imx-irqsteer: Add multi output interrupts support")
Signed-off-by: Fabio Estevam <festevam@nabladev.com>
Signed-off-by: Zhipeng Wang <zhipeng.wang_1@nxp.com>
---
drivers/irqchip/irq-imx-irqsteer.c | 7 ++-----
1 file changed, 2 insertions(+), 5 deletions(-)
diff --git a/drivers/irqchip/irq-imx-irqsteer.c b/drivers/irqchip/irq-imx-irqsteer.c
index 1b8d0c8eedb9..766756e76c47 100644
--- a/drivers/irqchip/irq-imx-irqsteer.c
+++ b/drivers/irqchip/irq-imx-irqsteer.c
@@ -218,6 +218,8 @@ static int imx_irqsteer_probe(struct platform_device *pdev)
*/
data->irq_count = DIV_ROUND_UP(irqs_num, 64);
data->reg_num = irqs_num / 32;
+ if (!data->irq_count || data->irq_count > CHAN_MAX_OUTPUT_INT)
+ return -EINVAL;
if (IS_ENABLED(CONFIG_PM)) {
data->saved_reg = devm_kzalloc(&pdev->dev,
@@ -246,11 +248,6 @@ static int imx_irqsteer_probe(struct platform_device *pdev)
}
irq_domain_set_pm_device(data->domain, &pdev->dev);
- if (!data->irq_count || data->irq_count > CHAN_MAX_OUTPUT_INT) {
- ret = -EINVAL;
- goto out;
- }
-
for (i = 0; i < data->irq_count; i++) {
data->irq[i] = irq_of_parse_and_map(np, i);
if (!data->irq[i])
--
2.34.1
^ permalink raw reply [flat|nested] 14+ messages in thread* Re: [PATCH v6 2/9] irqchip/imx-irqsteer: Validate IRQ count before creating domain
2026-10-08 9:02 ` [PATCH v6 2/9] irqchip/imx-irqsteer: Validate IRQ count before creating domain Zhipeng.wang_1
@ 2026-10-08 20:36 ` Frank Li
0 siblings, 0 replies; 14+ messages in thread
From: Frank Li @ 2026-10-08 20:36 UTC (permalink / raw)
To: Zhipeng.wang_1
Cc: Thomas Gleixner, Marc Zyngier, Frank Li, Radu Rendec,
Sascha Hauer, Pengutronix Kernel Team, Fabio Estevam,
Jindong Yue, xuegang.liu, linux-kernel, imx, linux-arm-kernel
On Thu, Oct 08, 2026 at 06:02:24PM +0900, Zhipeng.wang_1@oss.nxp.com wrote:
> From: Fabio Estevam <festevam@nabladev.com>
>
> The IRQ count is validated after creating the IRQ domain. If it is
> invalid, probe returns without removing the domain, leaving its host
> data pointing at devm-managed memory that is freed on probe failure.
>
> Validate the count before allocating resources to avoid the leak and
> dangling pointer.
>
> Fixes: 28528fca4908 ("irqchip/imx-irqsteer: Add multi output interrupts support")
> Signed-off-by: Fabio Estevam <festevam@nabladev.com>
> Signed-off-by: Zhipeng Wang <zhipeng.wang_1@nxp.com>
> ---
Reviewed-by: Frank Li <Frank.Li@nxp.com>
> drivers/irqchip/irq-imx-irqsteer.c | 7 ++-----
> 1 file changed, 2 insertions(+), 5 deletions(-)
>
> diff --git a/drivers/irqchip/irq-imx-irqsteer.c b/drivers/irqchip/irq-imx-irqsteer.c
> index 1b8d0c8eedb9..766756e76c47 100644
> --- a/drivers/irqchip/irq-imx-irqsteer.c
> +++ b/drivers/irqchip/irq-imx-irqsteer.c
> @@ -218,6 +218,8 @@ static int imx_irqsteer_probe(struct platform_device *pdev)
> */
> data->irq_count = DIV_ROUND_UP(irqs_num, 64);
> data->reg_num = irqs_num / 32;
> + if (!data->irq_count || data->irq_count > CHAN_MAX_OUTPUT_INT)
> + return -EINVAL;
>
> if (IS_ENABLED(CONFIG_PM)) {
> data->saved_reg = devm_kzalloc(&pdev->dev,
> @@ -246,11 +248,6 @@ static int imx_irqsteer_probe(struct platform_device *pdev)
> }
> irq_domain_set_pm_device(data->domain, &pdev->dev);
>
> - if (!data->irq_count || data->irq_count > CHAN_MAX_OUTPUT_INT) {
> - ret = -EINVAL;
> - goto out;
> - }
> -
> for (i = 0; i < data->irq_count; i++) {
> data->irq[i] = irq_of_parse_and_map(np, i);
> if (!data->irq[i])
> --
> 2.34.1
>
>
^ permalink raw reply [flat|nested] 14+ messages in thread
* [PATCH v6 3/9] irqchip/imx-irqsteer: Dispose of parent IRQ mappings in remove()
2026-10-08 9:02 [PATCH v6 0/9] irqchip/imx-irqsteer: Allow building as module Zhipeng.wang_1
2026-10-08 9:02 ` [PATCH v6 1/9] irqchip/imx-irqsteer: Call chained_irq_exit() on the handler error path Zhipeng.wang_1
2026-10-08 9:02 ` [PATCH v6 2/9] irqchip/imx-irqsteer: Validate IRQ count before creating domain Zhipeng.wang_1
@ 2026-10-08 9:02 ` Zhipeng.wang_1
2026-10-08 9:02 ` [PATCH v6 4/9] irqchip/imx-irqsteer: Convert to devm_pm_runtime_set_active_enabled() Zhipeng.wang_1
` (5 subsequent siblings)
8 siblings, 0 replies; 14+ messages in thread
From: Zhipeng.wang_1 @ 2026-10-08 9:02 UTC (permalink / raw)
To: Thomas Gleixner, Marc Zyngier, Frank Li
Cc: Radu Rendec, Sascha Hauer, Pengutronix Kernel Team,
Fabio Estevam, Jindong Yue, xuegang.liu, linux-kernel, imx,
linux-arm-kernel
From: Zhipeng Wang <zhipeng.wang_1@nxp.com>
probe() maps the parent output interrupts with irq_of_parse_and_map(),
but remove() only unchains the handlers and never disposes of those
mappings, leaking them on unbind. The child mappings handed out by the
domain are freed by their consumers and, together with the domain, are
now torn down by devres, so remove() only has to dispose of the parent
mappings it created itself.
Dispose of the parent mappings alongside the chained-handler teardown.
Fixes: 0136afa08967 ("irqchip: Add driver for imx-irqsteer controller")
Signed-off-by: Zhipeng Wang <zhipeng.wang_1@nxp.com>
Reviewed-by: Frank Li <Frank.Li@nxp.com>
Reviewed-by: Radu Rendec <radu@rendec.net>
---
drivers/irqchip/irq-imx-irqsteer.c | 1 +
1 file changed, 1 insertion(+)
diff --git a/drivers/irqchip/irq-imx-irqsteer.c b/drivers/irqchip/irq-imx-irqsteer.c
index 766756e76c47..d05de3d26fb2 100644
--- a/drivers/irqchip/irq-imx-irqsteer.c
+++ b/drivers/irqchip/irq-imx-irqsteer.c
@@ -280,6 +280,7 @@ static void imx_irqsteer_remove(struct platform_device *pdev)
irq_set_chained_handler_and_data(irqsteer_data->irq[i],
NULL, NULL);
+ irq_dispose_mapping(irqsteer_data->irq[i]);
}
irq_domain_remove(irqsteer_data->domain);
--
2.34.1
^ permalink raw reply [flat|nested] 14+ messages in thread* [PATCH v6 4/9] irqchip/imx-irqsteer: Convert to devm_pm_runtime_set_active_enabled()
2026-10-08 9:02 [PATCH v6 0/9] irqchip/imx-irqsteer: Allow building as module Zhipeng.wang_1
` (2 preceding siblings ...)
2026-10-08 9:02 ` [PATCH v6 3/9] irqchip/imx-irqsteer: Dispose of parent IRQ mappings in remove() Zhipeng.wang_1
@ 2026-10-08 9:02 ` Zhipeng.wang_1
2026-10-08 20:41 ` Frank Li
2026-10-08 9:02 ` [PATCH v6 5/9] irqchip/imx-irqsteer: Mask all interrupts in probe() Zhipeng.wang_1
` (4 subsequent siblings)
8 siblings, 1 reply; 14+ messages in thread
From: Zhipeng.wang_1 @ 2026-10-08 9:02 UTC (permalink / raw)
To: Thomas Gleixner, Marc Zyngier, Frank Li
Cc: Radu Rendec, Sascha Hauer, Pengutronix Kernel Team,
Fabio Estevam, Jindong Yue, xuegang.liu, linux-kernel, imx,
linux-arm-kernel
From: Zhipeng Wang <zhipeng.wang_1@nxp.com>
imx_irqsteer_probe() enables runtime PM with pm_runtime_enable(), but
imx_irqsteer_remove() never disables it. Runtime PM therefore stays
enabled after the device is unbound, and rebinding it triggers:
Unbalanced pm_runtime_enable!
Use devm_pm_runtime_set_active_enabled() so the driver core marks the
device active (matching the enabled clock) and disables runtime PM again
on unbind, keeping the enable balanced across unbind/rebind.
This becomes reachable once the driver can be unbound and reloaded, but
the imbalance already exists today on unbind, so fix it separately with
a Fixes tag for stable.
Fixes: 4730d2233311 ("irqchip/imx-irqsteer: Add runtime PM support")
Suggested-by: Fabio Estevam <festevam@nabladev.com>
Signed-off-by: Zhipeng Wang <zhipeng.wang_1@nxp.com>
---
drivers/irqchip/irq-imx-irqsteer.c | 10 ++++++----
1 file changed, 6 insertions(+), 4 deletions(-)
diff --git a/drivers/irqchip/irq-imx-irqsteer.c b/drivers/irqchip/irq-imx-irqsteer.c
index d05de3d26fb2..5acc04504e52 100644
--- a/drivers/irqchip/irq-imx-irqsteer.c
+++ b/drivers/irqchip/irq-imx-irqsteer.c
@@ -239,6 +239,10 @@ static int imx_irqsteer_probe(struct platform_device *pdev)
if (irqsteer_has_chanctrl(data->devtype_data))
writel_relaxed(BIT(data->channel), data->regs + CHANCTRL);
+ ret = devm_pm_runtime_set_active_enabled(&pdev->dev);
+ if (ret)
+ goto out;
+
data->domain = irq_domain_create_linear(dev_fwnode(&pdev->dev), data->reg_num * 32,
&imx_irqsteer_domain_ops, data);
if (!data->domain) {
@@ -260,9 +264,6 @@ static int imx_irqsteer_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, data);
- pm_runtime_set_active(&pdev->dev);
- pm_runtime_enable(&pdev->dev);
-
return 0;
out:
clk_disable_unprepare(data->ipg_clk);
@@ -285,7 +286,8 @@ static void imx_irqsteer_remove(struct platform_device *pdev)
irq_domain_remove(irqsteer_data->domain);
- clk_disable_unprepare(irqsteer_data->ipg_clk);
+ if (!pm_runtime_status_suspended(&pdev->dev))
+ clk_disable_unprepare(irqsteer_data->ipg_clk);
}
#ifdef CONFIG_PM
--
2.34.1
^ permalink raw reply [flat|nested] 14+ messages in thread* Re: [PATCH v6 4/9] irqchip/imx-irqsteer: Convert to devm_pm_runtime_set_active_enabled()
2026-10-08 9:02 ` [PATCH v6 4/9] irqchip/imx-irqsteer: Convert to devm_pm_runtime_set_active_enabled() Zhipeng.wang_1
@ 2026-10-08 20:41 ` Frank Li
0 siblings, 0 replies; 14+ messages in thread
From: Frank Li @ 2026-10-08 20:41 UTC (permalink / raw)
To: Zhipeng.wang_1
Cc: Thomas Gleixner, Marc Zyngier, Frank Li, Radu Rendec,
Sascha Hauer, Pengutronix Kernel Team, Fabio Estevam,
Jindong Yue, xuegang.liu, linux-kernel, imx, linux-arm-kernel
On Thu, Oct 08, 2026 at 06:02:26PM +0900, Zhipeng.wang_1@oss.nxp.com wrote:
> From: Zhipeng Wang <zhipeng.wang_1@nxp.com>
>
> imx_irqsteer_probe() enables runtime PM with pm_runtime_enable(), but
> imx_irqsteer_remove() never disables it. Runtime PM therefore stays
> enabled after the device is unbound, and rebinding it triggers:
>
> Unbalanced pm_runtime_enable!
>
> Use devm_pm_runtime_set_active_enabled() so the driver core marks the
> device active (matching the enabled clock) and disables runtime PM again
> on unbind, keeping the enable balanced across unbind/rebind.
>
> This becomes reachable once the driver can be unbound and reloaded, but
> the imbalance already exists today on unbind, so fix it separately with
> a Fixes tag for stable.
>
> Fixes: 4730d2233311 ("irqchip/imx-irqsteer: Add runtime PM support")
> Suggested-by: Fabio Estevam <festevam@nabladev.com>
> Signed-off-by: Zhipeng Wang <zhipeng.wang_1@nxp.com>
> ---
> drivers/irqchip/irq-imx-irqsteer.c | 10 ++++++----
> 1 file changed, 6 insertions(+), 4 deletions(-)
>
> diff --git a/drivers/irqchip/irq-imx-irqsteer.c b/drivers/irqchip/irq-imx-irqsteer.c
> index d05de3d26fb2..5acc04504e52 100644
> --- a/drivers/irqchip/irq-imx-irqsteer.c
> +++ b/drivers/irqchip/irq-imx-irqsteer.c
> @@ -239,6 +239,10 @@ static int imx_irqsteer_probe(struct platform_device *pdev)
> if (irqsteer_has_chanctrl(data->devtype_data))
> writel_relaxed(BIT(data->channel), data->regs + CHANCTRL);
>
> + ret = devm_pm_runtime_set_active_enabled(&pdev->dev);
> + if (ret)
> + goto out;
> +
> data->domain = irq_domain_create_linear(dev_fwnode(&pdev->dev), data->reg_num * 32,
> &imx_irqsteer_domain_ops, data);
> if (!data->domain) {
> @@ -260,9 +264,6 @@ static int imx_irqsteer_probe(struct platform_device *pdev)
>
> platform_set_drvdata(pdev, data);
>
> - pm_runtime_set_active(&pdev->dev);
> - pm_runtime_enable(&pdev->dev);
> -
> return 0;
> out:
> clk_disable_unprepare(data->ipg_clk);
> @@ -285,7 +286,8 @@ static void imx_irqsteer_remove(struct platform_device *pdev)
>
> irq_domain_remove(irqsteer_data->domain);
>
> - clk_disable_unprepare(irqsteer_data->ipg_clk);
> + if (!pm_runtime_status_suspended(&pdev->dev))
> + clk_disable_unprepare(irqsteer_data->ipg_clk);
Are you sure need do this? suppose runtime_pm tear down call runtime pm
susped to disable clock.
Frank
> }
>
> #ifdef CONFIG_PM
> --
> 2.34.1
>
>
^ permalink raw reply [flat|nested] 14+ messages in thread
* [PATCH v6 5/9] irqchip/imx-irqsteer: Mask all interrupts in probe()
2026-10-08 9:02 [PATCH v6 0/9] irqchip/imx-irqsteer: Allow building as module Zhipeng.wang_1
` (3 preceding siblings ...)
2026-10-08 9:02 ` [PATCH v6 4/9] irqchip/imx-irqsteer: Convert to devm_pm_runtime_set_active_enabled() Zhipeng.wang_1
@ 2026-10-08 9:02 ` Zhipeng.wang_1
2026-10-08 9:02 ` [PATCH v6 6/9] irqchip/imx-irqsteer: Let devres own the clock Zhipeng.wang_1
` (3 subsequent siblings)
8 siblings, 0 replies; 14+ messages in thread
From: Zhipeng.wang_1 @ 2026-10-08 9:02 UTC (permalink / raw)
To: Thomas Gleixner, Marc Zyngier, Frank Li
Cc: Radu Rendec, Sascha Hauer, Pengutronix Kernel Team,
Fabio Estevam, Jindong Yue, xuegang.liu, linux-kernel, imx,
linux-arm-kernel
From: Zhipeng Wang <zhipeng.wang_1@nxp.com>
probe() sets up the chained handlers without first masking the input
interrupts. For a built-in driver this happened to be harmless because
CHANMASK resets to all-masked, but once the driver can be unloaded and
reloaded a child interrupt left unmasked at unload time survives in
hardware. On the next probe() the parent interrupts are re-mapped and
unmasked before the new domain is ready, so a still-asserted line
immediately storms the parent with no handler to service it.
Mask all interrupts in probe() before wiring up the chained handlers.
CHANMASK uses inverted polarity (a set bit enables the interrupt), so
masking means writing zero. This mirrors the sibling NXP chained mux
irq-imx-intmux.c, which masks all sources at probe() time.
Masking is only done in probe(), not in remove(): the next probe()
quiesces the hardware before it re-maps and unmasks the parent
interrupts, which is the only window in which a stale line could storm.
Masking in remove() would also mean touching CHANMASK while the device
may already be runtime-suspended with the clock gated.
Signed-off-by: Zhipeng Wang <zhipeng.wang_1@nxp.com>
Reviewed-by: Radu Rendec <radu@rendec.net>
---
drivers/irqchip/irq-imx-irqsteer.c | 8 ++++++++
1 file changed, 8 insertions(+)
diff --git a/drivers/irqchip/irq-imx-irqsteer.c b/drivers/irqchip/irq-imx-irqsteer.c
index 5acc04504e52..fa233de9bc2b 100644
--- a/drivers/irqchip/irq-imx-irqsteer.c
+++ b/drivers/irqchip/irq-imx-irqsteer.c
@@ -239,6 +239,14 @@ static int imx_irqsteer_probe(struct platform_device *pdev)
if (irqsteer_has_chanctrl(data->devtype_data))
writel_relaxed(BIT(data->channel), data->regs + CHANCTRL);
+ /*
+ * Mask all interrupts before wiring up the chained handlers. CHANMASK
+ * has inverted polarity (a set bit enables the interrupt), so writing
+ * zero masks the source.
+ */
+ for (i = 0; i < data->reg_num; i++)
+ writel_relaxed(0, data->regs + CHANMASK(i, data->reg_num));
+
ret = devm_pm_runtime_set_active_enabled(&pdev->dev);
if (ret)
goto out;
--
2.34.1
^ permalink raw reply [flat|nested] 14+ messages in thread* [PATCH v6 6/9] irqchip/imx-irqsteer: Let devres own the clock
2026-10-08 9:02 [PATCH v6 0/9] irqchip/imx-irqsteer: Allow building as module Zhipeng.wang_1
` (4 preceding siblings ...)
2026-10-08 9:02 ` [PATCH v6 5/9] irqchip/imx-irqsteer: Mask all interrupts in probe() Zhipeng.wang_1
@ 2026-10-08 9:02 ` Zhipeng.wang_1
2026-10-08 20:54 ` Frank Li
2026-10-08 9:02 ` [PATCH v6 7/9] irqchip/imx-irqsteer: Allow building as module Zhipeng.wang_1
` (2 subsequent siblings)
8 siblings, 1 reply; 14+ messages in thread
From: Zhipeng.wang_1 @ 2026-10-08 9:02 UTC (permalink / raw)
To: Thomas Gleixner, Marc Zyngier, Frank Li
Cc: Radu Rendec, Sascha Hauer, Pengutronix Kernel Team,
Fabio Estevam, Jindong Yue, xuegang.liu, linux-kernel, imx,
linux-arm-kernel
From: Zhipeng Wang <zhipeng.wang_1@nxp.com>
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@nxp.com>
---
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
^ permalink raw reply [flat|nested] 14+ messages in thread* Re: [PATCH v6 6/9] irqchip/imx-irqsteer: Let devres own the clock
2026-10-08 9:02 ` [PATCH v6 6/9] irqchip/imx-irqsteer: Let devres own the clock Zhipeng.wang_1
@ 2026-10-08 20:54 ` Frank Li
0 siblings, 0 replies; 14+ messages in thread
From: Frank Li @ 2026-10-08 20:54 UTC (permalink / raw)
To: Zhipeng.wang_1
Cc: Thomas Gleixner, Marc Zyngier, Frank Li, Radu Rendec,
Sascha Hauer, Pengutronix Kernel Team, Fabio Estevam,
Jindong Yue, xuegang.liu, linux-kernel, imx, linux-arm-kernel
On Thu, Oct 08, 2026 at 06:02:28PM +0900, Zhipeng.wang_1@oss.nxp.com wrote:
> From: Zhipeng Wang <zhipeng.wang_1@nxp.com>
>
> 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@nxp.com>
> ---
> 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);
I think need new API/method to handle this case, which is quite common.
Anyways, this method should work. let as it now.
Reviewed-by: Frank Li <Frank.Li@nxp.com>
>
> 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
>
>
^ permalink raw reply [flat|nested] 14+ messages in thread
* [PATCH v6 7/9] irqchip/imx-irqsteer: Allow building as module
2026-10-08 9:02 [PATCH v6 0/9] irqchip/imx-irqsteer: Allow building as module Zhipeng.wang_1
` (5 preceding siblings ...)
2026-10-08 9:02 ` [PATCH v6 6/9] irqchip/imx-irqsteer: Let devres own the clock Zhipeng.wang_1
@ 2026-10-08 9:02 ` Zhipeng.wang_1
2026-10-08 9:02 ` [PATCH v6 8/9] genirq/irqdomain: Add devm_irq_domain_create_linear() Zhipeng.wang_1
2026-10-08 9:02 ` [PATCH v6 9/9] irqchip/imx-irqsteer: Use devm_irq_domain_create_linear() Zhipeng.wang_1
8 siblings, 0 replies; 14+ messages in thread
From: Zhipeng.wang_1 @ 2026-10-08 9:02 UTC (permalink / raw)
To: Thomas Gleixner, Marc Zyngier, Frank Li
Cc: Radu Rendec, Sascha Hauer, Pengutronix Kernel Team,
Fabio Estevam, Jindong Yue, xuegang.liu, linux-kernel, imx,
linux-arm-kernel
From: Jindong Yue <jindong.yue@nxp.com>
Make the driver buildable as a module by turning the Kconfig symbol into
a tristate and using module_platform_driver() instead of
builtin_platform_driver(), and add the MODULE_DEVICE_TABLE() and module
metadata needed for autoloading.
The clock, runtime PM and IRQ domain are already owned by devres, so no
further teardown is needed to support unbind and reload.
Signed-off-by: Jindong Yue <jindong.yue@nxp.com>
Signed-off-by: Zhipeng Wang <zhipeng.wang_1@nxp.com>
Reviewed-by: Radu Rendec <radu@rendec.net>
---
drivers/irqchip/Kconfig | 2 +-
drivers/irqchip/irq-imx-irqsteer.c | 7 ++++++-
2 files changed, 7 insertions(+), 2 deletions(-)
diff --git a/drivers/irqchip/Kconfig b/drivers/irqchip/Kconfig
index 20b77fbc51ee..105108d2e6ff 100644
--- a/drivers/irqchip/Kconfig
+++ b/drivers/irqchip/Kconfig
@@ -555,7 +555,7 @@ config CSKY_APB_INTC
the controller's register.
config IMX_IRQSTEER
- bool "i.MX IRQSTEER support"
+ tristate "i.MX IRQSTEER support"
depends on ARCH_MXC || ARCH_S32 || COMPILE_TEST
default y if ARCH_MXC || ARCH_S32
select IRQ_DOMAIN
diff --git a/drivers/irqchip/irq-imx-irqsteer.c b/drivers/irqchip/irq-imx-irqsteer.c
index 15ee50f65d54..85edd5e33ca9 100644
--- a/drivers/irqchip/irq-imx-irqsteer.c
+++ b/drivers/irqchip/irq-imx-irqsteer.c
@@ -10,6 +10,7 @@
#include <linux/irqchip/chained_irq.h>
#include <linux/irqdomain.h>
#include <linux/kernel.h>
+#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_irq.h>
#include <linux/platform_device.h>
@@ -359,6 +360,7 @@ static const struct of_device_id imx_irqsteer_dt_ids[] = {
{ .compatible = "nxp,s32n79-irqsteer", .data = &s32n79_data },
{},
};
+MODULE_DEVICE_TABLE(of, imx_irqsteer_dt_ids);
static struct platform_driver imx_irqsteer_driver = {
.driver = {
@@ -369,4 +371,7 @@ static struct platform_driver imx_irqsteer_driver = {
.probe = imx_irqsteer_probe,
.remove = imx_irqsteer_remove,
};
-builtin_platform_driver(imx_irqsteer_driver);
+module_platform_driver(imx_irqsteer_driver);
+
+MODULE_DESCRIPTION("i.MX IRQSTEER interrupt multiplexer/remapper driver");
+MODULE_LICENSE("GPL");
--
2.34.1
^ permalink raw reply [flat|nested] 14+ messages in thread* [PATCH v6 8/9] genirq/irqdomain: Add devm_irq_domain_create_linear()
2026-10-08 9:02 [PATCH v6 0/9] irqchip/imx-irqsteer: Allow building as module Zhipeng.wang_1
` (6 preceding siblings ...)
2026-10-08 9:02 ` [PATCH v6 7/9] irqchip/imx-irqsteer: Allow building as module Zhipeng.wang_1
@ 2026-10-08 9:02 ` Zhipeng.wang_1
2026-10-08 9:02 ` [PATCH v6 9/9] irqchip/imx-irqsteer: Use devm_irq_domain_create_linear() Zhipeng.wang_1
8 siblings, 0 replies; 14+ messages in thread
From: Zhipeng.wang_1 @ 2026-10-08 9:02 UTC (permalink / raw)
To: Thomas Gleixner, Marc Zyngier, Frank Li
Cc: Radu Rendec, Sascha Hauer, Pengutronix Kernel Team,
Fabio Estevam, Jindong Yue, xuegang.liu, linux-kernel, imx,
linux-arm-kernel
From: Zhipeng Wang <zhipeng.wang_1@nxp.com>
irq_domain_create_linear() has no devres-managed counterpart, so a
driver that wants a linear revmap domain tied to the device lifetime has
to open-code an irq_domain_info and call devm_irq_domain_instantiate()
directly.
Add devm_irq_domain_create_linear() as the devres-managed sibling of
irq_domain_create_linear(): it builds the same linear-revmap
irq_domain_info and hands it to devm_irq_domain_instantiate(), so the
domain is removed when the owning device is unbound. The return
convention matches irq_domain_create_linear() (NULL on failure).
Signed-off-by: Zhipeng Wang <zhipeng.wang_1@nxp.com>
Reviewed-by: Frank Li <Frank.Li@nxp.com>
---
include/linux/irqdomain.h | 30 ++++++++++++++++++++++++++++++
1 file changed, 30 insertions(+)
diff --git a/include/linux/irqdomain.h b/include/linux/irqdomain.h
index 73c25d40846c..b6b360cb6525 100644
--- a/include/linux/irqdomain.h
+++ b/include/linux/irqdomain.h
@@ -457,6 +457,36 @@ static inline struct irq_domain *irq_domain_create_linear(struct fwnode_handle *
return IS_ERR(d) ? NULL : d;
}
+/**
+ * devm_irq_domain_create_linear - Allocate and register a linear revmap
+ * irq_domain tied to the device lifetime.
+ * @dev: Device that owns the domain. The domain is removed via devres
+ * when the device is unbound.
+ * @fwnode: pointer to interrupt controller's FW node.
+ * @size: Number of interrupts in the domain.
+ * @ops: map/unmap domain callbacks
+ * @host_data: Controller private data pointer
+ *
+ * Returns: Newly created irq_domain, or NULL on failure.
+ */
+static inline struct irq_domain *devm_irq_domain_create_linear(struct device *dev,
+ struct fwnode_handle *fwnode,
+ unsigned int size,
+ const struct irq_domain_ops *ops,
+ void *host_data)
+{
+ const struct irq_domain_info info = {
+ .fwnode = fwnode,
+ .size = size,
+ .hwirq_max = size,
+ .ops = ops,
+ .host_data = host_data,
+ };
+ struct irq_domain *d = devm_irq_domain_instantiate(dev, &info);
+
+ return IS_ERR(d) ? NULL : d;
+}
+
static inline struct irq_domain *irq_domain_create_tree(struct fwnode_handle *fwnode,
const struct irq_domain_ops *ops,
void *host_data)
--
2.34.1
^ permalink raw reply [flat|nested] 14+ messages in thread* [PATCH v6 9/9] irqchip/imx-irqsteer: Use devm_irq_domain_create_linear()
2026-10-08 9:02 [PATCH v6 0/9] irqchip/imx-irqsteer: Allow building as module Zhipeng.wang_1
` (7 preceding siblings ...)
2026-10-08 9:02 ` [PATCH v6 8/9] genirq/irqdomain: Add devm_irq_domain_create_linear() Zhipeng.wang_1
@ 2026-10-08 9:02 ` Zhipeng.wang_1
2026-10-08 21:01 ` Frank Li
8 siblings, 1 reply; 14+ messages in thread
From: Zhipeng.wang_1 @ 2026-10-08 9:02 UTC (permalink / raw)
To: Thomas Gleixner, Marc Zyngier, Frank Li
Cc: Radu Rendec, Sascha Hauer, Pengutronix Kernel Team,
Fabio Estevam, Jindong Yue, xuegang.liu, linux-kernel, imx,
linux-arm-kernel
From: Zhipeng Wang <zhipeng.wang_1@nxp.com>
The driver open-codes an irq_domain_info and calls
devm_irq_domain_instantiate() only to get a linear revmap domain tied to
the device lifetime. Use the new devm_irq_domain_create_linear() helper
instead. No functional change.
Signed-off-by: Zhipeng Wang <zhipeng.wang_1@nxp.com>
---
drivers/irqchip/irq-imx-irqsteer.c | 7 +++----
1 file changed, 3 insertions(+), 4 deletions(-)
diff --git a/drivers/irqchip/irq-imx-irqsteer.c b/drivers/irqchip/irq-imx-irqsteer.c
index 85edd5e33ca9..f5601769d036 100644
--- a/drivers/irqchip/irq-imx-irqsteer.c
+++ b/drivers/irqchip/irq-imx-irqsteer.c
@@ -246,8 +246,9 @@ static int imx_irqsteer_probe(struct platform_device *pdev)
if (ret)
return ret;
- data->domain = irq_domain_create_linear(dev_fwnode(&pdev->dev), data->reg_num * 32,
- &imx_irqsteer_domain_ops, data);
+ data->domain = devm_irq_domain_create_linear(&pdev->dev, 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");
return -ENOMEM;
@@ -294,8 +295,6 @@ static void imx_irqsteer_remove(struct platform_device *pdev)
irq_dispose_mapping(irqsteer_data->irq[i]);
}
- irq_domain_remove(irqsteer_data->domain);
-
if (ret >= 0)
pm_runtime_put_noidle(&pdev->dev);
}
--
2.34.1
^ permalink raw reply [flat|nested] 14+ messages in thread* Re: [PATCH v6 9/9] irqchip/imx-irqsteer: Use devm_irq_domain_create_linear()
2026-10-08 9:02 ` [PATCH v6 9/9] irqchip/imx-irqsteer: Use devm_irq_domain_create_linear() Zhipeng.wang_1
@ 2026-10-08 21:01 ` Frank Li
0 siblings, 0 replies; 14+ messages in thread
From: Frank Li @ 2026-10-08 21:01 UTC (permalink / raw)
To: Zhipeng.wang_1
Cc: Thomas Gleixner, Marc Zyngier, Frank Li, Radu Rendec,
Sascha Hauer, Pengutronix Kernel Team, Fabio Estevam,
Jindong Yue, xuegang.liu, linux-kernel, imx, linux-arm-kernel
On Thu, Oct 08, 2026 at 06:02:31PM +0900, Zhipeng.wang_1@oss.nxp.com wrote:
> From: Zhipeng Wang <zhipeng.wang_1@nxp.com>
>
> The driver open-codes an irq_domain_info and calls
> devm_irq_domain_instantiate() only to get a linear revmap domain tied to
Miss match actual code change.
Frank
> the device lifetime. Use the new devm_irq_domain_create_linear() helper
> instead. No functional change.
>
> Signed-off-by: Zhipeng Wang <zhipeng.wang_1@nxp.com>
> ---
> drivers/irqchip/irq-imx-irqsteer.c | 7 +++----
> 1 file changed, 3 insertions(+), 4 deletions(-)
>
> diff --git a/drivers/irqchip/irq-imx-irqsteer.c b/drivers/irqchip/irq-imx-irqsteer.c
> index 85edd5e33ca9..f5601769d036 100644
> --- a/drivers/irqchip/irq-imx-irqsteer.c
> +++ b/drivers/irqchip/irq-imx-irqsteer.c
> @@ -246,8 +246,9 @@ static int imx_irqsteer_probe(struct platform_device *pdev)
> if (ret)
> return ret;
>
> - data->domain = irq_domain_create_linear(dev_fwnode(&pdev->dev), data->reg_num * 32,
> - &imx_irqsteer_domain_ops, data);
> + data->domain = devm_irq_domain_create_linear(&pdev->dev, 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");
> return -ENOMEM;
> @@ -294,8 +295,6 @@ static void imx_irqsteer_remove(struct platform_device *pdev)
> irq_dispose_mapping(irqsteer_data->irq[i]);
> }
>
> - irq_domain_remove(irqsteer_data->domain);
> -
> if (ret >= 0)
> pm_runtime_put_noidle(&pdev->dev);
> }
> --
> 2.34.1
>
>
^ permalink raw reply [flat|nested] 14+ messages in thread