1fdfcae4aSLuiz Otavio O Souza /*- 2fdfcae4aSLuiz Otavio O Souza * SPDX-License-Identifier: BSD-2-Clause-FreeBSD 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 __FBSDID("$FreeBSD$"); 30fdfcae4aSLuiz Otavio O Souza 31fdfcae4aSLuiz Otavio O Souza #include "opt_platform.h" 32fdfcae4aSLuiz Otavio O Souza 33fdfcae4aSLuiz Otavio O Souza #include <sys/param.h> 34fdfcae4aSLuiz Otavio O Souza #include <sys/systm.h> 35fdfcae4aSLuiz Otavio O Souza #include <sys/bus.h> 36fdfcae4aSLuiz Otavio O Souza #include <sys/gpio.h> 37fdfcae4aSLuiz Otavio O Souza #include <sys/kernel.h> 38fdfcae4aSLuiz Otavio O Souza #include <sys/module.h> 39fdfcae4aSLuiz Otavio O Souza #include <sys/socket.h> 40fdfcae4aSLuiz Otavio O Souza 41fdfcae4aSLuiz Otavio O Souza #include <net/if.h> 42fdfcae4aSLuiz Otavio O Souza #include <net/if_media.h> 43fdfcae4aSLuiz Otavio O Souza #include <net/if_types.h> 44fdfcae4aSLuiz Otavio O Souza 45fdfcae4aSLuiz Otavio O Souza #include <dev/fdt/fdt_common.h> 46fdfcae4aSLuiz Otavio O Souza #include <dev/gpio/gpiobusvar.h> 47fdfcae4aSLuiz Otavio O Souza #include <dev/mii/mii_bitbang.h> 48fdfcae4aSLuiz Otavio O Souza #include <dev/mii/miivar.h> 49fdfcae4aSLuiz Otavio O Souza #include <dev/ofw/ofw_bus.h> 50fdfcae4aSLuiz Otavio O Souza 51fdfcae4aSLuiz Otavio O Souza #include "gpiobus_if.h" 52fdfcae4aSLuiz Otavio O Souza #include "miibus_if.h" 53fdfcae4aSLuiz Otavio O Souza 54fdfcae4aSLuiz Otavio O Souza #define GPIOMDIO_MDC_DFLT 0 55fdfcae4aSLuiz Otavio O Souza #define GPIOMDIO_MDIO_DFLT 1 56fdfcae4aSLuiz Otavio O Souza #define GPIOMDIO_MIN_PINS 2 57fdfcae4aSLuiz Otavio O Souza 58fdfcae4aSLuiz Otavio O Souza #define MDO_BIT 0x01 59fdfcae4aSLuiz Otavio O Souza #define MDI_BIT 0x02 60fdfcae4aSLuiz Otavio O Souza #define MDC_BIT 0x04 61fdfcae4aSLuiz Otavio O Souza #define MDIRPHY_BIT 0x08 62fdfcae4aSLuiz Otavio O Souza #define MDIRHOST_BIT 0x10 63fdfcae4aSLuiz Otavio O Souza #define MDO sc->miibb_ops.mbo_bits[MII_BIT_MDO] 64fdfcae4aSLuiz Otavio O Souza #define MDI sc->miibb_ops.mbo_bits[MII_BIT_MDI] 65fdfcae4aSLuiz Otavio O Souza #define MDC sc->miibb_ops.mbo_bits[MII_BIT_MDC] 66fdfcae4aSLuiz Otavio O Souza #define MDIRPHY sc->miibb_ops.mbo_bits[MII_BIT_DIR_HOST_PHY] 67fdfcae4aSLuiz Otavio O Souza #define MDIRHOST sc->miibb_ops.mbo_bits[MII_BIT_DIR_PHY_HOST] 68fdfcae4aSLuiz Otavio O Souza 69fdfcae4aSLuiz Otavio O Souza static uint32_t gpiomdio_bb_read(device_t); 70fdfcae4aSLuiz Otavio O Souza static void gpiomdio_bb_write(device_t, uint32_t); 71fdfcae4aSLuiz Otavio O Souza 72fdfcae4aSLuiz Otavio O Souza struct gpiomdio_softc 73fdfcae4aSLuiz Otavio O Souza { 74fdfcae4aSLuiz Otavio O Souza device_t sc_dev; 75fdfcae4aSLuiz Otavio O Souza device_t sc_busdev; 76fdfcae4aSLuiz Otavio O Souza int mdc_pin; 77fdfcae4aSLuiz Otavio O Souza int mdio_pin; 78fdfcae4aSLuiz Otavio O Souza struct mii_bitbang_ops miibb_ops; 79fdfcae4aSLuiz Otavio O Souza }; 80fdfcae4aSLuiz Otavio O Souza 81fdfcae4aSLuiz Otavio O Souza 82fdfcae4aSLuiz Otavio O Souza static int 83fdfcae4aSLuiz Otavio O Souza gpiomdio_probe(device_t dev) 84fdfcae4aSLuiz Otavio O Souza { 85fdfcae4aSLuiz Otavio O Souza struct gpiobus_ivar *devi; 86fdfcae4aSLuiz Otavio O Souza 87fdfcae4aSLuiz Otavio O Souza if (!ofw_bus_status_okay(dev)) 88fdfcae4aSLuiz Otavio O Souza return (ENXIO); 89fdfcae4aSLuiz Otavio O Souza if (!ofw_bus_is_compatible(dev, "freebsd,gpiomdio")) 90fdfcae4aSLuiz Otavio O Souza return (ENXIO); 91fdfcae4aSLuiz Otavio O Souza devi = GPIOBUS_IVAR(dev); 92fdfcae4aSLuiz Otavio O Souza if (devi->npins < GPIOMDIO_MIN_PINS) { 93fdfcae4aSLuiz Otavio O Souza device_printf(dev, 94fdfcae4aSLuiz Otavio O Souza "gpiomdio needs at least %d GPIO pins (only %d given).\n", 95fdfcae4aSLuiz Otavio O Souza GPIOMDIO_MIN_PINS, devi->npins); 96fdfcae4aSLuiz Otavio O Souza return (ENXIO); 97fdfcae4aSLuiz Otavio O Souza } 98fdfcae4aSLuiz Otavio O Souza device_set_desc(dev, "GPIO MDIO bit-banging Bus driver"); 99fdfcae4aSLuiz Otavio O Souza 100fdfcae4aSLuiz Otavio O Souza return (BUS_PROBE_DEFAULT); 101fdfcae4aSLuiz Otavio O Souza } 102fdfcae4aSLuiz Otavio O Souza 103fdfcae4aSLuiz Otavio O Souza static int 104fdfcae4aSLuiz Otavio O Souza gpiomdio_attach(device_t dev) 105fdfcae4aSLuiz Otavio O Souza { 106fdfcae4aSLuiz Otavio O Souza phandle_t node; 107fdfcae4aSLuiz Otavio O Souza pcell_t pin; 108fdfcae4aSLuiz Otavio O Souza struct gpiobus_ivar *devi; 109fdfcae4aSLuiz Otavio O Souza struct gpiomdio_softc *sc; 110fdfcae4aSLuiz Otavio O Souza 111fdfcae4aSLuiz Otavio O Souza sc = device_get_softc(dev); 112fdfcae4aSLuiz Otavio O Souza sc->sc_dev = dev; 113fdfcae4aSLuiz Otavio O Souza sc->sc_busdev = device_get_parent(dev); 114fdfcae4aSLuiz Otavio O Souza 115fdfcae4aSLuiz Otavio O Souza if ((node = ofw_bus_get_node(dev)) == -1) 116fdfcae4aSLuiz Otavio O Souza return (ENXIO); 117fdfcae4aSLuiz Otavio O Souza if (OF_getencprop(node, "mdc", &pin, sizeof(pin)) > 0) 118fdfcae4aSLuiz Otavio O Souza sc->mdc_pin = (int)pin; 119fdfcae4aSLuiz Otavio O Souza if (OF_getencprop(node, "mdio", &pin, sizeof(pin)) > 0) 120fdfcae4aSLuiz Otavio O Souza sc->mdio_pin = (int)pin; 121fdfcae4aSLuiz Otavio O Souza 122fdfcae4aSLuiz Otavio O Souza if (sc->mdc_pin < 0 || sc->mdc_pin > 1) 123fdfcae4aSLuiz Otavio O Souza sc->mdc_pin = GPIOMDIO_MDC_DFLT; 124fdfcae4aSLuiz Otavio O Souza if (sc->mdio_pin < 0 || sc->mdio_pin > 1) 125fdfcae4aSLuiz Otavio O Souza sc->mdio_pin = GPIOMDIO_MDIO_DFLT; 126fdfcae4aSLuiz Otavio O Souza 127fdfcae4aSLuiz Otavio O Souza devi = GPIOBUS_IVAR(dev); 128fdfcae4aSLuiz Otavio O Souza device_printf(dev, "MDC pin: %d, MDIO pin: %d\n", 129fdfcae4aSLuiz Otavio O Souza devi->pins[sc->mdc_pin], devi->pins[sc->mdio_pin]); 130fdfcae4aSLuiz Otavio O Souza 131fdfcae4aSLuiz Otavio O Souza /* Initialize mii_bitbang_ops. */ 132fdfcae4aSLuiz Otavio O Souza MDO = MDO_BIT; 133fdfcae4aSLuiz Otavio O Souza MDI = MDI_BIT; 134fdfcae4aSLuiz Otavio O Souza MDC = MDC_BIT; 135fdfcae4aSLuiz Otavio O Souza MDIRPHY = MDIRPHY_BIT; 136fdfcae4aSLuiz Otavio O Souza MDIRHOST = MDIRHOST_BIT; 137fdfcae4aSLuiz Otavio O Souza sc->miibb_ops.mbo_read = gpiomdio_bb_read; 138fdfcae4aSLuiz Otavio O Souza sc->miibb_ops.mbo_write = gpiomdio_bb_write; 139fdfcae4aSLuiz Otavio O Souza 140fdfcae4aSLuiz Otavio O Souza /* Register our MDIO Bus device. */ 141fdfcae4aSLuiz Otavio O Souza OF_device_register_xref(OF_xref_from_node(node), dev); 142fdfcae4aSLuiz Otavio O Souza 143fdfcae4aSLuiz Otavio O Souza return (0); 144fdfcae4aSLuiz Otavio O Souza } 145fdfcae4aSLuiz Otavio O Souza 146fdfcae4aSLuiz Otavio O Souza static uint32_t 147fdfcae4aSLuiz Otavio O Souza gpiomdio_bb_read(device_t dev) 148fdfcae4aSLuiz Otavio O Souza { 149fdfcae4aSLuiz Otavio O Souza struct gpiomdio_softc *sc; 150fdfcae4aSLuiz Otavio O Souza unsigned int val; 151fdfcae4aSLuiz Otavio O Souza 152fdfcae4aSLuiz Otavio O Souza sc = device_get_softc(dev); 153fdfcae4aSLuiz Otavio O Souza GPIOBUS_PIN_GET(sc->sc_busdev, sc->sc_dev, sc->mdio_pin, &val); 154fdfcae4aSLuiz Otavio O Souza 155fdfcae4aSLuiz Otavio O Souza return (val != 0 ? MDI_BIT : 0); 156fdfcae4aSLuiz Otavio O Souza } 157fdfcae4aSLuiz Otavio O Souza 158fdfcae4aSLuiz Otavio O Souza static void 159fdfcae4aSLuiz Otavio O Souza gpiomdio_bb_write(device_t dev, uint32_t val) 160fdfcae4aSLuiz Otavio O Souza { 161fdfcae4aSLuiz Otavio O Souza struct gpiomdio_softc *sc; 162fdfcae4aSLuiz Otavio O Souza 163fdfcae4aSLuiz Otavio O Souza sc = device_get_softc(dev); 164fdfcae4aSLuiz Otavio O Souza 165fdfcae4aSLuiz Otavio O Souza /* Set the data pin state. */ 166fdfcae4aSLuiz Otavio O Souza if ((val & (MDIRPHY_BIT | MDO_BIT)) == (MDIRPHY_BIT | MDO_BIT)) 167fdfcae4aSLuiz Otavio O Souza GPIOBUS_PIN_SET(sc->sc_busdev, sc->sc_dev, sc->mdio_pin, 1); 168fdfcae4aSLuiz Otavio O Souza else if ((val & (MDIRPHY_BIT | MDO_BIT)) == MDIRPHY_BIT) 169fdfcae4aSLuiz Otavio O Souza GPIOBUS_PIN_SET(sc->sc_busdev, sc->sc_dev, sc->mdio_pin, 0); 170fdfcae4aSLuiz Otavio O Souza if (val & MDIRPHY_BIT) 171fdfcae4aSLuiz Otavio O Souza GPIOBUS_PIN_SETFLAGS(sc->sc_busdev, sc->sc_dev, sc->mdio_pin, 172fdfcae4aSLuiz Otavio O Souza GPIO_PIN_OUTPUT); 173fdfcae4aSLuiz Otavio O Souza else if (val & MDIRHOST_BIT) 174fdfcae4aSLuiz Otavio O Souza GPIOBUS_PIN_SETFLAGS(sc->sc_busdev, sc->sc_dev, sc->mdio_pin, 175fdfcae4aSLuiz Otavio O Souza GPIO_PIN_INPUT); 176fdfcae4aSLuiz Otavio O Souza 177fdfcae4aSLuiz Otavio O Souza /* And now the clock state. */ 178fdfcae4aSLuiz Otavio O Souza if (val & MDC_BIT) 179fdfcae4aSLuiz Otavio O Souza GPIOBUS_PIN_SET(sc->sc_busdev, sc->sc_dev, sc->mdc_pin, 1); 180fdfcae4aSLuiz Otavio O Souza else 181fdfcae4aSLuiz Otavio O Souza GPIOBUS_PIN_SET(sc->sc_busdev, sc->sc_dev, sc->mdc_pin, 0); 182fdfcae4aSLuiz Otavio O Souza GPIOBUS_PIN_SETFLAGS(sc->sc_busdev, sc->sc_dev, sc->mdc_pin, 183fdfcae4aSLuiz Otavio O Souza GPIO_PIN_OUTPUT); 184fdfcae4aSLuiz Otavio O Souza } 185fdfcae4aSLuiz Otavio O Souza 186fdfcae4aSLuiz Otavio O Souza static int 187fdfcae4aSLuiz Otavio O Souza gpiomdio_readreg(device_t dev, int phy, int reg) 188fdfcae4aSLuiz Otavio O Souza { 189fdfcae4aSLuiz Otavio O Souza struct gpiomdio_softc *sc; 190fdfcae4aSLuiz Otavio O Souza 191fdfcae4aSLuiz Otavio O Souza sc = device_get_softc(dev); 192fdfcae4aSLuiz Otavio O Souza 193fdfcae4aSLuiz Otavio O Souza return (mii_bitbang_readreg(dev, &sc->miibb_ops, phy, reg)); 194fdfcae4aSLuiz Otavio O Souza } 195fdfcae4aSLuiz Otavio O Souza 196fdfcae4aSLuiz Otavio O Souza static int 197fdfcae4aSLuiz Otavio O Souza gpiomdio_writereg(device_t dev, int phy, int reg, int val) 198fdfcae4aSLuiz Otavio O Souza { 199fdfcae4aSLuiz Otavio O Souza struct gpiomdio_softc *sc; 200fdfcae4aSLuiz Otavio O Souza 201fdfcae4aSLuiz Otavio O Souza sc = device_get_softc(dev); 202fdfcae4aSLuiz Otavio O Souza mii_bitbang_writereg(dev, &sc->miibb_ops, phy, reg, val); 203fdfcae4aSLuiz Otavio O Souza 204fdfcae4aSLuiz Otavio O Souza return (0); 205fdfcae4aSLuiz Otavio O Souza } 206fdfcae4aSLuiz Otavio O Souza 207fdfcae4aSLuiz Otavio O Souza static phandle_t 208fdfcae4aSLuiz Otavio O Souza gpiomdio_get_node(device_t bus, device_t dev) 209fdfcae4aSLuiz Otavio O Souza { 210fdfcae4aSLuiz Otavio O Souza 211fdfcae4aSLuiz Otavio O Souza return (ofw_bus_get_node(bus)); 212fdfcae4aSLuiz Otavio O Souza } 213fdfcae4aSLuiz Otavio O Souza 214fdfcae4aSLuiz Otavio O Souza static device_method_t gpiomdio_methods[] = { 215fdfcae4aSLuiz Otavio O Souza /* Device interface */ 216fdfcae4aSLuiz Otavio O Souza DEVMETHOD(device_probe, gpiomdio_probe), 217fdfcae4aSLuiz Otavio O Souza DEVMETHOD(device_attach, gpiomdio_attach), 218fdfcae4aSLuiz Otavio O Souza DEVMETHOD(device_detach, bus_generic_detach), 219fdfcae4aSLuiz Otavio O Souza 220fdfcae4aSLuiz Otavio O Souza /* MDIO interface */ 221fdfcae4aSLuiz Otavio O Souza DEVMETHOD(miibus_readreg, gpiomdio_readreg), 222fdfcae4aSLuiz Otavio O Souza DEVMETHOD(miibus_writereg, gpiomdio_writereg), 223fdfcae4aSLuiz Otavio O Souza 224fdfcae4aSLuiz Otavio O Souza /* OFW bus interface */ 225fdfcae4aSLuiz Otavio O Souza DEVMETHOD(ofw_bus_get_node, gpiomdio_get_node), 226fdfcae4aSLuiz Otavio O Souza 227fdfcae4aSLuiz Otavio O Souza DEVMETHOD_END 228fdfcae4aSLuiz Otavio O Souza }; 229fdfcae4aSLuiz Otavio O Souza 230fdfcae4aSLuiz Otavio O Souza static driver_t gpiomdio_driver = { 231fdfcae4aSLuiz Otavio O Souza "gpiomdio", 232fdfcae4aSLuiz Otavio O Souza gpiomdio_methods, 233fdfcae4aSLuiz Otavio O Souza sizeof(struct gpiomdio_softc), 234fdfcae4aSLuiz Otavio O Souza }; 235fdfcae4aSLuiz Otavio O Souza 236*84c5f982SJohn Baldwin EARLY_DRIVER_MODULE(gpiomdio, gpiobus, gpiomdio_driver, 0, 0, 237*84c5f982SJohn Baldwin BUS_PASS_BUS + BUS_PASS_ORDER_MIDDLE); 2383e38757dSJohn Baldwin DRIVER_MODULE(miibus, gpiomdio, miibus_driver, 0, 0); 239fdfcae4aSLuiz Otavio O Souza MODULE_DEPEND(gpiomdio, gpiobus, 1, 1, 1); 240fdfcae4aSLuiz Otavio O Souza MODULE_DEPEND(gpiomdio, miibus, 1, 1, 1); 241fdfcae4aSLuiz Otavio O Souza MODULE_DEPEND(gpiomdio, mii_bitbang, 1, 1, 1); 242