[PATCH v2 2/4] i2c: muxes: pca954x: free parent IRQ before dismantling IRQ domain

From: Pradhan, Sanman

Date: Thu Sep 03 2026 - 19:48:12 EST


From: Sanman Pradhan <psanman@xxxxxxxxxxx>

When the chip has an interrupt, the parent IRQ threaded handler reads the
mux status register and dispatches nested child IRQs via
handle_nested_irq(irq_find_mapping(data->irq, ...)). The parent IRQ is
requested with devm_request_threaded_irq(), so without an explicit free
it is only released after pca954x_remove() returns, i.e. after the IRQ
domain has already been removed in pca954x_cleanup(). A parent interrupt
arriving in that window would look up a mapping in a domain that no
longer exists.

Track whether the parent IRQ was requested and free it in
pca954x_cleanup() before the IRQ domain is torn down. The parent IRQ is
requested with IRQF_SHARED, so free it only after the child adapters have
been removed: this lets the child drivers quiesce their own interrupt
sources first and avoids leaving the shared line asserted with no handler
while children still exist. This keeps the ordering as: remove child
adapters, free the parent IRQ, dispose the child mappings and remove the
domain, then disable the regulator.

The flag also keeps the probe error-unwind path correct, since
pca954x_cleanup() is reused there and the parent IRQ may not have been
requested yet.

Fixes: f2114795f721 ("i2c: mux: pca954x: Add interrupt controller support")
Cc: stable@xxxxxxxxxxxxxxx
Signed-off-by: Sanman Pradhan <psanman@xxxxxxxxxxx>
---
drivers/i2c/muxes/i2c-mux-pca954x.c | 7 +++++++
1 file changed, 7 insertions(+)

diff --git a/drivers/i2c/muxes/i2c-mux-pca954x.c b/drivers/i2c/muxes/i2c-mux-pca954x.c
index f0b8879ae5fa3..3da54d5f33008 100644
--- a/drivers/i2c/muxes/i2c-mux-pca954x.c
+++ b/drivers/i2c/muxes/i2c-mux-pca954x.c
@@ -116,6 +116,7 @@ struct pca954x {
struct irq_domain *irq;
unsigned int irq_mask;
raw_spinlock_t lock;
+ bool irq_requested;
struct regulator *supply;

struct gpio_desc *reset_gpio;
@@ -468,6 +469,11 @@ static void pca954x_cleanup(struct i2c_mux_core *muxc)

i2c_mux_del_adapters(muxc);

+ if (data->irq_requested) {
+ devm_free_irq(&data->client->dev, data->client->irq, data);
+ data->irq_requested = false;
+ }
+
if (data->irq) {
for (c = 0; c < data->chip->nchans; c++) {
irq = irq_find_mapping(data->irq, c);
@@ -656,6 +662,7 @@ static int pca954x_probe(struct i2c_client *client)
"pca954x", data);
if (ret)
goto fail_cleanup;
+ data->irq_requested = true;
}

/*
--
2.34.1