i3c: mipi-i3c-hci: Ensure proper bus clean-up

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>
This commit is contained in:
Adrian Hunter 2026-01-13 09:26:43 +02:00 committed by Alexandre Belloni
parent 78f63ae4a8
commit 8bb9657588
4 changed files with 39 additions and 3 deletions

View 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);

View 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);

View 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

View 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));