summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAdrian Hunter <adrian.hunter@intel.com>2026-03-06 09:24:45 +0200
committerAlexandre Belloni <alexandre.belloni@bootlin.com>2026-03-11 22:10:02 +0100
commitb795e68bf3073d67bebbb5a44d93f49efc5b8cc7 (patch)
treeb731a939beb7d08e1df0370aef10a7fb2a76a131
parentf0b5159637ca0b8feaaa95de0f5ea38f1ba26729 (diff)
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: 9ad9a52cce282 ("i3c/master: introduce the mipi-i3c-hci driver") Cc: stable@vger.kernel.org Signed-off-by: Adrian Hunter <adrian.hunter@intel.com> Reviewed-by: Frank Li <Frank.Li@nxp.com> Link: https://patch.msgid.link/20260306072451.11131-9-adrian.hunter@intel.com Signed-off-by: Alexandre Belloni <alexandre.belloni@bootlin.com>
-rw-r--r--drivers/i3c/master/mipi-i3c-hci/dma.c25
1 files changed, 16 insertions, 9 deletions
diff --git a/drivers/i3c/master/mipi-i3c-hci/dma.c b/drivers/i3c/master/mipi-i3c-hci/dma.c
index d7840ff69e59..a3e8e01a35c9 100644
--- a/drivers/i3c/master/mipi-i3c-hci/dma.c
+++ b/drivers/i3c/master/mipi-i3c-hci/dma.c
@@ -546,18 +546,25 @@ 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);
- /* stop the ring */
- rh_reg_write(RING_CONTROL, RING_CTRL_ABORT);
- if (wait_for_completion_timeout(&rh->op_done, HZ) == 0) {
- /*
- * We're deep in it if ever this condition is ever met.
- * Hardware might still be writing to memory, etc.
- */
- dev_crit(&hci->master.dev, "unable to abort the ring\n");
- WARN_ON(1);
+ ring_status = rh_reg_read(RING_STATUS);
+ if (ring_status & RING_STATUS_RUNNING) {
+ /* stop the ring */
+ 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.
+ */
+ dev_crit(&hci->master.dev, "unable to abort the ring\n");
+ WARN_ON(1);
+ }
}
spin_lock_irq(&hci->lock);