/*- * SPDX-License-Identifier: BSD-2-Clause * * Copyright (c) 2019-2020 Thomas Skibo * * 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. */ /* Cadence / Zynq i2c driver. * * Reference: Zynq-7000 All Programmable SoC Technical Reference Manual. * (v1.12.2) July 1, 2018. Xilinx doc UG585. I2C Controller is documented * in Chapter 20. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "iicbus_if.h" #ifdef I2CDEBUG #define DPRINTF(...) do { printf(__VA_ARGS__); } while (0) #else #define DPRINTF(...) do { } while (0) #endif #if 0 #define HWTYPE_CDNS_R1P10 1 #endif #define HWTYPE_CDNS_R1P14 2 static struct ofw_compat_data compat_data[] = { #if 0 {"cdns,i2c-r1p10", HWTYPE_CDNS_R1P10}, #endif {"cdns,i2c-r1p14", HWTYPE_CDNS_R1P14}, {NULL, 0} }; struct cdnc_i2c_softc { device_t dev; device_t iicbus; struct mtx sc_mtx; struct resource *mem_res; struct resource *irq_res; void *intrhandle; uint16_t cfg_reg_shadow; uint16_t istat; clk_t ref_clk; uint32_t ref_clock_freq; uint32_t i2c_clock_freq; int hwtype; int hold; /* sysctls */ unsigned int i2c_clk_real_freq; unsigned int interrupts; unsigned int timeout_ints; }; #define I2C_SC_LOCK(sc) mtx_lock(&(sc)->sc_mtx) #define I2C_SC_UNLOCK(sc) mtx_unlock(&(sc)->sc_mtx) #define I2C_SC_LOCK_INIT(sc) \ mtx_init(&(sc)->sc_mtx, device_get_nameunit((sc)->dev), NULL, MTX_DEF) #define I2C_SC_LOCK_DESTROY(sc) mtx_destroy(&(sc)->sc_mtx) #define I2C_SC_ASSERT_LOCKED(sc) mtx_assert(&(sc)->sc_mtx, MA_OWNED) #define RD2(sc, off) (bus_read_2((sc)->mem_res, (off))) #define WR2(sc, off, val) (bus_write_2((sc)->mem_res, (off), (val))) #define RD1(sc, off) (bus_read_1((sc)->mem_res, (off))) #define WR1(sc, off, val) (bus_write_1((sc)->mem_res, (off), (val))) /* Cadence I2C controller device registers. */ #define CDNC_I2C_CR 0x0000 /* Config register. */ #define CDNC_I2C_CR_DIV_A_MASK (3 << 14) #define CDNC_I2C_CR_DIV_A_SHIFT 14 #define CDNC_I2C_CR_DIV_A(a) ((a) << 14) #define CDNC_I2C_CR_DIV_A_MAX 3 #define CDNC_I2C_CR_DIV_B_MASK (0x3f << 8) #define CDNC_I2C_CR_DIV_B_SHIFT 8 #define CDNC_I2C_CR_DIV_B(b) ((b) << 8) #define CDNC_I2C_CR_DIV_B_MAX 63 #define CDNC_I2C_CR_CLR_FIFO (1 << 6) #define CDNC_I2C_CR_SLVMON_MODE (1 << 5) #define CDNC_I2C_CR_HOLD (1 << 4) #define CDNC_I2C_CR_ACKEN (1 << 3) #define CDNC_I2C_CR_NEA (1 << 2) #define CDNC_I2C_CR_MAST (1 << 1) #define CDNC_I2C_CR_RNW (1 << 0) #define CDNC_I2C_SR 0x0004 /* Status register. */ #define CDNC_I2C_SR_BUS_ACTIVE (1 << 8) #define CDNC_I2C_SR_RX_OVF (1 << 7) #define CDNC_I2C_SR_TX_VALID (1 << 6) #define CDNC_I2C_SR_RX_VALID (1 << 5) #define CDNC_I2C_SR_RXRW (1 << 3) #define CDNC_I2C_ADDR 0x0008 /* i2c address register. */ #define CDNC_I2C_DATA 0x000C /* i2c data register. */ #define CDNC_I2C_ISR 0x0010 /* Int status register. */ #define CDNC_I2C_ISR_ARB_LOST (1 << 9) #define CDNC_I2C_ISR_RX_UNDF (1 << 7) #define CDNC_I2C_ISR_TX_OVF (1 << 6) #define CDNC_I2C_ISR_RX_OVF (1 << 5) #define CDNC_I2C_ISR_SLV_RDY (1 << 4) #define CDNC_I2C_ISR_XFER_TMOUT (1 << 3) #define CDNC_I2C_ISR_XFER_NACK (1 << 2) #define CDNC_I2C_ISR_XFER_DATA (1 << 1) #define CDNC_I2C_ISR_XFER_DONE (1 << 0) #define CDNC_I2C_ISR_ALL 0x2ff #define CDNC_I2C_TRANS_SIZE 0x0014 /* Transfer size. */ #define CDNC_I2C_PAUSE 0x0018 /* Slv Monitor Pause reg. */ #define CDNC_I2C_TIME_OUT 0x001C /* Time-out register. */ #define CDNC_I2C_TIME_OUT_MIN 31 #define CDNC_I2C_TIME_OUT_MAX 255 #define CDNC_I2C_IMR 0x0020 /* Int mask register. */ #define CDNC_I2C_IER 0x0024 /* Int enable register. */ #define CDNC_I2C_IDR 0x0028 /* Int disable register. */ #define CDNC_I2C_FIFO_SIZE 16 #define CDNC_I2C_DEFAULT_I2C_CLOCK 400000 /* 400Khz default */ #define CDNC_I2C_ISR_ERRS (CDNC_I2C_ISR_ARB_LOST | CDNC_I2C_ISR_RX_UNDF | \ CDNC_I2C_ISR_TX_OVF | CDNC_I2C_ISR_RX_OVF | CDNC_I2C_ISR_XFER_TMOUT | \ CDNC_I2C_ISR_XFER_NACK) /* Configure clock dividers. */ static int cdnc_i2c_set_freq(struct cdnc_i2c_softc *sc) { uint32_t div_a, div_b, err, clk_out; uint32_t best_div_a, best_div_b, best_err; best_div_a = 0; best_div_b = 0; best_err = ~0U; /* * The i2c controller has a two-stage clock divider to create * the "clock enable" signal used to sample the incoming SCL and * SDA signals. The Clock Enable signal is divided by 22 to create * the outgoing SCL signal. * * Try all div_a values and pick best match. */ for (div_a = 0; div_a <= CDNC_I2C_CR_DIV_A_MAX; div_a++) { div_b = sc->ref_clock_freq / (22 * sc->i2c_clock_freq * (div_a + 1)); if (div_b > CDNC_I2C_CR_DIV_B_MAX) continue; clk_out = sc->ref_clock_freq / (22 * (div_a + 1) * (div_b + 1)); err = clk_out > sc->i2c_clock_freq ? clk_out - sc->i2c_clock_freq : sc->i2c_clock_freq - clk_out; if (err < best_err) { best_err = err; best_div_a = div_a; best_div_b = div_b; } } if (best_err == ~0U) { device_printf(sc->dev, "cannot configure clock divider.\n"); return (EINVAL); /* out of range */ } clk_out = sc->ref_clock_freq / (22 * (best_div_a + 1) * (best_div_b + 1)); DPRINTF("%s: ref_clock_freq=%d i2c_clock_freq=%d\n", __func__, sc->ref_clock_freq, sc->i2c_clock_freq); DPRINTF("%s: div_a=%d div_b=%d real-freq=%d\n", __func__, best_div_a, best_div_b, clk_out); sc->cfg_reg_shadow &= ~(CDNC_I2C_CR_DIV_A_MASK | CDNC_I2C_CR_DIV_B_MASK); sc->cfg_reg_shadow |= CDNC_I2C_CR_DIV_A(best_div_a) | CDNC_I2C_CR_DIV_B(best_div_b); WR2(sc, CDNC_I2C_CR, sc->cfg_reg_shadow); sc->i2c_clk_real_freq = clk_out; return (0); } /* Initialize hardware. */ static int cdnc_i2c_init_hw(struct cdnc_i2c_softc *sc) { /* Reset config register and clear FIFO. */ sc->cfg_reg_shadow = 0; WR2(sc, CDNC_I2C_CR, CDNC_I2C_CR_CLR_FIFO); sc->hold = 0; /* Clear and disable all interrupts. */ WR2(sc, CDNC_I2C_ISR, CDNC_I2C_ISR_ALL); WR2(sc, CDNC_I2C_IDR, CDNC_I2C_ISR_ALL); /* Max out bogus time-out register. */ WR1(sc, CDNC_I2C_TIME_OUT, CDNC_I2C_TIME_OUT_MAX); /* Set up clock dividers. */ return (cdnc_i2c_set_freq(sc)); } static int cdnc_i2c_errs(struct cdnc_i2c_softc *sc, uint16_t istat) { DPRINTF("%s: istat=0x%x\n", __func__, istat); /* XXX: clean up after errors. */ /* Reset config register and clear FIFO. */ sc->cfg_reg_shadow &= CDNC_I2C_CR_DIV_A_MASK | CDNC_I2C_CR_DIV_B_MASK; WR2(sc, CDNC_I2C_CR, sc->cfg_reg_shadow | CDNC_I2C_CR_CLR_FIFO); sc->hold = 0; if (istat & CDNC_I2C_ISR_XFER_TMOUT) return (IIC_ETIMEOUT); else if (istat & CDNC_I2C_ISR_RX_UNDF) return (IIC_EUNDERFLOW); else if (istat & (CDNC_I2C_ISR_RX_OVF | CDNC_I2C_ISR_TX_OVF)) return (IIC_EOVERFLOW); else if (istat & CDNC_I2C_ISR_XFER_NACK) return (IIC_ENOACK); else if (istat & CDNC_I2C_ISR_ARB_LOST) return (IIC_EBUSERR); /* XXX: ???? */ else /* Should not happen */ return (IIC_NOERR); } static int cdnc_i2c_reset(device_t dev, u_char speed, u_char addr, u_char *oldaddr) { struct cdnc_i2c_softc *sc = device_get_softc(dev); int error; DPRINTF("%s: speed=%d addr=0x%x\n", __func__, speed, addr); I2C_SC_LOCK(sc); sc->i2c_clock_freq = IICBUS_GET_FREQUENCY(sc->iicbus, speed); error = cdnc_i2c_init_hw(sc); I2C_SC_UNLOCK(sc); return (error ? IIC_ENOTSUPP : IIC_NOERR); } static void cdnc_i2c_intr(void *arg) { struct cdnc_i2c_softc *sc = (struct cdnc_i2c_softc *)arg; uint16_t status; I2C_SC_LOCK(sc); sc->interrupts++; /* Read active interrupts. */ status = RD2(sc, CDNC_I2C_ISR) & ~RD2(sc, CDNC_I2C_IMR); /* Clear interrupts. */ WR2(sc, CDNC_I2C_ISR, status); if (status & CDNC_I2C_ISR_XFER_TMOUT) sc->timeout_ints++; sc->istat |= status; if (status) wakeup(sc); I2C_SC_UNLOCK(sc); } static int cdnc_i2c_xfer_rd(struct cdnc_i2c_softc *sc, struct iic_msg *msg) { int error = IIC_NOERR; uint16_t flags = msg->flags; uint16_t len = msg->len; int idx = 0, nbytes, last, first = 1; uint16_t statr; DPRINTF("%s: flags=0x%x len=%d\n", __func__, flags, len); #if 0 if (sc->hwtype == HWTYPE_CDNS_R1P10 && (flags & IIC_M_NOSTOP)) return (IIC_ENOTSUPP); #endif I2C_SC_ASSERT_LOCKED(sc); /* Program config register. */ sc->cfg_reg_shadow &= CDNC_I2C_CR_DIV_A_MASK | CDNC_I2C_CR_DIV_B_MASK; sc->cfg_reg_shadow |= CDNC_I2C_CR_HOLD | CDNC_I2C_CR_ACKEN | CDNC_I2C_CR_NEA | CDNC_I2C_CR_MAST | CDNC_I2C_CR_RNW; WR2(sc, CDNC_I2C_CR, sc->cfg_reg_shadow | CDNC_I2C_CR_CLR_FIFO); sc->hold = 1; while (len > 0) { nbytes = MIN(CDNC_I2C_FIFO_SIZE - 2, len); WR1(sc, CDNC_I2C_TRANS_SIZE, nbytes); last = nbytes == len && !(flags & IIC_M_NOSTOP); if (last) { /* Clear HOLD bit on last transfer. */ sc->cfg_reg_shadow &= ~CDNC_I2C_CR_HOLD; WR2(sc, CDNC_I2C_CR, sc->cfg_reg_shadow); sc->hold = 0; } /* Writing slv address for a start or repeated start. */ if (first && !(flags & IIC_M_NOSTART)) WR2(sc, CDNC_I2C_ADDR, msg->slave >> 1); first = 0; /* Enable FIFO interrupts and wait. */ if (last) WR2(sc, CDNC_I2C_IER, CDNC_I2C_ISR_XFER_DONE | CDNC_I2C_ISR_ERRS); else WR2(sc, CDNC_I2C_IER, CDNC_I2C_ISR_XFER_DATA | CDNC_I2C_ISR_ERRS); error = mtx_sleep(sc, &sc->sc_mtx, 0, "cdi2c", hz); /* Disable FIFO interrupts. */ WR2(sc, CDNC_I2C_IDR, CDNC_I2C_ISR_XFER_DATA | CDNC_I2C_ISR_XFER_DONE | CDNC_I2C_ISR_ERRS); if (error == EWOULDBLOCK) error = cdnc_i2c_errs(sc, CDNC_I2C_ISR_XFER_TMOUT); else if (sc->istat & CDNC_I2C_ISR_ERRS) error = cdnc_i2c_errs(sc, sc->istat); sc->istat = 0; if (error != IIC_NOERR) break; /* Read nbytes from FIFO. */ while (nbytes-- > 0) { statr = RD2(sc, CDNC_I2C_SR); if (!(statr & CDNC_I2C_SR_RX_VALID)) { printf("%s: RX FIFO underflow?\n", __func__); break; } msg->buf[idx++] = RD2(sc, CDNC_I2C_DATA); len--; } } return (error); } static int cdnc_i2c_xfer_wr(struct cdnc_i2c_softc *sc, struct iic_msg *msg) { int error = IIC_NOERR; uint16_t flags = msg->flags; uint16_t len = msg->len; int idx = 0, nbytes, last, first = 1; DPRINTF("%s: flags=0x%x len=%d\n", __func__, flags, len); I2C_SC_ASSERT_LOCKED(sc); /* Program config register. */ sc->cfg_reg_shadow &= CDNC_I2C_CR_DIV_A_MASK | CDNC_I2C_CR_DIV_B_MASK; sc->cfg_reg_shadow |= CDNC_I2C_CR_HOLD | CDNC_I2C_CR_ACKEN | CDNC_I2C_CR_NEA | CDNC_I2C_CR_MAST; WR2(sc, CDNC_I2C_CR, sc->cfg_reg_shadow | CDNC_I2C_CR_CLR_FIFO); sc->hold = 1; while (len > 0) { /* Put as much data into fifo as you can. */ nbytes = MIN(len, CDNC_I2C_FIFO_SIZE - RD1(sc, CDNC_I2C_TRANS_SIZE) - 1); len -= nbytes; while (nbytes-- > 0) WR2(sc, CDNC_I2C_DATA, msg->buf[idx++]); last = len == 0 && !(flags & IIC_M_NOSTOP); if (last) { /* Clear HOLD bit on last transfer. */ sc->cfg_reg_shadow &= ~CDNC_I2C_CR_HOLD; WR2(sc, CDNC_I2C_CR, sc->cfg_reg_shadow); sc->hold = 0; } /* Perform START if this is start or repeated start. */ if (first && !(flags & IIC_M_NOSTART)) WR2(sc, CDNC_I2C_ADDR, msg->slave >> 1); first = 0; /* Enable FIFO interrupts. */ WR2(sc, CDNC_I2C_IER, CDNC_I2C_ISR_XFER_DONE | CDNC_I2C_ISR_ERRS); /* Wait for end of data transfer. */ error = mtx_sleep(sc, &sc->sc_mtx, 0, "cdi2c", hz); /* Disable FIFO interrupts. */ WR2(sc, CDNC_I2C_IDR, CDNC_I2C_ISR_XFER_DONE | CDNC_I2C_ISR_ERRS); if (error == EWOULDBLOCK) error = cdnc_i2c_errs(sc, CDNC_I2C_ISR_XFER_TMOUT); else if (sc->istat & CDNC_I2C_ISR_ERRS) error = cdnc_i2c_errs(sc, sc->istat); sc->istat = 0; if (error) break; } return (error); } static int cdnc_i2c_transfer(device_t dev, struct iic_msg *msgs, uint32_t nmsgs) { struct cdnc_i2c_softc *sc = device_get_softc(dev); int i, error = IIC_NOERR; DPRINTF("%s: nmsgs=%d\n", __func__, nmsgs); I2C_SC_LOCK(sc); for (i = 0; i < nmsgs; i++) { DPRINTF("%s: msg[%d]: hold=%d slv=0x%x flags=0x%x len=%d\n", __func__, i, sc->hold, msgs[i].slave, msgs[i].flags, msgs[i].len); if (!sc->hold && (msgs[i].flags & IIC_M_NOSTART)) return (IIC_ENOTSUPP); if (msgs[i].flags & IIC_M_RD) { error = cdnc_i2c_xfer_rd(sc, &msgs[i]); if (error != IIC_NOERR) break; } else { error = cdnc_i2c_xfer_wr(sc, &msgs[i]); if (error != IIC_NOERR) break; } } I2C_SC_UNLOCK(sc); return (error); } static void cdnc_i2c_add_sysctls(device_t dev) { struct cdnc_i2c_softc *sc = device_get_softc(dev); struct sysctl_ctx_list *ctx; struct sysctl_oid_list *child; ctx = device_get_sysctl_ctx(dev); child = SYSCTL_CHILDREN(device_get_sysctl_tree(dev)); SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "i2c_clk_real_freq", CTLFLAG_RD, &sc->i2c_clk_real_freq, 0, "i2c clock real frequency"); SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "_interrupts", CTLFLAG_RD, &sc->interrupts, 0, "interrupt calls"); SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "_timeouts", CTLFLAG_RD, &sc->timeout_ints, 0, "hardware timeout interrupts"); } static int cdnc_i2c_probe(device_t dev) { if (!ofw_bus_status_okay(dev)) return (ENXIO); if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0) return (ENXIO); device_set_desc(dev, "Cadence I2C Controller"); return (BUS_PROBE_DEFAULT); } static int cdnc_i2c_detach(device_t); static int cdnc_i2c_attach(device_t dev) { struct cdnc_i2c_softc *sc; int rid, err; phandle_t node; pcell_t cell; uint64_t freq; sc = device_get_softc(dev); sc->dev = dev; sc->hwtype = ofw_bus_search_compatible(dev, compat_data)->ocd_data; I2C_SC_LOCK_INIT(sc); /* Get ref-clock and i2c-clock properties. */ node = ofw_bus_get_node(dev); if (OF_getprop(node, "ref-clock", &cell, sizeof(cell)) > 0) sc->ref_clock_freq = fdt32_to_cpu(cell); else if (clk_get_by_ofw_index(dev, node, 0, &sc->ref_clk) == 0) { if ((err = clk_enable(sc->ref_clk)) != 0) device_printf(dev, "Cannot enable clock. err=%d\n", err); else if ((err = clk_get_freq(sc->ref_clk, &freq)) != 0) device_printf(dev, "Cannot get clock frequency. err=%d\n", err); else sc->ref_clock_freq = freq; } else { device_printf(dev, "must have ref-clock property\n"); return (ENXIO); } if (OF_getprop(node, "clock-frequency", &cell, sizeof(cell)) > 0) sc->i2c_clock_freq = fdt32_to_cpu(cell); else sc->i2c_clock_freq = CDNC_I2C_DEFAULT_I2C_CLOCK; /* Get memory resource. */ rid = 0; sc->mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, RF_ACTIVE); if (sc->mem_res == NULL) { device_printf(dev, "could not allocate memory resources.\n"); cdnc_i2c_detach(dev); return (ENOMEM); } /* Allocate IRQ. */ rid = 0; sc->irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, RF_ACTIVE); if (sc->irq_res == NULL) { device_printf(dev, "could not allocate IRQ resource.\n"); cdnc_i2c_detach(dev); return (ENOMEM); } /* Activate the interrupt. */ err = bus_setup_intr(dev, sc->irq_res, INTR_TYPE_MISC | INTR_MPSAFE, NULL, cdnc_i2c_intr, sc, &sc->intrhandle); if (err) { device_printf(dev, "could not setup IRQ.\n"); cdnc_i2c_detach(dev); return (err); } /* Configure the device. */ err = cdnc_i2c_init_hw(sc); if (err) { cdnc_i2c_detach(dev); return (err); } sc->iicbus = device_add_child(dev, "iicbus", -1); cdnc_i2c_add_sysctls(dev); /* Probe and attach iicbus when interrupts work. */ return (bus_delayed_attach_children(dev)); } static int cdnc_i2c_detach(device_t dev) { struct cdnc_i2c_softc *sc = device_get_softc(dev); if (device_is_attached(dev)) bus_generic_detach(dev); if (sc->ref_clk != NULL) { clk_release(sc->ref_clk); sc->ref_clk = NULL; } /* Delete iic bus. */ if (sc->iicbus) device_delete_child(dev, sc->iicbus); /* Disable hardware. */ if (sc->mem_res != NULL) { sc->cfg_reg_shadow = 0; WR2(sc, CDNC_I2C_CR, CDNC_I2C_CR_CLR_FIFO); /* Clear and disable all interrupts. */ WR2(sc, CDNC_I2C_ISR, CDNC_I2C_ISR_ALL); WR2(sc, CDNC_I2C_IDR, CDNC_I2C_ISR_ALL); } /* Teardown and release interrupt. */ if (sc->irq_res != NULL) { if (sc->intrhandle) bus_teardown_intr(dev, sc->irq_res, sc->intrhandle); bus_release_resource(dev, SYS_RES_IRQ, rman_get_rid(sc->irq_res), sc->irq_res); sc->irq_res = NULL; } /* Release memory resource. */ if (sc->mem_res != NULL) { bus_release_resource(dev, SYS_RES_MEMORY, rman_get_rid(sc->mem_res), sc->mem_res); sc->mem_res = NULL; } I2C_SC_LOCK_DESTROY(sc); return (0); } static phandle_t cdnc_i2c_get_node(device_t bus, device_t dev) { return (ofw_bus_get_node(bus)); } static device_method_t cdnc_i2c_methods[] = { /* Device interface */ DEVMETHOD(device_probe, cdnc_i2c_probe), DEVMETHOD(device_attach, cdnc_i2c_attach), DEVMETHOD(device_detach, cdnc_i2c_detach), /* ofw_bus interface */ DEVMETHOD(ofw_bus_get_node, cdnc_i2c_get_node), /* iicbus methods */ DEVMETHOD(iicbus_callback, iicbus_null_callback), DEVMETHOD(iicbus_reset, cdnc_i2c_reset), DEVMETHOD(iicbus_transfer, cdnc_i2c_transfer), DEVMETHOD_END }; static driver_t cdnc_i2c_driver = { "cdnc_i2c", cdnc_i2c_methods, sizeof(struct cdnc_i2c_softc), }; DRIVER_MODULE(cdnc_i2c, simplebus, cdnc_i2c_driver, NULL, NULL); DRIVER_MODULE(ofw_iicbus, cdnc_i2c, ofw_iicbus_driver, NULL, NULL); MODULE_DEPEND(cdnc_i2c, iicbus, 1, 1, 1); MODULE_DEPEND(cdnc_i2c, ofw_iicbus, 1, 1, 1); SIMPLEBUS_PNP_INFO(compat_data);