diff options
author | avg <avg@FreeBSD.org> | 2016-10-28 14:50:23 +0000 |
---|---|---|
committer | avg <avg@FreeBSD.org> | 2016-10-28 14:50:23 +0000 |
commit | 60fc64af84ae1c1b31f4e16c5443e99c65865a5f (patch) | |
tree | 5aae6174c48f737166a512b2f60f62b0836a1f36 /sys/dev/smbus | |
parent | 02ceddeded0620de7642ea89f01fef8303d55d68 (diff) | |
download | FreeBSD-src-60fc64af84ae1c1b31f4e16c5443e99c65865a5f.zip FreeBSD-src-60fc64af84ae1c1b31f4e16c5443e99c65865a5f.tar.gz |
MFC r307130: smbus: allow child devices to be added via hints
Diffstat (limited to 'sys/dev/smbus')
-rw-r--r-- | sys/dev/smbus/smbconf.h | 4 | ||||
-rw-r--r-- | sys/dev/smbus/smbus.c | 198 |
2 files changed, 139 insertions, 63 deletions
diff --git a/sys/dev/smbus/smbconf.h b/sys/dev/smbus/smbconf.h index f39c442..2c120c1 100644 --- a/sys/dev/smbus/smbconf.h +++ b/sys/dev/smbus/smbconf.h @@ -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(). */ diff --git a/sys/dev/smbus/smbus.c b/sys/dev/smbus/smbus.c index 389efac..d485094 100644 --- a/sys/dev/smbus/smbus.c +++ b/sys/dev/smbus/smbus.c @@ -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 devaddr, char low, char high, int err) } 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,30 @@ smbus_child_location_str(device_t parent, device_t child, char *buf, 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) { - 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; + if (devi->addr != 0) + *result = devi->addr; + else + *result = -1; break; default: return (ENOENT); @@ -200,4 +210,66 @@ smbus_read_ivar(device_t parent, device_t child, int which, return (0); } +static int +smbus_write_ivar(device_t parent, device_t child, int which, uintptr_t value) +{ + struct smbus_ivar *devi; + + devi = device_get_ivars(child); + switch (which) { + case SMBUS_IVAR_ADDR: + /* Allow to set but no change the slave address. */ + if (devi->addr != 0) + return (EINVAL); + devi->addr = value; + break; + default: + return (ENOENT); + } + 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); |