1fdfcae4aSLuiz Otavio O Souza /*- 2*4d846d26SWarner Losh * SPDX-License-Identifier: BSD-2-Clause 3fdfcae4aSLuiz Otavio O Souza * 4fdfcae4aSLuiz Otavio O Souza * Copyright (c) 2019 Rubicon Communications, LLC (Netgate) 5fdfcae4aSLuiz Otavio O Souza * 6fdfcae4aSLuiz Otavio O Souza * Redistribution and use in source and binary forms, with or without 7fdfcae4aSLuiz Otavio O Souza * modification, are permitted provided that the following conditions 8fdfcae4aSLuiz Otavio O Souza * are met: 9fdfcae4aSLuiz Otavio O Souza * 1. Redistributions of source code must retain the above copyright 10fdfcae4aSLuiz Otavio O Souza * notice, this list of conditions and the following disclaimer. 11fdfcae4aSLuiz Otavio O Souza * 2. Redistributions in binary form must reproduce the above copyright 12fdfcae4aSLuiz Otavio O Souza * notice, this list of conditions and the following disclaimer in the 13fdfcae4aSLuiz Otavio O Souza * documentation and/or other materials provided with the distribution. 14fdfcae4aSLuiz Otavio O Souza * 15fdfcae4aSLuiz Otavio O Souza * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 16fdfcae4aSLuiz Otavio O Souza * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17fdfcae4aSLuiz Otavio O Souza * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18fdfcae4aSLuiz Otavio O Souza * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 19fdfcae4aSLuiz Otavio O Souza * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20fdfcae4aSLuiz Otavio O Souza * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 21fdfcae4aSLuiz Otavio O Souza * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22fdfcae4aSLuiz Otavio O Souza * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23fdfcae4aSLuiz Otavio O Souza * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24fdfcae4aSLuiz Otavio O Souza * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25fdfcae4aSLuiz Otavio O Souza * SUCH DAMAGE. 26fdfcae4aSLuiz Otavio O Souza */ 27fdfcae4aSLuiz Otavio O Souza 28fdfcae4aSLuiz Otavio O Souza #include <sys/cdefs.h> 29fdfcae4aSLuiz Otavio O Souza #include "opt_platform.h" 30fdfcae4aSLuiz Otavio O Souza 31fdfcae4aSLuiz Otavio O Souza #include <sys/param.h> 32fdfcae4aSLuiz Otavio O Souza #include <sys/systm.h> 33fdfcae4aSLuiz Otavio O Souza #include <sys/bus.h> 34fdfcae4aSLuiz Otavio O Souza #include <sys/gpio.h> 35fdfcae4aSLuiz Otavio O Souza #include <sys/kernel.h> 36fdfcae4aSLuiz Otavio O Souza #include <sys/module.h> 37fdfcae4aSLuiz Otavio O Souza #include <sys/socket.h> 38fdfcae4aSLuiz Otavio O Souza 39fdfcae4aSLuiz Otavio O Souza #include <net/if.h> 40fdfcae4aSLuiz Otavio O Souza #include <net/if_media.h> 41fdfcae4aSLuiz Otavio O Souza #include <net/if_types.h> 42fdfcae4aSLuiz Otavio O Souza 43fdfcae4aSLuiz Otavio O Souza #include <dev/fdt/fdt_common.h> 44fdfcae4aSLuiz Otavio O Souza #include <dev/gpio/gpiobusvar.h> 45fdfcae4aSLuiz Otavio O Souza #include <dev/mii/mii_bitbang.h> 46fdfcae4aSLuiz Otavio O Souza #include <dev/mii/miivar.h> 47fdfcae4aSLuiz Otavio O Souza #include <dev/ofw/ofw_bus.h> 48fdfcae4aSLuiz Otavio O Souza 49fdfcae4aSLuiz Otavio O Souza #include "gpiobus_if.h" 50fdfcae4aSLuiz Otavio O Souza #include "miibus_if.h" 51fdfcae4aSLuiz Otavio O Souza 52fdfcae4aSLuiz Otavio O Souza #define GPIOMDIO_MDC_DFLT 0 53fdfcae4aSLuiz Otavio O Souza #define GPIOMDIO_MDIO_DFLT 1 54fdfcae4aSLuiz Otavio O Souza #define GPIOMDIO_MIN_PINS 2 55fdfcae4aSLuiz Otavio O Souza 56fdfcae4aSLuiz Otavio O Souza #define MDO_BIT 0x01 57fdfcae4aSLuiz Otavio O Souza #define MDI_BIT 0x02 58fdfcae4aSLuiz Otavio O Souza #define MDC_BIT 0x04 59fdfcae4aSLuiz Otavio O Souza #define MDIRPHY_BIT 0x08 60fdfcae4aSLuiz Otavio O Souza #define MDIRHOST_BIT 0x10 61fdfcae4aSLuiz Otavio O Souza #define MDO sc->miibb_ops.mbo_bits[MII_BIT_MDO] 62fdfcae4aSLuiz Otavio O Souza #define MDI sc->miibb_ops.mbo_bits[MII_BIT_MDI] 63fdfcae4aSLuiz Otavio O Souza #define MDC sc->miibb_ops.mbo_bits[MII_BIT_MDC] 64fdfcae4aSLuiz Otavio O Souza #define MDIRPHY sc->miibb_ops.mbo_bits[MII_BIT_DIR_HOST_PHY] 65fdfcae4aSLuiz Otavio O Souza #define MDIRHOST sc->miibb_ops.mbo_bits[MII_BIT_DIR_PHY_HOST] 66fdfcae4aSLuiz Otavio O Souza 67fdfcae4aSLuiz Otavio O Souza static uint32_t gpiomdio_bb_read(device_t); 68fdfcae4aSLuiz Otavio O Souza static void gpiomdio_bb_write(device_t, uint32_t); 69fdfcae4aSLuiz Otavio O Souza 70fdfcae4aSLuiz Otavio O Souza struct gpiomdio_softc 71fdfcae4aSLuiz Otavio O Souza { 72fdfcae4aSLuiz Otavio O Souza device_t sc_dev; 73fdfcae4aSLuiz Otavio O Souza device_t sc_busdev; 74fdfcae4aSLuiz Otavio O Souza int mdc_pin; 75fdfcae4aSLuiz Otavio O Souza int mdio_pin; 76fdfcae4aSLuiz Otavio O Souza struct mii_bitbang_ops miibb_ops; 77fdfcae4aSLuiz Otavio O Souza }; 78fdfcae4aSLuiz Otavio O Souza 79fdfcae4aSLuiz Otavio O Souza 80fdfcae4aSLuiz Otavio O Souza static int 81fdfcae4aSLuiz Otavio O Souza gpiomdio_probe(device_t dev) 82fdfcae4aSLuiz Otavio O Souza { 83fdfcae4aSLuiz Otavio O Souza struct gpiobus_ivar *devi; 84fdfcae4aSLuiz Otavio O Souza 85fdfcae4aSLuiz Otavio O Souza if (!ofw_bus_status_okay(dev)) 86fdfcae4aSLuiz Otavio O Souza return (ENXIO); 87fdfcae4aSLuiz Otavio O Souza if (!ofw_bus_is_compatible(dev, "freebsd,gpiomdio")) 88fdfcae4aSLuiz Otavio O Souza return (ENXIO); 89fdfcae4aSLuiz Otavio O Souza devi = GPIOBUS_IVAR(dev); 90fdfcae4aSLuiz Otavio O Souza if (devi->npins < GPIOMDIO_MIN_PINS) { 91fdfcae4aSLuiz Otavio O Souza device_printf(dev, 92fdfcae4aSLuiz Otavio O Souza "gpiomdio needs at least %d GPIO pins (only %d given).\n", 93fdfcae4aSLuiz Otavio O Souza GPIOMDIO_MIN_PINS, devi->npins); 94fdfcae4aSLuiz Otavio O Souza return (ENXIO); 95fdfcae4aSLuiz Otavio O Souza } 96fdfcae4aSLuiz Otavio O Souza device_set_desc(dev, "GPIO MDIO bit-banging Bus driver"); 97fdfcae4aSLuiz Otavio O Souza 98fdfcae4aSLuiz Otavio O Souza return (BUS_PROBE_DEFAULT); 99fdfcae4aSLuiz Otavio O Souza } 100fdfcae4aSLuiz Otavio O Souza 101fdfcae4aSLuiz Otavio O Souza static int 102fdfcae4aSLuiz Otavio O Souza gpiomdio_attach(device_t dev) 103fdfcae4aSLuiz Otavio O Souza { 104fdfcae4aSLuiz Otavio O Souza phandle_t node; 105fdfcae4aSLuiz Otavio O Souza pcell_t pin; 106fdfcae4aSLuiz Otavio O Souza struct gpiobus_ivar *devi; 107fdfcae4aSLuiz Otavio O Souza struct gpiomdio_softc *sc; 108fdfcae4aSLuiz Otavio O Souza 109fdfcae4aSLuiz Otavio O Souza sc = device_get_softc(dev); 110fdfcae4aSLuiz Otavio O Souza sc->sc_dev = dev; 111fdfcae4aSLuiz Otavio O Souza sc->sc_busdev = device_get_parent(dev); 112fdfcae4aSLuiz Otavio O Souza 113fdfcae4aSLuiz Otavio O Souza if ((node = ofw_bus_get_node(dev)) == -1) 114fdfcae4aSLuiz Otavio O Souza return (ENXIO); 115fdfcae4aSLuiz Otavio O Souza if (OF_getencprop(node, "mdc", &pin, sizeof(pin)) > 0) 116fdfcae4aSLuiz Otavio O Souza sc->mdc_pin = (int)pin; 117fdfcae4aSLuiz Otavio O Souza if (OF_getencprop(node, "mdio", &pin, sizeof(pin)) > 0) 118fdfcae4aSLuiz Otavio O Souza sc->mdio_pin = (int)pin; 119fdfcae4aSLuiz Otavio O Souza 120fdfcae4aSLuiz Otavio O Souza if (sc->mdc_pin < 0 || sc->mdc_pin > 1) 121fdfcae4aSLuiz Otavio O Souza sc->mdc_pin = GPIOMDIO_MDC_DFLT; 122fdfcae4aSLuiz Otavio O Souza if (sc->mdio_pin < 0 || sc->mdio_pin > 1) 123fdfcae4aSLuiz Otavio O Souza sc->mdio_pin = GPIOMDIO_MDIO_DFLT; 124fdfcae4aSLuiz Otavio O Souza 125fdfcae4aSLuiz Otavio O Souza devi = GPIOBUS_IVAR(dev); 126fdfcae4aSLuiz Otavio O Souza device_printf(dev, "MDC pin: %d, MDIO pin: %d\n", 127fdfcae4aSLuiz Otavio O Souza devi->pins[sc->mdc_pin], devi->pins[sc->mdio_pin]); 128fdfcae4aSLuiz Otavio O Souza 129fdfcae4aSLuiz Otavio O Souza /* Initialize mii_bitbang_ops. */ 130fdfcae4aSLuiz Otavio O Souza MDO = MDO_BIT; 131fdfcae4aSLuiz Otavio O Souza MDI = MDI_BIT; 132fdfcae4aSLuiz Otavio O Souza MDC = MDC_BIT; 133fdfcae4aSLuiz Otavio O Souza MDIRPHY = MDIRPHY_BIT; 134fdfcae4aSLuiz Otavio O Souza MDIRHOST = MDIRHOST_BIT; 135fdfcae4aSLuiz Otavio O Souza sc->miibb_ops.mbo_read = gpiomdio_bb_read; 136fdfcae4aSLuiz Otavio O Souza sc->miibb_ops.mbo_write = gpiomdio_bb_write; 137fdfcae4aSLuiz Otavio O Souza 138fdfcae4aSLuiz Otavio O Souza /* Register our MDIO Bus device. */ 139fdfcae4aSLuiz Otavio O Souza OF_device_register_xref(OF_xref_from_node(node), dev); 140fdfcae4aSLuiz Otavio O Souza 141fdfcae4aSLuiz Otavio O Souza return (0); 142fdfcae4aSLuiz Otavio O Souza } 143fdfcae4aSLuiz Otavio O Souza 144fdfcae4aSLuiz Otavio O Souza static uint32_t 145fdfcae4aSLuiz Otavio O Souza gpiomdio_bb_read(device_t dev) 146fdfcae4aSLuiz Otavio O Souza { 147fdfcae4aSLuiz Otavio O Souza struct gpiomdio_softc *sc; 148fdfcae4aSLuiz Otavio O Souza unsigned int val; 149fdfcae4aSLuiz Otavio O Souza 150fdfcae4aSLuiz Otavio O Souza sc = device_get_softc(dev); 151fdfcae4aSLuiz Otavio O Souza GPIOBUS_PIN_GET(sc->sc_busdev, sc->sc_dev, sc->mdio_pin, &val); 152fdfcae4aSLuiz Otavio O Souza 153fdfcae4aSLuiz Otavio O Souza return (val != 0 ? MDI_BIT : 0); 154fdfcae4aSLuiz Otavio O Souza } 155fdfcae4aSLuiz Otavio O Souza 156fdfcae4aSLuiz Otavio O Souza static void 157fdfcae4aSLuiz Otavio O Souza gpiomdio_bb_write(device_t dev, uint32_t val) 158fdfcae4aSLuiz Otavio O Souza { 159fdfcae4aSLuiz Otavio O Souza struct gpiomdio_softc *sc; 160fdfcae4aSLuiz Otavio O Souza 161fdfcae4aSLuiz Otavio O Souza sc = device_get_softc(dev); 162fdfcae4aSLuiz Otavio O Souza 163fdfcae4aSLuiz Otavio O Souza /* Set the data pin state. */ 164fdfcae4aSLuiz Otavio O Souza if ((val & (MDIRPHY_BIT | MDO_BIT)) == (MDIRPHY_BIT | MDO_BIT)) 165fdfcae4aSLuiz Otavio O Souza GPIOBUS_PIN_SET(sc->sc_busdev, sc->sc_dev, sc->mdio_pin, 1); 166fdfcae4aSLuiz Otavio O Souza else if ((val & (MDIRPHY_BIT | MDO_BIT)) == MDIRPHY_BIT) 167fdfcae4aSLuiz Otavio O Souza GPIOBUS_PIN_SET(sc->sc_busdev, sc->sc_dev, sc->mdio_pin, 0); 168fdfcae4aSLuiz Otavio O Souza if (val & MDIRPHY_BIT) 169fdfcae4aSLuiz Otavio O Souza GPIOBUS_PIN_SETFLAGS(sc->sc_busdev, sc->sc_dev, sc->mdio_pin, 170fdfcae4aSLuiz Otavio O Souza GPIO_PIN_OUTPUT); 171fdfcae4aSLuiz Otavio O Souza else if (val & MDIRHOST_BIT) 172fdfcae4aSLuiz Otavio O Souza GPIOBUS_PIN_SETFLAGS(sc->sc_busdev, sc->sc_dev, sc->mdio_pin, 173fdfcae4aSLuiz Otavio O Souza GPIO_PIN_INPUT); 174fdfcae4aSLuiz Otavio O Souza 175fdfcae4aSLuiz Otavio O Souza /* And now the clock state. */ 176fdfcae4aSLuiz Otavio O Souza if (val & MDC_BIT) 177fdfcae4aSLuiz Otavio O Souza GPIOBUS_PIN_SET(sc->sc_busdev, sc->sc_dev, sc->mdc_pin, 1); 178fdfcae4aSLuiz Otavio O Souza else 179fdfcae4aSLuiz Otavio O Souza GPIOBUS_PIN_SET(sc->sc_busdev, sc->sc_dev, sc->mdc_pin, 0); 180fdfcae4aSLuiz Otavio O Souza GPIOBUS_PIN_SETFLAGS(sc->sc_busdev, sc->sc_dev, sc->mdc_pin, 181fdfcae4aSLuiz Otavio O Souza GPIO_PIN_OUTPUT); 182fdfcae4aSLuiz Otavio O Souza } 183fdfcae4aSLuiz Otavio O Souza 184fdfcae4aSLuiz Otavio O Souza static int 185fdfcae4aSLuiz Otavio O Souza gpiomdio_readreg(device_t dev, int phy, int reg) 186fdfcae4aSLuiz Otavio O Souza { 187fdfcae4aSLuiz Otavio O Souza struct gpiomdio_softc *sc; 188fdfcae4aSLuiz Otavio O Souza 189fdfcae4aSLuiz Otavio O Souza sc = device_get_softc(dev); 190fdfcae4aSLuiz Otavio O Souza 191fdfcae4aSLuiz Otavio O Souza return (mii_bitbang_readreg(dev, &sc->miibb_ops, phy, reg)); 192fdfcae4aSLuiz Otavio O Souza } 193fdfcae4aSLuiz Otavio O Souza 194fdfcae4aSLuiz Otavio O Souza static int 195fdfcae4aSLuiz Otavio O Souza gpiomdio_writereg(device_t dev, int phy, int reg, int val) 196fdfcae4aSLuiz Otavio O Souza { 197fdfcae4aSLuiz Otavio O Souza struct gpiomdio_softc *sc; 198fdfcae4aSLuiz Otavio O Souza 199fdfcae4aSLuiz Otavio O Souza sc = device_get_softc(dev); 200fdfcae4aSLuiz Otavio O Souza mii_bitbang_writereg(dev, &sc->miibb_ops, phy, reg, val); 201fdfcae4aSLuiz Otavio O Souza 202fdfcae4aSLuiz Otavio O Souza return (0); 203fdfcae4aSLuiz Otavio O Souza } 204fdfcae4aSLuiz Otavio O Souza 205fdfcae4aSLuiz Otavio O Souza static phandle_t 206fdfcae4aSLuiz Otavio O Souza gpiomdio_get_node(device_t bus, device_t dev) 207fdfcae4aSLuiz Otavio O Souza { 208fdfcae4aSLuiz Otavio O Souza 209fdfcae4aSLuiz Otavio O Souza return (ofw_bus_get_node(bus)); 210fdfcae4aSLuiz Otavio O Souza } 211fdfcae4aSLuiz Otavio O Souza 212fdfcae4aSLuiz Otavio O Souza static device_method_t gpiomdio_methods[] = { 213fdfcae4aSLuiz Otavio O Souza /* Device interface */ 214fdfcae4aSLuiz Otavio O Souza DEVMETHOD(device_probe, gpiomdio_probe), 215fdfcae4aSLuiz Otavio O Souza DEVMETHOD(device_attach, gpiomdio_attach), 216fdfcae4aSLuiz Otavio O Souza 217fdfcae4aSLuiz Otavio O Souza /* MDIO interface */ 218fdfcae4aSLuiz Otavio O Souza DEVMETHOD(miibus_readreg, gpiomdio_readreg), 219fdfcae4aSLuiz Otavio O Souza DEVMETHOD(miibus_writereg, gpiomdio_writereg), 220fdfcae4aSLuiz Otavio O Souza 221fdfcae4aSLuiz Otavio O Souza /* OFW bus interface */ 222fdfcae4aSLuiz Otavio O Souza DEVMETHOD(ofw_bus_get_node, gpiomdio_get_node), 223fdfcae4aSLuiz Otavio O Souza 224fdfcae4aSLuiz Otavio O Souza DEVMETHOD_END 225fdfcae4aSLuiz Otavio O Souza }; 226fdfcae4aSLuiz Otavio O Souza 227fdfcae4aSLuiz Otavio O Souza static driver_t gpiomdio_driver = { 228fdfcae4aSLuiz Otavio O Souza "gpiomdio", 229fdfcae4aSLuiz Otavio O Souza gpiomdio_methods, 230fdfcae4aSLuiz Otavio O Souza sizeof(struct gpiomdio_softc), 231fdfcae4aSLuiz Otavio O Souza }; 232fdfcae4aSLuiz Otavio O Souza 23384c5f982SJohn Baldwin EARLY_DRIVER_MODULE(gpiomdio, gpiobus, gpiomdio_driver, 0, 0, 23484c5f982SJohn Baldwin BUS_PASS_BUS + BUS_PASS_ORDER_MIDDLE); 2353e38757dSJohn Baldwin DRIVER_MODULE(miibus, gpiomdio, miibus_driver, 0, 0); 236fdfcae4aSLuiz Otavio O Souza MODULE_DEPEND(gpiomdio, gpiobus, 1, 1, 1); 237fdfcae4aSLuiz Otavio O Souza MODULE_DEPEND(gpiomdio, miibus, 1, 1, 1); 238fdfcae4aSLuiz Otavio O Souza MODULE_DEPEND(gpiomdio, mii_bitbang, 1, 1, 1); 239