The branch main has been updated by bz:

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

commit 44847114bd4c61ed3dfcc879fa0d7deb452e12d3
Author:     Pierre-Luc Drouin <pldro...@pldrouin.net>
AuthorDate: 2024-03-22 22:13:04 +0000
Commit:     Bjoern A. Zeeb <b...@freebsd.org>
CommitDate: 2024-03-29 23:05:03 +0000

    vf_i2c: update I2C controller logic
    
    Update the I2C controller logic to be more consistent with the
    newer version of the controller reference manual.
    This makes it work better on modern LS/LX platforms and avoids
    unnecessary delays.  Also fixes a lock leak.
    
    MFC after:      7 days
    Tested by:      bz (LS1088a FDT), Pierre-Luc Drouin (Honeycomb, ACPI)
    Differential Revision:  https://reviews.freebsd.org/D44021
---
 sys/dev/iicbus/controller/vybrid/vf_i2c.c      | 148 ++++++++++---------------
 sys/dev/iicbus/controller/vybrid/vf_i2c.h      |   3 +-
 sys/dev/iicbus/controller/vybrid/vf_i2c_acpi.c |   3 +-
 sys/dev/iicbus/controller/vybrid/vf_i2c_fdt.c  |   3 +-
 4 files changed, 65 insertions(+), 92 deletions(-)

diff --git a/sys/dev/iicbus/controller/vybrid/vf_i2c.c 
b/sys/dev/iicbus/controller/vybrid/vf_i2c.c
index 7a6ec3948e13..d3c3664af78c 100644
--- a/sys/dev/iicbus/controller/vybrid/vf_i2c.c
+++ b/sys/dev/iicbus/controller/vybrid/vf_i2c.c
@@ -28,7 +28,8 @@
 
 /*
  * Vybrid Family Inter-Integrated Circuit (I2C)
- * Chapter 48, Vybrid Reference Manual, Rev. 5, 07/2013
+ * Originally based on Chapter 48, Vybrid Reference Manual, Rev. 5, 07/2013
+ * Currently based on Chapter 21, LX2160A Reference Manual, Rev. 1, 10/2021
  *
  * The current implementation is based on the original driver by Ruslan Bukin,
  * later modified by Dawid Górecki, and split into FDT and ACPI drivers by Val
@@ -184,6 +185,7 @@ vf_i2c_attach_common(device_t dev)
        mtx_unlock(&sc->mutex);
 
        sc->iicbus = device_add_child(dev, "iicbus", -1);
+
        if (sc->iicbus == NULL) {
                device_printf(dev, "could not add iicbus child");
                mtx_destroy(&sc->mutex);
@@ -233,24 +235,6 @@ i2c_detach(device_t dev)
        return (0);
 }
 
-/* Wait for transfer interrupt flag */
-static int
-wait_for_iif(struct vf_i2c_softc *sc)
-{
-       int retry;
-
-       retry = 1000;
-       while (retry --) {
-               if (READ1(sc, I2C_IBSR) & IBSR_IBIF) {
-                       WRITE1(sc, I2C_IBSR, IBSR_IBIF);
-                       return (IIC_NOERR);
-               }
-               DELAY(10);
-       }
-
-       return (IIC_ETIMEOUT);
-}
-
 /* Wait for free bus */
 static int
 wait_for_nibb(struct vf_i2c_softc *sc)
