[PATCH 09/16] i3c: mipi-i3c-hci: Add DMA ring abort/reset quirk for Intel controllers

From: Adrian Hunter

Date: Thu Apr 16 2026 - 14:04:18 EST


Some Intel I3C HCI controllers cannot reliably restart a DMA ring after an
ABORT. Additional queue resets are required to recover, and must be
performed using PIO reset bits even while operating in DMA mode.

This behavior is non-standard. Introduce a controller quirk to opt into
the required PIO queue resets after a DMA ring abort, and enable it for
Intel LPSS I3C controllers.

Signed-off-by: Adrian Hunter <adrian.hunter@xxxxxxxxx>
---
drivers/i3c/master/mipi-i3c-hci/core.c | 15 ++++++++++++++-
drivers/i3c/master/mipi-i3c-hci/dma.c | 9 +++++++++
drivers/i3c/master/mipi-i3c-hci/hci.h | 2 ++
3 files changed, 25 insertions(+), 1 deletion(-)

diff --git a/drivers/i3c/master/mipi-i3c-hci/core.c b/drivers/i3c/master/mipi-i3c-hci/core.c
index 5e1bc6d819cf..84fb03e918b0 100644
--- a/drivers/i3c/master/mipi-i3c-hci/core.c
+++ b/drivers/i3c/master/mipi-i3c-hci/core.c
@@ -240,6 +240,18 @@ void mipi_i3c_hci_pio_reset(struct i3c_hci *hci)
reg_write(RESET_CONTROL, RX_FIFO_RST | TX_FIFO_RST | RESP_QUEUE_RST);
}

+#define ALL_QUEUES_RST (CMD_QUEUE_RST | RESP_QUEUE_RST | RX_FIFO_RST | TX_FIFO_RST | IBI_QUEUE_RST)
+
+void mipi_i3c_hci_pio_reset_all_queues(struct i3c_hci *hci)
+{
+ u32 regval;
+
+ reg_write(RESET_CONTROL, ALL_QUEUES_RST);
+ if (readx_poll_timeout_atomic(reg_read, RESET_CONTROL, regval,
+ !(regval & ALL_QUEUES_RST), 0, 20))
+ dev_err(&hci->master.dev, "%s: Reset queues failed\n", __func__);
+}
+
/* located here rather than dct.c because needed bits are in core reg space */
void mipi_i3c_hci_dct_index_reset(struct i3c_hci *hci)
{
@@ -1043,7 +1055,8 @@ MODULE_DEVICE_TABLE(acpi, i3c_hci_acpi_match);
static const struct platform_device_id i3c_hci_driver_ids[] = {
{ .name = "intel-lpss-i3c", HCI_QUIRK_RPM_ALLOWED |
HCI_QUIRK_RPM_IBI_ALLOWED |
- HCI_QUIRK_RPM_PARENT_MANAGED },
+ HCI_QUIRK_RPM_PARENT_MANAGED |
+ HCI_QUIRK_DMA_ABORT_REQUIRES_PIO_RESET },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(platform, i3c_hci_driver_ids);
diff --git a/drivers/i3c/master/mipi-i3c-hci/dma.c b/drivers/i3c/master/mipi-i3c-hci/dma.c
index 5b394220a509..26533d3cc8bf 100644
--- a/drivers/i3c/master/mipi-i3c-hci/dma.c
+++ b/drivers/i3c/master/mipi-i3c-hci/dma.c
@@ -597,6 +597,13 @@ static void hci_dma_xfer_done(struct i3c_hci *hci, struct hci_rh_data *rh)
rh_reg_write(RING_OPERATION1, op1_val);
}

+static void hci_dma_abort_requires_pio_reset_quirk(struct i3c_hci *hci, struct hci_rh_data *rh)
+{
+ if ((hci->quirks & HCI_QUIRK_DMA_ABORT_REQUIRES_PIO_RESET) &&
+ (rh_reg_read(RING_STATUS) & RING_STATUS_ABORTED))
+ mipi_i3c_hci_pio_reset_all_queues(hci);
+}
+
static void hci_dma_unblock_enqueue(struct i3c_hci *hci)
{
if (hci->enqueue_blocked) {
@@ -638,6 +645,8 @@ static bool hci_dma_dequeue_xfer(struct i3c_hci *hci,
}
}

+ hci_dma_abort_requires_pio_reset_quirk(hci, rh);
+
hci_dma_xfer_done(hci, rh);

for (i = 0; i < n; i++) {
diff --git a/drivers/i3c/master/mipi-i3c-hci/hci.h b/drivers/i3c/master/mipi-i3c-hci/hci.h
index 527345a995ad..352effc468d9 100644
--- a/drivers/i3c/master/mipi-i3c-hci/hci.h
+++ b/drivers/i3c/master/mipi-i3c-hci/hci.h
@@ -156,10 +156,12 @@ struct i3c_hci_dev_data {
#define HCI_QUIRK_RPM_ALLOWED BIT(5) /* Runtime PM allowed */
#define HCI_QUIRK_RPM_IBI_ALLOWED BIT(6) /* IBI and Hot-Join allowed while runtime suspended */
#define HCI_QUIRK_RPM_PARENT_MANAGED BIT(7) /* Runtime PM managed by parent device */
+#define HCI_QUIRK_DMA_ABORT_REQUIRES_PIO_RESET BIT(8) /* Do PIO queue SW resets after DMA abort */

/* global functions */
void mipi_i3c_hci_resume(struct i3c_hci *hci);
void mipi_i3c_hci_pio_reset(struct i3c_hci *hci);
+void mipi_i3c_hci_pio_reset_all_queues(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);
--
2.51.0