Author: avg
Date: Wed Oct 12 06:58:01 2016
New Revision: 307130
URL: https://svnweb.freebsd.org/changeset/base/307130

Log:
  smbus: allow child devices to be added via hints
  
  This will allow to add slave drivers in the same fashion as for iicbus.
  
  Also, allow other code to add a child device and set its 'addr' ivar.
  The ivar can only be set if it's unset, it can not be changed.
  That could be used, for example, by a platform driver that has
  a precise description of the hardware and, thus, knows what drivers
  can handle what slaves.
  
  The slave auto-probing code is unsafe and broken because it uses
  7-bit slave addresses.  It's going to be removed.
  
  Note: internally the driver uses address of zero as an unset address
  while smbus_get_addr() returns it as -1 for compatibility reasons.
  The address is expected to be unset only for children that do not
  work with slaves like, for example, smb(4).
  
  Reviewed by:  jhb
  Differential Revision: https://reviews.freebsd.org/D8173

Modified:
  head/sys/dev/smbus/smbconf.h
  head/sys/dev/smbus/smbus.c

Modified: head/sys/dev/smbus/smbconf.h
==============================================================================
--- head/sys/dev/smbus/smbconf.h        Wed Oct 12 05:50:47 2016        
(r307129)
+++ head/sys/dev/smbus/smbconf.h        Wed Oct 12 06:58:01 2016        
(r307130)
@@ -34,6 +34,10 @@
 
 #define n(flags) (~(flags) & (flags))
 
+/* Order constants for smbus children. */
+#define SMBUS_ORDER_HINTED     20
+#define SMBUS_ORDER_PNP                40
+
 /*
  * How tsleep() is called in smb_request_bus().
  */

Modified: head/sys/dev/smbus/smbus.c
==============================================================================
--- head/sys/dev/smbus/smbus.c  Wed Oct 12 05:50:47 2016        (r307129)
+++ head/sys/dev/smbus/smbus.c  Wed Oct 12 06:58:01 2016        (r307130)
@@ -31,6 +31,7 @@ __FBSDID("$FreeBSD$");
 #include <sys/param.h>
 #include <sys/systm.h>
 #include <sys/lock.h>
+#include <sys/malloc.h>
 #include <sys/module.h>
 #include <sys/mutex.h>
 #include <sys/bus.h>
@@ -41,54 +42,16 @@ __FBSDID("$FreeBSD$");
 #include "smbus_if.h"
 #include "bus_if.h"
 
+struct smbus_ivar
+{
+       uint8_t addr;
+};
 
 /*
  * Autoconfiguration and support routines for System Management bus
  */
+static void smbus_probe_device(device_t dev, u_char addr);
 
