* [PATCH 1/2] i3c: Add HDR_BT mode to the I3C framework
2026-10-13 8:04 [PATCH 0/2] i3c: master: dw-i3c-master: HDR-BT support Dineshkumar Parameswaran
@ 2026-10-13 8:04 ` Dineshkumar Parameswaran
2026-10-13 8:04 ` [PATCH 2/2] i3c: master: dw-i3c-master: add HDR-BT transfer support Dineshkumar Parameswaran
1 sibling, 0 replies; 3+ messages in thread
From: Dineshkumar Parameswaran @ 2026-10-13 8:04 UTC (permalink / raw)
To: linux-i3c
Cc: alexandre.belloni, Frank.Li, linux-kernel, Dineshkumar Parameswaran
Enables representing HDR-BT capability in the core transfer mode
definitions.
Signed-off-by: Dineshkumar Parameswaran <parame@synopsys.com>
---
include/linux/i3c/device.h | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/include/linux/i3c/device.h b/include/linux/i3c/device.h
index e30ea2cb13cb..87aa6bfab69d 100644
--- a/include/linux/i3c/device.h
+++ b/include/linux/i3c/device.h
@@ -43,13 +43,15 @@ enum i3c_error_code {
* @I3C_HDR_DDR: DDR mode
* @I3C_HDR_TSP: TSP mode
* @I3C_HDR_TSL: TSL mode
+ * @I3C_HDR_BT: BT mode
* @I3C_SDR: SDR mode (NOT HDR mode)
*/
enum i3c_xfer_mode {
- /* The below 3 value (I3C_HDR*) must match GETCAP1 Byte bit position */
+ /* The below 4 value (I3C_HDR*) must match GETCAP1 Byte bit position */
I3C_HDR_DDR = 0,
I3C_HDR_TSP = 1,
I3C_HDR_TSL = 2,
+ I3C_HDR_BT = 3,
/* Use for default SDR transfer mode */
I3C_SDR = 31,
};
--
2.34.1
^ permalink raw reply [flat|nested] 3+ messages in thread* [PATCH 2/2] i3c: master: dw-i3c-master: add HDR-BT transfer support
2026-10-13 8:04 [PATCH 0/2] i3c: master: dw-i3c-master: HDR-BT support Dineshkumar Parameswaran
2026-10-13 8:04 ` [PATCH 1/2] i3c: Add HDR_BT mode to the I3C framework Dineshkumar Parameswaran
@ 2026-10-13 8:04 ` Dineshkumar Parameswaran
1 sibling, 0 replies; 3+ messages in thread
From: Dineshkumar Parameswaran @ 2026-10-13 8:04 UTC (permalink / raw)
To: linux-i3c
Cc: alexandre.belloni, Frank.Li, linux-kernel, Dineshkumar Parameswaran
Key differences:
-Add HDR-BT support for the DesignWare I3C master by introducing the
unified command format required by HDR-BT transfers.
-The current driver builds legacy cmd_lo/cmd_hi command entries for SDR and
HDR-DDR only. HDR-BT requires a 4-DWORD unified command layout, so HDR-BT
transfers cannot be described correctly with the existing path.
- HDR BT mode uses to send data in blocks and protected using CRC.
Signed-off-by: Dineshkumar Parameswaran <parame@synopsys.com>
---
drivers/i3c/master/dw-i3c-master.c | 146 +++++++++++++++++++++--------
1 file changed, 106 insertions(+), 40 deletions(-)
diff --git a/drivers/i3c/master/dw-i3c-master.c b/drivers/i3c/master/dw-i3c-master.c
index 58c54746bbe5..fc58dea8fe4f 100644
--- a/drivers/i3c/master/dw-i3c-master.c
+++ b/drivers/i3c/master/dw-i3c-master.c
@@ -65,6 +65,29 @@
#define COMMAND_PORT_DEV_COUNT(x) (((x) << 21) & GENMASK(25, 21))
#define COMMAND_PORT_ADDR_ASSGN_CMD 0x03
+#define COMMAND_PORT_UNIFIED_DWORD0_CMD 0x04
+#define COMMAND_PORT_UNIFIED_DWORD0_TID(x) (((x) << 3) & GENMASK(6, 3))
+#define COMMAND_PORT_UNIFIED_DWORD0_RnW BIT(8)
+#define COMMAND_PORT_UNIFIED_DWORD0_DEV_INDEX(x) (((x) << 9) & GENMASK(13, 9))
+#define COMMAND_PORT_UNIFIED_DWORD0_SPEED_MODE(x) (((x) << 19) & GENMASK(22, 19))
+#define COMMAND_PORT_UNIFIED_DWORD0_WROC BIT(30)
+#define COMMAND_PORT_UNIFIED_DWORD0_TOC BIT(31)
+
+#define COMMAND_PORT_UNIFIED_DWORD1_DATA_LENGTH(x) ((x) & GENMASK(22, 0))
+#define COMMAND_PORT_UNIFIED_DWORD1_MLANE_MODE(x) (((x) << 24) & GENMASK(26, 24))
+#define COMMAND_PORT_UNIFIED_DWORD1_MLANE_CODE(x) (((x) << 27) & GENMASK(29, 27))
+
+#define COMMAND_PORT_UNIFIED_DWORD2_IDB0_CCC_CMD0(x) ((x) & GENMASK(7, 0))
+#define COMMAND_PORT_UNIFIED_DWORD2_IDB1_DB_CMD1(x) (((x) << 8) & GENMASK(15, 8))
+#define COMMAND_PORT_UNIFIED_DWORD2_IDB2_HDRCMD_CMD2(x) (((x) << 16) & GENMASK(23, 16))
+#define COMMAND_PORT_UNIFIED_DWORD2_IDB3_CMD3(x) (((x) << 24) & GENMASK(31, 24))
+
+#define COMMAND_PORT_UNIFIED_DWORD3_CONTROL(x) ((x) & GENMASK(7, 0))
+
+#define HDR_BT_SPD_MODE 0x8
+#define SINGLE_LANE 0x0
+#define SIZEOF_UNIFIED_CMD 4
+
#define RESPONSE_QUEUE_PORT 0x10
#define RESPONSE_PORT_ERR_STATUS(x) (((x) & GENMASK(31, 28)) >> 28)
#define RESPONSE_NO_ERROR 0
@@ -247,8 +270,15 @@
#define DW_I3C_ACPI_SKIP_CLK_RST BIT(3)
struct dw_i3c_cmd {
- u32 cmd_lo;
- u32 cmd_hi;
+ bool is_unified_cmd;
+ union {
+ struct {
+ u32 cmd_lo;
+ u32 cmd_hi;
+ u32 data[2];
+ };
+ u32 unified_cmd[4];
+ } cmd_data;
u16 tx_len;
u16 rx_len;
u8 error;
@@ -402,7 +432,7 @@ dw_i3c_master_alloc_xfer(struct dw_i3c_master *master, unsigned int ncmds)
static void dw_i3c_master_start_xfer_locked(struct dw_i3c_master *master)
{
struct dw_i3c_xfer *xfer = master->xferqueue.cur;
- unsigned int i;
+ unsigned int i, k;
u32 thld_ctrl;
if (!xfer)
@@ -422,8 +452,14 @@ static void dw_i3c_master_start_xfer_locked(struct dw_i3c_master *master)
for (i = 0; i < xfer->ncmds; i++) {
struct dw_i3c_cmd *cmd = &xfer->cmds[i];
- writel(cmd->cmd_hi, master->regs + COMMAND_QUEUE_PORT);
- writel(cmd->cmd_lo, master->regs + COMMAND_QUEUE_PORT);
+ if (cmd->is_unified_cmd) {
+ for (k = 0; k < SIZEOF_UNIFIED_CMD; k++)
+ writel(cmd->cmd_data.unified_cmd[k],
+ master->regs + COMMAND_QUEUE_PORT);
+ } else {
+ writel(cmd->cmd_data.cmd_hi, master->regs + COMMAND_QUEUE_PORT);
+ writel(cmd->cmd_data.cmd_lo, master->regs + COMMAND_QUEUE_PORT);
+ }
}
}
@@ -784,10 +820,10 @@ static int dw_i3c_ccc_set(struct dw_i3c_master *master,
cmd->tx_buf = ccc->dests[0].payload.data;
cmd->tx_len = ccc->dests[0].payload.len;
- cmd->cmd_hi = COMMAND_PORT_ARG_DATA_LEN(ccc->dests[0].payload.len) |
+ cmd->cmd_data.cmd_hi = COMMAND_PORT_ARG_DATA_LEN(ccc->dests[0].payload.len) |
COMMAND_PORT_TRANSFER_ARG;
- cmd->cmd_lo = COMMAND_PORT_CP |
+ cmd->cmd_data.cmd_lo = COMMAND_PORT_CP |
COMMAND_PORT_DEV_INDEX(pos) |
COMMAND_PORT_CMD(ccc->id) |
COMMAND_PORT_TOC |
@@ -823,10 +859,10 @@ static int dw_i3c_ccc_get(struct dw_i3c_master *master, struct i3c_ccc_cmd *ccc)
cmd->rx_buf = ccc->dests[0].payload.data;
cmd->rx_len = ccc->dests[0].payload.len;
- cmd->cmd_hi = COMMAND_PORT_ARG_DATA_LEN(ccc->dests[0].payload.len) |
+ cmd->cmd_data.cmd_hi = COMMAND_PORT_ARG_DATA_LEN(ccc->dests[0].payload.len) |
COMMAND_PORT_TRANSFER_ARG;
- cmd->cmd_lo = COMMAND_PORT_READ_TRANSFER |
+ cmd->cmd_data.cmd_lo = COMMAND_PORT_READ_TRANSFER |
COMMAND_PORT_CP |
COMMAND_PORT_DEV_INDEX(pos) |
COMMAND_PORT_CMD(ccc->id) |
@@ -924,8 +960,8 @@ static int dw_i3c_master_daa(struct i3c_master_controller *m)
}
cmd = &xfer->cmds[0];
cmd->rx_len = master->maxdevs;
- cmd->cmd_hi = COMMAND_PORT_TRANSFER_ARG;
- cmd->cmd_lo = COMMAND_PORT_DEV_COUNT(master->maxdevs - pos) |
+ cmd->cmd_data.cmd_hi = COMMAND_PORT_TRANSFER_ARG;
+ cmd->cmd_data.cmd_lo = COMMAND_PORT_DEV_COUNT(master->maxdevs - pos) |
COMMAND_PORT_DEV_INDEX(pos) |
COMMAND_PORT_CMD(I3C_CCC_ENTDAA) |
COMMAND_PORT_ADDR_ASSGN_CMD |
@@ -999,33 +1035,63 @@ static int dw_i3c_master_i3c_xfers(struct i3c_dev_desc *dev,
for (i = 0; i < i3c_nxfers; i++) {
struct dw_i3c_cmd *cmd = &xfer->cmds[i];
- cmd->cmd_hi = COMMAND_PORT_ARG_DATA_LEN(i3c_xfers[i].len) |
- COMMAND_PORT_TRANSFER_ARG;
-
- if (i3c_xfers[i].rnw) {
- cmd->rx_buf = i3c_xfers[i].data.in;
- cmd->rx_len = i3c_xfers[i].len;
- cmd->cmd_lo = COMMAND_PORT_READ_TRANSFER |
- COMMAND_PORT_SPEED(dev->info.max_read_ds);
-
+ if (mode == I3C_HDR_BT) {
+ cmd->cmd_data.unified_cmd[0] = COMMAND_PORT_UNIFIED_DWORD0_CMD |
+ COMMAND_PORT_UNIFIED_DWORD0_TID(i) |
+ COMMAND_PORT_UNIFIED_DWORD0_DEV_INDEX(data->index) |
+ COMMAND_PORT_UNIFIED_DWORD0_SPEED_MODE(HDR_BT_SPD_MODE) |
+ COMMAND_PORT_UNIFIED_DWORD0_WROC;
+ if (i3c_xfers[i].rnw) {
+ cmd->rx_buf = i3c_xfers[i].data.in;
+ cmd->rx_len = i3c_xfers[i].len;
+ cmd->cmd_data.unified_cmd[0] |= COMMAND_PORT_UNIFIED_DWORD0_RnW;
+ } else {
+ cmd->tx_buf = i3c_xfers[i].data.out;
+ cmd->tx_len = i3c_xfers[i].len;
+ }
+ cmd->cmd_data.unified_cmd[1] =
+ COMMAND_PORT_UNIFIED_DWORD1_DATA_LENGTH(i3c_xfers[i].len) |
+ COMMAND_PORT_UNIFIED_DWORD1_MLANE_CODE(SINGLE_LANE) |
+ COMMAND_PORT_UNIFIED_DWORD1_MLANE_MODE(SINGLE_LANE);
+ cmd->cmd_data.unified_cmd[2] =
+ COMMAND_PORT_UNIFIED_DWORD2_IDB0_CCC_CMD0(0x00) |
+ COMMAND_PORT_UNIFIED_DWORD2_IDB1_DB_CMD1(0x00) |
+ COMMAND_PORT_UNIFIED_DWORD2_IDB2_HDRCMD_CMD2(0x00) |
+ COMMAND_PORT_UNIFIED_DWORD2_IDB3_CMD3(0x00);
+ cmd->cmd_data.unified_cmd[3] = COMMAND_PORT_UNIFIED_DWORD3_CONTROL(0);
+ if (i == (i3c_nxfers - 1))
+ cmd->cmd_data.unified_cmd[0] |= COMMAND_PORT_UNIFIED_DWORD0_TOC;
+ cmd->is_unified_cmd = true;
} else {
- cmd->tx_buf = i3c_xfers[i].data.out;
- cmd->tx_len = i3c_xfers[i].len;
- cmd->cmd_lo =
- COMMAND_PORT_SPEED(dev->info.max_write_ds);
- }
- cmd->cmd_lo |= COMMAND_PORT_TID(i) |
- COMMAND_PORT_DEV_INDEX(data->index) |
- COMMAND_PORT_ROC;
-
- if (mode == I3C_HDR_DDR)
- cmd->cmd_lo |= COMMAND_PORT_CP |
- COMMAND_PORT_CMD(I3C_CCC_ENTHDR(0)) |
- COMMAND_PORT_SPEED(DW_I3C_HDR_DDR_SPEED);
-
- if (i == (i3c_nxfers - 1))
- cmd->cmd_lo |= COMMAND_PORT_TOC;
+ cmd->cmd_data.cmd_hi = COMMAND_PORT_ARG_DATA_LEN(i3c_xfers[i].len) |
+ COMMAND_PORT_TRANSFER_ARG;
+
+ if (i3c_xfers[i].rnw) {
+ cmd->rx_buf = i3c_xfers[i].data.in;
+ cmd->rx_len = i3c_xfers[i].len;
+ cmd->cmd_data.cmd_lo = COMMAND_PORT_READ_TRANSFER |
+ COMMAND_PORT_SPEED(dev->info.max_read_ds);
+
+ } else {
+ cmd->tx_buf = i3c_xfers[i].data.out;
+ cmd->tx_len = i3c_xfers[i].len;
+ cmd->cmd_data.cmd_lo =
+ COMMAND_PORT_SPEED(dev->info.max_write_ds);
+ }
+
+ cmd->cmd_data.cmd_lo |= COMMAND_PORT_TID(i) |
+ COMMAND_PORT_DEV_INDEX(data->index) |
+ COMMAND_PORT_ROC;
+
+ if (mode == I3C_HDR_DDR)
+ cmd->cmd_data.cmd_lo |= COMMAND_PORT_CP |
+ COMMAND_PORT_CMD(I3C_CCC_ENTHDR(0)) |
+ COMMAND_PORT_SPEED(DW_I3C_HDR_DDR_SPEED);
+
+ if (i == (i3c_nxfers - 1))
+ cmd->cmd_data.cmd_lo |= COMMAND_PORT_TOC;
+ }
}
dw_i3c_master_enqueue_xfer(master, xfer);
@@ -1162,15 +1228,15 @@ static int dw_i3c_master_i2c_xfers(struct i2c_dev_desc *dev,
for (i = 0; i < i2c_nxfers; i++) {
struct dw_i3c_cmd *cmd = &xfer->cmds[i];
- cmd->cmd_hi = COMMAND_PORT_ARG_DATA_LEN(i2c_xfers[i].len) |
+ cmd->cmd_data.cmd_hi = COMMAND_PORT_ARG_DATA_LEN(i2c_xfers[i].len) |
COMMAND_PORT_TRANSFER_ARG;
- cmd->cmd_lo = COMMAND_PORT_TID(i) |
+ cmd->cmd_data.cmd_lo = COMMAND_PORT_TID(i) |
COMMAND_PORT_DEV_INDEX(data->index) |
COMMAND_PORT_ROC;
if (i2c_xfers[i].flags & I2C_M_RD) {
- cmd->cmd_lo |= COMMAND_PORT_READ_TRANSFER;
+ cmd->cmd_data.cmd_lo |= COMMAND_PORT_READ_TRANSFER;
cmd->rx_buf = i2c_xfers[i].buf;
cmd->rx_len = i2c_xfers[i].len;
} else {
@@ -1179,7 +1245,7 @@ static int dw_i3c_master_i2c_xfers(struct i2c_dev_desc *dev,
}
if (i == (i2c_nxfers - 1))
- cmd->cmd_lo |= COMMAND_PORT_TOC;
+ cmd->cmd_data.cmd_lo |= COMMAND_PORT_TOC;
}
dw_i3c_master_enqueue_xfer(master, xfer);
--
2.34.1
^ permalink raw reply [flat|nested] 3+ messages in thread