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