-/*
- * Device methods
- */
-static int smbus_probe(device_t);
-static int smbus_attach(device_t);
-static int smbus_detach(device_t);
-
-static int smbus_child_location_str(device_t parent, device_t child,
-                   char *buf, size_t buflen);
-static int smbus_print_child(device_t parent, device_t child);
-static void smbus_probe_device(device_t dev, u_char* addr);
-static int smbus_read_ivar(device_t parent, device_t child, int which,
-                   uintptr_t *result);
-
-static device_method_t smbus_methods[] = {
-        /* device interface */
-        DEVMETHOD(device_probe,         smbus_probe),
-        DEVMETHOD(device_attach,        smbus_attach),
-        DEVMETHOD(device_detach,        smbus_detach),
-
-       /* bus interface */
-       DEVMETHOD(bus_add_child,        bus_generic_add_child),
-       DEVMETHOD(bus_child_location_str, smbus_child_location_str),
-       DEVMETHOD(bus_driver_added,     bus_generic_driver_added),
-       DEVMETHOD(bus_print_child,      smbus_print_child),
-       DEVMETHOD(bus_read_ivar,        smbus_read_ivar),
-
-       DEVMETHOD_END
-};
-
-driver_t smbus_driver = {
-        "smbus",
-        smbus_methods,
-        sizeof(struct smbus_softc),
-};
-
-devclass_t smbus_devclass;
-
-/*
- * At 'probe' time, we add all the devices which we know about to the
- * bus.  The generic attach routine will probe and attach them if they
- * are alive.
- */
 static int
 smbus_probe(device_t dev)
 {
@@ -107,9 +70,9 @@ smbus_attach(device_t dev)
        mtx_init(&sc->lock, device_get_nameunit(dev), "smbus", MTX_DEF);
        bus_generic_probe(dev);
        for (addr = SMBUS_ADDR_MIN; addr < SMBUS_ADDR_MAX; ++addr) {
-               sc->addrs[addr] = addr;
-               smbus_probe_device(dev, &sc->addrs[addr]);
+               smbus_probe_device(dev, addr);
        }
+       bus_enumerate_hinted_children(dev);
        bus_generic_attach(dev);
 
        return (0);
@@ -124,6 +87,7 @@ smbus_detach(device_t dev)
        error = bus_generic_detach(dev);
        if (error)
                return (error);
+       device_delete_children(dev);
        mtx_destroy(&sc->lock);
 
        return (0);
@@ -135,34 +99,78 @@ smbus_generic_intr(device_t dev, u_char 
 }
 
 static void
-smbus_probe_device(device_t dev, u_char* addr)
+smbus_probe_device(device_t dev, u_char addr)
 {
        device_t child;
        int error;
        u_char cmd;
        u_char buf[2];
+       struct smbus_ivar *devi;
 
        cmd = 0x01;
-       error = smbus_trans(dev, *addr, cmd,
+       error = smbus_trans(dev, addr, cmd,
                            SMB_TRANS_NOCNT | SMB_TRANS_NOREPORT,
                            NULL, 0, buf, 1, NULL);
        if (error == 0) {
                if (bootverbose)
-                       device_printf(dev, "Probed address 0x%02x\n", *addr);
-               child = device_add_child(dev, NULL, -1);
-               device_set_ivars(child, addr);
+                       device_printf(dev, "Probed address 0x%02x\n", addr);
+               child = BUS_ADD_CHILD(dev, SMBUS_ORDER_PNP, NULL, -1);
+               if (child == NULL)
+                       return;
+               devi = device_get_ivars(child);
+               devi->addr = addr;
+       }
+}
+
+static device_t
+smbus_add_child(device_t dev, u_int order, const char *name, int unit)
+{
+       struct smbus_ivar *devi;
+       device_t child;
+
+       child = device_add_child_ordered(dev, order, name, unit);
+       if (child == NULL)
+               return (child);
+       devi = malloc(sizeof(struct smbus_ivar), M_DEVBUF, M_NOWAIT | M_ZERO);
+       if (devi == NULL) {
+               device_delete_child(dev, child);
+               return (NULL);
+       }
+       device_set_ivars(child, devi);
+       return (child);
+}
+
+static void
+smbus_hinted_child(device_t bus, const char *dname, int dunit)
+{
+       struct smbus_ivar *devi;
+       device_t child;
+       int addr;
+
+       addr = 0;
+       resource_int_value(dname, dunit, "addr", &addr);
+       if (addr > UINT8_MAX) {
+               device_printf(bus, "ignored incorrect slave address hint 0x%x"
+                   " for %s%d\n", addr, dname, dunit);
+               return;
        }
+       child = BUS_ADD_CHILD(bus, SMBUS_ORDER_HINTED, dname, dunit);
+       if (child == NULL)
+               return;
+       devi = device_get_ivars(child);
+       devi->addr = addr;
 }
 
+
 static int
 smbus_child_location_str(device_t parent, device_t child, char *buf,
     size_t buflen)
 {
-       unsigned char *addr;
+       struct smbus_ivar *devi;
 
-       addr = device_get_ivars(child);
-       if (addr)
-               snprintf(buf, buflen, "addr=0x%x", *addr);
+       devi = device_get_ivars(child);
+       if (devi->addr != 0)
+               snprintf(buf, buflen, "addr=0x%x", devi->addr);
        else if (buflen)
                buf[0] = 0;
        return (0);
@@ -171,28 +179,49 @@ smbus_child_location_str(device_t parent
 static int
 smbus_print_child(device_t parent, device_t child)
 {
-       unsigned char *addr;
+       struct smbus_ivar *devi;
        int retval;
 
-       addr = device_get_ivars(child);
+       devi = device_get_ivars(child);
        retval = bus_print_child_header(parent, child);
-       if (addr)
-               retval += printf(" at addr 0x%x", *addr);
+       if (devi->addr != 0)
+               retval += printf(" at addr 0x%x", devi->addr);
        retval += bus_print_child_footer(parent, child);
 
        return (retval);
 }
 
 static int
-smbus_read_ivar(device_t parent, device_t child, int which,
-    uintptr_t *result)
+smbus_read_ivar(device_t parent, device_t child, int which, uintptr_t *result)
+{
+       struct smbus_ivar *devi;
+
+       devi = device_get_ivars(child);
+       switch (which) {
+       case SMBUS_IVAR_ADDR:
+               if (devi->addr != 0)
+                       *result = devi->addr;
+               else
+                       *result = -1;
+               break;
+       default:
+               return (ENOENT);
+       }
+       return (0);
+}
+
+static int
+smbus_write_ivar(device_t parent, device_t child, int which, uintptr_t value)
 {
-       unsigned char *addr;
+       struct smbus_ivar *devi;
 
-       addr = device_get_ivars(child);
+       devi = device_get_ivars(child);
        switch (which) {
        case SMBUS_IVAR_ADDR:
-               *result = (addr == NULL) ? -1 : *addr;
+               /* Allow to set but no change the slave address. */
+               if (devi->addr != 0)
+                       return (EINVAL);
+               devi->addr = value;
                break;
        default:
                return (ENOENT);
@@ -200,4 +229,47 @@ smbus_read_ivar(device_t parent, device_
        return (0);
 }
 
+static void
+smbus_probe_nomatch(device_t bus, device_t child)
+{
+       struct smbus_ivar *devi = device_get_ivars(child);
+
+       /*
+        * Ignore (self-identified) devices without a slave address set.
+        * For example, smb(4).
+        */
+       if (devi->addr != 0)
+               device_printf(bus, "<unknown device> at addr %#x\n",
+                   devi->addr);
+}
+
+/*
+ * Device methods
+ */
+static device_method_t smbus_methods[] = {
+        /* device interface */
+        DEVMETHOD(device_probe,         smbus_probe),
+        DEVMETHOD(device_attach,        smbus_attach),
+        DEVMETHOD(device_detach,        smbus_detach),
+
+       /* bus interface */
+       DEVMETHOD(bus_add_child,        smbus_add_child),
+       DEVMETHOD(bus_hinted_child,     smbus_hinted_child),
+       DEVMETHOD(bus_probe_nomatch,    smbus_probe_nomatch),
+       DEVMETHOD(bus_child_location_str, smbus_child_location_str),
+       DEVMETHOD(bus_print_child,      smbus_print_child),
+       DEVMETHOD(bus_read_ivar,        smbus_read_ivar),
+       DEVMETHOD(bus_write_ivar,       smbus_write_ivar),
+
+       DEVMETHOD_END
+};
+
+driver_t smbus_driver = {
+        "smbus",
+        smbus_methods,
+        sizeof(struct smbus_softc),
+};
+
+devclass_t smbus_devclass;
+
 MODULE_VERSION(smbus, SMBUS_MODVER);
_______________________________________________
svn-src-all@freebsd.org mailing list
https://lists.freebsd.org/mailman/listinfo/svn-src-all
To unsubscribe, send any mail to "svn-src-all-unsubscr...@freebsd.org"

Reply via email to