Index: head/sys/dev/smbus/smbconf.h =================================================================== --- head/sys/dev/smbus/smbconf.h (revision 307129) +++ head/sys/dev/smbus/smbconf.h (revision 307130) @@ -1,142 +1,146 @@ /*- * Copyright (c) 1998 Nicolas Souchu * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ */ #ifndef __SMBONF_H #define __SMBONF_H #include #define SMBPRI (PZERO+8) /* XXX sleep/wakeup queue priority */ #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(). */ #define SMB_DONTWAIT 0 #define SMB_NOINTR 0 #define SMB_WAIT 0x1 #define SMB_INTR 0x2 /* * callback index */ #define SMB_REQUEST_BUS 0x1 #define SMB_RELEASE_BUS 0x2 /* * SMB bus errors */ #define SMB_ENOERR 0x0 #define SMB_EBUSERR 0x1 #define SMB_ENOTSUPP 0x2 #define SMB_ENOACK 0x4 #define SMB_ECOLLI 0x8 #define SMB_EABORT 0x10 #define SMB_ETIMEOUT 0x20 #define SMB_EBUSY 0x40 #define SMB_EINVAL 0x100 /* * How Quick command is executed */ #define SMB_QWRITE 0x0 #define SMB_QREAD 0x1 /* * smbus transction op with pass-thru capabilities * * This smbus function is capable of doing a smbus command transaction * (read or write), and can be flagged to not issue the 'cmd' and/or * issue or expect a count field as well as flagged for chaining (no STOP), * which gives it an i2c pass-through capability. * * NOSTOP- Caller chaining transactions, do not issue STOP * NOCMD- Do not transmit the command field * NOCNT- Do not transmit (wr) or expect (rd) the count field */ #define SMB_TRANS_NOSTOP 0x0001 /* do not send STOP at end */ #define SMB_TRANS_NOCMD 0x0002 /* ignore cmd field (do not tx) */ #define SMB_TRANS_NOCNT 0x0004 /* do not tx or rx count field */ #define SMB_TRANS_7BIT 0x0008 /* change address mode to 7-bit */ #define SMB_TRANS_10BIT 0x0010 /* change address mode to 10-bit */ #define SMB_TRANS_NOREPORT 0x0020 /* do not report errors */ /* * ivars codes */ enum smbus_ivars { SMBUS_IVAR_ADDR, /* slave address of the device */ }; int smbus_request_bus(device_t, device_t, int); int smbus_release_bus(device_t, device_t); device_t smbus_alloc_bus(device_t); int smbus_error(int error); void smbus_intr(device_t, u_char, char low, char high, int error); #define SMBUS_ACCESSOR(var, ivar, type) \ __BUS_ACCESSOR(smbus, var, SMBUS, ivar, type) SMBUS_ACCESSOR(addr, ADDR, int) #undef SMBUS_ACCESSOR extern driver_t smbus_driver; extern devclass_t smbus_devclass; #define smbus_quick(bus,slave,how) \ (SMBUS_QUICK(device_get_parent(bus), slave, how)) #define smbus_sendb(bus,slave,byte) \ (SMBUS_SENDB(device_get_parent(bus), slave, byte)) #define smbus_recvb(bus,slave,byte) \ (SMBUS_RECVB(device_get_parent(bus), slave, byte)) #define smbus_writeb(bus,slave,cmd,byte) \ (SMBUS_WRITEB(device_get_parent(bus), slave, cmd, byte)) #define smbus_writew(bus,slave,cmd,word) \ (SMBUS_WRITEW(device_get_parent(bus), slave, cmd, word)) #define smbus_readb(bus,slave,cmd,byte) \ (SMBUS_READB(device_get_parent(bus), slave, cmd, byte)) #define smbus_readw(bus,slave,cmd,word) \ (SMBUS_READW(device_get_parent(bus), slave, cmd, word)) #define smbus_pcall(bus,slave,cmd,sdata,rdata) \ (SMBUS_PCALL(device_get_parent(bus), slave, cmd, sdata, rdata)) #define smbus_bwrite(bus,slave,cmd,count,buf) \ (SMBUS_BWRITE(device_get_parent(bus), slave, cmd, count, buf)) #define smbus_bread(bus,slave,cmd,count,buf) \ (SMBUS_BREAD(device_get_parent(bus), slave, cmd, count, buf)) #define smbus_trans(bus,slave,cmd,op,wbuf,wcount,rbuf,rcount,actualp) \ (SMBUS_TRANS(device_get_parent(bus), slave, cmd, op, \ wbuf, wcount, rbuf, rcount, actualp)) #define SMBUS_MODVER 1 #define SMBUS_MINVER 1 #define SMBUS_MAXVER 1 #define SMBUS_PREFVER SMBUS_MODVER #endif Index: head/sys/dev/smbus/smbus.c =================================================================== --- head/sys/dev/smbus/smbus.c (revision 307129) +++ head/sys/dev/smbus/smbus.c (revision 307130) @@ -1,203 +1,275 @@ /*- * Copyright (c) 1998, 2001 Nicolas Souchu * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * */ #include __FBSDID("$FreeBSD$"); #include #include #include +#include #include #include #include #include #include #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) { device_set_desc(dev, "System Management Bus"); return (0); } static int smbus_attach(device_t dev) { struct smbus_softc *sc = device_get_softc(dev); unsigned char addr; 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); } static int smbus_detach(device_t dev) { struct smbus_softc *sc = device_get_softc(dev); int error; error = bus_generic_detach(dev); if (error) return (error); + device_delete_children(dev); mtx_destroy(&sc->lock); return (0); } void 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); } 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); } 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, " 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);