18a82d567SLuiz Otavio O Souza /*- 28a82d567SLuiz Otavio O Souza * SPDX-License-Identifier: BSD-2-Clause-FreeBSD 38a82d567SLuiz Otavio O Souza * 48a82d567SLuiz Otavio O Souza * Copyright (c) 2018, 2019 Rubicon Communications, LLC (Netgate) 58a82d567SLuiz Otavio O Souza * 68a82d567SLuiz Otavio O Souza * Redistribution and use in source and binary forms, with or without 78a82d567SLuiz Otavio O Souza * modification, are permitted provided that the following conditions 88a82d567SLuiz Otavio O Souza * are met: 98a82d567SLuiz Otavio O Souza * 1. Redistributions of source code must retain the above copyright 108a82d567SLuiz Otavio O Souza * notice, this list of conditions and the following disclaimer. 118a82d567SLuiz Otavio O Souza * 2. Redistributions in binary form must reproduce the above copyright 128a82d567SLuiz Otavio O Souza * notice, this list of conditions and the following disclaimer in the 138a82d567SLuiz Otavio O Souza * documentation and/or other materials provided with the distribution. 148a82d567SLuiz Otavio O Souza * 158a82d567SLuiz Otavio O Souza * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 168a82d567SLuiz Otavio O Souza * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 178a82d567SLuiz Otavio O Souza * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 188a82d567SLuiz Otavio O Souza * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 198a82d567SLuiz Otavio O Souza * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 208a82d567SLuiz Otavio O Souza * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 218a82d567SLuiz Otavio O Souza * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 228a82d567SLuiz Otavio O Souza * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 238a82d567SLuiz Otavio O Souza * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 248a82d567SLuiz Otavio O Souza * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 258a82d567SLuiz Otavio O Souza * SUCH DAMAGE. 268a82d567SLuiz Otavio O Souza * 278a82d567SLuiz Otavio O Souza */ 288a82d567SLuiz Otavio O Souza #include <sys/cdefs.h> 298a82d567SLuiz Otavio O Souza __FBSDID("$FreeBSD$"); 308a82d567SLuiz Otavio O Souza 318a82d567SLuiz Otavio O Souza /* 328a82d567SLuiz Otavio O Souza * Driver for Armada 37x0 i2c controller. 338a82d567SLuiz Otavio O Souza */ 348a82d567SLuiz Otavio O Souza 358a82d567SLuiz Otavio O Souza #include <sys/param.h> 368a82d567SLuiz Otavio O Souza #include <sys/systm.h> 378a82d567SLuiz Otavio O Souza #include <sys/kernel.h> 388a82d567SLuiz Otavio O Souza #include <sys/lock.h> 398a82d567SLuiz Otavio O Souza #include <sys/module.h> 408a82d567SLuiz Otavio O Souza #include <sys/mutex.h> 418a82d567SLuiz Otavio O Souza #include <sys/bus.h> 428a82d567SLuiz Otavio O Souza #include <machine/resource.h> 438a82d567SLuiz Otavio O Souza #include <machine/bus.h> 448a82d567SLuiz Otavio O Souza #include <sys/rman.h> 458a82d567SLuiz Otavio O Souza #include <sys/sysctl.h> 468a82d567SLuiz Otavio O Souza 478a82d567SLuiz Otavio O Souza #include <dev/iicbus/iicbus.h> 488a82d567SLuiz Otavio O Souza #include <dev/iicbus/iiconf.h> 498a82d567SLuiz Otavio O Souza #include <dev/ofw/ofw_bus.h> 508a82d567SLuiz Otavio O Souza #include <dev/ofw/ofw_bus_subr.h> 518a82d567SLuiz Otavio O Souza 522b289d0bSLuiz Otavio O Souza #include <arm/mv/a37x0_iicreg.h> 538a82d567SLuiz Otavio O Souza 548a82d567SLuiz Otavio O Souza #include "iicbus_if.h" 558a82d567SLuiz Otavio O Souza 568a82d567SLuiz Otavio O Souza struct a37x0_iic_softc { 578a82d567SLuiz Otavio O Souza boolean_t sc_fast_mode; 588a82d567SLuiz Otavio O Souza bus_space_tag_t sc_bst; 598a82d567SLuiz Otavio O Souza bus_space_handle_t sc_bsh; 608a82d567SLuiz Otavio O Souza device_t sc_dev; 618a82d567SLuiz Otavio O Souza device_t sc_iicbus; 628a82d567SLuiz Otavio O Souza struct mtx sc_mtx; 638a82d567SLuiz Otavio O Souza struct resource *sc_mem_res; 648a82d567SLuiz Otavio O Souza struct resource *sc_irq_res; 658a82d567SLuiz Otavio O Souza void *sc_intrhand; 668a82d567SLuiz Otavio O Souza }; 678a82d567SLuiz Otavio O Souza 688a82d567SLuiz Otavio O Souza #define A37X0_IIC_WRITE(_sc, _off, _val) \ 698a82d567SLuiz Otavio O Souza bus_space_write_4((_sc)->sc_bst, (_sc)->sc_bsh, _off, _val) 708a82d567SLuiz Otavio O Souza #define A37X0_IIC_READ(_sc, _off) \ 718a82d567SLuiz Otavio O Souza bus_space_read_4((_sc)->sc_bst, (_sc)->sc_bsh, _off) 728a82d567SLuiz Otavio O Souza #define A37X0_IIC_LOCK(_sc) mtx_lock(&(_sc)->sc_mtx) 738a82d567SLuiz Otavio O Souza #define A37X0_IIC_UNLOCK(_sc) mtx_unlock(&(_sc)->sc_mtx) 748a82d567SLuiz Otavio O Souza 758a82d567SLuiz Otavio O Souza static struct ofw_compat_data compat_data[] = { 768a82d567SLuiz Otavio O Souza { "marvell,armada-3700-i2c", 1 }, 778a82d567SLuiz Otavio O Souza { NULL, 0 } 788a82d567SLuiz Otavio O Souza }; 798a82d567SLuiz Otavio O Souza 808a82d567SLuiz Otavio O Souza #undef A37x0_IIC_DEBUG 818a82d567SLuiz Otavio O Souza 828a82d567SLuiz Otavio O Souza static void a37x0_iic_intr(void *); 838a82d567SLuiz Otavio O Souza static int a37x0_iic_detach(device_t); 848a82d567SLuiz Otavio O Souza 858a82d567SLuiz Otavio O Souza static void 868a82d567SLuiz Otavio O Souza a37x0_iic_rmw(struct a37x0_iic_softc *sc, uint32_t off, uint32_t mask, 878a82d567SLuiz Otavio O Souza uint32_t value) 888a82d567SLuiz Otavio O Souza { 898a82d567SLuiz Otavio O Souza uint32_t reg; 908a82d567SLuiz Otavio O Souza 918a82d567SLuiz Otavio O Souza mtx_assert(&sc->sc_mtx, MA_OWNED); 928a82d567SLuiz Otavio O Souza reg = A37X0_IIC_READ(sc, off); 938a82d567SLuiz Otavio O Souza reg &= ~mask; 948a82d567SLuiz Otavio O Souza reg |= value; 958a82d567SLuiz Otavio O Souza A37X0_IIC_WRITE(sc, off, reg); 968a82d567SLuiz Otavio O Souza } 978a82d567SLuiz Otavio O Souza 988a82d567SLuiz Otavio O Souza static int 998a82d567SLuiz Otavio O Souza a37x0_iic_wait_clear(struct a37x0_iic_softc *sc, uint32_t mask) 1008a82d567SLuiz Otavio O Souza { 1018a82d567SLuiz Otavio O Souza int timeout; 1028a82d567SLuiz Otavio O Souza uint32_t status; 1038a82d567SLuiz Otavio O Souza 1048a82d567SLuiz Otavio O Souza mtx_assert(&sc->sc_mtx, MA_OWNED); 1058a82d567SLuiz Otavio O Souza timeout = 1000; 1068a82d567SLuiz Otavio O Souza do { 1078a82d567SLuiz Otavio O Souza DELAY(10); 1088a82d567SLuiz Otavio O Souza status = A37X0_IIC_READ(sc, A37X0_IIC_ISR); 1098a82d567SLuiz Otavio O Souza if (--timeout == 0) 1108a82d567SLuiz Otavio O Souza return (0); 1118a82d567SLuiz Otavio O Souza } while ((status & mask) != 0); 1128a82d567SLuiz Otavio O Souza 1138a82d567SLuiz Otavio O Souza return (1); 1148a82d567SLuiz Otavio O Souza } 1158a82d567SLuiz Otavio O Souza 1168a82d567SLuiz Otavio O Souza static int 1178a82d567SLuiz Otavio O Souza a37x0_iic_wait_set(struct a37x0_iic_softc *sc, uint32_t mask) 1188a82d567SLuiz Otavio O Souza { 1198a82d567SLuiz Otavio O Souza int timeout; 1208a82d567SLuiz Otavio O Souza uint32_t status; 1218a82d567SLuiz Otavio O Souza 1228a82d567SLuiz Otavio O Souza mtx_assert(&sc->sc_mtx, MA_OWNED); 1238a82d567SLuiz Otavio O Souza timeout = 1000; 1248a82d567SLuiz Otavio O Souza do { 1258a82d567SLuiz Otavio O Souza DELAY(10); 1268a82d567SLuiz Otavio O Souza status = A37X0_IIC_READ(sc, A37X0_IIC_ISR); 1278a82d567SLuiz Otavio O Souza if (--timeout == 0) 1288a82d567SLuiz Otavio O Souza return (0); 1298a82d567SLuiz Otavio O Souza } while ((status & mask) != mask); 1308a82d567SLuiz Otavio O Souza 1318a82d567SLuiz Otavio O Souza return (1); 1328a82d567SLuiz Otavio O Souza } 1338a82d567SLuiz Otavio O Souza 1348a82d567SLuiz Otavio O Souza #ifdef A37x0_IIC_DEBUG 1358a82d567SLuiz Otavio O Souza static void 1368a82d567SLuiz Otavio O Souza a37x0_iic_regdump(struct a37x0_iic_softc *sc) 1378a82d567SLuiz Otavio O Souza { 1388a82d567SLuiz Otavio O Souza 1398a82d567SLuiz Otavio O Souza mtx_assert(&sc->sc_mtx, MA_OWNED); 1408a82d567SLuiz Otavio O Souza printf("%s: IBMR: %#x\n", __func__, A37X0_IIC_READ(sc, A37X0_IIC_IBMR)); 1418a82d567SLuiz Otavio O Souza printf("%s: ICR: %#x\n", __func__, A37X0_IIC_READ(sc, A37X0_IIC_ICR)); 1428a82d567SLuiz Otavio O Souza printf("%s: ISR: %#x\n", __func__, A37X0_IIC_READ(sc, A37X0_IIC_ISR)); 1438a82d567SLuiz Otavio O Souza } 1448a82d567SLuiz Otavio O Souza #endif 1458a82d567SLuiz Otavio O Souza 1468a82d567SLuiz Otavio O Souza static void 1478a82d567SLuiz Otavio O Souza a37x0_iic_reset(struct a37x0_iic_softc *sc) 1488a82d567SLuiz Otavio O Souza { 1498a82d567SLuiz Otavio O Souza uint32_t mode, reg; 1508a82d567SLuiz Otavio O Souza 1518a82d567SLuiz Otavio O Souza mtx_assert(&sc->sc_mtx, MA_OWNED); 1528a82d567SLuiz Otavio O Souza 1538a82d567SLuiz Otavio O Souza /* Disable the controller. */ 1548a82d567SLuiz Otavio O Souza reg = A37X0_IIC_READ(sc, A37X0_IIC_ICR); 1558a82d567SLuiz Otavio O Souza mode = reg & ICR_MODE_MASK; 1568a82d567SLuiz Otavio O Souza A37X0_IIC_WRITE(sc, A37X0_IIC_ICR, reg & ~ICR_IUE); 1578a82d567SLuiz Otavio O Souza A37X0_IIC_WRITE(sc, A37X0_IIC_ICR, reg | ICR_UR); 1588a82d567SLuiz Otavio O Souza DELAY(100); 1598a82d567SLuiz Otavio O Souza A37X0_IIC_WRITE(sc, A37X0_IIC_ICR, reg & ~ICR_IUE); 1608a82d567SLuiz Otavio O Souza 1618a82d567SLuiz Otavio O Souza /* Enable the controller. */ 1628a82d567SLuiz Otavio O Souza reg = A37X0_IIC_READ(sc, A37X0_IIC_ICR); 1638a82d567SLuiz Otavio O Souza reg |= mode | ICR_IUE | ICR_GCD | ICR_SCLE; 1648a82d567SLuiz Otavio O Souza A37X0_IIC_WRITE(sc, A37X0_IIC_ICR, reg); 1658a82d567SLuiz Otavio O Souza #ifdef A37x0_IIC_DEBUG 1668a82d567SLuiz Otavio O Souza a37x0_iic_regdump(sc); 1678a82d567SLuiz Otavio O Souza #endif 1688a82d567SLuiz Otavio O Souza } 1698a82d567SLuiz Otavio O Souza 1708a82d567SLuiz Otavio O Souza static int 1718a82d567SLuiz Otavio O Souza a37x0_iic_probe(device_t dev) 1728a82d567SLuiz Otavio O Souza { 1738a82d567SLuiz Otavio O Souza 1748a82d567SLuiz Otavio O Souza if (!ofw_bus_status_okay(dev)) 1758a82d567SLuiz Otavio O Souza return (ENXIO); 1768a82d567SLuiz Otavio O Souza 1778a82d567SLuiz Otavio O Souza if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0) 1788a82d567SLuiz Otavio O Souza return (ENXIO); 1798a82d567SLuiz Otavio O Souza 1808a82d567SLuiz Otavio O Souza device_set_desc(dev, "Marvell Armada 37x0 IIC controller"); 1818a82d567SLuiz Otavio O Souza 1828a82d567SLuiz Otavio O Souza return (BUS_PROBE_DEFAULT); 1838a82d567SLuiz Otavio O Souza } 1848a82d567SLuiz Otavio O Souza 1858a82d567SLuiz Otavio O Souza static int 1868a82d567SLuiz Otavio O Souza a37x0_iic_attach(device_t dev) 1878a82d567SLuiz Otavio O Souza { 1888a82d567SLuiz Otavio O Souza int rid; 1898a82d567SLuiz Otavio O Souza phandle_t node; 1908a82d567SLuiz Otavio O Souza struct a37x0_iic_softc *sc; 1918a82d567SLuiz Otavio O Souza 1928a82d567SLuiz Otavio O Souza sc = device_get_softc(dev); 1938a82d567SLuiz Otavio O Souza sc->sc_dev = dev; 1948a82d567SLuiz Otavio O Souza 1958a82d567SLuiz Otavio O Souza rid = 0; 1968a82d567SLuiz Otavio O Souza sc->sc_mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, 1978a82d567SLuiz Otavio O Souza RF_ACTIVE); 1988a82d567SLuiz Otavio O Souza if (!sc->sc_mem_res) { 1998a82d567SLuiz Otavio O Souza device_printf(dev, "cannot allocate memory window\n"); 2008a82d567SLuiz Otavio O Souza return (ENXIO); 2018a82d567SLuiz Otavio O Souza } 2028a82d567SLuiz Otavio O Souza 2038a82d567SLuiz Otavio O Souza sc->sc_bst = rman_get_bustag(sc->sc_mem_res); 2048a82d567SLuiz Otavio O Souza sc->sc_bsh = rman_get_bushandle(sc->sc_mem_res); 2058a82d567SLuiz Otavio O Souza 2068a82d567SLuiz Otavio O Souza rid = 0; 2078a82d567SLuiz Otavio O Souza sc->sc_irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, 2088a82d567SLuiz Otavio O Souza RF_ACTIVE | RF_SHAREABLE); 2098a82d567SLuiz Otavio O Souza if (!sc->sc_irq_res) { 2108a82d567SLuiz Otavio O Souza bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->sc_mem_res); 2118a82d567SLuiz Otavio O Souza device_printf(dev, "cannot allocate interrupt\n"); 2128a82d567SLuiz Otavio O Souza return (ENXIO); 2138a82d567SLuiz Otavio O Souza } 2148a82d567SLuiz Otavio O Souza 2158a82d567SLuiz Otavio O Souza /* Hook up our interrupt handler. */ 2168a82d567SLuiz Otavio O Souza if (bus_setup_intr(dev, sc->sc_irq_res, INTR_TYPE_MISC | INTR_MPSAFE, 2178a82d567SLuiz Otavio O Souza NULL, a37x0_iic_intr, sc, &sc->sc_intrhand)) { 2188a82d567SLuiz Otavio O Souza bus_release_resource(dev, SYS_RES_IRQ, 0, sc->sc_irq_res); 2198a82d567SLuiz Otavio O Souza bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->sc_mem_res); 2208a82d567SLuiz Otavio O Souza device_printf(dev, "cannot setup the interrupt handler\n"); 2218a82d567SLuiz Otavio O Souza return (ENXIO); 2228a82d567SLuiz Otavio O Souza } 2238a82d567SLuiz Otavio O Souza 2248a82d567SLuiz Otavio O Souza mtx_init(&sc->sc_mtx, "a37x0_iic", NULL, MTX_DEF); 2258a82d567SLuiz Otavio O Souza 2268a82d567SLuiz Otavio O Souza node = ofw_bus_get_node(dev); 2278a82d567SLuiz Otavio O Souza if (OF_hasprop(node, "mrvl,i2c-fast-mode")) 2288a82d567SLuiz Otavio O Souza sc->sc_fast_mode = true; 2298a82d567SLuiz Otavio O Souza 2308a82d567SLuiz Otavio O Souza /* Enable the controller. */ 2318a82d567SLuiz Otavio O Souza A37X0_IIC_LOCK(sc); 2328a82d567SLuiz Otavio O Souza a37x0_iic_reset(sc); 2338a82d567SLuiz Otavio O Souza A37X0_IIC_UNLOCK(sc); 2348a82d567SLuiz Otavio O Souza 2358a82d567SLuiz Otavio O Souza sc->sc_iicbus = device_add_child(dev, "iicbus", -1); 2368a82d567SLuiz Otavio O Souza if (sc->sc_iicbus == NULL) { 2378a82d567SLuiz Otavio O Souza a37x0_iic_detach(dev); 2388a82d567SLuiz Otavio O Souza return (ENXIO); 2398a82d567SLuiz Otavio O Souza } 2408a82d567SLuiz Otavio O Souza 2418a82d567SLuiz Otavio O Souza /* Probe and attach the iicbus. */ 2428a82d567SLuiz Otavio O Souza return (bus_generic_attach(dev)); 2438a82d567SLuiz Otavio O Souza } 2448a82d567SLuiz Otavio O Souza 2458a82d567SLuiz Otavio O Souza static int 2468a82d567SLuiz Otavio O Souza a37x0_iic_detach(device_t dev) 2478a82d567SLuiz Otavio O Souza { 2488a82d567SLuiz Otavio O Souza struct a37x0_iic_softc *sc; 2498a82d567SLuiz Otavio O Souza 2508a82d567SLuiz Otavio O Souza bus_generic_detach(dev); 2518a82d567SLuiz Otavio O Souza 2528a82d567SLuiz Otavio O Souza sc = device_get_softc(dev); 2538a82d567SLuiz Otavio O Souza if (sc->sc_iicbus != NULL) 2548a82d567SLuiz Otavio O Souza device_delete_child(dev, sc->sc_iicbus); 2558a82d567SLuiz Otavio O Souza mtx_destroy(&sc->sc_mtx); 2568a82d567SLuiz Otavio O Souza if (sc->sc_intrhand) 2578a82d567SLuiz Otavio O Souza bus_teardown_intr(dev, sc->sc_irq_res, sc->sc_intrhand); 2588a82d567SLuiz Otavio O Souza if (sc->sc_irq_res) 2598a82d567SLuiz Otavio O Souza bus_release_resource(dev, SYS_RES_IRQ, 0, sc->sc_irq_res); 2608a82d567SLuiz Otavio O Souza if (sc->sc_mem_res) 2618a82d567SLuiz Otavio O Souza bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->sc_mem_res); 2628a82d567SLuiz Otavio O Souza 2638a82d567SLuiz Otavio O Souza return (0); 2648a82d567SLuiz Otavio O Souza } 2658a82d567SLuiz Otavio O Souza 2668a82d567SLuiz Otavio O Souza static void 2678a82d567SLuiz Otavio O Souza a37x0_iic_intr(void *arg) 2688a82d567SLuiz Otavio O Souza { 2698a82d567SLuiz Otavio O Souza struct a37x0_iic_softc *sc; 2708a82d567SLuiz Otavio O Souza uint32_t status; 2718a82d567SLuiz Otavio O Souza 2728a82d567SLuiz Otavio O Souza /* Not used, the interrupts are not enabled. */ 2738a82d567SLuiz Otavio O Souza sc = (struct a37x0_iic_softc *)arg; 2748a82d567SLuiz Otavio O Souza A37X0_IIC_LOCK(sc); 2758a82d567SLuiz Otavio O Souza status = A37X0_IIC_READ(sc, A37X0_IIC_ISR); 2768a82d567SLuiz Otavio O Souza #ifdef A37x0_IIC_DEBUG 2778a82d567SLuiz Otavio O Souza a37x0_iic_regdump(sc); 2788a82d567SLuiz Otavio O Souza #endif 2798a82d567SLuiz Otavio O Souza 2808a82d567SLuiz Otavio O Souza /* Clear pending interrrupts. */ 2818a82d567SLuiz Otavio O Souza A37X0_IIC_WRITE(sc, A37X0_IIC_ISR, status); 2828a82d567SLuiz Otavio O Souza A37X0_IIC_UNLOCK(sc); 2838a82d567SLuiz Otavio O Souza } 2848a82d567SLuiz Otavio O Souza 2858a82d567SLuiz Otavio O Souza static int 2868a82d567SLuiz Otavio O Souza a37x0_iic_stop(device_t dev) 2878a82d567SLuiz Otavio O Souza { 2888a82d567SLuiz Otavio O Souza struct a37x0_iic_softc *sc; 2898a82d567SLuiz Otavio O Souza uint32_t reg; 2908a82d567SLuiz Otavio O Souza 2918a82d567SLuiz Otavio O Souza sc = device_get_softc(dev); 2928a82d567SLuiz Otavio O Souza A37X0_IIC_LOCK(sc); 2938a82d567SLuiz Otavio O Souza /* Clear the STOP condition. */ 2948a82d567SLuiz Otavio O Souza reg = A37X0_IIC_READ(sc, A37X0_IIC_ICR); 2958a82d567SLuiz Otavio O Souza if (reg & (ICR_ACKNAK | ICR_STOP)) { 2968a82d567SLuiz Otavio O Souza reg &= ~(ICR_START | ICR_ACKNAK | ICR_STOP); 2978a82d567SLuiz Otavio O Souza A37X0_IIC_WRITE(sc, A37X0_IIC_ICR, reg); 2988a82d567SLuiz Otavio O Souza } 2998a82d567SLuiz Otavio O Souza /* Clear interrupts. */ 3008a82d567SLuiz Otavio O Souza reg = A37X0_IIC_READ(sc, A37X0_IIC_ISR); 3018a82d567SLuiz Otavio O Souza A37X0_IIC_WRITE(sc, A37X0_IIC_ISR, reg); 3028a82d567SLuiz Otavio O Souza A37X0_IIC_UNLOCK(sc); 3038a82d567SLuiz Otavio O Souza 3048a82d567SLuiz Otavio O Souza return (IIC_NOERR); 3058a82d567SLuiz Otavio O Souza } 3068a82d567SLuiz Otavio O Souza 3078a82d567SLuiz Otavio O Souza static int 3088a82d567SLuiz Otavio O Souza a37x0_iic_start(device_t dev, u_char slave, int timeout) 3098a82d567SLuiz Otavio O Souza { 3108a82d567SLuiz Otavio O Souza int rv; 3118a82d567SLuiz Otavio O Souza struct a37x0_iic_softc *sc; 3128a82d567SLuiz Otavio O Souza uint32_t reg, status; 3138a82d567SLuiz Otavio O Souza 3148a82d567SLuiz Otavio O Souza sc = device_get_softc(dev); 3158a82d567SLuiz Otavio O Souza A37X0_IIC_LOCK(sc); 3168a82d567SLuiz Otavio O Souza 3178a82d567SLuiz Otavio O Souza /* Wait for the bus to be free before start a transaction. */ 3188a82d567SLuiz Otavio O Souza if (a37x0_iic_wait_clear(sc, ISR_IBB) == 0) { 3198a82d567SLuiz Otavio O Souza A37X0_IIC_UNLOCK(sc); 3208a82d567SLuiz Otavio O Souza return (IIC_ETIMEOUT); 3218a82d567SLuiz Otavio O Souza } 3228a82d567SLuiz Otavio O Souza 3238a82d567SLuiz Otavio O Souza /* Write the slave address. */ 3248a82d567SLuiz Otavio O Souza A37X0_IIC_WRITE(sc, A37X0_IIC_IDBR, slave); 3258a82d567SLuiz Otavio O Souza 3268a82d567SLuiz Otavio O Souza /* Send Start condition (with slave address). */ 3278a82d567SLuiz Otavio O Souza reg = A37X0_IIC_READ(sc, A37X0_IIC_ICR); 3288a82d567SLuiz Otavio O Souza reg &= ~(ICR_STOP | ICR_ACKNAK); 3298a82d567SLuiz Otavio O Souza A37X0_IIC_WRITE(sc, A37X0_IIC_ICR, reg | ICR_START | ICR_TB); 3308a82d567SLuiz Otavio O Souza 3318a82d567SLuiz Otavio O Souza rv = IIC_NOERR; 3328a82d567SLuiz Otavio O Souza if (a37x0_iic_wait_set(sc, ISR_ITE) == 0) 3338a82d567SLuiz Otavio O Souza rv = IIC_ETIMEOUT; 3348a82d567SLuiz Otavio O Souza if (rv == IIC_NOERR) { 3358a82d567SLuiz Otavio O Souza status = A37X0_IIC_READ(sc, A37X0_IIC_ISR); 3368a82d567SLuiz Otavio O Souza A37X0_IIC_WRITE(sc, A37X0_IIC_ISR, status | ISR_ITE); 3378a82d567SLuiz Otavio O Souza if (a37x0_iic_wait_clear(sc, ISR_ACKNAK) == 0) 3388a82d567SLuiz Otavio O Souza rv = IIC_ENOACK; 3398a82d567SLuiz Otavio O Souza } 3408a82d567SLuiz Otavio O Souza 3418a82d567SLuiz Otavio O Souza A37X0_IIC_UNLOCK(sc); 3428a82d567SLuiz Otavio O Souza if (rv != IIC_NOERR) 3438a82d567SLuiz Otavio O Souza a37x0_iic_stop(dev); 3448a82d567SLuiz Otavio O Souza 3458a82d567SLuiz Otavio O Souza return (rv); 3468a82d567SLuiz Otavio O Souza } 3478a82d567SLuiz Otavio O Souza 3488a82d567SLuiz Otavio O Souza static int 3498a82d567SLuiz Otavio O Souza a37x0_iic_bus_reset(device_t dev, u_char speed, u_char addr, u_char *oldaddr) 3508a82d567SLuiz Otavio O Souza { 3518a82d567SLuiz Otavio O Souza struct a37x0_iic_softc *sc; 3528a82d567SLuiz Otavio O Souza uint32_t busfreq; 3538a82d567SLuiz Otavio O Souza 3548a82d567SLuiz Otavio O Souza sc = device_get_softc(dev); 3558a82d567SLuiz Otavio O Souza A37X0_IIC_LOCK(sc); 3568a82d567SLuiz Otavio O Souza a37x0_iic_reset(sc); 3578a82d567SLuiz Otavio O Souza if (sc->sc_iicbus == NULL) 3588a82d567SLuiz Otavio O Souza busfreq = 100000; 3598a82d567SLuiz Otavio O Souza else 3608a82d567SLuiz Otavio O Souza busfreq = IICBUS_GET_FREQUENCY(sc->sc_iicbus, speed); 3618a82d567SLuiz Otavio O Souza a37x0_iic_rmw(sc, A37X0_IIC_ICR, ICR_MODE_MASK, 3628a82d567SLuiz Otavio O Souza (busfreq > 100000) ? ICR_FAST_MODE : 0); 3638a82d567SLuiz Otavio O Souza A37X0_IIC_UNLOCK(sc); 3648a82d567SLuiz Otavio O Souza 3658a82d567SLuiz Otavio O Souza return (IIC_ENOADDR); 3668a82d567SLuiz Otavio O Souza } 3678a82d567SLuiz Otavio O Souza 3688a82d567SLuiz Otavio O Souza static int 3698a82d567SLuiz Otavio O Souza a37x0_iic_read(device_t dev, char *buf, int len, int *read, int last, int delay) 3708a82d567SLuiz Otavio O Souza { 3718a82d567SLuiz Otavio O Souza int rv; 3728a82d567SLuiz Otavio O Souza struct a37x0_iic_softc *sc; 3738a82d567SLuiz Otavio O Souza uint32_t reg, status; 3748a82d567SLuiz Otavio O Souza 3758a82d567SLuiz Otavio O Souza sc = device_get_softc(dev); 3768a82d567SLuiz Otavio O Souza A37X0_IIC_LOCK(sc); 3778a82d567SLuiz Otavio O Souza reg = A37X0_IIC_READ(sc, A37X0_IIC_ISR); 3788a82d567SLuiz Otavio O Souza if ((reg & (ISR_UB | ISR_IBB)) != ISR_UB) { 3798a82d567SLuiz Otavio O Souza A37X0_IIC_UNLOCK(sc); 3808a82d567SLuiz Otavio O Souza return (IIC_EBUSERR); 3818a82d567SLuiz Otavio O Souza } 3828a82d567SLuiz Otavio O Souza 3838a82d567SLuiz Otavio O Souza *read = 0; 3848a82d567SLuiz Otavio O Souza rv = IIC_NOERR; 3858a82d567SLuiz Otavio O Souza while (*read < len) { 3868a82d567SLuiz Otavio O Souza reg = A37X0_IIC_READ(sc, A37X0_IIC_ICR); 3878a82d567SLuiz Otavio O Souza reg &= ~(ICR_START | ICR_STOP | ICR_ACKNAK); 3888a82d567SLuiz Otavio O Souza if (*read == len - 1) 3898a82d567SLuiz Otavio O Souza reg |= ICR_ACKNAK | ICR_STOP; 3908a82d567SLuiz Otavio O Souza A37X0_IIC_WRITE(sc, A37X0_IIC_ICR, reg | ICR_TB); 3918a82d567SLuiz Otavio O Souza if (a37x0_iic_wait_set(sc, ISR_IRF) == 0) { 3928a82d567SLuiz Otavio O Souza rv = IIC_ETIMEOUT; 3938a82d567SLuiz Otavio O Souza break; 3948a82d567SLuiz Otavio O Souza } 3958a82d567SLuiz Otavio O Souza *buf++ = A37X0_IIC_READ(sc, A37X0_IIC_IDBR); 3968a82d567SLuiz Otavio O Souza (*read)++; 3978a82d567SLuiz Otavio O Souza status = A37X0_IIC_READ(sc, A37X0_IIC_ISR); 3988a82d567SLuiz Otavio O Souza A37X0_IIC_WRITE(sc, A37X0_IIC_ISR, status | ISR_IRF); 3998a82d567SLuiz Otavio O Souza } 4008a82d567SLuiz Otavio O Souza A37X0_IIC_UNLOCK(sc); 4018a82d567SLuiz Otavio O Souza 4028a82d567SLuiz Otavio O Souza return (rv); 4038a82d567SLuiz Otavio O Souza } 4048a82d567SLuiz Otavio O Souza 4058a82d567SLuiz Otavio O Souza static int 4068a82d567SLuiz Otavio O Souza a37x0_iic_write(device_t dev, const char *buf, int len, int *sent, int timeout) 4078a82d567SLuiz Otavio O Souza { 4088a82d567SLuiz Otavio O Souza int rv; 4098a82d567SLuiz Otavio O Souza struct a37x0_iic_softc *sc; 4108a82d567SLuiz Otavio O Souza uint32_t reg, status; 4118a82d567SLuiz Otavio O Souza 4128a82d567SLuiz Otavio O Souza sc = device_get_softc(dev); 4138a82d567SLuiz Otavio O Souza A37X0_IIC_LOCK(sc); 4148a82d567SLuiz Otavio O Souza reg = A37X0_IIC_READ(sc, A37X0_IIC_ISR); 4158a82d567SLuiz Otavio O Souza if ((reg & (ISR_UB | ISR_IBB)) != ISR_UB) { 4168a82d567SLuiz Otavio O Souza A37X0_IIC_UNLOCK(sc); 4178a82d567SLuiz Otavio O Souza return (IIC_EBUSERR); 4188a82d567SLuiz Otavio O Souza } 4198a82d567SLuiz Otavio O Souza 4208a82d567SLuiz Otavio O Souza rv = IIC_NOERR; 4218a82d567SLuiz Otavio O Souza *sent = 0; 4228a82d567SLuiz Otavio O Souza while (*sent < len) { 4238a82d567SLuiz Otavio O Souza A37X0_IIC_WRITE(sc, A37X0_IIC_IDBR, *buf++); 4248a82d567SLuiz Otavio O Souza reg = A37X0_IIC_READ(sc, A37X0_IIC_ICR); 4258a82d567SLuiz Otavio O Souza reg &= ~(ICR_START | ICR_STOP | ICR_ACKNAK); 4268a82d567SLuiz Otavio O Souza if (*sent == len - 1) 4278a82d567SLuiz Otavio O Souza reg |= ICR_STOP; 4288a82d567SLuiz Otavio O Souza A37X0_IIC_WRITE(sc, A37X0_IIC_ICR, reg | ICR_TB); 4298a82d567SLuiz Otavio O Souza if (a37x0_iic_wait_set(sc, ISR_ITE) == 0) { 4308a82d567SLuiz Otavio O Souza rv = IIC_ETIMEOUT; 4318a82d567SLuiz Otavio O Souza break; 4328a82d567SLuiz Otavio O Souza } 4338a82d567SLuiz Otavio O Souza (*sent)++; 4348a82d567SLuiz Otavio O Souza status = A37X0_IIC_READ(sc, A37X0_IIC_ISR); 4358a82d567SLuiz Otavio O Souza A37X0_IIC_WRITE(sc, A37X0_IIC_ISR, status | ISR_ITE); 4368a82d567SLuiz Otavio O Souza if (a37x0_iic_wait_clear(sc, ISR_ACKNAK) == 0) { 4378a82d567SLuiz Otavio O Souza rv = IIC_ENOACK; 4388a82d567SLuiz Otavio O Souza break; 4398a82d567SLuiz Otavio O Souza } 4408a82d567SLuiz Otavio O Souza } 4418a82d567SLuiz Otavio O Souza A37X0_IIC_UNLOCK(sc); 4428a82d567SLuiz Otavio O Souza 4438a82d567SLuiz Otavio O Souza return (rv); 4448a82d567SLuiz Otavio O Souza } 4458a82d567SLuiz Otavio O Souza 4468a82d567SLuiz Otavio O Souza static phandle_t 4478a82d567SLuiz Otavio O Souza a37x0_iic_get_node(device_t bus, device_t dev) 4488a82d567SLuiz Otavio O Souza { 4498a82d567SLuiz Otavio O Souza 4508a82d567SLuiz Otavio O Souza return (ofw_bus_get_node(bus)); 4518a82d567SLuiz Otavio O Souza } 4528a82d567SLuiz Otavio O Souza 4538a82d567SLuiz Otavio O Souza static device_method_t a37x0_iic_methods[] = { 4548a82d567SLuiz Otavio O Souza /* Device interface */ 4558a82d567SLuiz Otavio O Souza DEVMETHOD(device_probe, a37x0_iic_probe), 4568a82d567SLuiz Otavio O Souza DEVMETHOD(device_attach, a37x0_iic_attach), 4578a82d567SLuiz Otavio O Souza DEVMETHOD(device_detach, a37x0_iic_detach), 4588a82d567SLuiz Otavio O Souza 4598a82d567SLuiz Otavio O Souza /* iicbus interface */ 4608a82d567SLuiz Otavio O Souza DEVMETHOD(iicbus_reset, a37x0_iic_bus_reset), 4618a82d567SLuiz Otavio O Souza DEVMETHOD(iicbus_callback, iicbus_null_callback), 4628a82d567SLuiz Otavio O Souza DEVMETHOD(iicbus_transfer, iicbus_transfer_gen), 4638a82d567SLuiz Otavio O Souza DEVMETHOD(iicbus_repeated_start, a37x0_iic_start), 4648a82d567SLuiz Otavio O Souza DEVMETHOD(iicbus_start, a37x0_iic_start), 4658a82d567SLuiz Otavio O Souza DEVMETHOD(iicbus_stop, a37x0_iic_stop), 4668a82d567SLuiz Otavio O Souza DEVMETHOD(iicbus_read, a37x0_iic_read), 4678a82d567SLuiz Otavio O Souza DEVMETHOD(iicbus_write, a37x0_iic_write), 4688a82d567SLuiz Otavio O Souza 4698a82d567SLuiz Otavio O Souza /* ofw_bus interface */ 4708a82d567SLuiz Otavio O Souza DEVMETHOD(ofw_bus_get_node, a37x0_iic_get_node), 4718a82d567SLuiz Otavio O Souza 4728a82d567SLuiz Otavio O Souza DEVMETHOD_END 4738a82d567SLuiz Otavio O Souza }; 4748a82d567SLuiz Otavio O Souza 4758a82d567SLuiz Otavio O Souza static devclass_t a37x0_iic_devclass; 4768a82d567SLuiz Otavio O Souza 4778a82d567SLuiz Otavio O Souza static driver_t a37x0_iic_driver = { 4788a82d567SLuiz Otavio O Souza "iichb", 4798a82d567SLuiz Otavio O Souza a37x0_iic_methods, 4808a82d567SLuiz Otavio O Souza sizeof(struct a37x0_iic_softc), 4818a82d567SLuiz Otavio O Souza }; 4828a82d567SLuiz Otavio O Souza 483*676ea8e1SJohn Baldwin DRIVER_MODULE(iicbus, a37x0_iic, iicbus_driver, 0, 0); 4848a82d567SLuiz Otavio O Souza DRIVER_MODULE(a37x0_iic, simplebus, a37x0_iic_driver, a37x0_iic_devclass, 0, 0); 485