Index: stable/12/sys/dev/iicbus/iicbb.c =================================================================== --- stable/12/sys/dev/iicbus/iicbb.c (revision 366356) +++ stable/12/sys/dev/iicbus/iicbb.c (revision 366357) @@ -1,601 +1,601 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * 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 #include #ifdef FDT #include #include #include #endif #include #include #include #include "iicbus_if.h" #include "iicbb_if.h" /* Based on the SMBus specification. */ #define DEFAULT_SCL_LOW_TIMEOUT (25 * 1000) struct iicbb_softc { device_t iicbus; u_int udelay; /* signal toggle delay in usec */ u_int io_latency; /* approximate pin toggling latency */ u_int scl_low_timeout; }; 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_repstart(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); static void iicbb_set_speed(struct iicbb_softc *sc, u_char); #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_repstart), 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->scl_low_timeout = DEFAULT_SCL_LOW_TIMEOUT; SYSCTL_ADD_UINT(device_get_sysctl_ctx(dev), SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, "delay", CTLFLAG_RD, &sc->udelay, 0, "Signal change delay controlled by bus frequency, microseconds"); SYSCTL_ADD_UINT(device_get_sysctl_ctx(dev), SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, "scl_low_timeout", CTLFLAG_RWTUN, &sc->scl_low_timeout, 0, "SCL low timeout, microseconds"); SYSCTL_ADD_UINT(device_get_sysctl_ctx(dev), SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, "io_latency", CTLFLAG_RWTUN, &sc->io_latency, 0, "Estimate of pin toggling latency, microseconds"); bus_generic_attach(dev); return (0); } static int iicbb_detach(device_t dev) { bus_generic_detach(dev); 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 IICBB_DEBUG #ifdef IICBB_DEBUG static int i2c_debug = 0; -static SYSCTL_NODE(_hw, OID_AUTO, i2c, CTLFLAG_RW, 0, "i2c debug"); +SYSCTL_DECL(_hw_i2c); SYSCTL_INT(_hw_i2c, OID_AUTO, iicbb_debug, CTLFLAG_RWTUN, &i2c_debug, 0, "Enable i2c bit-banging driver debug"); #define I2C_DEBUG(x) do { \ if (i2c_debug) (x); \ } while (0) #else #define I2C_DEBUG(x) #endif #define I2C_GETSDA(dev) (IICBB_GETSDA(device_get_parent(dev))) #define I2C_SETSDA(dev, x) (IICBB_SETSDA(device_get_parent(dev), x)) #define I2C_GETSCL(dev) (IICBB_GETSCL(device_get_parent(dev))) #define I2C_SETSCL(dev, x) (IICBB_SETSCL(device_get_parent(dev), x)) static int iicbb_waitforscl(device_t dev) { struct iicbb_softc *sc = device_get_softc(dev); sbintime_t fast_timeout; sbintime_t now, timeout; /* Spin for up to 1 ms, then switch to pause. */ now = sbinuptime(); fast_timeout = now + SBT_1MS; timeout = now + sc->scl_low_timeout * SBT_1US; do { if (I2C_GETSCL(dev)) return (0); now = sbinuptime(); } while (now < fast_timeout); do { I2C_DEBUG(printf(".")); pause_sbt("iicbb-scl-low", SBT_1MS, C_PREL(8), 0); if (I2C_GETSCL(dev)) return (0); now = sbinuptime(); } while (now < timeout); I2C_DEBUG(printf("*")); return (IIC_ETIMEOUT); } /* Start the high phase of the clock. */ static int iicbb_clockin(device_t dev, int sda) { /* * Precondition: SCL is low. * Action: * - set SDA to the value; * - release SCL and wait until it's high. * The caller is responsible for keeping SCL high for udelay. * * There should be a data set-up time, 250 ns minimum, between setting * SDA and raising SCL. It's expected that the I/O access latency will * naturally provide that delay. */ I2C_SETSDA(dev, sda); I2C_SETSCL(dev, 1); return (iicbb_waitforscl(dev)); } /* * End the high phase of the clock and wait out the low phase * as nothing interesting happens during it anyway. */ static void iicbb_clockout(device_t dev) { struct iicbb_softc *sc = device_get_softc(dev); /* * Precondition: SCL is high. * Action: * - pull SCL low and hold for udelay. */ I2C_SETSCL(dev, 0); DELAY(sc->udelay); } static int iicbb_sendbit(device_t dev, int bit) { struct iicbb_softc *sc = device_get_softc(dev); int err; err = iicbb_clockin(dev, bit); if (err != 0) return (err); DELAY(sc->udelay); iicbb_clockout(dev); return (0); } /* * 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_getack(device_t dev) { struct iicbb_softc *sc = device_get_softc(dev); int noack, err; int t; /* Release SDA so that the slave can drive it. */ err = iicbb_clockin(dev, 1); if (err != 0) { I2C_DEBUG(printf("! ")); return (err); } /* Sample SDA until ACK (low) or udelay runs out. */ for (t = 0; t < sc->udelay; t++) { noack = I2C_GETSDA(dev); if (!noack) break; DELAY(1); } DELAY(sc->udelay - t); iicbb_clockout(dev); I2C_DEBUG(printf("%c ", noack ? '-' : '+')); return (noack ? IIC_ENOACK : 0); } static int iicbb_sendbyte(device_t dev, uint8_t data) { int err, i; for (i = 7; i >= 0; i--) { err = iicbb_sendbit(dev, (data & (1 << i)) != 0); if (err != 0) { I2C_DEBUG(printf("w!")); return (err); } } I2C_DEBUG(printf("w%02x", data)); return (0); } static int iicbb_readbyte(device_t dev, bool last, uint8_t *data) { struct iicbb_softc *sc = device_get_softc(dev); int i, err; /* * Release SDA so that the slave can drive it. * We do not use iicbb_clockin() here because we need to release SDA * only once and then we just pulse the SCL. */ *data = 0; I2C_SETSDA(dev, 1); for (i = 7; i >= 0; i--) { I2C_SETSCL(dev, 1); err = iicbb_waitforscl(dev); if (err != 0) { I2C_DEBUG(printf("r! ")); return (err); } DELAY((sc->udelay + 1) / 2); if (I2C_GETSDA(dev)) *data |= 1 << i; DELAY((sc->udelay + 1) / 2); iicbb_clockout(dev); } /* * Send master->slave ACK (low) for more data, * NoACK (high) otherwise. */ iicbb_sendbit(dev, last); I2C_DEBUG(printf("r%02x%c ", *data, last ? '-' : '+')); return (0); } static int iicbb_callback(device_t dev, int index, caddr_t data) { return (IICBB_CALLBACK(device_get_parent(dev), index, data)); } static int iicbb_reset(device_t dev, u_char speed, u_char addr, u_char *oldaddr) { iicbb_set_speed(device_get_softc(dev), speed); return (IICBB_RESET(device_get_parent(dev), speed, addr, oldaddr)); } static int iicbb_start_impl(device_t dev, u_char slave, bool repstart) { struct iicbb_softc *sc = device_get_softc(dev); int error; if (!repstart) { I2C_DEBUG(printf("<<")); /* SCL must be high on the idle bus. */ if (iicbb_waitforscl(dev) != 0) { I2C_DEBUG(printf("C!\n")); return (IIC_EBUSERR); } } else { I2C_DEBUG(printf("<")); error = iicbb_clockin(dev, 1); if (error != 0) return (error); /* SDA will go low in the middle of the SCL high phase. */ DELAY((sc->udelay + 1) / 2); } /* * SDA must be high after the earlier stop condition or the end * of Ack/NoAck pulse. */ if (!I2C_GETSDA(dev)) { I2C_DEBUG(printf("D!\n")); return (IIC_EBUSERR); } /* Start: SDA high->low. */ I2C_SETSDA(dev, 0); /* Wait the second half of the SCL high phase. */ DELAY((sc->udelay + 1) / 2); /* Pull SCL low to keep the bus reserved. */ iicbb_clockout(dev); /* send address */ error = iicbb_sendbyte(dev, slave); /* check for ack */ if (error == 0) error = iicbb_getack(dev); if (error != 0) (void)iicbb_stop(dev); return (error); } /* NB: the timeout is ignored. */ static int iicbb_start(device_t dev, u_char slave, int timeout) { return (iicbb_start_impl(dev, slave, false)); } /* NB: the timeout is ignored. */ static int iicbb_repstart(device_t dev, u_char slave, int timeout) { return (iicbb_start_impl(dev, slave, true)); } static int iicbb_stop(device_t dev) { struct iicbb_softc *sc = device_get_softc(dev); int err = 0; /* * Stop: SDA goes from low to high in the middle of the SCL high phase. */ err = iicbb_clockin(dev, 0); if (err != 0) return (err); DELAY((sc->udelay + 1) / 2); I2C_SETSDA(dev, 1); DELAY((sc->udelay + 1) / 2); I2C_DEBUG(printf("%s>>", err != 0 ? "!" : "")); I2C_DEBUG(printf("\n")); return (err); } /* NB: the timeout is ignored. */ static int iicbb_write(device_t dev, const char *buf, int len, int *sent, int timeout) { int bytes, error = 0; bytes = 0; while (len > 0) { /* send byte */ iicbb_sendbyte(dev, (uint8_t)*buf++); /* check for ack */ error = iicbb_getack(dev); if (error != 0) break; bytes++; len--; } *sent = bytes; return (error); } /* NB: whatever delay is, it's ignored. */ static int iicbb_read(device_t dev, char *buf, int len, int *read, int last, int delay) { int bytes = 0; int err = 0; while (len > 0) { err = iicbb_readbyte(dev, (len == 1) ? last : 0, (uint8_t *)buf); if (err != 0) break; buf++; bytes++; len--; } *read = bytes; return (err); } 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); } static void iicbb_set_speed(struct iicbb_softc *sc, u_char speed) { u_int busfreq; int period; /* * udelay is half a period, the clock is held high or low for this long. */ busfreq = IICBUS_GET_FREQUENCY(sc->iicbus, speed); period = 1000000 / 2 / busfreq; /* Hz -> uS */ period -= sc->io_latency; sc->udelay = MAX(period, 1); } 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/12/sys/dev/iicbus/iicbus.c =================================================================== --- stable/12/sys/dev/iicbus/iicbus.c (revision 366356) +++ stable/12/sys/dev/iicbus/iicbus.c (revision 366357) @@ -1,389 +1,391 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * 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 #include "iicbus_if.h" /* See comments below for why auto-scanning is a bad idea. */ #define SCAN_IICBUS 0 +SYSCTL_NODE(_hw, OID_AUTO, i2c, CTLFLAG_RW, 0, "i2c controls"); + 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. */ int iicbus_attach_common(device_t dev, u_int bus_freq) { #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, bus_freq); 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_attach(device_t dev) { return (iicbus_attach_common(dev, 0)); } int iicbus_detach(device_t dev) { struct iicbus_softc *sc = IICBUS_SOFTC(dev); int err; if ((err = device_delete_children(dev)) != 0) return (err); iicbus_reset(dev, IIC_FASTEST, 0, NULL); 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); resource_list_print_type(&devi->rl, "irq", SYS_RES_IRQ, "%jd"); retval += bus_print_child_footer(dev, child); return (retval); } void iicbus_probe_nomatch(device_t bus, device_t child) { struct iicbus_ivar *devi = IICBUS_IVAR(child); device_printf(bus, " at addr %#x\n", devi->addr); } 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); } int iicbus_child_pnpinfo_str(device_t bus, device_t child, char *buf, size_t buflen) { *buf = '\0'; return (0); } 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); } int iicbus_write_ivar(device_t bus, device_t child, int which, uintptr_t value) { struct iicbus_ivar *devi = IICBUS_IVAR(child); switch (which) { default: return (EINVAL); case IICBUS_IVAR_ADDR: if (devi->addr != 0) return (EINVAL); devi->addr = value; } return (0); } device_t iicbus_add_child_common(device_t dev, u_int order, const char *name, int unit, size_t ivars_size) { device_t child; struct iicbus_ivar *devi; child = device_add_child_ordered(dev, order, name, unit); if (child == NULL) return (child); devi = malloc(ivars_size, M_DEVBUF, M_NOWAIT | M_ZERO); if (devi == NULL) { device_delete_child(dev, child); return (0); } resource_list_init(&devi->rl); device_set_ivars(child, devi); return (child); } static device_t iicbus_add_child(device_t dev, u_int order, const char *name, int unit) { return (iicbus_add_child_common( dev, order, name, unit, sizeof(struct iicbus_ivar))); } static void iicbus_hinted_child(device_t bus, const char *dname, int dunit) { device_t child; int irq; struct iicbus_ivar *devi; child = BUS_ADD_CHILD(bus, 0, dname, dunit); devi = IICBUS_IVAR(child); resource_int_value(dname, dunit, "addr", &devi->addr); if (resource_int_value(dname, dunit, "irq", &irq) == 0) { if (bus_set_resource(child, SYS_RES_IRQ, 0, irq, 1) != 0) device_printf(bus, "warning: bus_set_resource() failed\n"); } } static struct resource_list * iicbus_get_resource_list(device_t bus __unused, device_t child) { struct iicbus_ivar *devi; devi = IICBUS_IVAR(child); return (&devi->rl); } 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), DEVMETHOD(device_suspend, bus_generic_suspend), DEVMETHOD(device_resume, bus_generic_resume), /* bus interface */ DEVMETHOD(bus_setup_intr, bus_generic_setup_intr), DEVMETHOD(bus_teardown_intr, bus_generic_teardown_intr), DEVMETHOD(bus_activate_resource, bus_generic_activate_resource), DEVMETHOD(bus_deactivate_resource, bus_generic_deactivate_resource), DEVMETHOD(bus_adjust_resource, bus_generic_adjust_resource), DEVMETHOD(bus_alloc_resource, bus_generic_rl_alloc_resource), DEVMETHOD(bus_get_resource, bus_generic_rl_get_resource), DEVMETHOD(bus_release_resource, bus_generic_rl_release_resource), DEVMETHOD(bus_set_resource, bus_generic_rl_set_resource), DEVMETHOD(bus_get_resource_list, iicbus_get_resource_list), 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_write_ivar, iicbus_write_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/12 =================================================================== --- stable/12 (revision 366356) +++ stable/12 (revision 366357) Property changes on: stable/12 ___________________________________________________________________ Modified: svn:mergeinfo ## -0,0 +0,1 ## Merged /head:r365290