[PATCH] HSI: omap_ssi: Quiesce GDD before removing controller children

From: Myeonghun Pak

Date: Sun Oct 11 2026 - 19:52:00 EST


The GDD interrupt can schedule its tasklet after the remove path has
killed it. The tasklet also follows message client and port pointers,
so draining it only when the controller is removed is too late: the
outer remove path has already unregistered those children.

Disable the GDD IRQ before killing its tasklet and remove the child
ports only after that drain. Do this in the shared controller teardown
helper so failed child population follows the same order. Unregister
the clock notifier before removing the ports, and remove the controller
debugfs directory after the ports have removed their own directories.

The IRQ handler and tasklet balance a separate disable/enable pair; the
remove path's disable reference keeps the IRQ disabled even if the
last tasklet enables it.

This issue was found by static analysis.

Fixes: b209e047bc74 ("HSI: Introduce OMAP SSI driver")
Assisted-by: LLM
Co-developed-by: Ijae Kim <ae878000@xxxxxxxxx>
Signed-off-by: Ijae Kim <ae878000@xxxxxxxxx>
Signed-off-by: Myeonghun Pak <mhun512@xxxxxxxxx>
---
drivers/hsi/controllers/omap_ssi_core.c | 20 +++++++++-----------
1 file changed, 9 insertions(+), 11 deletions(-)

diff --git a/drivers/hsi/controllers/omap_ssi_core.c b/drivers/hsi/controllers/omap_ssi_core.c
index 0f5a6f7522ad..4a9b1057a32f 100644
--- a/drivers/hsi/controllers/omap_ssi_core.c
+++ b/drivers/hsi/controllers/omap_ssi_core.c
@@ -432,13 +432,21 @@ static int ssi_hw_init(struct hsi_controller *ssi)
return 0;
}

+static int ssi_remove_ports(struct device *dev, void *c);
+
static void ssi_remove_controller(struct hsi_controller *ssi)
{
struct omap_ssi_controller *omap_ssi = hsi_controller_drvdata(ssi);
int id = ssi->id;
+
+ disable_irq(omap_ssi->gdd_irq);
tasklet_kill(&omap_ssi->gdd_tasklet);
- hsi_unregister_controller(ssi);
clk_notifier_unregister(omap_ssi->fck, &omap_ssi->fck_nb);
+ device_for_each_child(ssi->device.parent, NULL, ssi_remove_ports);
+#ifdef CONFIG_DEBUG_FS
+ ssi_debug_remove_ctrl(ssi);
+#endif
+ hsi_unregister_controller(ssi);
ida_free(&platform_omap_ssi_ida, id);
}

@@ -531,10 +539,6 @@ static int ssi_probe(struct platform_device *pd)
ssi->id, num_ports);
return err;
out3:
- device_for_each_child(&pd->dev, NULL, ssi_remove_ports);
-#ifdef CONFIG_DEBUG_FS
- ssi_debug_remove_ctrl(ssi);
-#endif
out2:
ssi_remove_controller(ssi);
pm_runtime_disable(&pd->dev);
@@ -548,12 +552,6 @@ static void ssi_remove(struct platform_device *pd)
{
struct hsi_controller *ssi = platform_get_drvdata(pd);

- /* cleanup of of_platform_populate() call */
- device_for_each_child(&pd->dev, NULL, ssi_remove_ports);
-
-#ifdef CONFIG_DEBUG_FS
- ssi_debug_remove_ctrl(ssi);
-#endif
ssi_remove_controller(ssi);
platform_set_drvdata(pd, NULL);