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