Source-Changes-HG archive

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

[src/thorpej-i2c-spi-conf]: src/sys/arch/macppc/dev Adapt the Keywest i2c con...



details:   https://anonhg.NetBSD.org/src/rev/a8d6458ea4ac
branches:  thorpej-i2c-spi-conf
changeset: 1020791:a8d6458ea4ac
user:      thorpej <thorpej%NetBSD.org@localhost>
date:      Sat May 08 21:58:12 2021 +0000

description:
Adapt the Keywest i2c controller driver to the new i2c device enumeration
mechanism.  We need to provide our own enumeration callback because these
controllers do not use the standard OpenFirmware bindings.

The Keywest controller supports 2 physical i2c busses on a single controller,
so we logically split it up that way now, rather than encoding the channel in
in the i2c address as was done previously.

Different systems have different I2C device tree topologies.

Some systems use a scheme like this:

        /u3@0,f8000000/i2c@f8001000/temp-monitor@98
        /u3@0,f8000000/i2c@f8001000/fan@15e

Here, we see the channel encoded in bit #8 of the address.

Other systems use a scheme like this:

        /ht@0,f2000000/pci@4000,0,0/mac-io@7/i2c@18000/i2c-bus@0
        /ht@0,f2000000/pci@4000,0,0/mac-io@7/i2c@18000/i2c-bus@0/codec@8c

        /u4@0,f8000000/i2c@f8001000/i2c-bus@1
        /u4@0,f8000000/i2c@f8001000/i2c-bus@1/temp-monitor@94

Here, a separate device tree node represents the channel.
Note that in BOTH cases, the I2C address of the devices are
shifted left by 1 (as it would be on the wire to leave room
for the read/write bit).

diffstat:

 sys/arch/macppc/dev/ki2c.c    |  527 +++++++++++++++++++++++++++++------------
 sys/arch/macppc/dev/ki2cvar.h |   28 +-
 2 files changed, 382 insertions(+), 173 deletions(-)

diffs (truncated from 716 to 300 lines):

diff -r b108f06fb2f9 -r a8d6458ea4ac sys/arch/macppc/dev/ki2c.c
--- a/sys/arch/macppc/dev/ki2c.c        Sat May 08 16:46:43 2021 +0000
+++ b/sys/arch/macppc/dev/ki2c.c        Sat May 08 21:58:12 2021 +0000
@@ -1,4 +1,4 @@
-/*     $NetBSD: ki2c.c,v 1.31 2021/04/24 23:36:41 thorpej Exp $        */
+/*     $NetBSD: ki2c.c,v 1.31.2.1 2021/05/08 21:58:12 thorpej Exp $    */
 /*     Id: ki2c.c,v 1.7 2002/10/05 09:56:05 tsubai Exp */
 
 /*-
@@ -30,6 +30,7 @@
 #include <sys/param.h>
 #include <sys/device.h>
 #include <sys/systm.h>
+#include <sys/kmem.h>
 #include <sys/mutex.h>
 
 #include <dev/ofw/openfirm.h>
@@ -38,35 +39,220 @@
 #include "opt_ki2c.h"
 #include <macppc/dev/ki2cvar.h>
 
+#include "locators.h"
+
 #ifdef KI2C_DEBUG
 #define DPRINTF printf
 #else
 #define DPRINTF while (0) printf
 #endif
 
-int ki2c_match(device_t, cfdata_t, void *);
-void ki2c_attach(device_t, device_t, void *);
-inline uint8_t ki2c_readreg(struct ki2c_softc *, int);
-inline void ki2c_writereg(struct ki2c_softc *, int, uint8_t);
-u_int ki2c_getmode(struct ki2c_softc *);
-void ki2c_setmode(struct ki2c_softc *, u_int);
-u_int ki2c_getspeed(struct ki2c_softc *);
-void ki2c_setspeed(struct ki2c_softc *, u_int);
-int ki2c_intr(struct ki2c_softc *);
-int ki2c_poll(struct ki2c_softc *, int);
-int ki2c_start(struct ki2c_softc *, int, int, void *, int);
-int ki2c_read(struct ki2c_softc *, int, int, void *, int);
-int ki2c_write(struct ki2c_softc *, int, int, void *, int);
+static int     ki2c_match(device_t, cfdata_t, void *);
+static void    ki2c_attach(device_t, device_t, void *);
+static int     ki2c_intr(struct ki2c_softc *);
 
 /* I2C glue */
