xref: /freebsd/sys/dev/gpio/gpiomdio.c (revision 7ebc7d1ab76b9d06be9400d6c9fc74fcc43603a1)
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