xref: /freebsd/sys/dev/dpaa/fman_xmdio.c (revision f9bc005b8ef3b507a5ecbc2d7fe4411ef70eff38)
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 const struct ofw_bus_devinfo *xmdio_fdt_get_devinfo(device_t, device_t);
55 static int xmdio_miibus_readreg(device_t dev, int phy, int reg);
56 static int xmdio_miibus_writereg(device_t dev, int phy, int reg, int value);
57 static int xmdio_mdio_readextreg(device_t dev, int phy, int devad, int reg);
58 static int xmdio_mdio_writeextreg(device_t dev, int phy, int devad, int reg,
59     int val);
60 
61 struct xmdio_softc {
62 	struct mtx sc_lock;
63 	struct resource *sc_res;
64 };
65 
66 static struct ofw_compat_data mdio_compat_data[] = {
67 	{"fsl,fman-memac-mdio", 0},
68 	{"fsl,fman-xmdio", 0},
69 	{NULL, 0}
70 };
71 
72 static device_method_t xmdio_methods[] = {
73 	/* Device interface */
74 	DEVMETHOD(device_probe,		xmdio_fdt_probe),
75 	DEVMETHOD(device_attach,	xmdio_fdt_attach),
76 	DEVMETHOD(device_detach,	xmdio_detach),
77 	DEVMETHOD(bus_add_child,	bus_generic_add_child),
78 
79 	/* MII interface */
80 	DEVMETHOD(miibus_readreg,	xmdio_miibus_readreg),
81 	DEVMETHOD(miibus_writereg,	xmdio_miibus_writereg),
82 
83 	/* MDIO interface */
84 	DEVMETHOD(mdio_readreg,		xmdio_miibus_readreg),
85 	DEVMETHOD(mdio_writereg,	xmdio_miibus_writereg),
86 	DEVMETHOD(mdio_readextreg,	xmdio_mdio_readextreg),
87 	DEVMETHOD(mdio_writeextreg,	xmdio_mdio_writeextreg),
88 
89 	/* ofw_bus interface -- expose devinfo of the FDT children we added. */
90 	DEVMETHOD(ofw_bus_get_devinfo,	xmdio_fdt_get_devinfo),
91 	DEVMETHOD(ofw_bus_get_compat,	ofw_bus_gen_get_compat),
92 	DEVMETHOD(ofw_bus_get_model,	ofw_bus_gen_get_model),
93 	DEVMETHOD(ofw_bus_get_name,	ofw_bus_gen_get_name),
94 	DEVMETHOD(ofw_bus_get_node,	ofw_bus_gen_get_node),
95 	DEVMETHOD(ofw_bus_get_type,	ofw_bus_gen_get_type),
96 
97 	DEVMETHOD_END
98 };
99 
100 static driver_t xmdio_driver = {
101 	"mdio",
102 	xmdio_methods,
103 	sizeof(struct xmdio_softc),
104 };
105 
106 EARLY_DRIVER_MODULE(mdio, fman, xmdio_driver, 0, 0, BUS_PASS_SUPPORTDEV);
107 MODULE_DEPEND(mdio, miibus, 1, 1, 1);
108 
109 static int
110 xmdio_fdt_probe(device_t dev)
111 {
112 
113 	if (!ofw_bus_status_okay(dev))
114 		return (ENXIO);
115 
116 	if (!ofw_bus_search_compatible(dev, mdio_compat_data)->ocd_str)
117 		return (ENXIO);
118 
119 	device_set_desc(dev, "Freescale XGMAC MDIO");
120 
121 	return (BUS_PROBE_DEFAULT);
122 }
123 
124 static int
125 xmdio_fdt_attach(device_t dev)
126 {
127 	struct xmdio_softc *sc;
128 	struct ofw_bus_devinfo *obd;
129 	phandle_t node, child;
130 	device_t cdev;
131 
132 	sc = device_get_softc(dev);
133 
134 	sc->sc_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, 0, RF_ACTIVE);
135 
136 	OF_device_register_xref(OF_xref_from_node(ofw_bus_get_node(dev)), dev);
137 
138 	mtx_init(&sc->sc_lock, device_get_nameunit(dev), "XMDIO lock",
139 	    MTX_DEF);
140 
141 	node = ofw_bus_get_node(dev);
142 	for (child = OF_child(node); child != 0; child = OF_peer(child)) {
143 		if (!OF_hasprop(child, "reg"))
144 			continue;
145 		obd = malloc(sizeof(*obd), M_DEVBUF, M_WAITOK | M_ZERO);
146 		if (ofw_bus_gen_setup_devinfo(obd, child) != 0) {
147 			free(obd, M_DEVBUF);
148 			continue;
149 		}
150 		cdev = device_add_child(dev, NULL, DEVICE_UNIT_ANY);
151 		if (cdev == NULL) {
152 			ofw_bus_gen_destroy_devinfo(obd);
153 			free(obd, M_DEVBUF);
154 			continue;
155 		}
156 		device_set_ivars(cdev, obd);
157 	}
158 
159 	bus_identify_children(dev);
160 	bus_enumerate_hinted_children(dev);
161 	bus_attach_children(dev);
162 	return (0);
163 
164 	return (0);
165 }
166 
167 static int
168 xmdio_detach(device_t dev)
169 {
170 	struct xmdio_softc *sc;
171 
172 	sc = device_get_softc(dev);
173 
174 	mtx_destroy(&sc->sc_lock);
175 
176 	return (0);
177 }
178 
179 static void
180 set_clause45(struct xmdio_softc *sc)
181 {
182 	uint32_t reg;
183 
184 	reg = MDIO_READ4(sc, MDIO_CFG);
185 	MDIO_WRITE4(sc, MDIO_CFG, reg | CFG_ENC45);
186 }
187 
188 static void
189 set_clause22(struct xmdio_softc *sc)
190 {
191 	uint32_t reg;
192 
193 	reg = MDIO_READ4(sc, MDIO_CFG);
194 	MDIO_WRITE4(sc, MDIO_CFG, reg & ~CFG_ENC45);
195 }
196 
197 static int
198 xmdio_wait_no_busy(struct xmdio_softc *sc)
199 {
200 	uint32_t count, val;
201 
202 	for (count = 1000; count > 0; count--) {
203 		val = MDIO_READ4(sc, MDIO_CFG);
204 		if ((val & STAT_BUSY) == 0)
205 			break;
206 		DELAY(1);
207 	}
208 
209 	if (count == 0)
210 		return (0xffff);
211 
212 	return (0);
213 }
214 
215 int
216 xmdio_miibus_readreg(device_t dev, int phy, int reg)
217 {
218 	struct xmdio_softc *sc;
219 	int                  rv;
220 	uint32_t ctl;
221 
222 	sc = device_get_softc(dev);
223 
224 	MDIO_LOCK();
225 
226 	set_clause22(sc);
227 	ctl = (phy << 5) | reg;
228 	MDIO_WRITE4(sc, MDIO_CTL, ctl | CTL_READ);
229 
230 	MDIO_READ4(sc, MDIO_CTL);
231 
232 	if (xmdio_wait_no_busy(sc))
233 		rv = 0xffff;
234 	else
235 		rv = MDIO_READ4(sc, MDIO_DATA);
236 
237 	MDIO_WRITE4(sc, MDIO_CTL, 0);
238 	MDIO_UNLOCK();
239 
240 	return (rv);
241 }
242 
243 int
244 xmdio_miibus_writereg(device_t dev, int phy, int reg, int value)
245 {
246 	struct xmdio_softc *sc;
247 
248 	sc = device_get_softc(dev);
249 
250 	MDIO_LOCK();
251 	set_clause22(sc);
252 	/* Stop the MII management read cycle */
253 	MDIO_WRITE4(sc, MDIO_CTL, (phy << 5) | reg);
254 
255 	MDIO_WRITE4(sc, MDIO_DATA, value);
256 
257 	/* Wait till MII management write is complete */
258 	xmdio_wait_no_busy(sc);
259 	MDIO_UNLOCK();
260 
261 	return (0);
262 }
263 
264 static int
265 xmdio_mdio_readextreg(device_t dev, int phy, int devad, int reg)
266 {
267 	struct xmdio_softc *sc;
268 	int                  rv;
269 	uint32_t ctl;
270 
271 	sc = device_get_softc(dev);
272 
273 	MDIO_LOCK();
274 
275 	set_clause45(sc);
276 	ctl = (phy << 5) | devad;
277 	MDIO_WRITE4(sc, MDIO_CTL, ctl);
278 	MDIO_WRITE4(sc, MDIO_ADDR, reg);
279 	xmdio_wait_no_busy(sc);
280 	MDIO_WRITE4(sc, MDIO_CTL, ctl | CTL_READ);
281 	MDIO_READ4(sc, MDIO_CTL);
282 
283 	xmdio_wait_no_busy(sc);
284 
285 	if (MDIO_READ4(sc, MDIO_STAT) & STAT_MDIO_RD_ER)
286 		rv = 0xffff;
287 	else
288 		rv = MDIO_READ4(sc, MDIO_DATA);
289 
290 	MDIO_WRITE4(sc, MDIO_CTL, 0);
291 	MDIO_UNLOCK();
292 
293 	return (rv);
294 }
295 
296 static int
297 xmdio_mdio_writeextreg(device_t dev, int phy, int devad, int reg, int val)
298 {
299 	struct xmdio_softc *sc;
300 
301 	sc = device_get_softc(dev);
302 
303 	MDIO_LOCK();
304 	set_clause45(sc);
305 	/* Stop the MII management read cycle */
306 	MDIO_WRITE4(sc, MDIO_CTL, (phy << 5) | devad);
307 
308 	MDIO_WRITE4(sc, MDIO_DATA, val);
309 
310 	/* Wait till MII management write is complete */
311 	xmdio_wait_no_busy(sc);
312 	MDIO_UNLOCK();
313 
314 	return (0);
315 }
316 
317 static const struct ofw_bus_devinfo *
318 xmdio_fdt_get_devinfo(device_t bus __unused, device_t child)
319 {
320 
321 	return (device_get_ivars(child));
322 }
323 
324