Index: stable/10/sys/dev/iicbus/iicbb.c =================================================================== --- stable/10/sys/dev/iicbus/iicbb.c (revision 294489) +++ stable/10/sys/dev/iicbus/iicbb.c (revision 294490) @@ -1,471 +1,458 @@ /*- * 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$"); /* * Generic I2C bit-banging code * * Example: * * iicbus * / \ * iicbb pcf * | \ * bti2c lpbb * * From Linux I2C generic interface * (c) 1998 Gerd Knorr * */ #include "opt_platform.h" #include #include #include #include #include #include #ifdef FDT #include #include #include #endif #include #include #include #include "iicbus_if.h" #include "iicbb_if.h" struct iicbb_softc { device_t iicbus; int udelay; /* signal toggle delay in usec */ }; static int iicbb_attach(device_t); static void iicbb_child_detached(device_t, device_t); static int iicbb_detach(device_t); static int iicbb_print_child(device_t, device_t); static int iicbb_probe(device_t); static int iicbb_callback(device_t, int, caddr_t); static int iicbb_start(device_t, u_char, int); static int iicbb_stop(device_t); static int iicbb_write(device_t, const char *, int, int *, int); static int iicbb_read(device_t, char *, int, int *, int, int); static int iicbb_reset(device_t, u_char, u_char, u_char *); static int iicbb_transfer(device_t dev, struct iic_msg *msgs, uint32_t nmsgs); #ifdef FDT static phandle_t iicbb_get_node(device_t, device_t); #endif static device_method_t iicbb_methods[] = { /* device interface */ DEVMETHOD(device_probe, iicbb_probe), DEVMETHOD(device_attach, iicbb_attach), DEVMETHOD(device_detach, iicbb_detach), /* bus interface */ DEVMETHOD(bus_child_detached, iicbb_child_detached), DEVMETHOD(bus_print_child, iicbb_print_child), /* iicbus interface */ DEVMETHOD(iicbus_callback, iicbb_callback), DEVMETHOD(iicbus_start, iicbb_start), DEVMETHOD(iicbus_repeated_start, iicbb_start), DEVMETHOD(iicbus_stop, iicbb_stop), DEVMETHOD(iicbus_write, iicbb_write), DEVMETHOD(iicbus_read, iicbb_read), DEVMETHOD(iicbus_reset, iicbb_reset), DEVMETHOD(iicbus_transfer, iicbb_transfer), #ifdef FDT /* ofw_bus interface */ DEVMETHOD(ofw_bus_get_node, iicbb_get_node), #endif { 0, 0 } }; driver_t iicbb_driver = { "iicbb", iicbb_methods, sizeof(struct iicbb_softc), }; devclass_t iicbb_devclass; static int iicbb_probe(device_t dev) { device_set_desc(dev, "I2C bit-banging driver"); return (0); } static int iicbb_attach(device_t dev) { struct iicbb_softc *sc = (struct iicbb_softc *)device_get_softc(dev); sc->iicbus = device_add_child(dev, "iicbus", -1); if (!sc->iicbus) return (ENXIO); sc->udelay = 10; /* 10 uS default */ bus_generic_attach(dev); return (0); } static int iicbb_detach(device_t dev) { - struct iicbb_softc *sc = (struct iicbb_softc *)device_get_softc(dev); - device_t child; - /* - * We need to save child because the detach indirectly causes - * sc->iicbus to be zeroed. Since we added the device - * unconditionally in iicbb_attach, we need to make sure we - * delete it here. See iicbb_child_detached. We need that - * callback in case newbus detached our children w/o detaching - * us (say iicbus is a module and unloaded w/o iicbb being - * unloaded). - */ - child = sc->iicbus; bus_generic_detach(dev); - if (child) - device_delete_child(dev, child); + device_delete_children(dev); return (0); } #ifdef FDT static phandle_t iicbb_get_node(device_t bus, device_t dev) { /* We only have one child, the I2C bus, which needs our own node. */ return (ofw_bus_get_node(bus)); } #endif static void iicbb_child_detached( device_t dev, device_t child ) { struct iicbb_softc *sc = (struct iicbb_softc *)device_get_softc(dev); if (child == sc->iicbus) sc->iicbus = NULL; } static int iicbb_print_child(device_t bus, device_t dev) { int error; int retval = 0; u_char oldaddr; retval += bus_print_child_header(bus, dev); /* retrieve the interface I2C address */ error = IICBB_RESET(device_get_parent(bus), IIC_FASTEST, 0, &oldaddr); if (error == IIC_ENOADDR) { retval += printf(" on %s master-only\n", device_get_nameunit(bus)); } else { /* restore the address */ IICBB_RESET(device_get_parent(bus), IIC_FASTEST, oldaddr, NULL); retval += printf(" on %s addr 0x%x\n", device_get_nameunit(bus), oldaddr & 0xff); } return (retval); } #define I2C_SETSDA(sc,dev,val) do { \ IICBB_SETSDA(device_get_parent(dev), val); \ DELAY(sc->udelay); \ } while (0) #define I2C_SETSCL(dev,val) do { \ iicbb_setscl(dev, val, 100); \ } while (0) #define I2C_SET(sc,dev,ctrl,data) do { \ I2C_SETSCL(dev, ctrl); \ I2C_SETSDA(sc, dev, data); \ } while (0) #define I2C_GETSDA(dev) (IICBB_GETSDA(device_get_parent(dev))) #define I2C_GETSCL(dev) (IICBB_GETSCL(device_get_parent(dev))) static int i2c_debug = 0; #define I2C_DEBUG(x) do { \ if (i2c_debug) (x); \ } while (0) #define I2C_LOG(format,args...) do { \ printf(format, args); \ } while (0) static void iicbb_setscl(device_t dev, int val, int timeout) { struct iicbb_softc *sc = device_get_softc(dev); int k = 0; IICBB_SETSCL(device_get_parent(dev), val); DELAY(sc->udelay); while (val && !I2C_GETSCL(dev) && k++ < timeout) { IICBB_SETSCL(device_get_parent(dev), val); DELAY(sc->udelay); } return; } static void iicbb_one(device_t dev, int timeout) { struct iicbb_softc *sc = device_get_softc(dev); I2C_SET(sc,dev,0,1); I2C_SET(sc,dev,1,1); I2C_SET(sc,dev,0,1); return; } static void iicbb_zero(device_t dev, int timeout) { struct iicbb_softc *sc = device_get_softc(dev); I2C_SET(sc,dev,0,0); I2C_SET(sc,dev,1,0); I2C_SET(sc,dev,0,0); return; } /* * Waiting for ACKNOWLEDGE. * * When a chip is being addressed or has received data it will issue an * ACKNOWLEDGE pulse. Therefore the MASTER must release the DATA line * (set it to high level) and then release the CLOCK line. * Now it must wait for the SLAVE to pull the DATA line low. * Actually on the bus this looks like a START condition so nothing happens * because of the fact that the IC's that have not been addressed are doing * nothing. * * When the SLAVE has pulled this line low the MASTER will take the CLOCK * line low and then the SLAVE will release the SDA (data) line. */ static int iicbb_ack(device_t dev, int timeout) { struct iicbb_softc *sc = device_get_softc(dev); int noack; int k = 0; I2C_SET(sc,dev,0,1); I2C_SET(sc,dev,1,1); do { noack = I2C_GETSDA(dev); if (!noack) break; DELAY(1); k++; } while (k < timeout); I2C_SET(sc,dev,0,1); I2C_DEBUG(printf("%c ",noack?'-':'+')); return (noack); } static void iicbb_sendbyte(device_t dev, u_char data, int timeout) { int i; for (i=7; i>=0; i--) { if (data&(1<=0; i--) { I2C_SET(sc,dev,1,1); if (I2C_GETSDA(dev)) data |= (1<")); I2C_DEBUG(printf("\n")); return (0); } static int iicbb_write(device_t dev, const char *buf, int len, int *sent, int timeout) { int bytes, error = 0; bytes = 0; while (len) { /* send byte */ iicbb_sendbyte(dev,(u_char)*buf++, timeout); /* check for ack */ if (iicbb_ack(dev, timeout)) { error = IIC_ENOACK; goto error; } bytes ++; len --; } error: *sent = bytes; return (error); } static int iicbb_read(device_t dev, char * buf, int len, int *read, int last, int delay) { int bytes; bytes = 0; while (len) { /* XXX should insert delay here */ *buf++ = (char)iicbb_readbyte(dev, (len == 1) ? last : 0, delay); bytes ++; len --; } *read = bytes; return (0); } static int iicbb_transfer(device_t dev, struct iic_msg *msgs, uint32_t nmsgs) { int error; error = IICBB_PRE_XFER(device_get_parent(dev)); if (error) return (error); error = iicbus_transfer_gen(dev, msgs, nmsgs); IICBB_POST_XFER(device_get_parent(dev)); return (error); } DRIVER_MODULE(iicbus, iicbb, iicbus_driver, iicbus_devclass, 0, 0); MODULE_DEPEND(iicbb, iicbus, IICBUS_MINVER, IICBUS_PREFVER, IICBUS_MAXVER); MODULE_VERSION(iicbb, IICBB_MODVER); Index: stable/10/sys/dev/iicbus/iicbus.c =================================================================== --- stable/10/sys/dev/iicbus/iicbus.c (revision 294489) +++ stable/10/sys/dev/iicbus/iicbus.c (revision 294490) @@ -1,325 +1,326 @@ /*- * 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$"); /* * Autoconfiguration and support routines for the Philips serial I2C bus */ #include #include #include #include #include #include #include #include #include #include #include #include "iicbus_if.h" /* See comments below for why auto-scanning is a bad idea. */ #define SCAN_IICBUS 0 static int iicbus_probe(device_t dev) { device_set_desc(dev, "Philips I2C bus"); /* Allow other subclasses to override this driver. */ return (BUS_PROBE_GENERIC); } #if SCAN_IICBUS static int iic_probe_device(device_t dev, u_char addr) { int count; char byte; if ((addr & 1) == 0) { /* is device writable? */ if (!iicbus_start(dev, (u_char)addr, 0)) { iicbus_stop(dev); return (1); } } else { /* is device readable? */ if (!iicbus_block_read(dev, (u_char)addr, &byte, 1, &count)) return (1); } return (0); } #endif /* * We add all the devices which we know about. * The generic attach routine will attach them if they are alive. */ static int iicbus_attach(device_t dev) { #if SCAN_IICBUS unsigned char addr; #endif struct iicbus_softc *sc = IICBUS_SOFTC(dev); int strict; sc->dev = dev; mtx_init(&sc->lock, "iicbus", NULL, MTX_DEF); iicbus_init_frequency(dev, 0); iicbus_reset(dev, IIC_FASTEST, 0, NULL); if (resource_int_value(device_get_name(dev), device_get_unit(dev), "strict", &strict) == 0) sc->strict = strict; else sc->strict = 1; /* device probing is meaningless since the bus is supposed to be * hot-plug. Moreover, some I2C chips do not appreciate random * accesses like stop after start to fast, reads for less than * x bytes... */ #if SCAN_IICBUS printf("Probing for devices on iicbus%d:", device_get_unit(dev)); /* probe any devices */ for (addr = 16; addr < 240; addr++) { if (iic_probe_device(dev, (u_char)addr)) { printf(" <%x>", addr); } } printf("\n"); #endif bus_generic_probe(dev); bus_enumerate_hinted_children(dev); bus_generic_attach(dev); return (0); } static int iicbus_detach(device_t dev) { struct iicbus_softc *sc = IICBUS_SOFTC(dev); iicbus_reset(dev, IIC_FASTEST, 0, NULL); bus_generic_detach(dev); + device_delete_children(dev); mtx_destroy(&sc->lock); return (0); } static int iicbus_print_child(device_t dev, device_t child) { struct iicbus_ivar *devi = IICBUS_IVAR(child); int retval = 0; retval += bus_print_child_header(dev, child); if (devi->addr != 0) retval += printf(" at addr %#x", devi->addr); retval += bus_print_child_footer(dev, child); return (retval); } static void iicbus_probe_nomatch(device_t bus, device_t child) { struct iicbus_ivar *devi = IICBUS_IVAR(child); device_printf(bus, ""); printf(" at addr %#x\n", devi->addr); return; } static int iicbus_child_location_str(device_t bus, device_t child, char *buf, size_t buflen) { struct iicbus_ivar *devi = IICBUS_IVAR(child); snprintf(buf, buflen, "addr=%#x", devi->addr); return (0); } static int iicbus_child_pnpinfo_str(device_t bus, device_t child, char *buf, size_t buflen) { *buf = '\0'; return (0); } static int iicbus_read_ivar(device_t bus, device_t child, int which, uintptr_t *result) { struct iicbus_ivar *devi = IICBUS_IVAR(child); switch (which) { default: return (EINVAL); case IICBUS_IVAR_ADDR: *result = devi->addr; break; } return (0); } static device_t iicbus_add_child(device_t dev, u_int order, const char *name, int unit) { device_t child; struct iicbus_ivar *devi; child = device_add_child_ordered(dev, order, name, unit); if (child == NULL) return (child); devi = malloc(sizeof(struct iicbus_ivar), M_DEVBUF, M_NOWAIT | M_ZERO); if (devi == NULL) { device_delete_child(dev, child); return (0); } device_set_ivars(child, devi); return (child); } static void iicbus_hinted_child(device_t bus, const char *dname, int dunit) { device_t child; struct iicbus_ivar *devi; child = BUS_ADD_CHILD(bus, 0, dname, dunit); devi = IICBUS_IVAR(child); resource_int_value(dname, dunit, "addr", &devi->addr); } int iicbus_generic_intr(device_t dev, int event, char *buf) { return (0); } int iicbus_null_callback(device_t dev, int index, caddr_t data) { return (0); } int iicbus_null_repeated_start(device_t dev, u_char addr) { return (IIC_ENOTSUPP); } void iicbus_init_frequency(device_t dev, u_int bus_freq) { struct iicbus_softc *sc = IICBUS_SOFTC(dev); /* * If a bus frequency value was passed in, use it. Otherwise initialize * it first to the standard i2c 100KHz frequency, then override that * from a hint if one exists. */ if (bus_freq > 0) sc->bus_freq = bus_freq; else { sc->bus_freq = 100000; resource_int_value(device_get_name(dev), device_get_unit(dev), "frequency", (int *)&sc->bus_freq); } /* * Set up the sysctl that allows the bus frequency to be changed. * It is flagged as a tunable so that the user can set the value in * loader(8), and that will override any other setting from any source. * The sysctl tunable/value is the one most directly controlled by the * user and thus the one that always takes precedence. */ SYSCTL_ADD_UINT(device_get_sysctl_ctx(dev), SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, "frequency", CTLFLAG_RW | CTLFLAG_TUN, &sc->bus_freq, sc->bus_freq, "Bus frequency in Hz"); } static u_int iicbus_get_frequency(device_t dev, u_char speed) { struct iicbus_softc *sc = IICBUS_SOFTC(dev); /* * If the frequency has not been configured for the bus, or the request * is specifically for SLOW speed, use the standard 100KHz rate, else * use the configured bus speed. */ if (sc->bus_freq == 0 || speed == IIC_SLOW) return (100000); return (sc->bus_freq); } static device_method_t iicbus_methods[] = { /* device interface */ DEVMETHOD(device_probe, iicbus_probe), DEVMETHOD(device_attach, iicbus_attach), DEVMETHOD(device_detach, iicbus_detach), /* bus interface */ DEVMETHOD(bus_add_child, iicbus_add_child), DEVMETHOD(bus_print_child, iicbus_print_child), DEVMETHOD(bus_probe_nomatch, iicbus_probe_nomatch), DEVMETHOD(bus_read_ivar, iicbus_read_ivar), DEVMETHOD(bus_child_pnpinfo_str, iicbus_child_pnpinfo_str), DEVMETHOD(bus_child_location_str, iicbus_child_location_str), DEVMETHOD(bus_hinted_child, iicbus_hinted_child), /* iicbus interface */ DEVMETHOD(iicbus_transfer, iicbus_transfer), DEVMETHOD(iicbus_get_frequency, iicbus_get_frequency), DEVMETHOD_END }; driver_t iicbus_driver = { "iicbus", iicbus_methods, sizeof(struct iicbus_softc), }; devclass_t iicbus_devclass; MODULE_VERSION(iicbus, IICBUS_MODVER); DRIVER_MODULE(iicbus, iichb, iicbus_driver, iicbus_devclass, 0, 0); Index: stable/10/sys/dev/iicbus/iicoc.c =================================================================== --- stable/10/sys/dev/iicbus/iicoc.c (revision 294489) +++ stable/10/sys/dev/iicbus/iicoc.c (revision 294490) @@ -1,390 +1,391 @@ /*- * Copyright (c) 2003-2012 Broadcom Corporation * 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 BROADCOM ``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 BROADCOM 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 #include #include #include #include #include "iicbus_if.h" static devclass_t iicoc_devclass; /* * Device methods */ static int iicoc_probe(device_t); static int iicoc_attach(device_t); static int iicoc_detach(device_t); static int iicoc_start(device_t dev, u_char slave, int timeout); static int iicoc_stop(device_t dev); static int iicoc_read(device_t dev, char *buf, int len, int *read, int last, int delay); static int iicoc_write(device_t dev, const char *buf, int len, int *sent, int timeout); static int iicoc_repeated_start(device_t dev, u_char slave, int timeout); struct iicoc_softc { device_t dev; /* Self */ u_int reg_shift; /* Chip specific */ u_int clockfreq; u_int i2cfreq; struct resource *mem_res; /* Memory resource */ int mem_rid; int sc_started; uint8_t i2cdev_addr; device_t iicbus; struct mtx sc_mtx; }; static void iicoc_dev_write(device_t dev, int reg, int value) { struct iicoc_softc *sc; sc = device_get_softc(dev); bus_write_1(sc->mem_res, reg<reg_shift, value); } static int iicoc_dev_read(device_t dev, int reg) { uint8_t val; struct iicoc_softc *sc; sc = device_get_softc(dev); val = bus_read_1(sc->mem_res, reg<reg_shift); return (val); } static int iicoc_wait_on_status(device_t dev, uint8_t bit) { int tries = I2C_TIMEOUT; uint8_t status; do { status = iicoc_dev_read(dev, OC_I2C_STATUS_REG); } while ((status & bit) != 0 && --tries > 0); return (tries == 0 ? -1: 0); } static int iicoc_rd_cmd(device_t dev, uint8_t cmd) { uint8_t data; iicoc_dev_write(dev, OC_I2C_CMD_REG, cmd); if (iicoc_wait_on_status(dev, OC_STATUS_TIP) < 0) { device_printf(dev, "read: Timeout waiting for TIP clear.\n"); return (-1); } data = iicoc_dev_read(dev, OC_I2C_DATA_REG); return (data); } static int iicoc_wr_cmd(device_t dev, uint8_t data, uint8_t cmd) { iicoc_dev_write(dev, OC_I2C_DATA_REG, data); iicoc_dev_write(dev, OC_I2C_CMD_REG, cmd); if (iicoc_wait_on_status(dev, OC_STATUS_TIP) < 0) { device_printf(dev, "write: Timeout waiting for TIP clear.\n"); return (-1); } return (0); } static int iicoc_wr_ack_cmd(device_t dev, uint8_t data, uint8_t cmd) { if (iicoc_wr_cmd(dev, data, cmd) < 0) return (-1); if (iicoc_dev_read(dev, OC_I2C_STATUS_REG) & OC_STATUS_NACK) { device_printf(dev, "write: I2C command ACK Error.\n"); return (IIC_ENOACK); } return (0); } static int iicoc_init(device_t dev) { struct iicoc_softc *sc; int value; sc = device_get_softc(dev); value = iicoc_dev_read(dev, OC_I2C_CTRL_REG); iicoc_dev_write(dev, OC_I2C_CTRL_REG, value & ~(OC_CONTROL_EN | OC_CONTROL_IEN)); value = (sc->clockfreq/(5 * sc->i2cfreq)) - 1; iicoc_dev_write(dev, OC_I2C_PRESCALE_LO_REG, value & 0xff); iicoc_dev_write(dev, OC_I2C_PRESCALE_HI_REG, value >> 8); value = iicoc_dev_read(dev, OC_I2C_CTRL_REG); iicoc_dev_write(dev, OC_I2C_CTRL_REG, value | OC_CONTROL_EN); value = iicoc_dev_read(dev, OC_I2C_CTRL_REG); /* return 0 on success, 1 on error */ return ((value & OC_CONTROL_EN) == 0); } static int iicoc_probe(device_t dev) { struct iicoc_softc *sc; sc = device_get_softc(dev); if ((pci_get_vendor(dev) == 0x184e) && (pci_get_device(dev) == 0x1011)) { sc->clockfreq = XLP_I2C_CLKFREQ; sc->i2cfreq = XLP_I2C_FREQ; sc->reg_shift = 2; device_set_desc(dev, "Netlogic XLP I2C Controller"); return (BUS_PROBE_DEFAULT); } return (ENXIO); } /* * We add all the devices which we know about. * The generic attach routine will attach them if they are alive. */ static int iicoc_attach(device_t dev) { int bus; struct iicoc_softc *sc; sc = device_get_softc(dev); bus = device_get_unit(dev); sc->dev = dev; mtx_init(&sc->sc_mtx, "iicoc", "iicoc", MTX_DEF); sc->mem_rid = 0; sc->mem_res = bus_alloc_resource(dev, SYS_RES_MEMORY, &sc->mem_rid, 0ul, ~0ul, 0x100, RF_ACTIVE); if (sc->mem_res == NULL) { device_printf(dev, "Could not allocate bus resource.\n"); return (-1); } iicoc_init(dev); sc->iicbus = device_add_child(dev, "iicbus", -1); if (sc->iicbus == NULL) { device_printf(dev, "Could not allocate iicbus instance.\n"); return (-1); } bus_generic_attach(dev); return (0); } static int iicoc_detach(device_t dev) { bus_generic_detach(dev); + device_delete_children(dev); return (0); } static int iicoc_start(device_t dev, u_char slave, int timeout) { int error = IIC_EBUSERR; struct iicoc_softc *sc; sc = device_get_softc(dev); mtx_lock(&sc->sc_mtx); sc->i2cdev_addr = (slave >> 1); /* Verify the bus is idle */ if (iicoc_wait_on_status(dev, OC_STATUS_BUSY) < 0) goto i2c_stx_error; /* Write Slave Address */ if (iicoc_wr_ack_cmd(dev, slave, OC_COMMAND_START)) { device_printf(dev, "I2C write slave address [0x%x] failed.\n", slave); error = IIC_ENOACK; goto i2c_stx_error; } /* Verify Arbitration is not Lost */ if (iicoc_dev_read(dev, OC_I2C_STATUS_REG) & OC_STATUS_AL) { device_printf(dev, "I2C Bus Arbitration Lost, Aborting.\n"); error = IIC_EBUSERR; goto i2c_stx_error; } error = IIC_NOERR; mtx_unlock(&sc->sc_mtx); return (error); i2c_stx_error: iicoc_dev_write(dev, OC_I2C_CMD_REG, OC_COMMAND_STOP); iicoc_wait_on_status(dev, OC_STATUS_BUSY); /* wait for idle */ mtx_unlock(&sc->sc_mtx); return (error); } static int iicoc_stop(device_t dev) { int error = 0; struct iicoc_softc *sc; sc = device_get_softc(dev); mtx_lock(&sc->sc_mtx); iicoc_dev_write(dev, OC_I2C_CMD_REG, OC_COMMAND_STOP); iicoc_wait_on_status(dev, OC_STATUS_BUSY); /* wait for idle */ mtx_unlock(&sc->sc_mtx); return (error); } static int iicoc_write(device_t dev, const char *buf, int len, int *sent, int timeout /* us */ ) { uint8_t value; int i; value = buf[0]; /* Write Slave Offset */ if (iicoc_wr_ack_cmd(dev, value, OC_COMMAND_WRITE)) { device_printf(dev, "I2C write slave offset failed.\n"); goto i2c_tx_error; } for (i = 1; i < len; i++) { /* Write data byte */ value = buf[i]; if (iicoc_wr_cmd(dev, value, OC_COMMAND_WRITE)) { device_printf(dev, "I2C write data byte %d failed.\n", i); goto i2c_tx_error; } } *sent = len; return (IIC_NOERR); i2c_tx_error: return (IIC_EBUSERR); } static int iicoc_read(device_t dev, char *buf, int len, int *read, int last, int delay) { int data, i; uint8_t cmd; for (i = 0; i < len; i++) { /* Read data byte */ cmd = (i == len - 1) ? OC_COMMAND_RDNACK : OC_COMMAND_READ; data = iicoc_rd_cmd(dev, cmd); if (data < 0) { device_printf(dev, "I2C read data byte %d failed.\n", i); goto i2c_rx_error; } buf[i] = (uint8_t)data; } *read = len; return (IIC_NOERR); i2c_rx_error: return (IIC_EBUSERR); } static int iicoc_reset(device_t dev, u_char speed, u_char addr, u_char *oldadr) { int error; struct iicoc_softc *sc; sc = device_get_softc(dev); mtx_lock(&sc->sc_mtx); error = iicoc_init(dev); mtx_unlock(&sc->sc_mtx); return (error); } static int iicoc_repeated_start(device_t dev, u_char slave, int timeout) { return 0; } static device_method_t iicoc_methods[] = { /* device interface */ DEVMETHOD(device_probe, iicoc_probe), DEVMETHOD(device_attach, iicoc_attach), DEVMETHOD(device_detach, iicoc_detach), /* iicbus interface */ DEVMETHOD(iicbus_callback, iicbus_null_callback), DEVMETHOD(iicbus_repeated_start, iicoc_repeated_start), DEVMETHOD(iicbus_start, iicoc_start), DEVMETHOD(iicbus_stop, iicoc_stop), DEVMETHOD(iicbus_reset, iicoc_reset), DEVMETHOD(iicbus_write, iicoc_write), DEVMETHOD(iicbus_read, iicoc_read), DEVMETHOD(iicbus_transfer, iicbus_transfer_gen), DEVMETHOD_END }; static driver_t iicoc_driver = { "iicoc", iicoc_methods, sizeof(struct iicoc_softc), }; DRIVER_MODULE(iicoc, pci, iicoc_driver, iicoc_devclass, 0, 0); DRIVER_MODULE(iicbus, iicoc, iicbus_driver, iicbus_devclass, 0, 0); Index: stable/10/sys/dev/iicbus/iicsmb.c =================================================================== --- stable/10/sys/dev/iicbus/iicsmb.c (revision 294489) +++ stable/10/sys/dev/iicbus/iicsmb.c (revision 294490) @@ -1,518 +1,516 @@ /*- * 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$"); /* * I2C to SMB bridge * * Example: * * smb bttv * \ / * smbus * / \ * iicsmb bti2c * | * iicbus * / | \ * iicbb pcf ... * | * lpbb */ #include #include #include #include #include #include #include #include #include #include #include #include "iicbus_if.h" #include "smbus_if.h" struct iicsmb_softc { #define SMB_WAITING_ADDR 0x0 #define SMB_WAITING_LOW 0x1 #define SMB_WAITING_HIGH 0x2 #define SMB_DONE 0x3 int state; u_char devaddr; /* slave device address */ char low; /* low byte received first */ char high; /* high byte */ struct mtx lock; device_t smbus; }; static int iicsmb_probe(device_t); static int iicsmb_attach(device_t); static int iicsmb_detach(device_t); static void iicsmb_identify(driver_t *driver, device_t parent); static int iicsmb_intr(device_t dev, int event, char *buf); static int iicsmb_callback(device_t dev, int index, void *data); static int iicsmb_quick(device_t dev, u_char slave, int how); static int iicsmb_sendb(device_t dev, u_char slave, char byte); static int iicsmb_recvb(device_t dev, u_char slave, char *byte); static int iicsmb_writeb(device_t dev, u_char slave, char cmd, char byte); static int iicsmb_writew(device_t dev, u_char slave, char cmd, short word); static int iicsmb_readb(device_t dev, u_char slave, char cmd, char *byte); static int iicsmb_readw(device_t dev, u_char slave, char cmd, short *word); static int iicsmb_pcall(device_t dev, u_char slave, char cmd, short sdata, short *rdata); static int iicsmb_bwrite(device_t dev, u_char slave, char cmd, u_char count, char *buf); static int iicsmb_bread(device_t dev, u_char slave, char cmd, u_char *count, char *buf); static devclass_t iicsmb_devclass; static device_method_t iicsmb_methods[] = { /* device interface */ DEVMETHOD(device_identify, iicsmb_identify), DEVMETHOD(device_probe, iicsmb_probe), DEVMETHOD(device_attach, iicsmb_attach), DEVMETHOD(device_detach, iicsmb_detach), /* iicbus interface */ DEVMETHOD(iicbus_intr, iicsmb_intr), /* smbus interface */ DEVMETHOD(smbus_callback, iicsmb_callback), DEVMETHOD(smbus_quick, iicsmb_quick), DEVMETHOD(smbus_sendb, iicsmb_sendb), DEVMETHOD(smbus_recvb, iicsmb_recvb), DEVMETHOD(smbus_writeb, iicsmb_writeb), DEVMETHOD(smbus_writew, iicsmb_writew), DEVMETHOD(smbus_readb, iicsmb_readb), DEVMETHOD(smbus_readw, iicsmb_readw), DEVMETHOD(smbus_pcall, iicsmb_pcall), DEVMETHOD(smbus_bwrite, iicsmb_bwrite), DEVMETHOD(smbus_bread, iicsmb_bread), DEVMETHOD_END }; static driver_t iicsmb_driver = { "iicsmb", iicsmb_methods, sizeof(struct iicsmb_softc), }; #define IICBUS_TIMEOUT 100 /* us */ static void iicsmb_identify(driver_t *driver, device_t parent) { if (device_find_child(parent, "iicsmb", -1) == NULL) BUS_ADD_CHILD(parent, 0, "iicsmb", -1); } static int iicsmb_probe(device_t dev) { device_set_desc(dev, "SMBus over I2C bridge"); return (BUS_PROBE_NOWILDCARD); } static int iicsmb_attach(device_t dev) { struct iicsmb_softc *sc = (struct iicsmb_softc *)device_get_softc(dev); mtx_init(&sc->lock, "iicsmb", NULL, MTX_DEF); sc->smbus = device_add_child(dev, "smbus", -1); /* probe and attach the smbus */ bus_generic_attach(dev); return (0); } static int iicsmb_detach(device_t dev) { struct iicsmb_softc *sc = (struct iicsmb_softc *)device_get_softc(dev); - + bus_generic_detach(dev); - if (sc->smbus) { - device_delete_child(dev, sc->smbus); - } + device_delete_children(dev); mtx_destroy(&sc->lock); return (0); } /* * iicsmb_intr() * * iicbus interrupt handler */ static int iicsmb_intr(device_t dev, int event, char *buf) { struct iicsmb_softc *sc = (struct iicsmb_softc *)device_get_softc(dev); mtx_lock(&sc->lock); switch (event) { case INTR_GENERAL: case INTR_START: sc->state = SMB_WAITING_ADDR; break; case INTR_STOP: /* call smbus intr handler */ smbus_intr(sc->smbus, sc->devaddr, sc->low, sc->high, SMB_ENOERR); break; case INTR_RECEIVE: switch (sc->state) { case SMB_DONE: /* XXX too much data, discard */ printf("%s: too much data from 0x%x\n", __func__, sc->devaddr & 0xff); goto end; case SMB_WAITING_ADDR: sc->devaddr = (u_char)*buf; sc->state = SMB_WAITING_LOW; break; case SMB_WAITING_LOW: sc->low = *buf; sc->state = SMB_WAITING_HIGH; break; case SMB_WAITING_HIGH: sc->high = *buf; sc->state = SMB_DONE; break; } end: break; case INTR_TRANSMIT: case INTR_NOACK: break; case INTR_ERROR: switch (*buf) { case IIC_EBUSERR: smbus_intr(sc->smbus, sc->devaddr, 0, 0, SMB_EBUSERR); break; default: printf("%s unknown error 0x%x!\n", __func__, (int)*buf); break; } break; default: panic("%s: unknown event (%d)!", __func__, event); } mtx_unlock(&sc->lock); return (0); } static int iicsmb_callback(device_t dev, int index, void *data) { device_t parent = device_get_parent(dev); int error = 0; int how; switch (index) { case SMB_REQUEST_BUS: /* request underlying iicbus */ how = *(int *)data; error = iicbus_request_bus(parent, dev, how); break; case SMB_RELEASE_BUS: /* release underlying iicbus */ error = iicbus_release_bus(parent, dev); break; default: error = EINVAL; } return (error); } static int iicsmb_quick(device_t dev, u_char slave, int how) { device_t parent = device_get_parent(dev); int error; switch (how) { case SMB_QWRITE: error = iicbus_start(parent, slave & ~LSB, IICBUS_TIMEOUT); break; case SMB_QREAD: error = iicbus_start(parent, slave | LSB, IICBUS_TIMEOUT); break; default: error = EINVAL; break; } if (!error) error = iicbus_stop(parent); return (error); } static int iicsmb_sendb(device_t dev, u_char slave, char byte) { device_t parent = device_get_parent(dev); int error, sent; error = iicbus_start(parent, slave & ~LSB, IICBUS_TIMEOUT); if (!error) { error = iicbus_write(parent, &byte, 1, &sent, IICBUS_TIMEOUT); iicbus_stop(parent); } return (error); } static int iicsmb_recvb(device_t dev, u_char slave, char *byte) { device_t parent = device_get_parent(dev); int error, read; error = iicbus_start(parent, slave | LSB, 0); if (!error) { error = iicbus_read(parent, byte, 1, &read, IIC_LAST_READ, IICBUS_TIMEOUT); iicbus_stop(parent); } return (error); } static int iicsmb_writeb(device_t dev, u_char slave, char cmd, char byte) { device_t parent = device_get_parent(dev); int error, sent; error = iicbus_start(parent, slave & ~LSB, 0); if (!error) { if (!(error = iicbus_write(parent, &cmd, 1, &sent, IICBUS_TIMEOUT))) error = iicbus_write(parent, &byte, 1, &sent, IICBUS_TIMEOUT); iicbus_stop(parent); } return (error); } static int iicsmb_writew(device_t dev, u_char slave, char cmd, short word) { device_t parent = device_get_parent(dev); int error, sent; char low = (char)(word & 0xff); char high = (char)((word & 0xff00) >> 8); error = iicbus_start(parent, slave & ~LSB, 0); if (!error) { if (!(error = iicbus_write(parent, &cmd, 1, &sent, IICBUS_TIMEOUT))) if (!(error = iicbus_write(parent, &low, 1, &sent, IICBUS_TIMEOUT))) error = iicbus_write(parent, &high, 1, &sent, IICBUS_TIMEOUT); iicbus_stop(parent); } return (error); } static int iicsmb_readb(device_t dev, u_char slave, char cmd, char *byte) { device_t parent = device_get_parent(dev); int error, sent, read; if ((error = iicbus_start(parent, slave & ~LSB, IICBUS_TIMEOUT))) return (error); if ((error = iicbus_write(parent, &cmd, 1, &sent, IICBUS_TIMEOUT))) goto error; if ((error = iicbus_repeated_start(parent, slave | LSB, IICBUS_TIMEOUT))) goto error; if ((error = iicbus_read(parent, byte, 1, &read, IIC_LAST_READ, IICBUS_TIMEOUT))) goto error; error: iicbus_stop(parent); return (error); } #define BUF2SHORT(low,high) \ ((short)(((high) & 0xff) << 8) | (short)((low) & 0xff)) static int iicsmb_readw(device_t dev, u_char slave, char cmd, short *word) { device_t parent = device_get_parent(dev); int error, sent, read; char buf[2]; if ((error = iicbus_start(parent, slave & ~LSB, IICBUS_TIMEOUT))) return (error); if ((error = iicbus_write(parent, &cmd, 1, &sent, IICBUS_TIMEOUT))) goto error; if ((error = iicbus_repeated_start(parent, slave | LSB, IICBUS_TIMEOUT))) goto error; if ((error = iicbus_read(parent, buf, 2, &read, IIC_LAST_READ, IICBUS_TIMEOUT))) goto error; /* first, receive low, then high byte */ *word = BUF2SHORT(buf[0], buf[1]); error: iicbus_stop(parent); return (error); } static int iicsmb_pcall(device_t dev, u_char slave, char cmd, short sdata, short *rdata) { device_t parent = device_get_parent(dev); int error, sent, read; char buf[2]; if ((error = iicbus_start(parent, slave & ~LSB, IICBUS_TIMEOUT))) return (error); if ((error = iicbus_write(parent, &cmd, 1, &sent, IICBUS_TIMEOUT))) goto error; /* first, send low, then high byte */ buf[0] = (char)(sdata & 0xff); buf[1] = (char)((sdata & 0xff00) >> 8); if ((error = iicbus_write(parent, buf, 2, &sent, IICBUS_TIMEOUT))) goto error; if ((error = iicbus_repeated_start(parent, slave | LSB, IICBUS_TIMEOUT))) goto error; if ((error = iicbus_read(parent, buf, 2, &read, IIC_LAST_READ, IICBUS_TIMEOUT))) goto error; /* first, receive low, then high byte */ *rdata = BUF2SHORT(buf[0], buf[1]); error: iicbus_stop(parent); return (error); } static int iicsmb_bwrite(device_t dev, u_char slave, char cmd, u_char count, char *buf) { device_t parent = device_get_parent(dev); int error, sent; if ((error = iicbus_start(parent, slave & ~LSB, IICBUS_TIMEOUT))) goto error; if ((error = iicbus_write(parent, &cmd, 1, &sent, IICBUS_TIMEOUT))) goto error; if ((error = iicbus_write(parent, buf, (int)count, &sent, IICBUS_TIMEOUT))) goto error; if ((error = iicbus_stop(parent))) goto error; error: return (error); } static int iicsmb_bread(device_t dev, u_char slave, char cmd, u_char *count, char *buf) { device_t parent = device_get_parent(dev); int error, sent, read; if ((error = iicbus_start(parent, slave & ~LSB, IICBUS_TIMEOUT))) return (error); if ((error = iicbus_write(parent, &cmd, 1, &sent, IICBUS_TIMEOUT))) goto error; if ((error = iicbus_repeated_start(parent, slave | LSB, IICBUS_TIMEOUT))) goto error; if ((error = iicbus_read(parent, buf, (int)*count, &read, IIC_LAST_READ, IICBUS_TIMEOUT))) goto error; *count = read; error: iicbus_stop(parent); return (error); } DRIVER_MODULE(iicsmb, iicbus, iicsmb_driver, iicsmb_devclass, 0, 0); DRIVER_MODULE(smbus, iicsmb, smbus_driver, smbus_devclass, 0, 0); MODULE_DEPEND(iicsmb, iicbus, IICBUS_MINVER, IICBUS_PREFVER, IICBUS_MAXVER); MODULE_DEPEND(iicsmb, smbus, SMBUS_MINVER, SMBUS_PREFVER, SMBUS_MAXVER); MODULE_VERSION(iicsmb, 1); Index: stable/10 =================================================================== --- stable/10 (revision 294489) +++ stable/10 (revision 294490) Property changes on: stable/10 ___________________________________________________________________ Modified: svn:mergeinfo ## -0,0 +0,1 ## Merged /head:r289657