git: 0deaf4be346f - main - rk_i2c: emulate repeated start

From: Andriy Gapon <avg_at_FreeBSD.org>
Date: Thu, 27 Jun 2024 10:33:17 UTC
The branch main has been updated by avg:

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

commit 0deaf4be346f7f343bd2cf771bbf5d172d1c0d2a
Author:     Andriy Gapon <avg@FreeBSD.org>
AuthorDate: 2024-06-27 07:40:22 +0000
Commit:     Andriy Gapon <avg@FreeBSD.org>
CommitDate: 2024-06-27 10:32:15 +0000

    rk_i2c: emulate repeated start
    
    rk_i2c_send_stop is modified so that it sends a stop condition, like it
    always did, if there is no IIC_M_NOSTOP flag.
    But if the flag is set then the function completely resets the control
    register and sets the driver state to transfer completed.
    Something like this was previously done for a write with IIC_M_NOSTOP.
    Now it is done for a read with IIC_M_NOSTOP as well.
    
    Linux code says that the hardware does not support the repeated start
    condition and the documentation, indeed, does not mention it.
    But according to the Linux driver clearing the control register and then
    sending a start condition acts as if it were a repeated start.
    
    While here, add braces around a single-line 'if' branch to balance it
    with a multi-line 'else' branch.
    
    Tested with max44009(4).
    
    MFC after:      2 weeks
---
 sys/dev/iicbus/controller/rockchip/rk_i2c.c | 34 +++++++++++++++++++----------
 1 file changed, 22 insertions(+), 12 deletions(-)

diff --git a/sys/dev/iicbus/controller/rockchip/rk_i2c.c b/sys/dev/iicbus/controller/rockchip/rk_i2c.c
index 4a431649de49..9f2a98bcea12 100644
--- a/sys/dev/iicbus/controller/rockchip/rk_i2c.c
+++ b/sys/dev/iicbus/controller/rockchip/rk_i2c.c
@@ -281,13 +281,26 @@ rk_i2c_send_stop(struct rk_i2c_softc *sc)
 {
 	uint32_t reg;
 
-	RK_I2C_WRITE(sc, RK_I2C_IEN, RK_I2C_IEN_STOPIEN);
+	if (!(sc->msg->flags & IIC_M_NOSTOP)) {
+		RK_I2C_WRITE(sc, RK_I2C_IEN, RK_I2C_IEN_STOPIEN);
 
-	sc->state = STATE_STOP;
+		sc->state = STATE_STOP;
 
-	reg = RK_I2C_READ(sc, RK_I2C_CON);
-	reg |= RK_I2C_CON_STOP;
-	RK_I2C_WRITE(sc, RK_I2C_CON, reg);
+		reg = RK_I2C_READ(sc, RK_I2C_CON);
+		reg |= RK_I2C_CON_STOP;
+		RK_I2C_WRITE(sc, RK_I2C_CON, reg);
+	} else {
+		/*
+		 * Do not actually set stop bit, set up conditions to
+		 * emulate repeated start by clearing all state.
+		 */
+		sc->state = STATE_IDLE;
+		sc->transfer_done = 1;
+
+		reg = RK_I2C_READ(sc, RK_I2C_CON);
+		reg &= ~RK_I2C_CON_CTRL_MASK;
+		RK_I2C_WRITE(sc, RK_I2C_CON, reg);
+	}
 }
 
 static void
@@ -350,9 +363,9 @@ rk_i2c_intr_locked(struct rk_i2c_softc *sc)
 	case STATE_READ:
 		rk_i2c_drain_rx(sc);
 
-		if (sc->cnt == sc->msg->len)
+		if (sc->cnt == sc->msg->len) {
 			rk_i2c_send_stop(sc);
-		else {
+		} else {
 			sc->mode = RK_I2C_CON_MODE_RX;
 			reg = RK_I2C_READ(sc, RK_I2C_CON) & \
 			    ~RK_I2C_CON_CTRL_MASK;
@@ -369,7 +382,6 @@ rk_i2c_intr_locked(struct rk_i2c_softc *sc)
 			RK_I2C_WRITE(sc, RK_I2C_CON, reg);
 			RK_I2C_WRITE(sc, RK_I2C_MRXCNT, transfer_len);
 		}
-
 		break;
 	case STATE_WRITE:
 		if (sc->cnt < sc->msg->len) {
@@ -378,12 +390,10 @@ rk_i2c_intr_locked(struct rk_i2c_softc *sc)
 			    RK_I2C_IEN_NAKRCVIEN);
 			transfer_len = rk_i2c_fill_tx(sc);
 			RK_I2C_WRITE(sc, RK_I2C_MTXCNT, transfer_len);
-			break;
-		} else if (!(sc->msg->flags & IIC_M_NOSTOP)) {
+		} else {
 			rk_i2c_send_stop(sc);
-			break;
 		}
-		/* passthru */
+		break;
 	case STATE_STOP:
 		/* Disable stop bit */
 		reg = RK_I2C_READ(sc, RK_I2C_CON);