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

i3c: mipi-i3c-hci: Correct RING_CTRL_ABORT handling in DMA dequeue



The logic used to abort the DMA ring contains several flaws:

 1. The driver unconditionally issues a ring abort even when the ring has
    already stopped.
 2. The completion used to wait for abort completion is never
    re-initialized, resulting in incorrect wait behavior.
 3. The abort sequence unintentionally clears RING_CTRL_ENABLE, which
    resets hardware ring pointers and disrupts the controller state.
 4. If the ring is already stopped, the abort operation should be
    considered successful without attempting further action.

Fix the abort handling by checking whether the ring is running before
issuing an abort, re-initializing the completion when needed, ensuring that
RING_CTRL_ENABLE remains asserted during abort, and treating an already
stopped ring as a successful condition.

Fixes: 9ad9a52c ("i3c/master: introduce the mipi-i3c-hci driver")
Cc: stable@vger.kernel.org
Signed-off-by: default avatarAdrian Hunter <adrian.hunter@intel.com>
Reviewed-by: default avatarFrank Li <Frank.Li@nxp.com>
Link: https://patch.msgid.link/20260306072451.11131-9-adrian.hunter@intel.com


Signed-off-by: default avatarAlexandre Belloni <alexandre.belloni@bootlin.com>
parent f0b51596
Loading
Loading
Loading
Loading
+16 −9
Original line number Diff line number Diff line
@@ -546,12 +546,18 @@ static bool hci_dma_dequeue_xfer(struct i3c_hci *hci,
	struct hci_rh_data *rh = &rings->headers[xfer_list[0].ring_number];
	unsigned int i;
	bool did_unqueue = false;
	u32 ring_status;

	guard(mutex)(&hci->control_mutex);

	ring_status = rh_reg_read(RING_STATUS);
	if (ring_status & RING_STATUS_RUNNING) {
		/* stop the ring */
	rh_reg_write(RING_CONTROL, RING_CTRL_ABORT);
	if (wait_for_completion_timeout(&rh->op_done, HZ) == 0) {
		reinit_completion(&rh->op_done);
		rh_reg_write(RING_CONTROL, RING_CTRL_ENABLE | RING_CTRL_ABORT);
		wait_for_completion_timeout(&rh->op_done, HZ);
		ring_status = rh_reg_read(RING_STATUS);
		if (ring_status & RING_STATUS_RUNNING) {
			/*
			 * We're deep in it if ever this condition is ever met.
			 * Hardware might still be writing to memory, etc.
@@ -559,6 +565,7 @@ static bool hci_dma_dequeue_xfer(struct i3c_hci *hci,
			dev_crit(&hci->master.dev, "unable to abort the ring\n");
			WARN_ON(1);
		}
	}

	spin_lock_irq(&hci->lock);