-static int ki2c_i2c_exec(void *, i2c_op_t, i2c_addr_t, const void *, size_t,
-                   void *, size_t, int);
-
+static int     ki2c_i2c_exec(void *, i2c_op_t, i2c_addr_t, const void *,
+                   size_t, void *, size_t, int);
+static int     ki2c_i2c_acquire_bus(void *, int);
+static void    ki2c_i2c_release_bus(void *, int);
 
 CFATTACH_DECL_NEW(ki2c, sizeof(struct ki2c_softc), ki2c_match, ki2c_attach,
        NULL, NULL);
 
-int
+static prop_dictionary_t
+ki2c_i2c_device_props(struct ki2c_softc *sc, int node)
+{
+       prop_dictionary_t props = prop_dictionary_create();
+       uint32_t reg;
+       char descr[32], num[8];
+
+       /* We're fetching descriptions for sensors. */
+
+       for (node = OF_child(node); node != 0; node = OF_peer(node)) {
+               if (of_getprop_uint32(node, "reg", &reg) == -1) {
+                       continue;
+               }
+               if (OF_getprop(node, "location", descr, sizeof(descr)) <= 0) {
+                       continue;
+               }
+               snprintf(num, sizeof(num), "s%02x", reg);
+
+               aprint_debug_dev(sc->sc_dev,
+                   "%s: sensor %s -> %s\n", __func__, num, descr);
+
+               prop_dictionary_set_string(props, num, descr);
+       }
+
+       return props;
+}
+
+static bool
+ki2c_i2c_enumerate_device(struct ki2c_softc *sc, device_t dev, int node,
+    const char *name, uint32_t addr,
+    struct i2c_enumerate_devices_args * const args)
+{
+       int compat_size;
+       prop_dictionary_t props;
+       char compat_buf[32];
+       char *compat;
+       bool cbrv;
+
+       compat_size = OF_getproplen(node, "compatible");
+       if (compat_size <= 0) {
+               /* some i2c device nodes don't have 'compatible' */
+               aprint_debug_dev(sc->sc_dev,
+                   "no compatible property for phandle %d; using '%s'\n",
+                   node, name);
+               compat = compat_buf;
+               strlcpy(compat, name, sizeof(compat));
+               compat_size = strlen(compat) + 1;
+       } else {
+               compat = kmem_tmpbuf_alloc(compat_size, compat_buf,
+                   sizeof(compat_buf), KM_SLEEP);
+               if (OF_getprop(node, "compatible", compat,
+                             sizeof(compat)) <= 0) {
+                       aprint_error_dev(sc->sc_dev,
+                           "unable to get compatible property for "
+                           "phandle %d ('%s')\n", node, name);
+                       goto bad;
+               }
+       }
+
+       props = ki2c_i2c_device_props(sc, node);
+
+       args->ia->ia_addr = (i2c_addr_t)addr;
+       args->ia->ia_name = name;
+       args->ia->ia_clist = compat;
+       args->ia->ia_clist_size = compat_size;
+       args->ia->ia_prop = props;
+       args->ia->ia_devhandle = devhandle_from_of(node);
+
+       cbrv = args->callback(dev, args);
+
+       prop_object_release(props);
+
+       return cbrv;    /* callback decides if we keep enumerating */
+
+ bad:
+       if (compat != compat_buf) {
+               kmem_tmpbuf_free(compat, compat_size, compat_buf);
+       }
+       return true;                    /* keep enumerating */
+}
+
+static int
+ki2c_i2c_enumerate_devices(device_t dev, devhandle_t call_handle, void *v)
+{
+       struct i2c_enumerate_devices_args *args = v;
+       int bus_phandle, node;
+       uint32_t addr;
+       char name[32];
+
+       /* dev is the "iic" bus instance.  ki2c channel is in args. */
+       struct ki2c_channel *ch = args->ia->ia_tag->ic_cookie;
+       struct ki2c_softc *sc = ch->ch_ki2c;
+
+       /*
+        * If we're not using the separate nodes scheme, we need
+        * to filter out devices from the other channel.  We detect
+        * this by comparing the bus phandle to the controller phandle,
+        * and if they match, we are NOT using the separate nodes
+        * scheme.
+        */
+       bus_phandle = devhandle_to_of(device_handle(dev));
+       bool filter_by_channel =
+           bus_phandle == devhandle_to_of(device_handle(sc->sc_dev));
+
+       for (node = OF_child(bus_phandle); node != 0; node = OF_peer(node)) {
+               if (OF_getprop(node, "name", name, sizeof(name)) <= 0) {
+                       aprint_error_dev(sc->sc_dev,
+                           "unable to get name property for phandle %d\n",
+                           node);
+                       continue;
+               } 
+               if (of_getprop_uint32(node, "reg", &addr) == -1 &&
+                   of_getprop_uint32(node, "i2c-address", &addr) == -1) {
+                       aprint_error_dev(sc->sc_dev,
+                           "unable to get i2c address for phandle %d ('%s')\n",
+                           node, name);
+                       continue;
+               }
+               if (filter_by_channel && ((addr >> 8) & 1) != ch->ch_channel) {
+                       continue;
+               }
+               addr = (addr & 0xff) >> 1;
+               if (!ki2c_i2c_enumerate_device(sc, dev, node, name, addr,
+                                              args)) {
+                       break;
+               }
+       }
+
+       return 0;
+}
+
+static device_call_t
+ki2c_devhandle_lookup_device_call(devhandle_t handle, const char *name,
+    devhandle_t *call_handlep)
+{
+       if (strcmp(name, "i2c-enumerate-devices") == 0) {
+               return ki2c_i2c_enumerate_devices;
+       }
+
+       /* Defer everything else to the "super". */
+       return NULL;
+}
+
+static inline uint8_t
+ki2c_readreg(struct ki2c_softc *sc, int reg)
+{
+
+       return bus_space_read_1(sc->sc_tag, sc->sc_bh, sc->sc_regstep * reg);
+}
+
+static inline void
+ki2c_writereg(struct ki2c_softc *sc, int reg, uint8_t val)
+{
+       
+       bus_space_write_1(sc->sc_tag, sc->sc_bh, reg * sc->sc_regstep, val);
+       delay(10);
+}
+
+#if 0
+static u_int
+ki2c_getmode(struct ki2c_softc *sc)
+{
+       return ki2c_readreg(sc, MODE) & I2C_MODE;
+}
+#endif
+
+static void
+ki2c_setmode(struct ki2c_softc *sc, u_int mode)
+{
+       ki2c_writereg(sc, MODE, mode);
+}
+
+#if 0
+static u_int
+ki2c_getspeed(struct ki2c_softc *sc)
+{
+       return ki2c_readreg(sc, MODE) & I2C_SPEED;
+}
+#endif
+
+static void
+ki2c_setspeed(struct ki2c_softc *sc, u_int speed)
+{
+       u_int x;
+
+       KASSERT((speed & ~I2C_SPEED) == 0);
+       x = ki2c_readreg(sc, MODE);
+       x &= ~I2C_SPEED;
+       x |= speed;
+       ki2c_writereg(sc, MODE, x);
+}
+
+static int
 ki2c_match(device_t parent, cfdata_t match, void *aux)
 {
        struct confargs *ca = aux;
@@ -77,21 +263,17 @@
        return 0;
 }
 
