1 /*
2 * Copyright (c) 2026 Justin Hibbits
3 *
4 * SPDX-License-Identifier: BSD-2-Clause
5 */
6
7 #include <sys/param.h>
8 #include <sys/systm.h>
9 #include <sys/kernel.h>
10 #include <sys/bus.h>
11 #include <sys/module.h>
12 #include <sys/mutex.h>
13 #include <sys/resource.h>
14 #include <sys/socket.h>
15
16 #include <machine/bus.h>
17
18 #include <net/if.h>
19 #include <net/if_media.h>
20 #include <net/if_types.h>
21 #include <net/if_var.h>
22
23 #include <dev/mdio/mdio.h>
24 #include <dev/mii/mii.h>
25 #include <dev/mii/miivar.h>
26
27 #include <dev/ofw/ofw_bus.h>
28 #include <dev/ofw/ofw_bus_subr.h>
29
30 #include "fman.h"
31 #include "miibus_if.h"
32 #include "mdio_if.h"
33
34 #define MDIO_LOCK() mtx_lock(&sc->sc_lock)
35 #define MDIO_UNLOCK() mtx_unlock(&sc->sc_lock)
36 #define MDIO_WRITE4(sc, r, v) \
37 bus_write_4(sc->sc_res, r, v)
38 #define MDIO_READ4(sc, r) \
39 bus_read_4(sc->sc_res, r)
40
41 #define MDIO_CFG 0x30
42 #define CFG_ENC45 0x00000040
43 #define MDIO_STAT 0x30
44 #define STAT_BUSY 0x80000000
45 #define STAT_MDIO_RD_ER 0x00000002
46 #define MDIO_CTL 0x34
47 #define CTL_READ 0x00008000
48 #define MDIO_DATA 0x38
49 #define MDIO_ADDR 0x3c
50
51 static int xmdio_fdt_probe(device_t dev);
52 static int xmdio_fdt_attach(device_t dev);
53 static int xmdio_detach(device_t dev);
54 static int xmdio_miibus_readreg(device_t dev, int phy, int reg);
55 static int xmdio_miibus_writereg(device_t dev, int phy, int reg, int value);
56 static int xmdio_mdio_readextreg(device_t dev, int phy, int devad, int reg);
57 static int xmdio_mdio_writeextreg(device_t dev, int phy, int devad, int reg,
58 int val);
59
60 struct xmdio_softc {
61 struct mtx sc_lock;
62 struct resource *sc_res;
63 };
64
65 static struct ofw_compat_data mdio_compat_data[] = {
66 {"fsl,fman-memac-mdio", 0},
67 {"fsl,fman-xmdio", 0},
68 {NULL, 0}
69 };
70
71 static device_method_t xmdio_methods[] = {
72 /* Device interface */
73 DEVMETHOD(device_probe, xmdio_fdt_probe),
74 DEVMETHOD(device_attach, xmdio_fdt_attach),
75 DEVMETHOD(device_detach, xmdio_detach),
76 DEVMETHOD(bus_add_child, bus_generic_add_child),
77
78 /* MII interface */
79 DEVMETHOD(miibus_readreg, xmdio_miibus_readreg),
80 DEVMETHOD(miibus_writereg, xmdio_miibus_writereg),
81
82 /* MDIO interface */
83 DEVMETHOD(mdio_readreg, xmdio_miibus_readreg),
84 DEVMETHOD(mdio_writereg, xmdio_miibus_writereg),
85 DEVMETHOD(mdio_readextreg, xmdio_mdio_readextreg),
86 DEVMETHOD(mdio_writeextreg, xmdio_mdio_writeextreg),
87
88 DEVMETHOD_END
89 };
90
91 static driver_t xmdio_driver = {
92 "xmdio",
93 xmdio_methods,
94 sizeof(struct xmdio_softc),
95 };
96
97 EARLY_DRIVER_MODULE(xmdio, fman, xmdio_driver, 0, 0,
98 BUS_PASS_SUPPORTDEV);
99 DRIVER_MODULE(miibus, xmdio, miibus_driver, 0, 0);
100 DRIVER_MODULE(mdio, xmdio, mdio_driver, 0, 0);
101 MODULE_DEPEND(xmdio, miibus, 1, 1, 1);
102
103 static int
xmdio_fdt_probe(device_t dev)104 xmdio_fdt_probe(device_t dev)
105 {
106
107 if (!ofw_bus_status_okay(dev))
108 return (ENXIO);
109
110 if (!ofw_bus_search_compatible(dev, mdio_compat_data)->ocd_str)
111 return (ENXIO);
112
113 device_set_desc(dev, "Freescale XGMAC MDIO");
114
115 return (BUS_PROBE_DEFAULT);
116 }
117
118 static int
xmdio_fdt_attach(device_t dev)119 xmdio_fdt_attach(device_t dev)
120 {
121 struct xmdio_softc *sc;
122
123 sc = device_get_softc(dev);
124
125 sc->sc_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, 0, RF_ACTIVE);
126
127 OF_device_register_xref(OF_xref_from_node(ofw_bus_get_node(dev)), dev);
128
129 mtx_init(&sc->sc_lock, device_get_nameunit(dev), "XMDIO lock",
130 MTX_DEF);
131
132 return (0);
133 }
134
135 static int
xmdio_detach(device_t dev)136 xmdio_detach(device_t dev)
137 {
138 struct xmdio_softc *sc;
139
140 sc = device_get_softc(dev);
141
142 mtx_destroy(&sc->sc_lock);
143
144 return (0);
145 }
146
147 static void
set_clause45(struct xmdio_softc * sc)148 set_clause45(struct xmdio_softc *sc)
149 {
150 uint32_t reg;
151
152 reg = MDIO_READ4(sc, MDIO_CFG);
153 MDIO_WRITE4(sc, MDIO_CFG, reg | CFG_ENC45);
154 }
155
156 static void
set_clause22(struct xmdio_softc * sc)157 set_clause22(struct xmdio_softc *sc)
158 {
159 uint32_t reg;
160
161 reg = MDIO_READ4(sc, MDIO_CFG);
162 MDIO_WRITE4(sc, MDIO_CFG, reg & ~CFG_ENC45);
163 }
164
165 static int
xmdio_wait_no_busy(struct xmdio_softc * sc)166 xmdio_wait_no_busy(struct xmdio_softc *sc)
167 {
168 uint32_t count, val;
169
170 for (count = 1000; count > 0; count--) {
171 val = MDIO_READ4(sc, MDIO_CFG);
172 if ((val & STAT_BUSY) == 0)
173 break;
174 DELAY(1);
175 }
176
177 if (count == 0)
178 return (0xffff);
179
180 return (0);
181 }
182
183 int
xmdio_miibus_readreg(device_t dev,int phy,int reg)184 xmdio_miibus_readreg(device_t dev, int phy, int reg)
185 {
186 struct xmdio_softc *sc;
187 int rv;
188 uint32_t ctl;
189
190 sc = device_get_softc(dev);
191
192 MDIO_LOCK();
193
194 set_clause22(sc);
195 ctl = (phy << 5) | reg;
196 MDIO_WRITE4(sc, MDIO_CTL, ctl | CTL_READ);
197
198 MDIO_READ4(sc, MDIO_CTL);
199
200 if (xmdio_wait_no_busy(sc))
201 rv = 0xffff;
202 else
203 rv = MDIO_READ4(sc, MDIO_DATA);
204
205 MDIO_WRITE4(sc, MDIO_CTL, 0);
206 MDIO_UNLOCK();
207
208 return (rv);
209 }
210
211 int
xmdio_miibus_writereg(device_t dev,int phy,int reg,int value)212 xmdio_miibus_writereg(device_t dev, int phy, int reg, int value)
213 {
214 struct xmdio_softc *sc;
215
216 sc = device_get_softc(dev);
217
218 MDIO_LOCK();
219 set_clause22(sc);
220 /* Stop the MII management read cycle */
221 MDIO_WRITE4(sc, MDIO_CTL, (phy << 5) | reg);
222
223 MDIO_WRITE4(sc, MDIO_DATA, value);
224
225 /* Wait till MII management write is complete */
226 xmdio_wait_no_busy(sc);
227 MDIO_UNLOCK();
228
229 return (0);
230 }
231
232 static int
xmdio_mdio_readextreg(device_t dev,int phy,int devad,int reg)233 xmdio_mdio_readextreg(device_t dev, int phy, int devad, int reg)
234 {
235 struct xmdio_softc *sc;
236 int rv;
237 uint32_t ctl;
238
239 sc = device_get_softc(dev);
240
241 MDIO_LOCK();
242
243 set_clause45(sc);
244 ctl = (phy << 5) | devad;
245 MDIO_WRITE4(sc, MDIO_CTL, ctl);
246 MDIO_WRITE4(sc, MDIO_ADDR, reg);
247 xmdio_wait_no_busy(sc);
248 MDIO_WRITE4(sc, MDIO_CTL, ctl | CTL_READ);
249 MDIO_READ4(sc, MDIO_CTL);
250
251 xmdio_wait_no_busy(sc);
252
253 if (MDIO_READ4(sc, MDIO_STAT) & STAT_MDIO_RD_ER)
254 rv = 0xffff;
255 else
256 rv = MDIO_READ4(sc, MDIO_DATA);
257
258 MDIO_WRITE4(sc, MDIO_CTL, 0);
259 MDIO_UNLOCK();
260
261 return (rv);
262 }
263
264 static int
xmdio_mdio_writeextreg(device_t dev,int phy,int devad,int reg,int val)265 xmdio_mdio_writeextreg(device_t dev, int phy, int devad, int reg, int val)
266 {
267 struct xmdio_softc *sc;
268
269 sc = device_get_softc(dev);
270
271 MDIO_LOCK();
272 set_clause45(sc);
273 /* Stop the MII management read cycle */
274 MDIO_WRITE4(sc, MDIO_CTL, (phy << 5) | devad);
275
276 MDIO_WRITE4(sc, MDIO_DATA, val);
277
278 /* Wait till MII management write is complete */
279 xmdio_wait_no_busy(sc);
280 MDIO_UNLOCK();
281
282 return (0);
283 }
284
285