]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
i3c: mipi-i3c-hci: Ensure proper bus clean-up
authorAdrian Hunter <adrian.hunter@intel.com>
Tue, 13 Jan 2026 07:26:43 +0000 (09:26 +0200)
committerAlexandre Belloni <alexandre.belloni@bootlin.com>
Wed, 14 Jan 2026 16:21:09 +0000 (17:21 +0100)
Wait for the bus to fully disable before proceeding, ensuring that no
operations are still in progress.  Synchronize the IRQ handler only after
interrupt signals have been disabled.  This approach also handles cases
where bus disable might fail, preventing race conditions and ensuring a
consistent shutdown sequence.

Signed-off-by: Adrian Hunter <adrian.hunter@intel.com>
Reviewed-by: Frank Li <Frank.Li@nxp.com>
Link: https://patch.msgid.link/20260113072702.16268-3-adrian.hunter@intel.com
Signed-off-by: Alexandre Belloni <alexandre.belloni@bootlin.com>
drivers/i3c/master/mipi-i3c-hci/core.c
drivers/i3c/master/mipi-i3c-hci/dma.c
drivers/i3c/master/mipi-i3c-hci/hci.h
drivers/i3c/master/mipi-i3c-hci/pio.c

index 6da5daf18166ff63fe4d74a6b5aa2fe0ef44cf37..0d3ec674878d7037e65a10f5c46081a6bdda4231 100644 (file)
@@ -151,13 +151,39 @@ static int i3c_hci_bus_init(struct i3c_master_controller *m)
        return 0;
 }
 
+/* Bus disable should never fail, so be generous with the timeout */
+#define BUS_DISABLE_TIMEOUT_US (500 * USEC_PER_MSEC)
+
+static int i3c_hci_bus_disable(struct i3c_hci *hci)
+{
+       u32 regval;
+       int ret;
+
+       reg_clear(HC_CONTROL, HC_CONTROL_BUS_ENABLE);
+
+       /* Ensure controller is disabled */
+       ret = readx_poll_timeout(reg_read, HC_CONTROL, regval,
+                                !(regval & HC_CONTROL_BUS_ENABLE), 0, BUS_DISABLE_TIMEOUT_US);
+       if (ret)
+               dev_err(&hci->master.dev, "%s: Failed to disable bus\n", __func__);
+
+       return ret;
+}
+
+void i3c_hci_sync_irq_inactive(struct i3c_hci *hci)
+{
+       struct platform_device *pdev = to_platform_device(hci->master.dev.parent);
+       int irq = platform_get_irq(pdev, 0);
+
+       reg_write(INTR_SIGNAL_ENABLE, 0x0);
+       synchronize_irq(irq);
+}
+
 static void i3c_hci_bus_cleanup(struct i3c_master_controller *m)
 {
        struct i3c_hci *hci = to_i3c_hci(m);
-       struct platform_device *pdev = to_platform_device(m->dev.parent);
 
-       reg_clear(HC_CONTROL, HC_CONTROL_BUS_ENABLE);
-       synchronize_irq(platform_get_irq(pdev, 0));
+       i3c_hci_bus_disable(hci);
        hci->io->cleanup(hci);
        if (hci->cmd == &mipi_i3c_hci_cmd_v1)
                mipi_i3c_hci_dat_v1.cleanup(hci);
index 5515ed740ca4b56ddbcf0f59b21fe33ebaf18308..54849aa98fad1afc983aff4e96978c73d7367585 100644 (file)
@@ -160,6 +160,13 @@ static void hci_dma_cleanup(struct i3c_hci *hci)
 
                rh_reg_write(INTR_SIGNAL_ENABLE, 0);
                rh_reg_write(RING_CONTROL, 0);
+       }
+
+       i3c_hci_sync_irq_inactive(hci);
+
+       for (i = 0; i < rings->total; i++) {
+               rh = &rings->headers[i];
+
                rh_reg_write(CR_SETUP, 0);
                rh_reg_write(IBI_SETUP, 0);
 
index 3f88b67bc5cc285b5896af4829bd9754905f89aa..fd08b701d094f599f9dcff84dafbe938383959c0 100644 (file)
@@ -142,5 +142,6 @@ void mipi_i3c_hci_pio_reset(struct i3c_hci *hci);
 void mipi_i3c_hci_dct_index_reset(struct i3c_hci *hci);
 void amd_set_od_pp_timing(struct i3c_hci *hci);
 void amd_set_resp_buf_thld(struct i3c_hci *hci);
+void i3c_hci_sync_irq_inactive(struct i3c_hci *hci);
 
 #endif
index 109c6c5d83d6bea9ad070e5ff232b288eec1824f..90dca56fc0c53900b5c7696fe63c7272d4a26106 100644 (file)
@@ -211,6 +211,8 @@ static void hci_pio_cleanup(struct i3c_hci *hci)
 
        pio_reg_write(INTR_SIGNAL_ENABLE, 0x0);
 
+       i3c_hci_sync_irq_inactive(hci);
+
        if (pio) {
                dev_dbg(&hci->master.dev, "status = %#x/%#x",
                        pio_reg_read(INTR_STATUS), pio_reg_read(INTR_SIGNAL_ENABLE));