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