1 /*-
2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3 *
4 * Copyright (c) 2011-2012 Stefan Bethke.
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26 * SUCH DAMAGE.
27 *
28 * $FreeBSD$
29 */
30
31 #include <sys/param.h>
32 #include <sys/bus.h>
33 #include <sys/kernel.h>
34 #include <sys/malloc.h>
35 #include <sys/module.h>
36 #include <sys/socket.h>
37 #include <sys/sockio.h>
38 #include <sys/systm.h>
39
40 #include <net/if.h>
41 #include <net/if_media.h>
42
43 #include <dev/etherswitch/miiproxy.h>
44 #include <dev/mii/mii.h>
45 #include <dev/mii/miivar.h>
46
47 #include "mdio_if.h"
48 #include "miibus_if.h"
49
50
51 MALLOC_DECLARE(M_MIIPROXY);
52 MALLOC_DEFINE(M_MIIPROXY, "miiproxy", "miiproxy data structures");
53
54 driver_t miiproxy_driver;
55 driver_t mdioproxy_driver;
56
57 struct miiproxy_softc {
58 device_t parent;
59 device_t proxy;
60 device_t mdio;
61 };
62
63 struct mdioproxy_softc {
64 };
65
66 /*
67 * The rendezvous data structures and functions allow two device endpoints to
68 * match up, so that the proxy endpoint can be associated with a target
69 * endpoint. The proxy has to know the device name of the target that it
70 * wants to associate with, for example through a hint. The rendezvous code
71 * makes no assumptions about the devices that want to meet.
72 */
73 struct rendezvous_entry;
74
75 enum rendezvous_op {
76 RENDEZVOUS_ATTACH,
77 RENDEZVOUS_DETACH
78 };
79
80 typedef int (*rendezvous_callback_t)(enum rendezvous_op,
81 struct rendezvous_entry *);
82
83 static SLIST_HEAD(rendezvoushead, rendezvous_entry) rendezvoushead =
84 SLIST_HEAD_INITIALIZER(rendezvoushead);
85
86 struct rendezvous_endpoint {
87 device_t device;
88 const char *name;
89 rendezvous_callback_t callback;
90 };
91
92 struct rendezvous_entry {
93 SLIST_ENTRY(rendezvous_entry) entries;
94 struct rendezvous_endpoint proxy;
95 struct rendezvous_endpoint target;
96 };
97
98 /*
99 * Call the callback routines for both the proxy and the target. If either
100 * returns an error, undo the attachment.
101 */
102 static int
103 rendezvous_attach(struct rendezvous_entry *e, struct rendezvous_endpoint *ep)
104 {
105 int error;
106
107 error = e->proxy.callback(RENDEZVOUS_ATTACH, e);
108 if (error == 0) {
109 error = e->target.callback(RENDEZVOUS_ATTACH, e);
110 if (error != 0) {
111 e->proxy.callback(RENDEZVOUS_DETACH, e);
112 ep->device = NULL;
113 ep->callback = NULL;
114 }
115 }
116 return (error);
117 }
118
119 /*
120 * Create an entry for the proxy in the rendezvous list. The name parameter
121 * indicates the name of the device that is the target endpoint for this
122 * rendezvous. The callback will be invoked as soon as the target is
123 * registered: either immediately if the target registered itself earlier,
124 * or once the target registers. Returns ENXIO if the target has not yet
125 * registered.
126 */
127 static int
128 rendezvous_register_proxy(device_t dev, const char *name,
129 rendezvous_callback_t callback)
130 {
131 struct rendezvous_entry *e;
132
133 KASSERT(callback != NULL, ("callback must be set"));
134 SLIST_FOREACH(e, &rendezvoushead, entries) {
135 if (strcmp(name, e->target.name) == 0) {
136 /* the target is already attached */
137 e->proxy.name = device_get_nameunit(dev);
138 e->proxy.device = dev;
139 e->proxy.callback = callback;
140 return (rendezvous_attach(e, &e->proxy));
141 }
142 }
143 e = malloc(sizeof(*e), M_MIIPROXY, M_WAITOK | M_ZERO);
144 e->proxy.name = device_get_nameunit(dev);
145 e->proxy.device = dev;
146 e->proxy.callback = callback;
147 e->target.name = name;
148 SLIST_INSERT_HEAD(&rendezvoushead, e, entries);
149 return (ENXIO);
150 }
151
152 /*
153 * Create an entry in the rendezvous list for the target.
154 * Returns ENXIO if the proxy has not yet registered.
155 */
156 static int
157 rendezvous_register_target(device_t dev, rendezvous_callback_t callback)
158 {
159 struct rendezvous_entry *e;
160 const char *name;
161
162 KASSERT(callback != NULL, ("callback must be set"));
163 name = device_get_nameunit(dev);
164 SLIST_FOREACH(e, &rendezvoushead, entries) {
165 if (strcmp(name, e->target.name) == 0) {
166 e->target.device = dev;
167 e->target.callback = callback;
168 return (rendezvous_attach(e, &e->target));
169 }
170 }
171 e = malloc(sizeof(*e), M_MIIPROXY, M_WAITOK | M_ZERO);
172 e->target.name = name;
173 e->target.device = dev;
174 e->target.callback = callback;
175 SLIST_INSERT_HEAD(&rendezvoushead, e, entries);
176 return (ENXIO);
177 }
178
179 /*
180 * Remove the registration for the proxy.
181 */
182 static int
183 rendezvous_unregister_proxy(device_t dev)
184 {
185 struct rendezvous_entry *e;
186 int error = 0;
187
188 SLIST_FOREACH(e, &rendezvoushead, entries) {
189 if (e->proxy.device == dev) {
190 if (e->target.device == NULL) {
191 SLIST_REMOVE(&rendezvoushead, e, rendezvous_entry, entries);
192 free(e, M_MIIPROXY);
193 return (0);
194 } else {
195 e->proxy.callback(RENDEZVOUS_DETACH, e);
196 e->target.callback(RENDEZVOUS_DETACH, e);
197 }
198 e->proxy.device = NULL;
199 e->proxy.callback = NULL;
200 return (error);
201 }
202 }
203 return (ENOENT);
204 }
205
206 /*
207 * Remove the registration for the target.
208 */
209 static int
210 rendezvous_unregister_target(device_t dev)
211 {
212 struct rendezvous_entry *e;
213 int error = 0;
214
215 SLIST_FOREACH(e, &rendezvoushead, entries) {
216 if (e->target.device == dev) {
217 if (e->proxy.device == NULL) {
218 SLIST_REMOVE(&rendezvoushead, e, rendezvous_entry, entries);
219 free(e, M_MIIPROXY);
220 return (0);
221 } else {
222 e->proxy.callback(RENDEZVOUS_DETACH, e);
223 e->target.callback(RENDEZVOUS_DETACH, e);
224 }
225 e->target.device = NULL;
226 e->target.callback = NULL;
227 return (error);
228 }
229 }
230 return (ENOENT);
231 }
232
233 /*
234 * Functions of the proxy that is interposed between the ethernet interface
235 * driver and the miibus device.
236 */
237
238 static int
239 miiproxy_rendezvous_callback(enum rendezvous_op op, struct rendezvous_entry *rendezvous)
240 {
241 struct miiproxy_softc *sc = device_get_softc(rendezvous->proxy.device);
242
243 switch (op) {
244 case RENDEZVOUS_ATTACH:
245 sc->mdio = device_get_parent(rendezvous->target.device);
246 break;
247 case RENDEZVOUS_DETACH:
248 sc->mdio = NULL;
249 break;
250 }
251 return (0);
252 }
253
254 static int
255 miiproxy_probe(device_t dev)
256 {
257 device_set_desc(dev, "MII/MDIO proxy, MII side");
258
259 return (BUS_PROBE_SPECIFIC);
260 }
261
262 static int
263 miiproxy_attach(device_t dev)
264 {
265
266 /*
267 * The ethernet interface needs to call mii_attach_proxy() to pass
268 * the relevant parameters for rendezvous with the MDIO target.
269 */
270 return (bus_generic_attach(dev));
271 }
272
273 static int
274 miiproxy_detach(device_t dev)
275 {
276
277 rendezvous_unregister_proxy(dev);
278 bus_generic_detach(dev);
279 return (0);
280 }
281
282 static int
283 miiproxy_readreg(device_t dev, int phy, int reg)
284 {
285 struct miiproxy_softc *sc = device_get_softc(dev);
286
287 if (sc->mdio != NULL)
288 return (MDIO_READREG(sc->mdio, phy, reg));
289 return (-1);
290 }
291
292 static int
293 miiproxy_writereg(device_t dev, int phy, int reg, int val)
294 {
295 struct miiproxy_softc *sc = device_get_softc(dev);
296
297 if (sc->mdio != NULL)
298 return (MDIO_WRITEREG(sc->mdio, phy, reg, val));
299 return (-1);
300 }
301
302 static void
303 miiproxy_statchg(device_t dev)
304 {
305
306 MIIBUS_STATCHG(device_get_parent(dev));
307 }
308
309 static void
310 miiproxy_linkchg(device_t dev)
311 {
312
313 MIIBUS_LINKCHG(device_get_parent(dev));
314 }
315
316 static void
317 miiproxy_mediainit(device_t dev)
318 {
319
320 MIIBUS_MEDIAINIT(device_get_parent(dev));
321 }
322
323 /*
324 * Functions for the MDIO target device driver.
325 */
326 static int
327 mdioproxy_rendezvous_callback(enum rendezvous_op op, struct rendezvous_entry *rendezvous)
328 {
329 return (0);
330 }
331
332 static void
333 mdioproxy_identify(driver_t *driver, device_t parent)
334 {
335 if (device_find_child(parent, driver->name, -1) == NULL) {
336 BUS_ADD_CHILD(parent, 0, driver->name, -1);
337 }
338 }
339
340 static int
341 mdioproxy_probe(device_t dev)
342 {
343 device_set_desc(dev, "MII/MDIO proxy, MDIO side");
344
345 return (BUS_PROBE_SPECIFIC);
346 }
347
348 static int
349 mdioproxy_attach(device_t dev)
350 {
351
352 rendezvous_register_target(dev, mdioproxy_rendezvous_callback);
353 return (bus_generic_attach(dev));
354 }
355
356 static int
357 mdioproxy_detach(device_t dev)
358 {
359
360 rendezvous_unregister_target(dev);
361 bus_generic_detach(dev);
362 return (0);
363 }
364
365 /*
366 * Attach this proxy in place of miibus. The target MDIO must be attached
367 * already. Returns NULL on error.
368 */
369 device_t
370 mii_attach_proxy(device_t dev)
371 {
372 struct miiproxy_softc *sc;
373 int error;
374 const char *name;
375 device_t miiproxy;
376
377 if (resource_string_value(device_get_name(dev),
378 device_get_unit(dev), "mdio", &name) != 0) {
379 if (bootverbose)
380 printf("mii_attach_proxy: not attaching, no mdio"
381 " device hint for %s\n", device_get_nameunit(dev));
382 return (NULL);
383 }
384
385 miiproxy = device_add_child(dev, miiproxy_driver.name, -1);
386 error = bus_generic_attach(dev);
387 if (error != 0) {
388 device_printf(dev, "can't attach miiproxy\n");
389 return (NULL);
390 }
391 sc = device_get_softc(miiproxy);
392 sc->parent = dev;
393 sc->proxy = miiproxy;
394 if (rendezvous_register_proxy(miiproxy, name, miiproxy_rendezvous_callback) != 0) {
395 device_printf(dev, "can't attach proxy\n");
396 return (NULL);
397 }
398 device_printf(miiproxy, "attached to target %s\n", device_get_nameunit(sc->mdio));
399 return (miiproxy);
400 }
401
402 static device_method_t miiproxy_methods[] = {
403 /* device interface */
404 DEVMETHOD(device_probe, miiproxy_probe),
405 DEVMETHOD(device_attach, miiproxy_attach),
406 DEVMETHOD(device_detach, miiproxy_detach),
407 DEVMETHOD(device_shutdown, bus_generic_shutdown),
408
409 /* MII interface */
410 DEVMETHOD(miibus_readreg, miiproxy_readreg),
411 DEVMETHOD(miibus_writereg, miiproxy_writereg),
412 DEVMETHOD(miibus_statchg, miiproxy_statchg),
413 DEVMETHOD(miibus_linkchg, miiproxy_linkchg),
414 DEVMETHOD(miibus_mediainit, miiproxy_mediainit),
415
416 DEVMETHOD_END
417 };
418
419 static device_method_t mdioproxy_methods[] = {
420 /* device interface */
421 DEVMETHOD(device_identify, mdioproxy_identify),
422 DEVMETHOD(device_probe, mdioproxy_probe),
423 DEVMETHOD(device_attach, mdioproxy_attach),
424 DEVMETHOD(device_detach, mdioproxy_detach),
425 DEVMETHOD(device_shutdown, bus_generic_shutdown),
426
427 DEVMETHOD_END
428 };
429
430 DEFINE_CLASS_0(miiproxy, miiproxy_driver, miiproxy_methods,
431 sizeof(struct miiproxy_softc));
432 DEFINE_CLASS_0(mdioproxy, mdioproxy_driver, mdioproxy_methods,
433 sizeof(struct mdioproxy_softc));
434
435 DRIVER_MODULE(mdioproxy, mdio, mdioproxy_driver, 0, 0);
436 DRIVER_MODULE(miibus, miiproxy, miibus_driver, 0, 0);
437 MODULE_VERSION(miiproxy, 1);
438 MODULE_DEPEND(miiproxy, miibus, 1, 1, 1);
Cache object: 48808a0c23c4b9a12cfa44b29c953108
|