Index: stable/12/sys/arm/freescale/imx/imx_i2c.c =================================================================== --- stable/12/sys/arm/freescale/imx/imx_i2c.c (revision 356282) +++ stable/12/sys/arm/freescale/imx/imx_i2c.c (revision 356283) @@ -1,687 +1,686 @@ /*- * Copyright (C) 2008-2009 Semihalf, Michal Hajduk * Copyright (c) 2012, 2013 The FreeBSD Foundation * Copyright (c) 2015 Ian Lepore * All rights reserved. * * Portions of this software were developed by Oleksandr Rybalko * under sponsorship from the FreeBSD Foundation. * * 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 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 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. */ /* * I2C driver for Freescale i.MX hardware. * * Note that the hardware is capable of running as both a master and a slave. * This driver currently implements only master-mode operations. * * This driver supports multi-master i2c buses, by detecting bus arbitration * loss and returning IIC_EBUSBSY status. Notably, it does not do any kind of * retries if some other master jumps onto the bus and interrupts one of our * transfer cycles resulting in arbitration loss in mid-transfer. The caller * must handle retries in a way that makes sense for the slave being addressed. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "iicbus_if.h" #include #include #include #include #include #define I2C_ADDR_REG 0x00 /* I2C slave address register */ #define I2C_FDR_REG 0x04 /* I2C frequency divider register */ #define I2C_CONTROL_REG 0x08 /* I2C control register */ #define I2C_STATUS_REG 0x0C /* I2C status register */ #define I2C_DATA_REG 0x10 /* I2C data register */ #define I2C_DFSRR_REG 0x14 /* I2C Digital Filter Sampling rate */ #define I2CCR_MEN (1 << 7) /* Module enable */ #define I2CCR_MSTA (1 << 5) /* Master/slave mode */ #define I2CCR_MTX (1 << 4) /* Transmit/receive mode */ #define I2CCR_TXAK (1 << 3) /* Transfer acknowledge */ #define I2CCR_RSTA (1 << 2) /* Repeated START */ #define I2CSR_MCF (1 << 7) /* Data transfer */ #define I2CSR_MASS (1 << 6) /* Addressed as a slave */ #define I2CSR_MBB (1 << 5) /* Bus busy */ #define I2CSR_MAL (1 << 4) /* Arbitration lost */ #define I2CSR_SRW (1 << 2) /* Slave read/write */ #define I2CSR_MIF (1 << 1) /* Module interrupt */ #define I2CSR_RXAK (1 << 0) /* Received acknowledge */ #define I2C_BAUD_RATE_FAST 0x31 #define I2C_BAUD_RATE_DEF 0x3F #define I2C_DFSSR_DIV 0x10 /* * A table of available divisors and the associated coded values to put in the * FDR register to achieve that divisor.. There is no algorithmic relationship I * can see between divisors and the codes that go into the register. The table * begins and ends with entries that handle insane configuration values. */ struct clkdiv { u_int divisor; u_int regcode; }; static struct clkdiv clkdiv_table[] = { { 0, 0x20 }, { 22, 0x20 }, { 24, 0x21 }, { 26, 0x22 }, { 28, 0x23 }, { 30, 0x00 }, { 32, 0x24 }, { 36, 0x25 }, { 40, 0x26 }, { 42, 0x03 }, { 44, 0x27 }, { 48, 0x28 }, { 52, 0x05 }, { 56, 0x29 }, { 60, 0x06 }, { 64, 0x2a }, { 72, 0x2b }, { 80, 0x2c }, { 88, 0x09 }, { 96, 0x2d }, { 104, 0x0a }, { 112, 0x2e }, { 128, 0x2f }, { 144, 0x0c }, { 160, 0x30 }, { 192, 0x31 }, { 224, 0x32 }, { 240, 0x0f }, { 256, 0x33 }, { 288, 0x10 }, { 320, 0x34 }, { 384, 0x35 }, { 448, 0x36 }, { 480, 0x13 }, { 512, 0x37 }, { 576, 0x14 }, { 640, 0x38 }, { 768, 0x39 }, { 896, 0x3a }, { 960, 0x17 }, { 1024, 0x3b }, { 1152, 0x18 }, { 1280, 0x3c }, { 1536, 0x3d }, { 1792, 0x3e }, { 1920, 0x1b }, { 2048, 0x3f }, { 2304, 0x1c }, { 2560, 0x1d }, { 3072, 0x1e }, { 3840, 0x1f }, {UINT_MAX, 0x1f} }; static struct ofw_compat_data compat_data[] = { {"fsl,imx6q-i2c", 1}, {"fsl,imx-i2c", 1}, {NULL, 0} }; struct i2c_softc { device_t dev; device_t iicbus; struct resource *res; int rid; sbintime_t byte_time_sbt; int rb_pinctl_idx; gpio_pin_t rb_sclpin; gpio_pin_t rb_sdapin; u_int debug; u_int slave; }; #define DEVICE_DEBUGF(sc, lvl, fmt, args...) \ if ((lvl) <= (sc)->debug) \ device_printf((sc)->dev, fmt, ##args) #define DEBUGF(sc, lvl, fmt, args...) \ if ((lvl) <= (sc)->debug) \ printf(fmt, ##args) static phandle_t i2c_get_node(device_t, device_t); static int i2c_probe(device_t); static int i2c_attach(device_t); static int i2c_detach(device_t); static int i2c_repeated_start(device_t, u_char, int); static int i2c_start(device_t, u_char, int); static int i2c_stop(device_t); static int i2c_reset(device_t, u_char, u_char, u_char *); static int i2c_read(device_t, char *, int, int *, int, int); static int i2c_write(device_t, const char *, int, int *, int); static device_method_t i2c_methods[] = { DEVMETHOD(device_probe, i2c_probe), DEVMETHOD(device_attach, i2c_attach), DEVMETHOD(device_detach, i2c_detach), /* OFW methods */ DEVMETHOD(ofw_bus_get_node, i2c_get_node), DEVMETHOD(iicbus_callback, iicbus_null_callback), DEVMETHOD(iicbus_repeated_start, i2c_repeated_start), DEVMETHOD(iicbus_start, i2c_start), DEVMETHOD(iicbus_stop, i2c_stop), DEVMETHOD(iicbus_reset, i2c_reset), DEVMETHOD(iicbus_read, i2c_read), DEVMETHOD(iicbus_write, i2c_write), DEVMETHOD(iicbus_transfer, iicbus_transfer_gen), DEVMETHOD_END }; static driver_t i2c_driver = { "imx_i2c", i2c_methods, sizeof(struct i2c_softc), }; static devclass_t i2c_devclass; DRIVER_MODULE(imx_i2c, simplebus, i2c_driver, i2c_devclass, 0, 0); DRIVER_MODULE(ofw_iicbus, imx_i2c, ofw_iicbus_driver, ofw_iicbus_devclass, 0, 0); MODULE_DEPEND(imx_i2c, iicbus, 1, 1, 1); -MODULE_DEPEND(imx_i2c, ofw_iicbus, 1, 1, 1); SIMPLEBUS_PNP_INFO(compat_data); static phandle_t i2c_get_node(device_t bus, device_t dev) { /* * Share controller node with iicbus device */ return ofw_bus_get_node(bus); } static __inline void i2c_write_reg(struct i2c_softc *sc, bus_size_t off, uint8_t val) { bus_write_1(sc->res, off, val); } static __inline uint8_t i2c_read_reg(struct i2c_softc *sc, bus_size_t off) { return (bus_read_1(sc->res, off)); } static __inline void i2c_flag_set(struct i2c_softc *sc, bus_size_t off, uint8_t mask) { uint8_t status; status = i2c_read_reg(sc, off); status |= mask; i2c_write_reg(sc, off, status); } /* Wait for bus to become busy or not-busy. */ static int wait_for_busbusy(struct i2c_softc *sc, int wantbusy) { int retry, srb; retry = 1000; while (retry --) { srb = i2c_read_reg(sc, I2C_STATUS_REG) & I2CSR_MBB; if ((srb && wantbusy) || (!srb && !wantbusy)) return (IIC_NOERR); DELAY(1); } return (IIC_ETIMEOUT); } /* Wait for transfer to complete, optionally check RXAK. */ static int wait_for_xfer(struct i2c_softc *sc, int checkack) { int retry, sr; /* * Sleep for about the time it takes to transfer a byte (with precision * set to tolerate 5% oversleep). We calculate the approximate byte * transfer time when we set the bus speed divisor. Slaves are allowed * to do clock-stretching so the actual transfer time can be larger, but * this gets the bulk of the waiting out of the way without tying up the * processor the whole time. */ pause_sbt("imxi2c", sc->byte_time_sbt, sc->byte_time_sbt / 20, 0); retry = 10000; while (retry --) { sr = i2c_read_reg(sc, I2C_STATUS_REG); if (sr & I2CSR_MIF) { if (sr & I2CSR_MAL) return (IIC_EBUSERR); else if (checkack && (sr & I2CSR_RXAK)) return (IIC_ENOACK); else return (IIC_NOERR); } DELAY(1); } return (IIC_ETIMEOUT); } /* * Implement the error handling shown in the state diagram of the imx6 reference * manual. If there was an error, then: * - Clear master mode (MSTA and MTX). * - Wait for the bus to become free or for a timeout to happen. * - Disable the controller. */ static int i2c_error_handler(struct i2c_softc *sc, int error) { if (error != 0) { i2c_write_reg(sc, I2C_STATUS_REG, 0); i2c_write_reg(sc, I2C_CONTROL_REG, I2CCR_MEN); wait_for_busbusy(sc, false); i2c_write_reg(sc, I2C_CONTROL_REG, 0); } return (error); } static int i2c_recover_getsda(void *ctx) { bool active; gpio_pin_is_active(((struct i2c_softc *)ctx)->rb_sdapin, &active); return (active); } static void i2c_recover_setsda(void *ctx, int value) { gpio_pin_set_active(((struct i2c_softc *)ctx)->rb_sdapin, value); } static int i2c_recover_getscl(void *ctx) { bool active; gpio_pin_is_active(((struct i2c_softc *)ctx)->rb_sclpin, &active); return (active); } static void i2c_recover_setscl(void *ctx, int value) { gpio_pin_set_active(((struct i2c_softc *)ctx)->rb_sclpin, value); } static int i2c_recover_bus(struct i2c_softc *sc) { struct iicrb_pin_access pins; int err; /* * If we have gpio pinmux config, reconfigure the pins to gpio mode, * invoke iic_recover_bus which checks for a hung bus and bitbangs a * recovery sequence if necessary, then configure the pins back to i2c * mode (idx 0). */ if (sc->rb_pinctl_idx == 0) return (0); fdt_pinctrl_configure(sc->dev, sc->rb_pinctl_idx); pins.ctx = sc; pins.getsda = i2c_recover_getsda; pins.setsda = i2c_recover_setsda; pins.getscl = i2c_recover_getscl; pins.setscl = i2c_recover_setscl; err = iic_recover_bus(&pins); fdt_pinctrl_configure(sc->dev, 0); return (err); } static int 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, "Freescale i.MX I2C"); return (BUS_PROBE_DEFAULT); } static int i2c_attach(device_t dev) { char wrkstr[16]; struct i2c_softc *sc; phandle_t node; int err, cfgidx; sc = device_get_softc(dev); sc->dev = dev; sc->rid = 0; sc->res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &sc->rid, RF_ACTIVE); if (sc->res == NULL) { device_printf(dev, "could not allocate resources"); return (ENXIO); } sc->iicbus = device_add_child(dev, "iicbus", -1); if (sc->iicbus == NULL) { device_printf(dev, "could not add iicbus child"); return (ENXIO); } /* Set up debug-enable sysctl. */ SYSCTL_ADD_INT(device_get_sysctl_ctx(sc->dev), SYSCTL_CHILDREN(device_get_sysctl_tree(sc->dev)), OID_AUTO, "debug", CTLFLAG_RWTUN, &sc->debug, 0, "Enable debug; 1=reads/writes, 2=add starts/stops"); /* * Set up for bus recovery using gpio pins, if the pinctrl and gpio * properties are present. This is optional. If all the config data is * not in place, we just don't do gpio bitbang bus recovery. */ node = ofw_bus_get_node(sc->dev); err = gpio_pin_get_by_ofw_property(dev, node, "scl-gpios", &sc->rb_sclpin); if (err != 0) goto no_recovery; err = gpio_pin_get_by_ofw_property(dev, node, "sda-gpios", &sc->rb_sdapin); if (err != 0) goto no_recovery; /* * Preset the gpio pins to output high (idle bus state). The signal * won't actually appear on the pins until the bus recovery code changes * the pinmux config from i2c to gpio. */ gpio_pin_setflags(sc->rb_sclpin, GPIO_PIN_OUTPUT); gpio_pin_setflags(sc->rb_sdapin, GPIO_PIN_OUTPUT); gpio_pin_set_active(sc->rb_sclpin, true); gpio_pin_set_active(sc->rb_sdapin, true); /* * Obtain the index of pinctrl node for bus recovery using gpio pins, * then confirm that pinctrl properties exist for that index and for the * default pinctrl-0. If sc->rb_pinctl_idx is non-zero, the reset code * will also do a bus recovery, so setting this value must be last. */ err = ofw_bus_find_string_index(node, "pinctrl-names", "gpio", &cfgidx); if (err == 0) { snprintf(wrkstr, sizeof(wrkstr), "pinctrl-%d", cfgidx); if (OF_hasprop(node, "pinctrl-0") && OF_hasprop(node, wrkstr)) sc->rb_pinctl_idx = cfgidx; } no_recovery: /* We don't do a hardware reset here because iicbus_attach() does it. */ /* Probe and attach the iicbus when interrupts are available. */ return (bus_delayed_attach_children(dev)); } static int i2c_detach(device_t dev) { struct i2c_softc *sc; int error; sc = device_get_softc(dev); if ((error = bus_generic_detach(sc->dev)) != 0) { device_printf(sc->dev, "cannot detach child devices\n"); return (error); } if (sc->iicbus != NULL) device_delete_child(dev, sc->iicbus); /* Release bus-recover pins; gpio_pin_release() handles NULL args. */ gpio_pin_release(sc->rb_sclpin); gpio_pin_release(sc->rb_sdapin); if (sc->res != NULL) bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->res); return (0); } static int i2c_repeated_start(device_t dev, u_char slave, int timeout) { struct i2c_softc *sc; int error; sc = device_get_softc(dev); if ((i2c_read_reg(sc, I2C_STATUS_REG) & I2CSR_MBB) == 0) { return (IIC_EBUSERR); } /* * Set repeated start condition, delay (per reference manual, min 156nS) * before writing slave address, wait for ack after write. */ i2c_flag_set(sc, I2C_CONTROL_REG, I2CCR_RSTA); DELAY(1); i2c_write_reg(sc, I2C_STATUS_REG, 0x0); i2c_write_reg(sc, I2C_DATA_REG, slave); sc->slave = slave; DEVICE_DEBUGF(sc, 2, "rstart 0x%02x\n", sc->slave); error = wait_for_xfer(sc, true); return (i2c_error_handler(sc, error)); } static int i2c_start_ll(device_t dev, u_char slave, int timeout) { struct i2c_softc *sc; int error; sc = device_get_softc(dev); i2c_write_reg(sc, I2C_CONTROL_REG, I2CCR_MEN); DELAY(10); /* Delay for controller to sample bus state. */ if (i2c_read_reg(sc, I2C_STATUS_REG) & I2CSR_MBB) { return (i2c_error_handler(sc, IIC_EBUSERR)); } i2c_write_reg(sc, I2C_CONTROL_REG, I2CCR_MEN | I2CCR_MSTA | I2CCR_MTX); if ((error = wait_for_busbusy(sc, true)) != IIC_NOERR) return (i2c_error_handler(sc, error)); i2c_write_reg(sc, I2C_STATUS_REG, 0); i2c_write_reg(sc, I2C_DATA_REG, slave); sc->slave = slave; DEVICE_DEBUGF(sc, 2, "start 0x%02x\n", sc->slave); error = wait_for_xfer(sc, true); return (i2c_error_handler(sc, error)); } static int i2c_start(device_t dev, u_char slave, int timeout) { struct i2c_softc *sc; int error; sc = device_get_softc(dev); /* * Invoke the low-level code to put the bus into master mode and address * the given slave. If that fails, idle the controller and attempt a * bus recovery, and then try again one time. Signaling a start and * addressing the slave is the only operation that a low-level driver * can safely retry without any help from the upper layers that know * more about the slave device. */ if ((error = i2c_start_ll(dev, slave, timeout)) != 0) { i2c_write_reg(sc, I2C_CONTROL_REG, 0x0); if ((error = i2c_recover_bus(sc)) != 0) return (error); error = i2c_start_ll(dev, slave, timeout); } return (error); } static int i2c_stop(device_t dev) { struct i2c_softc *sc; sc = device_get_softc(dev); i2c_write_reg(sc, I2C_CONTROL_REG, I2CCR_MEN); wait_for_busbusy(sc, false); i2c_write_reg(sc, I2C_CONTROL_REG, 0); DEVICE_DEBUGF(sc, 2, "stop 0x%02x\n", sc->slave); return (IIC_NOERR); } static int i2c_reset(device_t dev, u_char speed, u_char addr, u_char *oldadr) { struct i2c_softc *sc; u_int busfreq, div, i, ipgfreq; sc = device_get_softc(dev); DEVICE_DEBUGF(sc, 1, "reset\n"); /* * Look up the divisor that gives the nearest speed that doesn't exceed * the configured value for the bus. */ ipgfreq = imx_ccm_ipg_hz(); busfreq = IICBUS_GET_FREQUENCY(sc->iicbus, speed); div = howmany(ipgfreq, busfreq); for (i = 0; i < nitems(clkdiv_table); i++) { if (clkdiv_table[i].divisor >= div) break; } /* * Calculate roughly how long it will take to transfer a byte (which * requires 9 clock cycles) at the new bus speed. This value is used to * pause() while waiting for transfer-complete. With a 66MHz IPG clock * and the actual i2c bus speeds that leads to, for nominal 100KHz and * 400KHz bus speeds the transfer times are roughly 104uS and 22uS. */ busfreq = ipgfreq / clkdiv_table[i].divisor; sc->byte_time_sbt = SBT_1US * (9000000 / busfreq); /* * Disable the controller (do the reset), and set the new clock divisor. */ i2c_write_reg(sc, I2C_STATUS_REG, 0x0); i2c_write_reg(sc, I2C_CONTROL_REG, 0x0); i2c_write_reg(sc, I2C_FDR_REG, (uint8_t)clkdiv_table[i].regcode); /* * Now that the controller is idle, perform bus recovery. If the bus * isn't hung, this a fairly fast no-op. */ return (i2c_recover_bus(sc)); } static int i2c_read(device_t dev, char *buf, int len, int *read, int last, int delay) { struct i2c_softc *sc; int error, reg; sc = device_get_softc(dev); *read = 0; DEVICE_DEBUGF(sc, 1, "read 0x%02x len %d: ", sc->slave, len); if (len) { if (len == 1) i2c_write_reg(sc, I2C_CONTROL_REG, I2CCR_MEN | I2CCR_MSTA | I2CCR_TXAK); else i2c_write_reg(sc, I2C_CONTROL_REG, I2CCR_MEN | I2CCR_MSTA); /* Dummy read to prime the receiver. */ i2c_write_reg(sc, I2C_STATUS_REG, 0x0); i2c_read_reg(sc, I2C_DATA_REG); } error = 0; *read = 0; while (*read < len) { if ((error = wait_for_xfer(sc, false)) != IIC_NOERR) break; i2c_write_reg(sc, I2C_STATUS_REG, 0x0); if (last) { if (*read == len - 2) { /* NO ACK on last byte */ i2c_write_reg(sc, I2C_CONTROL_REG, I2CCR_MEN | I2CCR_MSTA | I2CCR_TXAK); } else if (*read == len - 1) { /* Transfer done, signal stop. */ i2c_write_reg(sc, I2C_CONTROL_REG, I2CCR_MEN | I2CCR_TXAK); wait_for_busbusy(sc, false); } } reg = i2c_read_reg(sc, I2C_DATA_REG); DEBUGF(sc, 1, "0x%02x ", reg); *buf++ = reg; (*read)++; } DEBUGF(sc, 1, "\n"); return (i2c_error_handler(sc, error)); } static int i2c_write(device_t dev, const char *buf, int len, int *sent, int timeout) { struct i2c_softc *sc; int error; sc = device_get_softc(dev); error = 0; *sent = 0; DEVICE_DEBUGF(sc, 1, "write 0x%02x len %d: ", sc->slave, len); while (*sent < len) { DEBUGF(sc, 1, "0x%02x ", *buf); i2c_write_reg(sc, I2C_STATUS_REG, 0x0); i2c_write_reg(sc, I2C_DATA_REG, *buf++); if ((error = wait_for_xfer(sc, true)) != IIC_NOERR) break; (*sent)++; } DEBUGF(sc, 1, "\n"); return (i2c_error_handler(sc, error)); } Index: stable/12/sys/arm64/rockchip/rk_i2c.c =================================================================== --- stable/12/sys/arm64/rockchip/rk_i2c.c (revision 356282) +++ stable/12/sys/arm64/rockchip/rk_i2c.c (revision 356283) @@ -1,611 +1,610 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright (c) 2018 Emmanuel Vadot * * 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 #include #include #include "iicbus_if.h" #include "opt_soc.h" #define RK_I2C_CON 0x00 #define RK_I2C_CON_EN (1 << 0) #define RK_I2C_CON_MODE_SHIFT 1 #define RK_I2C_CON_MODE_TX 0 #define RK_I2C_CON_MODE_RRX 1 #define RK_I2C_CON_MODE_RX 2 #define RK_I2C_CON_MODE_RTX 3 #define RK_I2C_CON_MODE_MASK 0x6 #define RK_I2C_CON_START (1 << 3) #define RK_I2C_CON_STOP (1 << 4) #define RK_I2C_CON_LASTACK (1 << 5) #define RK_I2C_CON_NAKSTOP (1 << 6) #define RK_I2C_CLKDIV 0x04 #define RK_I2C_CLKDIVL_MASK 0xFFFF #define RK_I2C_CLKDIVL_SHIFT 0 #define RK_I2C_CLKDIVH_MASK 0xFFFF0000 #define RK_I2C_CLKDIVH_SHIFT 16 #define RK_I2C_CLKDIV_MUL 8 #define RK_I2C_MRXADDR 0x08 #define RK_I2C_MRXADDR_SADDR_MASK 0xFFFFFF #define RK_I2C_MRXADDR_VALID(x) (1 << (24 + x)) #define RK_I2C_MRXRADDR 0x0C #define RK_I2C_MRXRADDR_SRADDR_MASK 0xFFFFFF #define RK_I2C_MRXRADDR_VALID(x) (1 << (24 + x)) #define RK_I2C_MTXCNT 0x10 #define RK_I2C_MTXCNT_MASK 0x3F #define RK_I2C_MRXCNT 0x14 #define RK_I2C_MRXCNT_MASK 0x3F #define RK_I2C_IEN 0x18 #define RK_I2C_IEN_BTFIEN (1 << 0) #define RK_I2C_IEN_BRFIEN (1 << 1) #define RK_I2C_IEN_MBTFIEN (1 << 2) #define RK_I2C_IEN_MBRFIEN (1 << 3) #define RK_I2C_IEN_STARTIEN (1 << 4) #define RK_I2C_IEN_STOPIEN (1 << 5) #define RK_I2C_IEN_NAKRCVIEN (1 << 6) #define RK_I2C_IEN_ALL (RK_I2C_IEN_BTFIEN | \ RK_I2C_IEN_BRFIEN | RK_I2C_IEN_MBTFIEN | RK_I2C_IEN_MBRFIEN | \ RK_I2C_IEN_STARTIEN | RK_I2C_IEN_STOPIEN | RK_I2C_IEN_NAKRCVIEN) #define RK_I2C_IPD 0x1C #define RK_I2C_IPD_BTFIPD (1 << 0) #define RK_I2C_IPD_BRFIPD (1 << 1) #define RK_I2C_IPD_MBTFIPD (1 << 2) #define RK_I2C_IPD_MBRFIPD (1 << 3) #define RK_I2C_IPD_STARTIPD (1 << 4) #define RK_I2C_IPD_STOPIPD (1 << 5) #define RK_I2C_IPD_NAKRCVIPD (1 << 6) #define RK_I2C_IPD_ALL (RK_I2C_IPD_BTFIPD | \ RK_I2C_IPD_BRFIPD | RK_I2C_IPD_MBTFIPD | RK_I2C_IPD_MBRFIPD | \ RK_I2C_IPD_STARTIPD | RK_I2C_IPD_STOPIPD | RK_I2C_IPD_NAKRCVIPD) #define RK_I2C_FNCT 0x20 #define RK_I2C_FNCT_MASK 0x3F #define RK_I2C_TXDATA_BASE 0x100 #define RK_I2C_RXDATA_BASE 0x200 enum rk_i2c_state { STATE_IDLE = 0, STATE_START, STATE_READ, STATE_WRITE, STATE_STOP }; struct rk_i2c_softc { device_t dev; struct resource *res[2]; struct mtx mtx; clk_t sclk; clk_t pclk; int busy; void * intrhand; uint32_t intr; uint32_t ipd; struct iic_msg *msg; size_t cnt; int transfer_done; int nak_recv; uint8_t mode; uint8_t state; device_t iicbus; }; static struct ofw_compat_data compat_data[] = { #ifdef SOC_ROCKCHIP_RK3328 {"rockchip,rk3328-i2c", 1}, #endif #ifdef SOC_ROCKCHIP_RK3399 {"rockchip,rk3399-i2c", 1}, #endif {NULL, 0} }; static struct resource_spec rk_i2c_spec[] = { { SYS_RES_MEMORY, 0, RF_ACTIVE }, { SYS_RES_IRQ, 0, RF_ACTIVE | RF_SHAREABLE }, { -1, 0 } }; static int rk_i2c_probe(device_t dev); static int rk_i2c_attach(device_t dev); static int rk_i2c_detach(device_t dev); #define RK_I2C_LOCK(sc) mtx_lock(&(sc)->mtx) #define RK_I2C_UNLOCK(sc) mtx_unlock(&(sc)->mtx) #define RK_I2C_ASSERT_LOCKED(sc) mtx_assert(&(sc)->mtx, MA_OWNED) #define RK_I2C_READ(sc, reg) bus_read_4((sc)->res[0], (reg)) #define RK_I2C_WRITE(sc, reg, val) bus_write_4((sc)->res[0], (reg), (val)) static uint32_t rk_i2c_get_clkdiv(struct rk_i2c_softc *sc, uint64_t speed) { uint64_t sclk_freq; uint32_t clkdiv; int err; err = clk_get_freq(sc->sclk, &sclk_freq); if (err != 0) return (err); clkdiv = (sclk_freq / speed / RK_I2C_CLKDIV_MUL / 2) - 1; clkdiv &= RK_I2C_CLKDIVL_MASK; clkdiv = clkdiv << RK_I2C_CLKDIVH_SHIFT | clkdiv; return (clkdiv); } static int rk_i2c_reset(device_t dev, u_char speed, u_char addr, u_char *oldaddr) { struct rk_i2c_softc *sc; uint32_t clkdiv; u_int busfreq; sc = device_get_softc(dev); busfreq = IICBUS_GET_FREQUENCY(sc->iicbus, speed); clkdiv = rk_i2c_get_clkdiv(sc, busfreq); RK_I2C_LOCK(sc); /* Set the clock divider */ RK_I2C_WRITE(sc, RK_I2C_CLKDIV, clkdiv); /* Disable the module */ RK_I2C_WRITE(sc, RK_I2C_CON, 0); RK_I2C_UNLOCK(sc); return (0); } static void rk_i2c_fill_tx(struct rk_i2c_softc *sc) { uint32_t buf32; uint8_t buf; int i, j, len; if (sc->msg == NULL || sc->msg->len == sc->cnt) return; len = sc->msg->len - sc->cnt; if (len > 8) len = 8; for (i = 0; i < len; i++) { buf32 = 0; for (j = 0; j < 4 ; j++) { if (sc->cnt == sc->msg->len) break; /* Fill the addr if needed */ if (sc->cnt == 0) { buf = sc->msg->slave; } else buf = sc->msg->buf[sc->cnt - 1]; buf32 |= buf << (j * 8); sc->cnt++; } RK_I2C_WRITE(sc, RK_I2C_TXDATA_BASE + 4 * i, buf32); if (sc->cnt == sc->msg->len) break; } } static void rk_i2c_drain_rx(struct rk_i2c_softc *sc) { uint32_t buf32 = 0; uint8_t buf8; int len; int i; if (sc->msg == NULL) { device_printf(sc->dev, "No current iic msg\n"); return; } len = sc->msg->len - sc->cnt; if (len > 32) len = 32; for (i = 0; i < len; i++) { if (i % 4 == 0) buf32 = RK_I2C_READ(sc, RK_I2C_RXDATA_BASE + (i / 4) * 4); buf8 = (buf32 >> ((i % 4) * 8)) & 0xFF; sc->msg->buf[sc->cnt++] = buf8; } } static void rk_i2c_send_start(struct rk_i2c_softc *sc) { uint32_t reg; RK_I2C_WRITE(sc, RK_I2C_IEN, RK_I2C_IEN_STARTIEN); sc->state = STATE_START; reg = RK_I2C_READ(sc, RK_I2C_CON); reg |= RK_I2C_CON_START; reg |= RK_I2C_CON_EN; reg &= ~RK_I2C_CON_MODE_MASK; reg |= sc->mode << RK_I2C_CON_MODE_SHIFT; RK_I2C_WRITE(sc, RK_I2C_CON, reg); } static void rk_i2c_send_stop(struct rk_i2c_softc *sc) { uint32_t reg; RK_I2C_WRITE(sc, RK_I2C_IEN, RK_I2C_IEN_STOPIEN); sc->state = STATE_STOP; reg = RK_I2C_READ(sc, RK_I2C_CON); reg |= RK_I2C_CON_STOP; RK_I2C_WRITE(sc, RK_I2C_CON, reg); } static void rk_i2c_intr(void *arg) { struct rk_i2c_softc *sc; uint32_t reg; sc = (struct rk_i2c_softc *)arg; RK_I2C_LOCK(sc); sc->ipd = RK_I2C_READ(sc, RK_I2C_IPD); RK_I2C_WRITE(sc, RK_I2C_IPD, sc->ipd); switch (sc->state) { case STATE_START: /* Disable start bit */ reg = RK_I2C_READ(sc, RK_I2C_CON); reg &= ~RK_I2C_CON_START; RK_I2C_WRITE(sc, RK_I2C_CON, reg); if (sc->mode == RK_I2C_CON_MODE_RRX || sc->mode == RK_I2C_CON_MODE_RX) { sc->state = STATE_READ; RK_I2C_WRITE(sc, RK_I2C_IEN, RK_I2C_IEN_MBRFIEN | RK_I2C_IEN_NAKRCVIEN); reg = RK_I2C_READ(sc, RK_I2C_CON); reg |= RK_I2C_CON_LASTACK; RK_I2C_WRITE(sc, RK_I2C_CON, reg); RK_I2C_WRITE(sc, RK_I2C_MRXCNT, sc->msg->len); } else { sc->state = STATE_WRITE; RK_I2C_WRITE(sc, RK_I2C_IEN, RK_I2C_IEN_MBTFIEN | RK_I2C_IEN_NAKRCVIEN); sc->msg->len += 1; rk_i2c_fill_tx(sc); RK_I2C_WRITE(sc, RK_I2C_MTXCNT, sc->msg->len); } break; case STATE_READ: rk_i2c_drain_rx(sc); if (sc->cnt == sc->msg->len) rk_i2c_send_stop(sc); break; case STATE_WRITE: if (sc->cnt == sc->msg->len) rk_i2c_send_stop(sc); break; case STATE_STOP: /* Disable stop bit */ reg = RK_I2C_READ(sc, RK_I2C_CON); reg &= ~RK_I2C_CON_STOP; RK_I2C_WRITE(sc, RK_I2C_CON, reg); sc->transfer_done = 1; sc->state = STATE_IDLE; break; case STATE_IDLE: break; } wakeup(sc); RK_I2C_UNLOCK(sc); } static int rk_i2c_transfer(device_t dev, struct iic_msg *msgs, uint32_t nmsgs) { struct rk_i2c_softc *sc; uint32_t reg; int i, j, msgskip, err = 0; sc = device_get_softc(dev); while (sc->busy) mtx_sleep(sc, &sc->mtx, 0, "i2cbuswait", 0); sc->busy = 1; err = clk_enable(sc->pclk); if (err != 0) { device_printf(dev, "cannot enable pclk clock\n"); goto out; } err = clk_enable(sc->sclk); if (err != 0) { device_printf(dev, "cannot enable i2c clock\n"); goto out; } RK_I2C_LOCK(sc); /* Clean stale interrupts */ RK_I2C_WRITE(sc, RK_I2C_IPD, RK_I2C_IPD_ALL); for (i = 0; i < nmsgs; i += msgskip) { if (nmsgs - i >= 2 && !(msgs[i].flags & IIC_M_RD) && msgs[i + 1].flags & IIC_M_RD && msgs[i].len <= 4) { sc->mode = RK_I2C_CON_MODE_RRX; msgskip = 2; sc->msg = &msgs[i + 1]; /* Write slave address */ reg = msgs[i].slave | RK_I2C_MRXADDR_VALID(0); RK_I2C_WRITE(sc, RK_I2C_MRXADDR, reg); /* Write slave register address */ for (j = 0, reg = 0; j < msgs[i].len; j++) { reg |= (msgs[i].buf[j] & 0xff) << (j * 8); reg |= RK_I2C_MRXADDR_VALID(j); } RK_I2C_WRITE(sc, RK_I2C_MRXRADDR, reg); } else { if (msgs[i].flags & IIC_M_RD) { sc->mode = RK_I2C_CON_MODE_RX; msgs[i].slave |= LSB; } else { sc->mode = RK_I2C_CON_MODE_TX; msgs[i].slave &= ~LSB; } msgskip = 1; sc->msg = &msgs[i]; } sc->transfer_done = 0; sc->cnt = 0; sc->state = STATE_IDLE; rk_i2c_send_start(sc); while (err == 0 && sc->transfer_done != 1) { err = msleep(sc, &sc->mtx, 0, "rk_i2c", 10 * hz); } } /* Disable the module and interrupts */ RK_I2C_WRITE(sc, RK_I2C_CON, 0); RK_I2C_WRITE(sc, RK_I2C_IEN, 0); sc->busy = 0; RK_I2C_UNLOCK(sc); err = clk_disable(sc->pclk); if (err != 0) { device_printf(dev, "cannot enable pclk clock\n"); goto out; } err = clk_disable(sc->sclk); if (err != 0) { device_printf(dev, "cannot enable i2c clock\n"); goto out; } out: return (err); } static int rk_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, "RockChip I2C"); return (BUS_PROBE_DEFAULT); } static int rk_i2c_attach(device_t dev) { struct rk_i2c_softc *sc; int error; sc = device_get_softc(dev); sc->dev = dev; mtx_init(&sc->mtx, device_get_nameunit(dev), "rk_i2c", MTX_DEF); if (bus_alloc_resources(dev, rk_i2c_spec, sc->res) != 0) { device_printf(dev, "cannot allocate resources for device\n"); error = ENXIO; goto fail; } if (bus_setup_intr(dev, sc->res[1], INTR_TYPE_MISC | INTR_MPSAFE, NULL, rk_i2c_intr, sc, &sc->intrhand)) { bus_release_resources(dev, rk_i2c_spec, sc->res); device_printf(dev, "cannot setup interrupt handler\n"); return (ENXIO); } clk_set_assigned(dev, ofw_bus_get_node(dev)); /* Activate the module clocks. */ error = clk_get_by_ofw_name(dev, 0, "i2c", &sc->sclk); if (error != 0) { device_printf(dev, "cannot get i2c clock\n"); goto fail; } error = clk_get_by_ofw_name(dev, 0, "pclk", &sc->pclk); if (error != 0) { device_printf(dev, "cannot get pclk clock\n"); goto fail; } sc->iicbus = device_add_child(dev, "iicbus", -1); if (sc->iicbus == NULL) { device_printf(dev, "cannot add iicbus child device\n"); error = ENXIO; goto fail; } bus_generic_attach(dev); return (0); fail: if (rk_i2c_detach(dev) != 0) device_printf(dev, "Failed to detach\n"); return (error); } static int rk_i2c_detach(device_t dev) { struct rk_i2c_softc *sc; int error; sc = device_get_softc(dev); if ((error = bus_generic_detach(dev)) != 0) return (error); if (sc->iicbus != NULL) if ((error = device_delete_child(dev, sc->iicbus)) != 0) return (error); if (sc->sclk != NULL) clk_release(sc->sclk); if (sc->pclk != NULL) clk_release(sc->pclk); if (sc->intrhand != NULL) bus_teardown_intr(sc->dev, sc->res[1], sc->intrhand); bus_release_resources(dev, rk_i2c_spec, sc->res); mtx_destroy(&sc->mtx); return (0); } static phandle_t rk_i2c_get_node(device_t bus, device_t dev) { return ofw_bus_get_node(bus); } static device_method_t rk_i2c_methods[] = { DEVMETHOD(device_probe, rk_i2c_probe), DEVMETHOD(device_attach, rk_i2c_attach), DEVMETHOD(device_detach, rk_i2c_detach), /* OFW methods */ DEVMETHOD(ofw_bus_get_node, rk_i2c_get_node), DEVMETHOD(iicbus_callback, iicbus_null_callback), DEVMETHOD(iicbus_reset, rk_i2c_reset), DEVMETHOD(iicbus_transfer, rk_i2c_transfer), DEVMETHOD_END }; static driver_t rk_i2c_driver = { "rk_i2c", rk_i2c_methods, sizeof(struct rk_i2c_softc), }; static devclass_t rk_i2c_devclass; EARLY_DRIVER_MODULE(rk_i2c, simplebus, rk_i2c_driver, rk_i2c_devclass, 0, 0, BUS_PASS_INTERRUPT + BUS_PASS_ORDER_LATE); EARLY_DRIVER_MODULE(ofw_iicbus, rk_i2c, ofw_iicbus_driver, ofw_iicbus_devclass, 0, 0, BUS_PASS_INTERRUPT + BUS_PASS_ORDER_LATE); MODULE_DEPEND(rk_i2c, iicbus, 1, 1, 1); -MODULE_DEPEND(rk_i2c, ofw_iicbus, 1, 1, 1); MODULE_VERSION(rk_i2c, 1); Index: stable/12 =================================================================== --- stable/12 (revision 356282) +++ stable/12 (revision 356283) Property changes on: stable/12 ___________________________________________________________________ Modified: svn:mergeinfo ## -0,0 +0,1 ## Merged /head:r355860