[PATCH v2 2/3] i3c: dw: Track the transmit and receive buffer depths separately
From: Shubham Patil
Date: Thu Nov 05 2026 - 03:55:41 EST
The driver currently uses the transmit buffer depth to validate both
transmit and receive transfers. Since the transmit and receive buffers
can have different capacities, receive transfers must instead be
validated against the receive buffer depth.
QUEUE_SIZE_CAPABILITY reports the configured size of each buffer. Take
the receive depth from there, leave the transmit and command depths on
the status level registers they already use, and split
caps.datafifodepth into caps.txfifodepth and caps.rxfifodepth so that
each direction is checked against its own buffer.
That register was already defined at the right offset, but under a name
that does not appear in the databook and with no user at all. Rename
EXTENDED_CAPABILITY to QUEUE_SIZE_CAPABILITY and add the field
accessor.
Publish both depths in bytes through base.tx_fifo_bytes and
base.rx_fifo_bytes, so that device drivers can size a transfer this
controller will accept.
Signed-off-by: Shubham Patil <shubhamsanjay.patil@xxxxxxx>
---
drivers/i3c/master/dw-i3c-master.c | 22 ++++++++++++++++------
drivers/i3c/master/dw-i3c-master.h | 3 ++-
2 files changed, 18 insertions(+), 7 deletions(-)
diff --git a/drivers/i3c/master/dw-i3c-master.c b/drivers/i3c/master/dw-i3c-master.c
index d1119f203d1b..dd5462b12951 100644
--- a/drivers/i3c/master/dw-i3c-master.c
+++ b/drivers/i3c/master/dw-i3c-master.c
@@ -203,7 +203,10 @@
#define BUS_IDLE_TIMING 0xd8
#define I3C_VER_ID 0xe0
#define I3C_VER_TYPE 0xe4
-#define EXTENDED_CAPABILITY 0xe8
+#define QUEUE_SIZE_CAPABILITY 0xe8
+#define QUEUE_SIZE_CAPABILITY_RX_BUF(x) (((x) & GENMASK(7, 4)) >> 4)
+/* Sizes are encoded, 0 meaning 2 DWORDs and every further step doubling it. */
+#define QUEUE_SIZE_TO_DWORDS(x) (2U << (x))
#define SLAVE_CONFIG 0xec
#define DYN_ADDR_LO_MASK GENMASK(4, 0)
@@ -978,8 +981,8 @@ static int dw_i3c_master_i3c_xfers(struct i3c_dev_desc *dev,
ntxwords += DIV_ROUND_UP(i3c_xfers[i].len, 4);
}
- if (ntxwords > master->caps.datafifodepth ||
- nrxwords > master->caps.datafifodepth)
+ if (ntxwords > master->caps.txfifodepth ||
+ nrxwords > master->caps.rxfifodepth)
return -EOPNOTSUPP;
struct dw_i3c_xfer *xfer __free(kfree) = dw_i3c_master_alloc_xfer(master, i3c_nxfers);
@@ -1136,8 +1139,8 @@ static int dw_i3c_master_i2c_xfers(struct i2c_dev_desc *dev,
ntxwords += DIV_ROUND_UP(i2c_xfers[i].len, 4);
}
- if (ntxwords > master->caps.datafifodepth ||
- nrxwords > master->caps.datafifodepth)
+ if (ntxwords > master->caps.txfifodepth ||
+ nrxwords > master->caps.rxfifodepth)
return -EOPNOTSUPP;
struct dw_i3c_xfer *xfer __free(kfree) = dw_i3c_master_alloc_xfer(master, i2c_nxfers);
@@ -1738,7 +1741,14 @@ int dw_i3c_common_probe(struct dw_i3c_master *master,
master->caps.cmdfifodepth = QUEUE_STATUS_LEVEL_CMD(ret);
ret = readl(master->regs + DATA_BUFFER_STATUS_LEVEL);
- master->caps.datafifodepth = DATA_BUFFER_STATUS_LEVEL_TX(ret);
+ master->caps.txfifodepth = DATA_BUFFER_STATUS_LEVEL_TX(ret);
+
+ ret = readl(master->regs + QUEUE_SIZE_CAPABILITY);
+ master->caps.rxfifodepth =
+ QUEUE_SIZE_TO_DWORDS(QUEUE_SIZE_CAPABILITY_RX_BUF(ret));
+
+ master->base.tx_fifo_bytes = master->caps.txfifodepth * 4;
+ master->base.rx_fifo_bytes = master->caps.rxfifodepth * 4;
ret = readl(master->regs + DEVICE_ADDR_TABLE_POINTER);
master->datstartaddr = ret;
diff --git a/drivers/i3c/master/dw-i3c-master.h b/drivers/i3c/master/dw-i3c-master.h
index f5b4491b247a..e23554bfc5e4 100644
--- a/drivers/i3c/master/dw-i3c-master.h
+++ b/drivers/i3c/master/dw-i3c-master.h
@@ -14,7 +14,8 @@
struct dw_i3c_master_caps {
u8 cmdfifodepth;
- u8 datafifodepth;
+ u8 txfifodepth;
+ u8 rxfifodepth;
};
struct dw_i3c_dat_entry {
--
2.34.1