]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/dev/gpio/gpiomdio.c
ZFS: MFV 2.0-rc1-ga00c61
[FreeBSD/FreeBSD.git] / sys / dev / gpio / gpiomdio.c
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
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 __FBSDID("$FreeBSD$");
30
31 #include "opt_platform.h"
32
33 #include <sys/param.h>
34 #include <sys/systm.h>
35 #include <sys/bus.h>
36 #include <sys/gpio.h>
37 #include <sys/kernel.h>
38 #include <sys/module.h>
39 #include <sys/socket.h>
40
41 #include <net/if.h>
42 #include <net/if_media.h>
43 #include <net/if_types.h>
44
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>
50
51 #include "gpiobus_if.h"
52 #include "miibus_if.h"
53
54 #define GPIOMDIO_MDC_DFLT       0
55 #define GPIOMDIO_MDIO_DFLT      1
56 #define GPIOMDIO_MIN_PINS       2
57
58 #define MDO_BIT                 0x01
59 #define MDI_BIT                 0x02
60 #define MDC_BIT                 0x04
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]
68
69 static uint32_t gpiomdio_bb_read(device_t);
70 static void gpiomdio_bb_write(device_t, uint32_t);
71
72 struct gpiomdio_softc
73 {
74         device_t                sc_dev;
75         device_t                sc_busdev;
76         int                     mdc_pin;
77         int                     mdio_pin;
78         struct mii_bitbang_ops  miibb_ops;
79 };
80
81
82 static int
83 gpiomdio_probe(device_t dev)
84 {
85         struct gpiobus_ivar *devi;
86
87         if (!ofw_bus_status_okay(dev))
88                 return (ENXIO);
89         if (!ofw_bus_is_compatible(dev, "freebsd,gpiomdio"))
90                 return (ENXIO);
91         devi = GPIOBUS_IVAR(dev);
92         if (devi->npins < GPIOMDIO_MIN_PINS) {
93                 device_printf(dev,
94                     "gpiomdio needs at least %d GPIO pins (only %d given).\n",
95                     GPIOMDIO_MIN_PINS, devi->npins);
96                 return (ENXIO);
97         }
98         device_set_desc(dev, "GPIO MDIO bit-banging Bus driver");
99
100         return (BUS_PROBE_DEFAULT);
101 }
102
103 static int
104 gpiomdio_attach(device_t dev)
105 {
106         phandle_t               node;
107         pcell_t                 pin;
108         struct gpiobus_ivar     *devi;
109         struct gpiomdio_softc   *sc;
110
111         sc = device_get_softc(dev);
112         sc->sc_dev = dev;
113         sc->sc_busdev = device_get_parent(dev);
114
115         if ((node = ofw_bus_get_node(dev)) == -1)
116                 return (ENXIO);
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;
121
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;
126
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]);
130
131         /* Initialize mii_bitbang_ops. */
132         MDO = MDO_BIT;
133         MDI = MDI_BIT;
134         MDC = MDC_BIT;
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;
139
140         /* Register our MDIO Bus device. */
141         OF_device_register_xref(OF_xref_from_node(node), dev);
142
143         return (0);
144 }
145
146 static uint32_t
147 gpiomdio_bb_read(device_t dev)
148 {
149         struct gpiomdio_softc *sc;
150         unsigned int val;
151
152         sc = device_get_softc(dev);
153         GPIOBUS_PIN_GET(sc->sc_busdev, sc->sc_dev, sc->mdio_pin, &val);
154
155         return (val != 0 ? MDI_BIT : 0);
156 }
157
158 static void
159 gpiomdio_bb_write(device_t dev, uint32_t val)
160 {
161         struct gpiomdio_softc *sc;
162
163         sc = device_get_softc(dev);
164
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,
172                     GPIO_PIN_OUTPUT);
173         else if (val & MDIRHOST_BIT)
174                 GPIOBUS_PIN_SETFLAGS(sc->sc_busdev, sc->sc_dev, sc->mdio_pin,
175                     GPIO_PIN_INPUT);
176
177         /* And now the clock state. */
178         if (val & MDC_BIT)
179                 GPIOBUS_PIN_SET(sc->sc_busdev, sc->sc_dev, sc->mdc_pin, 1);
180         else
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,
183             GPIO_PIN_OUTPUT);
184 }
185
186 static int
187 gpiomdio_readreg(device_t dev, int phy, int reg)
188 {
189         struct gpiomdio_softc   *sc;
190
191         sc = device_get_softc(dev);
192
193         return (mii_bitbang_readreg(dev, &sc->miibb_ops, phy, reg));
194 }
195
196 static int
197 gpiomdio_writereg(device_t dev, int phy, int reg, int val)
198 {
199         struct gpiomdio_softc   *sc;
200
201         sc = device_get_softc(dev);
202         mii_bitbang_writereg(dev, &sc->miibb_ops, phy, reg, val);
203
204         return (0);
205 }
206
207 static phandle_t
208 gpiomdio_get_node(device_t bus, device_t dev)
209 {
210
211         return (ofw_bus_get_node(bus));
212 }
213
214 static devclass_t gpiomdio_devclass;
215
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),
221
222         /* MDIO interface */
223         DEVMETHOD(miibus_readreg,       gpiomdio_readreg),
224         DEVMETHOD(miibus_writereg,      gpiomdio_writereg),
225
226         /* OFW bus interface */
227         DEVMETHOD(ofw_bus_get_node,     gpiomdio_get_node),
228
229         DEVMETHOD_END
230 };
231
232 static driver_t gpiomdio_driver = {
233         "gpiomdio",
234         gpiomdio_methods,
235         sizeof(struct gpiomdio_softc),
236 };
237
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);