From ad774796ffa3bf63aef19c1fa311b9bc85ebb2e9 Mon Sep 17 00:00:00 2001 From: Mahesh Kurapati Date: Thu, 26 Mar 2026 07:52:55 -0500 Subject: [PATCH 1/2] i3c: mipi-i3c-hci: introduce aspeed_pio_fifo_reset for DMA status check The ASPEED I3C controller reuses the PIO FIFO in DMA mode. When transfer errors or aborts occur, the interrupt may be raised before the DMA engine completes the transfer, leading to potential data corruption if the FIFO is reset immediately. Add aspeed_pio_fifo_reset() function that: - Polls WDMA_DBG_LO and RDMA_DBG_LO registers for I3C_DMA_DBG_LO_BUSY bit - Uses readl_poll_timeout_atomic() with 100ms timeout for reliable polling - Only resets PIO FIFOs after confirming DMA engines are idle - Replaces direct FIFO reset calls on transfer aborts Also add DMA debug register definitions and status bit masks to vendor_aspeed.h to support the polling mechanism. Signed-off-by: Billy Tsai --- drivers/i3c/master/mipi-i3c-hci/dma.c | 41 ++++++++++++++++--- .../i3c/master/mipi-i3c-hci/vendor_aspeed.h | 30 ++++++++++++++ 2 files changed, 65 insertions(+), 6 deletions(-) diff --git a/drivers/i3c/master/mipi-i3c-hci/dma.c b/drivers/i3c/master/mipi-i3c-hci/dma.c index d404f8c0c70099..d25618cde8d250 100644 --- a/drivers/i3c/master/mipi-i3c-hci/dma.c +++ b/drivers/i3c/master/mipi-i3c-hci/dma.c @@ -14,6 +14,7 @@ #include #include #include +#include #include "hci.h" #include "cmd.h" @@ -859,6 +860,39 @@ static void hci_dma_process_ibi(struct i3c_hci *hci, struct hci_rh_data *rh) rh_reg_write(CHUNK_CONTROL, rh_reg_read(CHUNK_CONTROL) + ibi_chunks); } +#ifdef CONFIG_ARCH_ASPEED +/* + * The ASPEED I3C controller reuses the PIO FIFO in DMA mode. On transfer error or abort, poll the + * DMA debug busy bit to ensure all transfers are complete before resetting the FIFO, as the + * interrupt will be raised before the DMA engine completes the transfer. + */ +static void aspeed_pio_fifo_reset(struct i3c_hci *hci) +{ + u32 wdma_dbg, rdma_dbg; + int ret; + + dev_dbg(&hci->master.dev, "WDMA_DBG_LO = 0x%x, RDMA_DBG_LO = 0x%x RING_STATUS = 0x%x\n", + readl(hci->base_regs + ASPEED_I3C_WDMA_DBG_LO), + readl(hci->base_regs + ASPEED_I3C_RDMA_DBG_LO), + readl(hci->base_regs + ASPEED_I3C_RING_STATUS)); + + /* Poll I3C_DMA_DBG_LO_BUSY until it becomes 0 */ + ret = readl_poll_timeout_atomic(hci->base_regs + ASPEED_I3C_WDMA_DBG_LO, wdma_dbg, + !(wdma_dbg & I3C_DMA_DBG_LO_BUSY), 1, 100000); + if (ret) + dev_warn(&hci->master.dev, "WDMA still busy after timeout: 0x%x\n", wdma_dbg); + + ret = readl_poll_timeout_atomic(hci->base_regs + ASPEED_I3C_RDMA_DBG_LO, rdma_dbg, + !(rdma_dbg & I3C_DMA_DBG_LO_BUSY), 1, 100000); + if (ret) + dev_warn(&hci->master.dev, "RDMA still busy after timeout: 0x%x\n", rdma_dbg); + + mipi_i3c_hci_pio_ibi_reset(hci); + mipi_i3c_hci_pio_reset(hci); +} + +#endif + static bool hci_dma_irq_handler(struct i3c_hci *hci, unsigned int mask) { struct hci_rings_data *rings = hci->io_data; @@ -897,12 +931,7 @@ static bool hci_dma_irq_handler(struct i3c_hci *hci, unsigned int mask) dev_notice_ratelimited(&hci->master.dev, "ring %d: Transfer Aborted\n", i); #ifdef CONFIG_ARCH_ASPEED - /* - * Aspeed i3c controller will reuse the PIO fifo in DMA mode, - * so we need to reset the PIO fifo when the transfer is aborted. - */ - mipi_i3c_hci_pio_ibi_reset(hci); - mipi_i3c_hci_pio_reset(hci); + aspeed_pio_fifo_reset(hci); #endif mipi_i3c_hci_resume(hci); } diff --git a/drivers/i3c/master/mipi-i3c-hci/vendor_aspeed.h b/drivers/i3c/master/mipi-i3c-hci/vendor_aspeed.h index 130829086fbfdb..29f1be3052a884 100644 --- a/drivers/i3c/master/mipi-i3c-hci/vendor_aspeed.h +++ b/drivers/i3c/master/mipi-i3c-hci/vendor_aspeed.h @@ -59,6 +59,36 @@ #define ASPEED_I3C_AUTOCMD_SEL_112_119 0x78 #define ASPEED_I3C_AUTOCMD_SEL_120_127 0x7C +#define ASPEED_I3C_WDMA_CTRL 0x80 +#define ASPEED_I3C_WDMA_DBG_LO 0x84 +#define ASPEED_I3C_WDMA_DBG_HI 0x88 +#define ASPEED_I3C_RDMA_CTRL 0x90 +#define ASPEED_I3C_RDMA_DBG_LO 0x94 +#define ASPEED_I3C_RDMA_DBG_HI 0x98 +#define I3C_DMA_DBG_LO_ABORT BIT(0) +#define I3C_DMA_DBG_LO_BUSY BIT(1) +#define I3C_DMA_DBG_LO_DONE BIT(2) +#define I3C_DMA_DBG_LO_START BIT(3) +#define I3C_DMA_DBG_LO_READY BIT(4) +#define I3C_DMA_DBG_LO_VALID BIT(5) +#define I3C_DMA_DBG_LO_EN BIT(10) + +#define ASPEED_I3C_RING_STATUS 0x9C +#define I3C_RING_IDLE 0 +#define I3C_RING_GET_TRANSFER 1 +#define I3C_RING_GET_TX_DATA 2 +#define I3C_RING_CMD_PROCESS 3 +#define I3C_RING_WRITE_RX_DATA 4 +#define I3C_RING_WRITE_RESPONSE 5 +#define I3C_RING_UPDATE_CR_PTR 6 +#define I3C_RING_WRITE_IBI_STS 7 +#define I3C_RING_WRITE_IBI_DAT 8 +#define I3C_RING_UPDATE_IBI_PTR 9 +#define I3C_RING_ABORT 10 +#define I3C_RING_SLV_WRITE_DATA 11 +#define I3C_RING_SLV_WRITE_RESPONSE 12 +#define I3C_RING_SLV_UPDATE_PT 13 + #define ASPEED_I3C_SLV_CHAR_CTRL 0xA0 #define ASPEED_I3C_SLV_CHAR_CTRL_DCR GENMASK(23, 16) #define ASPEED_I3C_SLV_CHAR_CTRL_BCR GENMASK(15, 8) From dfe173cd6936b2dbb0a3779a8067056a3a6ddace Mon Sep 17 00:00:00 2001 From: Mahesh Kurapati Date: Thu, 26 Mar 2026 07:56:57 -0500 Subject: [PATCH 2/2] i3c: mipi-i3c-hci: add PIO FIFO reset on transfer errors for ASPEED The ASPEED I3C controller reuses PIO FIFO in DMA mode, but transfer errors were not clearing the FIFO properly. This causes data corruption issues when sending consecutive Regular Data Transfer Commands. When the first transfer gets NACKed, its data remains in the PIO FIFO. Without clearing the FIFO, subsequent transfers will: - Read shifted/stale data from the previous failed transfer - Reuse corrupted data leading to incorrect transfer results Add aspeed_pio_fifo_reset() call on INTR_TRANSFER_ERR to ensure the PIO FIFO is properly cleared before the controller is resumed. This prevents data contamination between transfers and ensures data integrity for subsequent operations. Signed-off-by: Billy Tsai --- drivers/i3c/master/mipi-i3c-hci/dma.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/i3c/master/mipi-i3c-hci/dma.c b/drivers/i3c/master/mipi-i3c-hci/dma.c index d25618cde8d250..899740fe8559f4 100644 --- a/drivers/i3c/master/mipi-i3c-hci/dma.c +++ b/drivers/i3c/master/mipi-i3c-hci/dma.c @@ -921,6 +921,9 @@ static bool hci_dma_irq_handler(struct i3c_hci *hci, unsigned int mask) if (unlikely(status & INTR_TRANSFER_ERR)) { dev_warn(&hci->master.dev, "ring %d: Transfer Error\n", i); +#ifdef CONFIG_ARCH_ASPEED + aspeed_pio_fifo_reset(hci); +#endif mipi_i3c_hci_resume(hci); } }