Add endianness handling to the FIFO access helpers i3c_readl_fifo() and
i3c_writel_fifo(). This ensures correct data transfers on platforms where
the FIFO registers are expected to be accessed in either big-endian or
little-endian format.
Update the Synopsys, Cadence, and Renesas I3C master controller drivers to
pass the appropriate endianness argument to these helpers.
Signed-off-by: Manikanta Guntupalli <manikanta.guntupalli@amd.com>
Reviewed-by: Frank Li <Frank.Li@nxp.com>
---
Changes since V7:
This patch introduced in V7.
Changes for V8:
Resolved conflicts with "i3c: fix big-endian FIFO transfers".
Updated description.
---
drivers/i3c/internals.h | 45 +++++++++++++++++++++-------
drivers/i3c/master/dw-i3c-master.c | 9 ++++--
drivers/i3c/master/i3c-master-cdns.c | 9 ++++--
drivers/i3c/master/renesas-i3c.c | 12 +++++---
4 files changed, 55 insertions(+), 20 deletions(-)
diff --git a/drivers/i3c/internals.h b/drivers/i3c/internals.h
index 77d56415cb99..d3630e9129ae 100644
--- a/drivers/i3c/internals.h
+++ b/drivers/i3c/internals.h
@@ -24,25 +24,40 @@ int i3c_dev_request_ibi_locked(struct i3c_dev_desc *dev,
const struct i3c_ibi_setup *req);
void i3c_dev_free_ibi_locked(struct i3c_dev_desc *dev);
+enum i3c_fifo_endian {
+ I3C_FIFO_LITTLE_ENDIAN,
+ I3C_FIFO_BIG_ENDIAN,
+};
+
/**
* i3c_writel_fifo - Write data buffer to 32bit FIFO
* @addr: FIFO Address to write to
* @buf: Pointer to the data bytes to write
* @nbytes: Number of bytes to write
+ * @endian: Endianness of FIFO write
*/
static inline void i3c_writel_fifo(void __iomem *addr, const void *buf,
- int nbytes)
+ int nbytes, enum i3c_fifo_endian endian)
{
- writesl(addr, buf, nbytes / 4);
+ if (endian)
+ writesl_be(addr, buf, nbytes / 4);
+ else
+ writesl(addr, buf, nbytes / 4);
+
if (nbytes & 3) {
u32 tmp = 0;
memcpy(&tmp, buf + (nbytes & ~3), nbytes & 3);
+
/*
- * writesl() instead of writel() to keep FIFO byte orderer to match
- * the order in the buffer regardless of the CPU endianess.
+ * writesl_be()/writesl() instead of writel_be()/writel() to keep
+ * FIFO byte orderer to match the order in the buffer regardless
+ * of the CPU endianess.
*/
- writesl(addr, &tmp, 1);
+ if (endian)
+ writesl_be(addr, &tmp, 1);
+ else
+ writesl(addr, &tmp, 1);
}
}
@@ -51,19 +66,29 @@ static inline void i3c_writel_fifo(void __iomem *addr, const void *buf,
* @addr: FIFO Address to read from
* @buf: Pointer to the buffer to store read bytes
* @nbytes: Number of bytes to read
+ * @endian: Endianness of FIFO read
*/
static inline void i3c_readl_fifo(const void __iomem *addr, void *buf,
- int nbytes)
+ int nbytes, enum i3c_fifo_endian endian)
{
- readsl(addr, buf, nbytes / 4);
+ if (endian)
+ readsl_be(addr, buf, nbytes / 4);
+ else
+ readsl(addr, buf, nbytes / 4);
+
if (nbytes & 3) {
u32 tmp;
/*
- * readsl() instead of readl() to keep FIFO byte orderer to match
- * the order in the buffer regardless of the CPU endianess.
+ * readsl_be()/readsl() instead of readl_be()/readl() to keep
+ * FIFO byte orderer to match the order in the buffer regardless
+ * of the CPU endianess.
*/
- readsl(addr, &tmp, 1);
+ if (endian)
+ readsl_be(addr, &tmp, 1);
+ else
+ readsl(addr, &tmp, 1);
+
memcpy(buf + (nbytes & ~3), &tmp, nbytes & 3);
}
}
diff --git a/drivers/i3c/master/dw-i3c-master.c b/drivers/i3c/master/dw-i3c-master.c
index 974122b2d20e..5d723ac041c2 100644
--- a/drivers/i3c/master/dw-i3c-master.c
+++ b/drivers/i3c/master/dw-i3c-master.c
@@ -337,19 +337,22 @@ static int dw_i3c_master_get_free_pos(struct dw_i3c_master *master)
static void dw_i3c_master_wr_tx_fifo(struct dw_i3c_master *master,
const u8 *bytes, int nbytes)
{
- i3c_writel_fifo(master->regs + RX_TX_DATA_PORT, bytes, nbytes);
+ i3c_writel_fifo(master->regs + RX_TX_DATA_PORT, bytes, nbytes,
+ I3C_FIFO_LITTLE_ENDIAN);
}
static void dw_i3c_master_read_rx_fifo(struct dw_i3c_master *master,
u8 *bytes, int nbytes)
{
- i3c_readl_fifo(master->regs + RX_TX_DATA_PORT, bytes, nbytes);
+ i3c_readl_fifo(master->regs + RX_TX_DATA_PORT, bytes, nbytes,
+ I3C_FIFO_LITTLE_ENDIAN);
}
static void dw_i3c_master_read_ibi_fifo(struct dw_i3c_master *master,
u8 *bytes, int nbytes)
{
- i3c_readl_fifo(master->regs + IBI_QUEUE_STATUS, bytes, nbytes);
+ i3c_readl_fifo(master->regs + IBI_QUEUE_STATUS, bytes, nbytes,
+ I3C_FIFO_LITTLE_ENDIAN);
}
static struct dw_i3c_xfer *
diff --git a/drivers/i3c/master/i3c-master-cdns.c b/drivers/i3c/master/i3c-master-cdns.c
index 97b151564d3d..de3b5e894b4b 100644
--- a/drivers/i3c/master/i3c-master-cdns.c
+++ b/drivers/i3c/master/i3c-master-cdns.c
@@ -428,13 +428,15 @@ to_cdns_i3c_master(struct i3c_master_controller *master)
static void cdns_i3c_master_wr_to_tx_fifo(struct cdns_i3c_master *master,
const u8 *bytes, int nbytes)
{
- i3c_writel_fifo(master->regs + TX_FIFO, bytes, nbytes);
+ i3c_writel_fifo(master->regs + TX_FIFO, bytes, nbytes,
+ I3C_FIFO_LITTLE_ENDIAN);
}
static void cdns_i3c_master_rd_from_rx_fifo(struct cdns_i3c_master *master,
u8 *bytes, int nbytes)
{
- i3c_readl_fifo(master->regs + RX_FIFO, bytes, nbytes);
+ i3c_readl_fifo(master->regs + RX_FIFO, bytes, nbytes,
+ I3C_FIFO_LITTLE_ENDIAN);
}
static bool cdns_i3c_master_supports_ccc_cmd(struct i3c_master_controller *m,
@@ -1319,7 +1321,8 @@ static void cdns_i3c_master_handle_ibi(struct cdns_i3c_master *master,
buf = slot->data;
nbytes = IBIR_XFER_BYTES(ibir);
- i3c_readl_fifo(master->regs + IBI_DATA_FIFO, buf, nbytes);
+ i3c_readl_fifo(master->regs + IBI_DATA_FIFO, buf, nbytes,
+ I3C_FIFO_LITTLE_ENDIAN);
slot->len = min_t(unsigned int, IBIR_XFER_BYTES(ibir),
dev->ibi->max_payload_len);
diff --git a/drivers/i3c/master/renesas-i3c.c b/drivers/i3c/master/renesas-i3c.c
index 174d3dc5d276..5610cf71740e 100644
--- a/drivers/i3c/master/renesas-i3c.c
+++ b/drivers/i3c/master/renesas-i3c.c
@@ -835,7 +835,8 @@ static int renesas_i3c_priv_xfers(struct i3c_dev_desc *dev, struct i3c_priv_xfer
}
if (!i3c_xfers[i].rnw && i3c_xfers[i].len > 4) {
- i3c_writel_fifo(i3c->regs + NTDTBP0, cmd->tx_buf, cmd->len);
+ i3c_writel_fifo(i3c->regs + NTDTBP0, cmd->tx_buf, cmd->len,
+ I3C_FIFO_LITTLE_ENDIAN);
if (cmd->len > NTDTBP0_DEPTH * sizeof(u32))
renesas_set_bit(i3c->regs, NTIE, NTIE_TDBEIE0);
}
@@ -1021,7 +1022,8 @@ static irqreturn_t renesas_i3c_tx_isr(int irq, void *data)
/* Clear the Transmit Buffer Empty status flag. */
renesas_clear_bit(i3c->regs, NTST, NTST_TDBEF0);
} else {
- i3c_writel_fifo(i3c->regs + NTDTBP0, cmd->tx_buf, cmd->len);
+ i3c_writel_fifo(i3c->regs + NTDTBP0, cmd->tx_buf, cmd->len,
+ I3C_FIFO_LITTLE_ENDIAN);
}
}
@@ -1061,7 +1063,8 @@ static irqreturn_t renesas_i3c_resp_isr(int irq, void *data)
if (NDBSTLV0_RDBLV(renesas_readl(i3c->regs, NDBSTLV0)) && !cmd->err)
bytes_remaining = data_len - cmd->rx_count;
- i3c_readl_fifo(i3c->regs + NTDTBP0, cmd->rx_buf, bytes_remaining);
+ i3c_readl_fifo(i3c->regs + NTDTBP0, cmd->rx_buf, bytes_remaining,
+ I3C_FIFO_LITTLE_ENDIAN);
renesas_clear_bit(i3c->regs, NTIE, NTIE_RDBFIE0);
break;
default:
@@ -1203,7 +1206,8 @@ static irqreturn_t renesas_i3c_rx_isr(int irq, void *data)
cmd->i2c_bytes_left--;
} else {
read_bytes = NDBSTLV0_RDBLV(renesas_readl(i3c->regs, NDBSTLV0)) * sizeof(u32);
- i3c_readl_fifo(i3c->regs + NTDTBP0, cmd->rx_buf, read_bytes);
+ i3c_readl_fifo(i3c->regs + NTDTBP0, cmd->rx_buf, read_bytes,
+ I3C_FIFO_LITTLE_ENDIAN);
cmd->rx_count = read_bytes;
}
--
2.34.1
On Fri, Sep 26, 2025, at 12:53, Manikanta Guntupalli wrote: > Add endianness handling to the FIFO access helpers i3c_readl_fifo() and > i3c_writel_fifo(). This ensures correct data transfers on platforms where > the FIFO registers are expected to be accessed in either big-endian or > little-endian format. > > Update the Synopsys, Cadence, and Renesas I3C master controller drivers to > pass the appropriate endianness argument to these helpers. > > Signed-off-by: Manikanta Guntupalli <manikanta.guntupalli@amd.com> > Reviewed-by: Frank Li <Frank.Li@nxp.com> I don't think this is a good interface, based on our discussion so far, and I had hoped you'd change it the way I had suggested with a separate function for the xi3c driver, so normal drivers don't have to worry about the AMD specific quirk. I think this should also avoid mentioning "endianess" in the changelog and in the code itself, since that would likely confuse readers into thinking (as I did in my earlier replies) that this is related to using big-endian kernels. i3c_readl_fifo()/i3c_writel_fifo() are already portable and handle endianess correctly by using the readsl()/writesl() functions. Arnd
On Fri, Sep 26, 2025 at 01:09:37PM +0200, Arnd Bergmann wrote: > On Fri, Sep 26, 2025, at 12:53, Manikanta Guntupalli wrote: > > Add endianness handling to the FIFO access helpers i3c_readl_fifo() and > > i3c_writel_fifo(). This ensures correct data transfers on platforms where > > the FIFO registers are expected to be accessed in either big-endian or > > little-endian format. > > > > Update the Synopsys, Cadence, and Renesas I3C master controller drivers to > > pass the appropriate endianness argument to these helpers. > > > > Signed-off-by: Manikanta Guntupalli <manikanta.guntupalli@amd.com> > > Reviewed-by: Frank Li <Frank.Li@nxp.com> > > I don't think this is a good interface, based on our discussion > so far, and I had hoped you'd change it the way I had suggested > with a separate function for the xi3c driver, so normal drivers > don't have to worry about the AMD specific quirk. > > I think this should also avoid mentioning "endianess" in the > changelog and in the code itself, since that would likely > confuse readers into thinking (as I did in my earlier replies) > that this is related to using big-endian kernels. > > i3c_readl_fifo()/i3c_writel_fifo() are already portable and > handle endianess correctly by using the readsl()/writesl() > functions. I agree. I think previous your suggested API is good. /* * BIT: 31..24 23..16 15..8 7..0 * B0 B1 B2 B3 * * Memory: * 0x0: B0 * 0x1: B1 * 0x2: B2 * 0x3: B3 */ i3c_writel_fifo_bytereversed() Frank > > Arnd > > -- > linux-i3c mailing list > linux-i3c@lists.infradead.org > http://lists.infradead.org/mailman/listinfo/linux-i3c
© 2016 - 2025 Red Hat, Inc.