@@ -272,14 +256,24 @@ static int
 wait_for_icf(struct vf_i2c_softc *sc)
 {
        int retry;
+       uint8_t ibsr;
+
+       vf_i2c_dbg(sc, "i2c wait for transfer complete + interrupt flag\n");
 
        retry = 1000;
        while (retry --) {
-               if (READ1(sc, I2C_IBSR) & IBSR_TCF) {
-                       if (READ1(sc, I2C_IBSR) & IBSR_IBIF) {
-                               WRITE1(sc, I2C_IBSR, IBSR_IBIF);
-                               return (IIC_NOERR);
+               ibsr = READ1(sc, I2C_IBSR);
+
+               if (ibsr & IBSR_IBIF) {
+                       WRITE1(sc, I2C_IBSR, IBSR_IBIF);
+
+                       if (ibsr & IBSR_IBAL) {
+                               WRITE1(sc, I2C_IBSR, IBSR_IBAL);
+                               return (IIC_EBUSBSY);
                        }
+
+                       if (ibsr & IBSR_TCF)
+                               return (IIC_NOERR);
                }
                DELAY(10);
        }
@@ -309,30 +303,24 @@ i2c_repeated_start(device_t dev, u_char slave, int 
timeout)
 
        mtx_lock(&sc->mutex);
 
-       WRITE1(sc, I2C_IBAD, slave);
-
        if ((READ1(sc, I2C_IBSR) & IBSR_IBB) == 0) {
+               vf_i2c_dbg(sc, "cant i2c repeat start: bus is no longer 
busy\n");
                mtx_unlock(&sc->mutex);
                return (IIC_EBUSERR);
        }
 
-       /* Set repeated start condition */
-       DELAY(10);
-
        reg = READ1(sc, I2C_IBCR);
        reg |= (IBCR_RSTA | IBCR_IBIE);
        WRITE1(sc, I2C_IBCR, reg);
 
-       DELAY(10);
-
        /* Write target address - LSB is R/W bit */
        WRITE1(sc, I2C_IBDR, slave);
 
-       error = wait_for_iif(sc);
+       error = wait_for_icf(sc);
 
        if (!tx_acked(sc)) {
-               vf_i2c_dbg(sc,
-                   "cant i2c start: missing ACK after slave addres\n");
+               mtx_unlock(&sc->mutex);
+               vf_i2c_dbg(sc, "cant i2c repeat start: missing ACK after slave 
address\n");
                return (IIC_ENOACK);
        }
 
@@ -357,27 +345,32 @@ i2c_start(device_t dev, u_char slave, int timeout)
 
        mtx_lock(&sc->mutex);
 
-       WRITE1(sc, I2C_IBAD, slave);
+       error = wait_for_nibb(sc);
 
-       if (READ1(sc, I2C_IBSR) & IBSR_IBB) {
+       /* Reset controller if bus is still busy. */
+       if (error == IIC_ETIMEOUT) {
+               WRITE1(sc, I2C_IBCR, IBCR_MDIS);
+               DELAY(1000);
+               WRITE1(sc, I2C_IBCR, IBCR_NOACK);
+               error = wait_for_nibb(sc);
+       }
+
+       if (error != 0) {
                mtx_unlock(&sc->mutex);
-               vf_i2c_dbg(sc, "cant i2c start: IIC_EBUSBSY\n");
-               return (IIC_EBUSERR);
+               vf_i2c_dbg(sc, "cant i2c start: %i\n", error);
+               return (error);
        }
 
        /* Set start condition */
-       reg = (IBCR_MSSL | IBCR_NOACK | IBCR_IBIE);
+       reg = (IBCR_MSSL | IBCR_NOACK | IBCR_IBIE | IBCR_TXRX);
        WRITE1(sc, I2C_IBCR, reg);
 
-       DELAY(100);
-
-       reg |= (IBCR_TXRX);
-       WRITE1(sc, I2C_IBCR, reg);
+       WRITE1(sc, I2C_IBSR, IBSR_IBIF);
 
        /* Write target address - LSB is R/W bit */
        WRITE1(sc, I2C_IBDR, slave);
 
-       error = wait_for_iif(sc);
+       error = wait_for_icf(sc);
        if (error != 0) {
                mtx_unlock(&sc->mutex);
                vf_i2c_dbg(sc, "cant i2c start: iif error\n");
@@ -386,8 +379,7 @@ i2c_start(device_t dev, u_char slave, int timeout)
        mtx_unlock(&sc->mutex);
 
        if (!tx_acked(sc)) {
-               vf_i2c_dbg(sc,
-                   "cant i2c start: missing QACK after slave addres\n");
+               vf_i2c_dbg(sc, "cant i2c start: missing ACK after slave 
address\n");
                return (IIC_ENOACK);
        }
 
@@ -405,16 +397,9 @@ i2c_stop(device_t dev)
 
        mtx_lock(&sc->mutex);
 
-       WRITE1(sc, I2C_IBCR, IBCR_NOACK | IBCR_IBIE);
-
-       DELAY(100);
+       if ((READ1(sc, I2C_IBCR) & IBCR_MSSL) != 0)
+               WRITE1(sc, I2C_IBCR, IBCR_NOACK | IBCR_IBIE);
 
-       /* Reset controller if bus still busy after STOP */
-       if (wait_for_nibb(sc) == IIC_ETIMEOUT) {
-               WRITE1(sc, I2C_IBCR, IBCR_MDIS);
-               DELAY(1000);
-               WRITE1(sc, I2C_IBCR, IBCR_NOACK);
-       }
        mtx_unlock(&sc->mutex);
 
        return (IIC_NOERR);
@@ -469,27 +454,14 @@ i2c_reset(device_t dev, u_char speed, u_char addr, u_char 
*oldadr)
        div_reg = i2c_get_div_val(dev);
        vf_i2c_dbg(sc, "i2c reset\n");
 
-       switch (speed) {
-       case IIC_FAST:
-       case IIC_SLOW:
-       case IIC_UNKNOWN:
-       case IIC_FASTEST:
-       default:
-               break;
-       }
-
        mtx_lock(&sc->mutex);
        WRITE1(sc, I2C_IBCR, IBCR_MDIS);
 
-       DELAY(1000);
-
        if(div_reg != DIV_REG_UNSET)
                WRITE1(sc, I2C_IBFD, div_reg);
 
        WRITE1(sc, I2C_IBCR, 0x0); /* Enable i2c */
 
-       DELAY(1000);
-
        mtx_unlock(&sc->mutex);
 
        return (IIC_NOERR);
@@ -511,36 +483,34 @@ i2c_read(device_t dev, char *buf, int len, int *read, int 
last, int delay)
 
        if (len) {
                if (len == 1)
-                       WRITE1(sc, I2C_IBCR, IBCR_IBIE | IBCR_MSSL |    \
-                           IBCR_NOACK);
+                       WRITE1(sc, I2C_IBCR, IBCR_IBIE | IBCR_MSSL | 
IBCR_NOACK);
                else
                        WRITE1(sc, I2C_IBCR, IBCR_IBIE | IBCR_MSSL);
 
                /* dummy read */
                READ1(sc, I2C_IBDR);
-               DELAY(1000);
-       }
 
-       while (*read < len) {
-               error = wait_for_icf(sc);
-               if (error != 0) {
-                       mtx_unlock(&sc->mutex);
-                       return (error);
-               }
+               while (*read < len) {
+                       error = wait_for_icf(sc);
+                       if (error != 0) {
+                               mtx_unlock(&sc->mutex);
+                               return (error);
+                       }
 
-               if ((*read == len - 2) && last) {
-                       /* NO ACK on last byte */
-                       WRITE1(sc, I2C_IBCR, IBCR_IBIE | IBCR_MSSL |    \
-                           IBCR_NOACK);
-               }
+                       if (last) {
+                               if (*read == len - 2) {
+                                       /* NO ACK on last byte */
+                                       WRITE1(sc, I2C_IBCR, IBCR_IBIE | 
IBCR_MSSL | IBCR_NOACK);
 
-               if ((*read == len - 1) && last) {
-                       /* Transfer done, remove master bit */
-                       WRITE1(sc, I2C_IBCR, IBCR_IBIE | IBCR_NOACK);
-               }
+                               } else if (*read == len - 1) {
+                                       /* Transfer done, remove master bit */
+                                       WRITE1(sc, I2C_IBCR, IBCR_IBIE | 
IBCR_NOACK);
+                               }
+                       }
 
-               *buf++ = READ1(sc, I2C_IBDR);
-               (*read)++;
+                       *buf++ = READ1(sc, I2C_IBDR);
+                       (*read)++;
+               }
        }
        mtx_unlock(&sc->mutex);
 
@@ -563,7 +533,7 @@ i2c_write(device_t dev, const char *buf, int len, int 
*sent, int timeout)
        while (*sent < len) {
                WRITE1(sc, I2C_IBDR, *buf++);
 
-               error = wait_for_iif(sc);
+               error = wait_for_icf(sc);
                if (error != 0) {
                        mtx_unlock(&sc->mutex);
                        return (error);
diff --git a/sys/dev/iicbus/controller/vybrid/vf_i2c.h 
b/sys/dev/iicbus/controller/vybrid/vf_i2c.h
index 3498911dfd9c..ba391e70bafc 100644
--- a/sys/dev/iicbus/controller/vybrid/vf_i2c.h
+++ b/sys/dev/iicbus/controller/vybrid/vf_i2c.h
@@ -28,7 +28,8 @@
 
 /*
  * Vybrid Family Inter-Integrated Circuit (I2C)
- * Chapter 48, Vybrid Reference Manual, Rev. 5, 07/2013
+ * Originally based on Chapter 48, Vybrid Reference Manual, Rev. 5, 07/2013
+ * Currently based on Chapter 21, LX2160A Reference Manual, Rev. 1, 10/2021
  *
  * The current implementation is based on the original driver by Ruslan Bukin,
  * later modified by Dawid Górecki, and split into FDT and ACPI drivers by Val
diff --git a/sys/dev/iicbus/controller/vybrid/vf_i2c_acpi.c 
b/sys/dev/iicbus/controller/vybrid/vf_i2c_acpi.c
index 03f5c99a45c6..0b18926ee2df 100644
--- a/sys/dev/iicbus/controller/vybrid/vf_i2c_acpi.c
+++ b/sys/dev/iicbus/controller/vybrid/vf_i2c_acpi.c
@@ -27,7 +27,8 @@
 
 /*
  * Vybrid Family Inter-Integrated Circuit (I2C)
- * Chapter 48, Vybrid Reference Manual, Rev. 5, 07/2013
+ * Originally based on Chapter 48, Vybrid Reference Manual, Rev. 5, 07/2013
+ * Currently based on Chapter 21, LX2160A Reference Manual, Rev. 1, 10/2021
  *
  * The current implementation is based on the original driver by Ruslan Bukin,
  * later modified by Dawid Górecki, and split into FDT and ACPI drivers by Val
diff --git a/sys/dev/iicbus/controller/vybrid/vf_i2c_fdt.c 
b/sys/dev/iicbus/controller/vybrid/vf_i2c_fdt.c
index 935f389ab516..def5ce109caf 100644
--- a/sys/dev/iicbus/controller/vybrid/vf_i2c_fdt.c
+++ b/sys/dev/iicbus/controller/vybrid/vf_i2c_fdt.c
@@ -28,7 +28,8 @@
 
 /*
  * Vybrid Family Inter-Integrated Circuit (I2C)
- * Chapter 48, Vybrid Reference Manual, Rev. 5, 07/2013
+ * Originally based on Chapter 48, Vybrid Reference Manual, Rev. 5, 07/2013
+ * Currently based on Chapter 21, LX2160A Reference Manual, Rev. 1, 10/2021
  *
  * The current implementation is based on the original driver by Ruslan Bukin,
  * later modified by Dawid Górecki, and split into FDT and ACPI drivers by Val

Reply via email to