-void
+static void
 ki2c_attach(device_t parent, device_t self, void *aux)
 {
        struct ki2c_softc *sc = device_private(self);
        struct confargs *ca = aux;
+       struct ki2c_channel *ch;
        int node = ca->ca_node;
-       uint32_t addr, channel, reg;
-       int rate, child, /*namelen,*/ i2cbus[2] = {0, 0};
+       uint32_t channel, addr;
+       int i, rate, child;
        struct i2cbus_attach_args iba;
-       prop_dictionary_t dict = device_properties(self);
-       prop_array_t cfg;
-       int devs, devc;
-       char compat[256], num[8], descr[32];
-       prop_dictionary_t dev;
-       prop_data_t data;
+       devhandle_t devhandle;
        char name[32];
 
        sc->sc_dev = self;
@@ -126,135 +308,122 @@
        ki2c_setspeed(sc, I2C_100kHz);          /* XXX rate */
        
        ki2c_writereg(sc, IER,I2C_INT_DATA|I2C_INT_ADDR|I2C_INT_STOP);
-       
-       cfg = prop_array_create();
-       prop_dictionary_set(dict, "i2c-child-devices", cfg);
-       prop_object_release(cfg);
+
+       /*
+        * Two physical I2C busses share a single controller.  It's not
+        * quite a mux, which is why we don't attach it that way.
+        *
+        * The locking order is:
+        *
+        *      iic bus mutex -> ctrl_lock
+        *
+        * ctrl_lock is taken in ki2c_i2c_acquire_bus.



Home | Main Index | Thread Index | Old Index