Commit 8bb96575 authored by Adrian Hunter's avatar Adrian Hunter Committed by Alexandre Belloni
Browse files

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: default avatarAdrian Hunter <adrian.hunter@intel.com>
Reviewed-by: default avatarFrank Li <Frank.Li@nxp.com>
Link: https://patch.msgid.link/20260113072702.16268-3-adrian.hunter@intel.com


Signed-off-by: default avatarAlexandre Belloni <alexandre.belloni@bootlin.com>
parent 78f63ae4
Loading
Loading
Loading
Loading
+29 −3
Original line number Diff line number Diff line
@@ -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);
+7 −0
Original line number Diff line number Diff line
@@ -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);

+1 −0
Original line number Diff line number Diff line
@@ -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
+2 −0
Original line number Diff line number Diff line
@@ -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));