2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
4 * Copyright (c) 2019 Rubicon Communications, LLC (Netgate)
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28 #include <sys/cdefs.h>
29 __FBSDID("$FreeBSD$");
31 #include "opt_platform.h"
33 #include <sys/param.h>
34 #include <sys/systm.h>
37 #include <sys/kernel.h>
38 #include <sys/module.h>
39 #include <sys/socket.h>
42 #include <net/if_media.h>
43 #include <net/if_types.h>
45 #include <dev/fdt/fdt_common.h>
46 #include <dev/gpio/gpiobusvar.h>
47 #include <dev/mii/mii_bitbang.h>
48 #include <dev/mii/miivar.h>
49 #include <dev/ofw/ofw_bus.h>
51 #include "gpiobus_if.h"
52 #include "miibus_if.h"
54 #define GPIOMDIO_MDC_DFLT 0
55 #define GPIOMDIO_MDIO_DFLT 1
56 #define GPIOMDIO_MIN_PINS 2
61 #define MDIRPHY_BIT 0x08
62 #define MDIRHOST_BIT 0x10
63 #define MDO sc->miibb_ops.mbo_bits[MII_BIT_MDO]
64 #define MDI sc->miibb_ops.mbo_bits[MII_BIT_MDI]
65 #define MDC sc->miibb_ops.mbo_bits[MII_BIT_MDC]
66 #define MDIRPHY sc->miibb_ops.mbo_bits[MII_BIT_DIR_HOST_PHY]
67 #define MDIRHOST sc->miibb_ops.mbo_bits[MII_BIT_DIR_PHY_HOST]
69 static uint32_t gpiomdio_bb_read(device_t);
70 static void gpiomdio_bb_write(device_t, uint32_t);
78 struct mii_bitbang_ops miibb_ops;
83 gpiomdio_probe(device_t dev)
85 struct gpiobus_ivar *devi;
87 if (!ofw_bus_status_okay(dev))
89 if (!ofw_bus_is_compatible(dev, "freebsd,gpiomdio"))
91 devi = GPIOBUS_IVAR(dev);
92 if (devi->npins < GPIOMDIO_MIN_PINS) {
94 "gpiomdio needs at least %d GPIO pins (only %d given).\n",
95 GPIOMDIO_MIN_PINS, devi->npins);
98 device_set_desc(dev, "GPIO MDIO bit-banging Bus driver");
100 return (BUS_PROBE_DEFAULT);
104 gpiomdio_attach(device_t dev)
108 struct gpiobus_ivar *devi;
109 struct gpiomdio_softc *sc;
111 sc = device_get_softc(dev);
113 sc->sc_busdev = device_get_parent(dev);
115 if ((node = ofw_bus_get_node(dev)) == -1)
117 if (OF_getencprop(node, "mdc", &pin, sizeof(pin)) > 0)
118 sc->mdc_pin = (int)pin;
119 if (OF_getencprop(node, "mdio", &pin, sizeof(pin)) > 0)
120 sc->mdio_pin = (int)pin;
122 if (sc->mdc_pin < 0 || sc->mdc_pin > 1)
123 sc->mdc_pin = GPIOMDIO_MDC_DFLT;
124 if (sc->mdio_pin < 0 || sc->mdio_pin > 1)
125 sc->mdio_pin = GPIOMDIO_MDIO_DFLT;
127 devi = GPIOBUS_IVAR(dev);
128 device_printf(dev, "MDC pin: %d, MDIO pin: %d\n",
129 devi->pins[sc->mdc_pin], devi->pins[sc->mdio_pin]);
131 /* Initialize mii_bitbang_ops. */
135 MDIRPHY = MDIRPHY_BIT;
136 MDIRHOST = MDIRHOST_BIT;
137 sc->miibb_ops.mbo_read = gpiomdio_bb_read;
138 sc->miibb_ops.mbo_write = gpiomdio_bb_write;
140 /* Register our MDIO Bus device. */
141 OF_device_register_xref(OF_xref_from_node(node), dev);
147 gpiomdio_bb_read(device_t dev)
149 struct gpiomdio_softc *sc;
152 sc = device_get_softc(dev);
153 GPIOBUS_PIN_GET(sc->sc_busdev, sc->sc_dev, sc->mdio_pin, &val);
155 return (val != 0 ? MDI_BIT : 0);
159 gpiomdio_bb_write(device_t dev, uint32_t val)
161 struct gpiomdio_softc *sc;
163 sc = device_get_softc(dev);
165 /* Set the data pin state. */
166 if ((val & (MDIRPHY_BIT | MDO_BIT)) == (MDIRPHY_BIT | MDO_BIT))
167 GPIOBUS_PIN_SET(sc->sc_busdev, sc->sc_dev, sc->mdio_pin, 1);
168 else if ((val & (MDIRPHY_BIT | MDO_BIT)) == MDIRPHY_BIT)
169 GPIOBUS_PIN_SET(sc->sc_busdev, sc->sc_dev, sc->mdio_pin, 0);
170 if (val & MDIRPHY_BIT)
171 GPIOBUS_PIN_SETFLAGS(sc->sc_busdev, sc->sc_dev, sc->mdio_pin,
173 else if (val & MDIRHOST_BIT)
174 GPIOBUS_PIN_SETFLAGS(sc->sc_busdev, sc->sc_dev, sc->mdio_pin,
177 /* And now the clock state. */
179 GPIOBUS_PIN_SET(sc->sc_busdev, sc->sc_dev, sc->mdc_pin, 1);
181 GPIOBUS_PIN_SET(sc->sc_busdev, sc->sc_dev, sc->mdc_pin, 0);
182 GPIOBUS_PIN_SETFLAGS(sc->sc_busdev, sc->sc_dev, sc->mdc_pin,
187 gpiomdio_readreg(device_t dev, int phy, int reg)
189 struct gpiomdio_softc *sc;
191 sc = device_get_softc(dev);
193 return (mii_bitbang_readreg(dev, &sc->miibb_ops, phy, reg));
197 gpiomdio_writereg(device_t dev, int phy, int reg, int val)
199 struct gpiomdio_softc *sc;
201 sc = device_get_softc(dev);
202 mii_bitbang_writereg(dev, &sc->miibb_ops, phy, reg, val);
208 gpiomdio_get_node(device_t bus, device_t dev)
211 return (ofw_bus_get_node(bus));
214 static devclass_t gpiomdio_devclass;
216 static device_method_t gpiomdio_methods[] = {
217 /* Device interface */
218 DEVMETHOD(device_probe, gpiomdio_probe),
219 DEVMETHOD(device_attach, gpiomdio_attach),
220 DEVMETHOD(device_detach, bus_generic_detach),
223 DEVMETHOD(miibus_readreg, gpiomdio_readreg),
224 DEVMETHOD(miibus_writereg, gpiomdio_writereg),
226 /* OFW bus interface */
227 DEVMETHOD(ofw_bus_get_node, gpiomdio_get_node),
232 static driver_t gpiomdio_driver = {
235 sizeof(struct gpiomdio_softc),
238 EARLY_DRIVER_MODULE(gpiomdio, gpiobus, gpiomdio_driver, gpiomdio_devclass,
239 0, 0, BUS_PASS_BUS + BUS_PASS_ORDER_MIDDLE);
240 DRIVER_MODULE(miibus, gpiomdio, miibus_driver, miibus_devclass, 0, 0);
241 MODULE_DEPEND(gpiomdio, gpiobus, 1, 1, 1);
242 MODULE_DEPEND(gpiomdio, miibus, 1, 1, 1);
243 MODULE_DEPEND(gpiomdio, mii_bitbang, 1, 1, 1);