git: c6635459510c - main - rk_i2c_fill_tx: fix a number of issues

From: Andriy Gapon <avg_at_FreeBSD.org>
Date: Wed, 15 Dec 2021 11:16:47 UTC
The branch main has been updated by avg:

URL: https://cgit.FreeBSD.org/src/commit/?id=c6635459510c9c03a439bc5b59fef37259d21967

commit c6635459510c9c03a439bc5b59fef37259d21967
Author:     Andriy Gapon <avg@FreeBSD.org>
AuthorDate: 2021-12-15 11:00:45 +0000
Commit:     Andriy Gapon <avg@FreeBSD.org>
CommitDate: 2021-12-15 11:16:14 +0000

    rk_i2c_fill_tx: fix a number of issues
    
    - maximum number of bytes that can be sent is 32, not 8;
    - previous interface required callers to bump sc->msg->len in addition
      to setting sc->tx_slave_addr;
    - because of the above there was an issue with writing one too many bytes
      because sc->cnt is not advanced when the slave address is written;
    - the inetraction between outer and inner loops was confusing as the former
      was bounded on the number of bytes to write and the counter was
      incremented by one, but the inner loop advanced four bytes at a time;
    - the return value was incorrect in the tx_slave_addr case; one call place
      had to use its own (and incorrect in some cases) notion of the write
      lenth.
    
    All of the above issues should be fixed.
    Some sanity asserts are added.
    All callers use the return value to program RK_I2C_MTXCNT.
    iic_msg::len no longer needs to be hacked.
    A constant is added to reflect the maximum number of octets that can be
    sent or received in one go (they are the same).
    
    MFC after:      1 week
---
 sys/arm64/rockchip/rk_i2c.c | 46 +++++++++++++++++++++++++--------------------
 1 file changed, 26 insertions(+), 20 deletions(-)

diff --git a/sys/arm64/rockchip/rk_i2c.c b/sys/arm64/rockchip/rk_i2c.c
index 6ad168f0c64c..67178f615b0a 100644
--- a/sys/arm64/rockchip/rk_i2c.c
+++ b/sys/arm64/rockchip/rk_i2c.c
@@ -110,6 +110,9 @@ __FBSDID("$FreeBSD$");
 
 #define	RK_I2C_RXDATA_BASE	0x200
 
+/* 8 data registers, 4 bytes each. */
+#define	RK_I2C_MAX_RXTX_LEN	32
+
 enum rk_i2c_state {
 	STATE_IDLE = 0,
 	STATE_START,
@@ -214,36 +217,40 @@ rk_i2c_fill_tx(struct rk_i2c_softc *sc)
 	uint8_t buf;
 	int i, j, len;
 
-	if (sc->msg == NULL || sc->msg->len == sc->cnt)
-		return (0);
-
 	len = sc->msg->len - sc->cnt;
-	if (len > 8)
-		len = 8;
+	if (sc->tx_slave_addr) {
+		KASSERT(sc->cnt == 0, ("tx_slave_addr in the middle of data"));
+		len++;
+	}
 
-	for (i = 0; i < len; i++) {
+	if (len > RK_I2C_MAX_RXTX_LEN)
+		len = RK_I2C_MAX_RXTX_LEN;
+
+	while (i < len) {
 		buf32 = 0;
-		for (j = 0; j < 4 ; j++) {
-			if (sc->cnt == sc->msg->len)
-				break;
 
+		/* Process next 4 bytes or whatever remains. */
+		for (j = 0; j < MIN(len - i, 4); j++) {
 			/* Fill the addr if needed */
-			if (sc->cnt == 0 && sc->tx_slave_addr) {
+			if (sc->tx_slave_addr) {
 				buf = sc->msg->slave;
 				sc->tx_slave_addr = false;
 			} else {
+				KASSERT(sc->cnt < sc->msg->len,
+				    ("%s: data buffer overrun", __func__));
 				buf = sc->msg->buf[sc->cnt];
 				sc->cnt++;
 			}
-			buf32 |= buf << (j * 8);
+			buf32 |= (uint32_t)buf << (j * 8);
 		}
-		RK_I2C_WRITE(sc, RK_I2C_TXDATA_BASE + 4 * i, buf32);
 
-		if (sc->cnt == sc->msg->len)
-			break;
+		KASSERT(i % 4 == 0, ("%s: misaligned write offset", __func__));
+		RK_I2C_WRITE(sc, RK_I2C_TXDATA_BASE + i, buf32);
+
+		i += j;
 	}
 
-	return (uint8_t)len;
+	return (len);
 }
 
 static void
@@ -260,8 +267,8 @@ rk_i2c_drain_rx(struct rk_i2c_softc *sc)
 	}
 
 	len = sc->msg->len - sc->cnt;
-	if (len > 32)
-		len = 32;
+	if (len > RK_I2C_MAX_RXTX_LEN)
+		len = RK_I2C_MAX_RXTX_LEN;
 
 	for (i = 0; i < len; i++) {
 		if (i % 4 == 0)
@@ -339,9 +346,8 @@ rk_i2c_intr_locked(struct rk_i2c_softc *sc)
 			RK_I2C_WRITE(sc, RK_I2C_IEN, RK_I2C_IEN_MBTFIEN |
 			    RK_I2C_IEN_NAKRCVIEN);
 
-			sc->msg->len += 1;
-			rk_i2c_fill_tx(sc);
-			RK_I2C_WRITE(sc, RK_I2C_MTXCNT, sc->msg->len);
+			transfer_len = rk_i2c_fill_tx(sc);
+			RK_I2C_WRITE(sc, RK_I2C_MTXCNT, transfer_len);
 		}
 		break;
 	case STATE_READ: