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
gpiomdio_probe(device_t dev)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
gpiomdio_attach(device_t dev)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
gpiomdio_bb_read(device_t dev)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
gpiomdio_bb_write(device_t dev,uint32_t val)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
gpiomdio_readreg(device_t dev,int phy,int reg)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
gpiomdio_writereg(device_t dev,int phy,int reg,int val)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
gpiomdio_get_node(device_t bus,device_t dev)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