[PATCH 4/8] i3c: mipi-i3c-hci: Add a quirk to clear the TX start threshold
Billy Tsai
billy_tsai at aspeedtech.com
Tue Sep 1 04:35:31 PDT 2026
The DATA_TX_START_THLD field of the PIO data buffer threshold register
holds off the start of a TX transfer until the FIFO holds a set amount
of data, which in PIO mode cuts down on the number of software writes.
The field resets to 0x1, requiring at least (2 ^ 2) DWORDs (16 bytes)
in the FIFO before transmission starts.
HCI controllers that support both PIO and DMA can expose the two as
separate register blocks; nothing in the specification says selecting
DMA mode disables the PIO block's own gating logic. On ASPEED
platforms it doesn't: DATA_TX_START_THLD still holds up transfer
start regardless of which mode feeds the FIFO, and in DMA mode that
threshold is never satisfied for some transfer sizes:
- 1-4 bytes: uses the Immediate Data Transfer Command.
- 13+ bytes: since the hardware fetches data in 4-byte chunks, a
13-byte transfer fetches 16 bytes into the FIFO and reaches the
threshold.
- 5-12 bytes: the threshold is never reached and the transfer stalls.
Add HCI_QUIRK_TX_START_THLD to clear the field whenever DMA mode is
selected. The clear lives in i3c_hci_set_io_mode() rather than in
probe so the reset-and-restore recovery path also reapplies it after
a controller soft reset. Move the PIO Access Area register and
bitfield definitions from pio.c to a new pio.h so this quirk can reuse
PIO_DATA_BUFFER_THLD_CTRL and DATA_TX_START_THLD instead of
redefining them.
Signed-off-by: Billy Tsai <billy_tsai at aspeedtech.com>
Assisted-by: Claude:claude-fable-5
---
drivers/i3c/master/mipi-i3c-hci/core.c | 14 +++++
drivers/i3c/master/mipi-i3c-hci/hci.h | 1 +
drivers/i3c/master/mipi-i3c-hci/pio.c | 97 +----------------------------
drivers/i3c/master/mipi-i3c-hci/pio.h | 109 +++++++++++++++++++++++++++++++++
4 files changed, 125 insertions(+), 96 deletions(-)
diff --git a/drivers/i3c/master/mipi-i3c-hci/core.c b/drivers/i3c/master/mipi-i3c-hci/core.c
index c03c3a9cbe4f..2290a889701c 100644
--- a/drivers/i3c/master/mipi-i3c-hci/core.c
+++ b/drivers/i3c/master/mipi-i3c-hci/core.c
@@ -26,6 +26,7 @@
#include "cmd.h"
#include "dat.h"
#include "ibi.h"
+#include "pio.h"
/*
* Host Controller Capabilities and Operation Registers
@@ -823,6 +824,19 @@ static int i3c_hci_set_io_mode(struct i3c_hci *hci, bool dma)
else
reg_set(HC_CONTROL, HC_CONTROL_PIO_MODE);
+ /*
+ * On the ASPEED AST2700 the TX start threshold gates transfer start
+ * even in DMA mode. Clear it so DMA transfers are not held back
+ * waiting for a PIO FIFO level that will never be reached.
+ */
+ if (dma && (hci->quirks & HCI_QUIRK_TX_START_THLD) && hci->PIO_regs) {
+ void __iomem *thld_reg = hci->PIO_regs + PIO_DATA_BUFFER_THLD_CTRL;
+ u32 thld_val = readl(thld_reg);
+
+ thld_val &= ~DATA_TX_START_THLD;
+ writel(thld_val, thld_reg);
+ }
+
if (!is_version_1_1_or_newer(hci))
return 0;
diff --git a/drivers/i3c/master/mipi-i3c-hci/hci.h b/drivers/i3c/master/mipi-i3c-hci/hci.h
index 2110f806a53c..d9c9e609a879 100644
--- a/drivers/i3c/master/mipi-i3c-hci/hci.h
+++ b/drivers/i3c/master/mipi-i3c-hci/hci.h
@@ -179,6 +179,7 @@ struct i3c_hci_dev_data {
#define HCI_QUIRK_DMA_REQUIRES_HC_ABORT BIT(9) /* Use HC_CONTROL ABORT to abort DMA */
#define HCI_QUIRK_DAT_INDEX_IS_ADDR BIT(10) /* DAT entries are indexed by device address */
#define HCI_QUIRK_DMA_64BIT BIT(11) /* Controller DMA supports 64-bit addressing */
+#define HCI_QUIRK_TX_START_THLD BIT(12) /* Clear TX start threshold in DMA mode */
/* global functions */
void mipi_i3c_hci_resume(struct i3c_hci *hci);
diff --git a/drivers/i3c/master/mipi-i3c-hci/pio.c b/drivers/i3c/master/mipi-i3c-hci/pio.c
index 439578a6eb54..6bd5e43364a1 100644
--- a/drivers/i3c/master/mipi-i3c-hci/pio.c
+++ b/drivers/i3c/master/mipi-i3c-hci/pio.c
@@ -14,6 +14,7 @@
#include "hci.h"
#include "cmd.h"
#include "ibi.h"
+#include "pio.h"
/*
* PIO Access Area
@@ -22,102 +23,6 @@
#define pio_reg_read(r) readl(hci->PIO_regs + (PIO_##r))
#define pio_reg_write(r, v) writel(v, hci->PIO_regs + (PIO_##r))
-#define PIO_COMMAND_QUEUE_PORT 0x00
-#define PIO_RESPONSE_QUEUE_PORT 0x04
-#define PIO_XFER_DATA_PORT 0x08
-#define PIO_IBI_PORT 0x0c
-
-#define PIO_QUEUE_THLD_CTRL 0x10
-#define QUEUE_IBI_STATUS_THLD GENMASK(31, 24)
-#define QUEUE_IBI_DATA_THLD GENMASK(23, 16)
-#define QUEUE_RESP_BUF_THLD GENMASK(15, 8)
-#define QUEUE_CMD_EMPTY_BUF_THLD GENMASK(7, 0)
-
-#define PIO_DATA_BUFFER_THLD_CTRL 0x14
-#define DATA_RX_START_THLD GENMASK(26, 24)
-#define DATA_TX_START_THLD GENMASK(18, 16)
-#define DATA_RX_BUF_THLD GENMASK(10, 8)
-#define DATA_TX_BUF_THLD GENMASK(2, 0)
-
-#define PIO_QUEUE_SIZE 0x18
-#define TX_DATA_BUFFER_SIZE GENMASK(31, 24)
-#define RX_DATA_BUFFER_SIZE GENMASK(23, 16)
-#define IBI_STATUS_SIZE GENMASK(15, 8)
-#define CR_QUEUE_SIZE GENMASK(7, 0)
-
-#define PIO_ALT_QUEUE_SIZE 0x1C
-#define EXT_IBI_QUEUE_EN BIT(28)
-#define ALT_RESP_QUEUE_EN BIT(24)
-#define ALT_RESP_QUEUE_SIZE GENMASK(7, 0)
-
-#define PIO_INTR_STATUS 0x20
-#define PIO_INTR_STATUS_ENABLE 0x24
-#define PIO_INTR_SIGNAL_ENABLE 0x28
-#define PIO_INTR_FORCE 0x2c
-#define STAT_TRANSFER_BLOCKED BIT(25)
-#define STAT_PERR_RESP_UFLOW BIT(24)
-#define STAT_PERR_CMD_OFLOW BIT(23)
-#define STAT_PERR_IBI_UFLOW BIT(22)
-#define STAT_PERR_RX_UFLOW BIT(21)
-#define STAT_PERR_TX_OFLOW BIT(20)
-#define STAT_ERR_RESP_QUEUE_FULL BIT(19)
-#define STAT_WARN_RESP_QUEUE_FULL BIT(18)
-#define STAT_ERR_IBI_QUEUE_FULL BIT(17)
-#define STAT_WARN_IBI_QUEUE_FULL BIT(16)
-#define STAT_ERR_RX_DATA_FULL BIT(15)
-#define STAT_WARN_RX_DATA_FULL BIT(14)
-#define STAT_ERR_TX_DATA_EMPTY BIT(13)
-#define STAT_WARN_TX_DATA_EMPTY BIT(12)
-#define STAT_TRANSFER_ERR BIT(9)
-#define STAT_WARN_INS_STOP_MODE BIT(7)
-#define STAT_TRANSFER_ABORT BIT(5)
-#define STAT_RESP_READY BIT(4)
-#define STAT_CMD_QUEUE_READY BIT(3)
-#define STAT_IBI_STATUS_THLD BIT(2)
-#define STAT_RX_THLD BIT(1)
-#define STAT_TX_THLD BIT(0)
-
-#define PIO_CONTROL 0x30
-#define PIO_CONTROL_ABORT BIT(2)
-#define PIO_CONTROL_RS BIT(1)
-#define PIO_CONTROL_ENABLE BIT(0)
-
-#define PIO_QUEUE_CUR_STATUS 0x38
-#define CUR_IBI_Q_LEVEL GENMASK(28, 20)
-#define CUR_RESP_Q_LEVEL GENMASK(18, 10)
-#define CUR_CMD_Q_EMPTY_LEVEL GENMASK(8, 0)
-
-#define PIO_DATA_BUFFER_CUR_STATUS 0x3c
-#define CUR_RX_BUF_LVL GENMASK(26, 16)
-#define CUR_TX_BUF_LVL GENMASK(10, 0)
-
-/*
- * Handy status bit combinations
- */
-
-#define STAT_LATENCY_WARNINGS (STAT_WARN_RESP_QUEUE_FULL | \
- STAT_WARN_IBI_QUEUE_FULL | \
- STAT_WARN_RX_DATA_FULL | \
- STAT_WARN_TX_DATA_EMPTY | \
- STAT_WARN_INS_STOP_MODE)
-
-#define STAT_LATENCY_ERRORS (STAT_ERR_RESP_QUEUE_FULL | \
- STAT_ERR_IBI_QUEUE_FULL | \
- STAT_ERR_RX_DATA_FULL | \
- STAT_ERR_TX_DATA_EMPTY)
-
-#define STAT_PROG_ERRORS (STAT_TRANSFER_BLOCKED | \
- STAT_PERR_RESP_UFLOW | \
- STAT_PERR_CMD_OFLOW | \
- STAT_PERR_IBI_UFLOW | \
- STAT_PERR_RX_UFLOW | \
- STAT_PERR_TX_OFLOW)
-
-#define STAT_ALL_ERRORS (STAT_TRANSFER_ABORT | \
- STAT_TRANSFER_ERR | \
- STAT_LATENCY_ERRORS | \
- STAT_PROG_ERRORS)
-
struct hci_pio_dev_ibi_data {
struct i3c_generic_ibi_pool *pool;
unsigned int max_len;
diff --git a/drivers/i3c/master/mipi-i3c-hci/pio.h b/drivers/i3c/master/mipi-i3c-hci/pio.h
new file mode 100644
index 000000000000..72318961e991
--- /dev/null
+++ b/drivers/i3c/master/mipi-i3c-hci/pio.h
@@ -0,0 +1,109 @@
+/* SPDX-License-Identifier: BSD-3-Clause */
+/*
+ * Copyright (c) 2020, MIPI Alliance, Inc.
+ *
+ * Author: Nicolas Pitre <npitre at baylibre.com>
+ *
+ * PIO Access Area register and bitfield definitions.
+ */
+
+#ifndef PIO_H
+#define PIO_H
+
+#define PIO_COMMAND_QUEUE_PORT 0x00
+#define PIO_RESPONSE_QUEUE_PORT 0x04
+#define PIO_XFER_DATA_PORT 0x08
+#define PIO_IBI_PORT 0x0c
+
+#define PIO_QUEUE_THLD_CTRL 0x10
+#define QUEUE_IBI_STATUS_THLD GENMASK(31, 24)
+#define QUEUE_IBI_DATA_THLD GENMASK(23, 16)
+#define QUEUE_RESP_BUF_THLD GENMASK(15, 8)
+#define QUEUE_CMD_EMPTY_BUF_THLD GENMASK(7, 0)
+
+#define PIO_DATA_BUFFER_THLD_CTRL 0x14
+#define DATA_TX_START_THLD GENMASK(18, 16)
+#define DATA_RX_START_THLD GENMASK(26, 24)
+#define DATA_RX_BUF_THLD GENMASK(10, 8)
+#define DATA_TX_BUF_THLD GENMASK(2, 0)
+
+#define PIO_QUEUE_SIZE 0x18
+#define TX_DATA_BUFFER_SIZE GENMASK(31, 24)
+#define RX_DATA_BUFFER_SIZE GENMASK(23, 16)
+#define IBI_STATUS_SIZE GENMASK(15, 8)
+#define CR_QUEUE_SIZE GENMASK(7, 0)
+
+#define PIO_ALT_QUEUE_SIZE 0x1C
+#define EXT_IBI_QUEUE_EN BIT(28)
+#define ALT_RESP_QUEUE_EN BIT(24)
+#define ALT_RESP_QUEUE_SIZE GENMASK(7, 0)
+
+#define PIO_INTR_STATUS 0x20
+#define PIO_INTR_STATUS_ENABLE 0x24
+#define PIO_INTR_SIGNAL_ENABLE 0x28
+#define PIO_INTR_FORCE 0x2c
+#define STAT_TRANSFER_BLOCKED BIT(25)
+#define STAT_PERR_RESP_UFLOW BIT(24)
+#define STAT_PERR_CMD_OFLOW BIT(23)
+#define STAT_PERR_IBI_UFLOW BIT(22)
+#define STAT_PERR_RX_UFLOW BIT(21)
+#define STAT_PERR_TX_OFLOW BIT(20)
+#define STAT_ERR_RESP_QUEUE_FULL BIT(19)
+#define STAT_WARN_RESP_QUEUE_FULL BIT(18)
+#define STAT_ERR_IBI_QUEUE_FULL BIT(17)
+#define STAT_WARN_IBI_QUEUE_FULL BIT(16)
+#define STAT_ERR_RX_DATA_FULL BIT(15)
+#define STAT_WARN_RX_DATA_FULL BIT(14)
+#define STAT_ERR_TX_DATA_EMPTY BIT(13)
+#define STAT_WARN_TX_DATA_EMPTY BIT(12)
+#define STAT_TRANSFER_ERR BIT(9)
+#define STAT_WARN_INS_STOP_MODE BIT(7)
+#define STAT_TRANSFER_ABORT BIT(5)
+#define STAT_RESP_READY BIT(4)
+#define STAT_CMD_QUEUE_READY BIT(3)
+#define STAT_IBI_STATUS_THLD BIT(2)
+#define STAT_RX_THLD BIT(1)
+#define STAT_TX_THLD BIT(0)
+
+#define PIO_CONTROL 0x30
+#define PIO_CONTROL_ABORT BIT(2)
+#define PIO_CONTROL_RS BIT(1)
+#define PIO_CONTROL_ENABLE BIT(0)
+
+#define PIO_QUEUE_CUR_STATUS 0x38
+#define CUR_IBI_Q_LEVEL GENMASK(28, 20)
+#define CUR_RESP_Q_LEVEL GENMASK(18, 10)
+#define CUR_CMD_Q_EMPTY_LEVEL GENMASK(8, 0)
+
+#define PIO_DATA_BUFFER_CUR_STATUS 0x3c
+#define CUR_RX_BUF_LVL GENMASK(26, 16)
+#define CUR_TX_BUF_LVL GENMASK(10, 0)
+
+/*
+ * Handy status bit combinations
+ */
+
+#define STAT_LATENCY_WARNINGS (STAT_WARN_RESP_QUEUE_FULL | \
+ STAT_WARN_IBI_QUEUE_FULL | \
+ STAT_WARN_RX_DATA_FULL | \
+ STAT_WARN_TX_DATA_EMPTY | \
+ STAT_WARN_INS_STOP_MODE)
+
+#define STAT_LATENCY_ERRORS (STAT_ERR_RESP_QUEUE_FULL | \
+ STAT_ERR_IBI_QUEUE_FULL | \
+ STAT_ERR_RX_DATA_FULL | \
+ STAT_ERR_TX_DATA_EMPTY)
+
+#define STAT_PROG_ERRORS (STAT_TRANSFER_BLOCKED | \
+ STAT_PERR_RESP_UFLOW | \
+ STAT_PERR_CMD_OFLOW | \
+ STAT_PERR_IBI_UFLOW | \
+ STAT_PERR_RX_UFLOW | \
+ STAT_PERR_TX_OFLOW)
+
+#define STAT_ALL_ERRORS (STAT_TRANSFER_ABORT | \
+ STAT_TRANSFER_ERR | \
+ STAT_LATENCY_ERRORS | \
+ STAT_PROG_ERRORS)
+
+#endif
--
2.34.1
More information about the linux-arm-kernel
mailing list