[PATCH 3/3] i2c: imx_lpi2c: Support read transfers longer than 256 bytes

fedorross at gmail.com fedorross at gmail.com
Wed Jul 24 11:59:43 CEST 2024


From: Fedor Ross <fedor.ross at ifm.com>

The TXFIFO register of LPI2C only has one byte length, and if the length
of the data that needs to be read exceeds 256 bytes, it needs to be
written to TXFIFO multiple times.

Signed-off-by: Fedor Ross <fedor.ross at ifm.com>
Cc: Heiko Schocher <hs at denx.de>
Cc: Tom Rini <trini at konsulko.com>
Cc: Marek Vasut <marex at denx.de>
---
 drivers/i2c/imx_lpi2c.c | 72 +++++++++++++++++++++++++++--------------
 1 file changed, 47 insertions(+), 25 deletions(-)

diff --git a/drivers/i2c/imx_lpi2c.c b/drivers/i2c/imx_lpi2c.c
index 7b10b2b252d..dd97228ddc7 100644
--- a/drivers/i2c/imx_lpi2c.c
+++ b/drivers/i2c/imx_lpi2c.c
@@ -19,6 +19,8 @@
 #define LPI2C_NACK_TOUT_MS 1
 #define LPI2C_TIMEOUT_MS 100
 
+#define CHUNK_DATA      256
+
 static int bus_i2c_init(struct udevice *bus, int speed);
 
 /* Weak linked function for overridden by some SoC power function */
@@ -118,8 +120,10 @@ static int bus_i2c_send(struct udevice *bus, u8 *txbuf, int len)
 
 static int bus_i2c_receive(struct udevice *bus, u8 *rxbuf, int len)
 {
+	struct dm_i2c_bus *i2c = dev_get_uclass_priv(bus);
 	struct imx_lpi2c_bus *i2c_bus = dev_get_priv(bus);
 	struct imx_lpi2c_reg *regs = (struct imx_lpi2c_reg *)(i2c_bus->base);
+	unsigned int chunk_len, rx_remain, timeout;
 	lpi2c_status_t result = LPI2C_SUCESS;
 	u32 val;
 	ulong start_time = get_timer(0);
@@ -128,33 +132,51 @@ static int bus_i2c_receive(struct udevice *bus, u8 *rxbuf, int len)
 	if (!len)
 		return result;
 
-	result = bus_i2c_wait_for_tx_ready(regs);
-	if (result) {
-		debug("i2c: receive wait for tx ready: %d\n", result);
-		return result;
-	}
+	/*
+	 * Extend the timeout for a bulk read if needed.
+	 * The calculated timeout is the result of multiplying the
+	 * transfer length with 8 bit + ACK + one clock of extra time,
+	 * considering the I2C bus frequency.
+	 */
+	timeout = max(len * 10 * 1000 / i2c->speed_hz, LPI2C_TIMEOUT_MS);
 
-	/* clear all status flags */
-	writel(0x7f00, &regs->msr);
-	/* send receive command */
-	val = LPI2C_MTDR_CMD(0x1) | LPI2C_MTDR_DATA(len - 1);
-	writel(val, &regs->mtdr);
+	rx_remain = len;
+	while (rx_remain > 0) {
+		chunk_len = clamp(rx_remain, 1, CHUNK_DATA) - 1;
 
-	while (len--) {
-		do {
-			result = imx_lpci2c_check_clear_error(regs);
-			if (result) {
-				debug("i2c: receive check clear error: %d\n",
-				      result);
-				return result;
-			}
-			if (get_timer(start_time) > LPI2C_TIMEOUT_MS) {
-				debug("i2c: receive mrdr: timeout\n");
-				return -1;
-			}
-			val = readl(&regs->mrdr);
-		} while (val & LPI2C_MRDR_RXEMPTY_MASK);
-		*rxbuf++ = LPI2C_MRDR_DATA(val);
+		result = bus_i2c_wait_for_tx_ready(regs);
+		if (result) {
+			debug("i2c: receive wait for tx ready: %d\n", result);
+			return result;
+		}
+
+		/* clear all status flags */
+		writel(0x7f00, &regs->msr);
+		/* send receive command */
+		val = LPI2C_MTDR_CMD(0x1) | LPI2C_MTDR_DATA(chunk_len);
+		writel(val, &regs->mtdr);
+		rx_remain = rx_remain - (chunk_len & 0xff) - 1;
+
+		while (len--) {
+			do {
+				result = imx_lpci2c_check_clear_error(regs);
+				if (result) {
+					debug("i2c: receive check clear error: %d\n",
+					      result);
+					return result;
+				}
+				if (get_timer(start_time) > timeout) {
+					debug("i2c: receive mrdr: timeout\n");
+					return -1;
+				}
+				val = readl(&regs->mrdr);
+			} while (val & LPI2C_MRDR_RXEMPTY_MASK);
+			*rxbuf++ = LPI2C_MRDR_DATA(val);
+
+			/* send next receive command before controller NACKs last byte */
+			if ((len - rx_remain) < 2 && rx_remain > 0)
+				break;
+		}
 	}
 
 	return result;
-- 
2.34.1



More information about the U-Boot mailing list