Source-Changes-HG archive

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]

[src/trunk]: src/sys/dev/i2c ssdfb: fix i2c transfer error with some controllers



details:   https://anonhg.NetBSD.org/src/rev/503a0778b2b2
branches:  trunk
changeset: 846216:503a0778b2b2
user:      tnn <tnn%NetBSD.org@localhost>
date:      Tue Nov 05 19:59:35 2019 +0000

description:
ssdfb: fix i2c transfer error with some controllers

If the controller doesn't support the full 128 byte transfer size we need,
then split the write across multiple transactions.

diffstat:

 sys/dev/i2c/ssdfb_i2c.c |  82 ++++++++++++++++++++++++++++++++++++++++--------
 1 files changed, 68 insertions(+), 14 deletions(-)

diffs (152 lines):

diff -r 529f81ac1a22 -r 503a0778b2b2 sys/dev/i2c/ssdfb_i2c.c
--- a/sys/dev/i2c/ssdfb_i2c.c   Tue Nov 05 12:59:54 2019 +0000
+++ b/sys/dev/i2c/ssdfb_i2c.c   Tue Nov 05 19:59:35 2019 +0000
@@ -1,4 +1,4 @@
-/* $NetBSD: ssdfb_i2c.c,v 1.4 2019/11/02 14:23:59 tnn Exp $ */
+/* $NetBSD: ssdfb_i2c.c,v 1.5 2019/11/05 19:59:35 tnn Exp $ */
 
 /*
  * Copyright (c) 2019 The NetBSD Foundation, Inc.
@@ -30,7 +30,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: ssdfb_i2c.c,v 1.4 2019/11/02 14:23:59 tnn Exp $");
+__KERNEL_RCSID(0, "$NetBSD: ssdfb_i2c.c,v 1.5 2019/11/05 19:59:35 tnn Exp $");
 
 #include <sys/param.h>
 #include <sys/device.h>
@@ -43,12 +43,16 @@
        struct          ssdfb_softc sc;
        i2c_tag_t       sc_i2c_tag;
        i2c_addr_t      sc_i2c_addr;
+       size_t          sc_transfer_size;
 };
 
 static int     ssdfb_i2c_match(device_t, cfdata_t, void *);
 static void    ssdfb_i2c_attach(device_t, device_t, void *);
 static int     ssdfb_i2c_detach(device_t, int);
 
+static int     ssdfb_i2c_probe_transfer_size(struct ssdfb_i2c_softc *, bool);
+static int     ssdfb_i2c_transfer(struct ssdfb_i2c_softc *, uint8_t, uint8_t *,
+                               size_t, int);
 static int     ssdfb_i2c_cmd(void *, uint8_t *, size_t, bool);
 static int     ssdfb_i2c_transfer_rect(void *, uint8_t, uint8_t, uint8_t,
                                uint8_t, uint8_t *, size_t, bool);
@@ -132,6 +136,61 @@
 }
 
 static int
+ssdfb_i2c_probe_transfer_size(struct ssdfb_i2c_softc *sc, bool usepoll)
+{
+       int flags = usepoll ? I2C_F_POLL : 0;
+       uint8_t cb = SSDFB_I2C_CTRL_BYTE_DATA_MASK;
+       int error;
+       uint8_t buf[128];
+       size_t len;
+
+       error = iic_acquire_bus(sc->sc_i2c_tag, flags);
+       if (error)
+               return error;
+       len = sizeof(buf);
+       memset(buf, 0, len);
+       while (len > 0) {
+               error = iic_exec(sc->sc_i2c_tag, I2C_OP_WRITE_WITH_STOP,
+                   sc->sc_i2c_addr, &cb, sizeof(cb), buf, len, flags);
+               if (!error) {
+                       break;
+               }
+               len >>= 1;
+       }
+       if (!error && len < 2) {
+               error = E2BIG;
+       } else {
+               sc->sc_transfer_size = len;
+       }
+       (void) iic_release_bus(sc->sc_i2c_tag, flags);
+
+       return error;
+}
+
+static int
+ssdfb_i2c_transfer(struct ssdfb_i2c_softc *sc, uint8_t cb, uint8_t *data,
+                  size_t len, int flags)
+{
+       int error;
+       size_t xfer_size = sc->sc_transfer_size;
+
+       while (len >= xfer_size) {
+               error = iic_exec(sc->sc_i2c_tag, I2C_OP_WRITE_WITH_STOP,
+                   sc->sc_i2c_addr, &cb, sizeof(cb), data, xfer_size, flags);
+               if (error)
+                       return error;
+               len -= xfer_size;
+               data += xfer_size;
+       }
+       if (len > 0) {
+               error = iic_exec(sc->sc_i2c_tag, I2C_OP_WRITE_WITH_STOP,
+                   sc->sc_i2c_addr, &cb, sizeof(cb), data, len, flags);
+       }
+
+       return error;
+}
+
+static int
 ssdfb_i2c_cmd(void *cookie, uint8_t *cmd, size_t len, bool usepoll)
 {
        struct ssdfb_i2c_softc *sc = (struct ssdfb_i2c_softc *)cookie;
@@ -154,9 +213,6 @@
     uint8_t frompage, uint8_t topage, uint8_t *p, size_t stride, bool usepoll)
 {
        struct ssdfb_i2c_softc *sc = (struct ssdfb_i2c_softc *)cookie;
-       int flags = usepoll ? I2C_F_POLL : 0;
-       uint8_t cb = SSDFB_I2C_CTRL_BYTE_DATA_MASK;
-       uint8_t data[] = {0, 0, 0};
        uint8_t cmd[2];
        int error;
 
@@ -177,13 +233,12 @@
        }
 
        if (sc->sc.sc_transfer_rect != ssdfb_smbus_transfer_rect) {
-               error = iic_acquire_bus(sc->sc_i2c_tag, flags);
+               error = ssdfb_i2c_probe_transfer_size(sc, usepoll);
                if (error)
                        return error;
-               error = iic_exec(sc->sc_i2c_tag, I2C_OP_WRITE_WITH_STOP,
-                   sc->sc_i2c_addr, &cb, sizeof(cb), data, sizeof(data), flags);
-               (void) iic_release_bus(sc->sc_i2c_tag, flags);
-               if (error) {
+               aprint_verbose_dev(sc->sc.sc_dev, "%zd-byte transfers\n",
+                   sc->sc_transfer_size);
+               if (sc->sc_transfer_size == 2) {
                        sc->sc.sc_transfer_rect = ssdfb_smbus_transfer_rect;
                }
        }
@@ -238,8 +293,7 @@
        if (error)
                goto out;
        while (frompage <= topage) {
-               error = iic_exec(sc->sc_i2c_tag, I2C_OP_WRITE_WITH_STOP,
-                   sc->sc_i2c_addr, &cb, sizeof(cb), p, len, flags);
+               error = ssdfb_i2c_transfer(sc, cb, p, len, flags);
                if (error)
                        goto out;
                frompage++;
@@ -278,8 +332,7 @@
                    sc->sc_i2c_addr, &cc, sizeof(cc), cmds, sizeof(cmds), flags);
                if (error)
                        goto out;
-               error = iic_exec(sc->sc_i2c_tag, I2C_OP_WRITE_WITH_STOP,
-                   sc->sc_i2c_addr, &cb, sizeof(cb), p, len, flags);
+               error = ssdfb_i2c_transfer(sc, cb, p, len, flags);
                if (error)
                        goto out;
                frompage++;
@@ -364,5 +417,6 @@
        }
 out:
        (void) iic_release_bus(sc->sc_i2c_tag, flags);
+
        return error;
 }



Home | Main Index | Thread Index | Old Index