From owner-freebsd-arch@FreeBSD.ORG Fri Jan 20 23:08:38 2012 Return-Path: Delivered-To: freebsd-arch@freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2001:4f8:fff6::34]) by hub.freebsd.org (Postfix) with ESMTP id 35DA0106564A for ; Fri, 20 Jan 2012 23:08:38 +0000 (UTC) (envelope-from stb@lassitu.de) Received: from gilb.zs64.net (gilb.zs64.net [IPv6:2001:470:1f0b:105e::1ea]) by mx1.freebsd.org (Postfix) with ESMTP id 986E08FC18 for ; Fri, 20 Jan 2012 23:08:36 +0000 (UTC) Received: by gilb.zs64.net (Postfix, from stb@lassitu.de) id 464C311D3F3 for ; Fri, 20 Jan 2012 23:08:35 +0000 (UTC) From: Stefan Bethke Mime-Version: 1.0 (Apple Message framework v1251.1) Content-Type: multipart/mixed; boundary="Apple-Mail=_213E4B8A-6009-4FF8-A23A-C6A4E1D4CE37" Date: Sat, 21 Jan 2012 00:08:34 +0100 In-Reply-To: <20120111193738.GB44286@alchemy.franken.de> To: FreeBSD-arch References: <8D025847-4BE4-4B2C-87D7-97E72CC9D325@lassitu.de> <20120104215930.GM90831@alchemy.franken.de> <47ABA638-7E08-4350-A03C-3D4A23BF2D7E@lassitu.de> <1763C3FF-1EA0-4DC0-891D-63816EBF4A04@lassitu.de> <20120106182756.GA88161@alchemy.franken.de> <95372FB3-406F-46C2-8684-4FDB672D9FCF@lassitu.de> <20120106214741.GB88161@alchemy.franken.de> <20120108130039.GG88161@alchemy.franken.de> <23477898-8D85-498C-8E30-192810BD68A8@lassitu.de> <20120111193738.GB44286@alchemy.franken.de> Message-Id: <66DDA0A2-F878-43FF-8824-54868F493B18@lassitu.de> X-Mailer: Apple Mail (2.1251.1) Subject: Re: Extending sys/dev/mii X-BeenThere: freebsd-arch@freebsd.org X-Mailman-Version: 2.1.5 Precedence: list List-Id: Discussion related to FreeBSD architecture List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Fri, 20 Jan 2012 23:08:38 -0000 --Apple-Mail=_213E4B8A-6009-4FF8-A23A-C6A4E1D4CE37 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset=us-ascii Am 11.01.2012 um 20:37 schrieb Marius Strobl: > Okay, I suggest to postpone this discussion until then. For the > scenario when mdiobus is the parent of miibus I see no technical > need to change miibus to support what you want to do, just implement > the miibus_if in mdiobus and redirect it to the device_t of the > MAC there. Moreover, that way the hack to sidestep newbus is contained > in the layer that actually needs it and not scattered over multiple > frameworks. I've posted to -net a patch that implements a workaround along those = lines. It solves two issues: talking to two upstream devices, and = providing a proper attachment for miibus. There's a number of things that made this harder than I would have = liked: - miibus has a funny way of attaching to it's parent. Making the parent = a bus that automatically attaches matching children does not lead to = good results. - miibus uses it's parents ivars. To clarify: device_get_ivars get's a = devices ivars, but those are owned by the parent; the bus uses = device_[gs]et_ivars(9) to manipulate it's own private per-child data = storage. The device must not manipulate them. I believe those = variables can be moved to mii_data. - miibus makes assumptions about it's children, namely that they have = specific softc's and that they provide callbacks outside of the newbus = method mechanism - miibus assumes that all devices attached to that mdio have certain = registers that have certain bits set or cleared - miibus calls it's parent methods and two explicit callbacks, plus = various calls into the interface management code. The second problem is that there's currently no way to express a = dependency between two devices other than a parent-child relationship. = I would be interested to learn why this appears to be so uncommon that I = could not find any discussion of such a feature. Has it really never = before come up? Leaving aside the miibus issue, the newbus problem is that the ethernet = driver (which is attached to some bus) needs to associate with some = other driver that might not be in it's vincinity, in particular neither = it's parent nor one of it's children. Compounding the problem is that = there is no way to express an attachment ordering constraint so that the = ethernet driver is only attached once the required mdio driver has = attached. Stefan --=20 Stefan Bethke Fon +49 151 14070811 --Apple-Mail=_213E4B8A-6009-4FF8-A23A-C6A4E1D4CE37 Content-Disposition: attachment; filename=miiproxy.patch Content-Type: application/octet-stream; name="miiproxy.patch" Content-Transfer-Encoding: 7bit diff --git a/sys/dev/etherswitch/mdio.c b/sys/dev/etherswitch/mdio.c new file mode 100644 index 0000000..9302075 --- /dev/null +++ b/sys/dev/etherswitch/mdio.c @@ -0,0 +1,156 @@ +/*- + * Copyright (c) 2011-2012 Stefan Bethke. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#include +#include +#include + +#include + +#include "mdio_if.h" + +static void +mdio_identify(driver_t *driver, device_t parent) +{ + if (device_find_child(parent, mdio_driver.name, -1) == NULL) + BUS_ADD_CHILD(parent, 0, mdio_driver.name, -1); +} + +static int +mdio_probe(device_t dev) +{ + device_set_desc(dev, "MDIO"); + + return (BUS_PROBE_SPECIFIC); +} + +static int +mdio_attach(device_t dev) +{ + bus_generic_probe(dev); + bus_enumerate_hinted_children(dev); + return (bus_generic_attach(dev)); +} + +static int +mdio_detach(device_t dev) +{ + bus_generic_detach(dev); + return (0); +} + +static int +mdio_readreg(device_t dev, int phy, int reg) +{ + return MDIO_READREG(device_get_parent(dev), phy, reg); +} + +static int +mdio_writereg(device_t dev, int phy, int reg, int val) +{ + return MDIO_WRITEREG(device_get_parent(dev), phy, reg, val); +} + +static int +mdio_print_child(device_t dev, device_t child) +{ + int retval; + + retval = bus_print_child_header(dev, child); + retval += bus_print_child_footer(dev, child); + + return (retval); +} + +static int +mdio_read_ivar(device_t dev, device_t child __unused, int which, + uintptr_t *result) +{ + struct miibus_ivars *ivars; + + ivars = device_get_ivars(dev); + switch (which) { + default: + return (ENOENT); + } + return (0); +} + +static int +mdio_child_pnpinfo_str(device_t dev __unused, device_t child, char *buf, + size_t buflen) +{ + buf[0] = '\0'; + return (0); +} + +static int +mdio_child_location_str(device_t dev __unused, device_t child, char *buf, + size_t buflen) +{ + buf[0] = '\0'; + return (0); +} + +static void +mdio_hinted_child(device_t dev, const char *name, int unit) +{ + device_add_child(dev, name, unit); +} + +static device_method_t mdio_methods[] = { + /* device interface */ + DEVMETHOD(device_identify, mdio_identify), + DEVMETHOD(device_probe, mdio_probe), + DEVMETHOD(device_attach, mdio_attach), + DEVMETHOD(device_detach, mdio_detach), + DEVMETHOD(device_shutdown, bus_generic_shutdown), + + /* bus interface */ + DEVMETHOD(bus_print_child, mdio_print_child), + DEVMETHOD(bus_read_ivar, mdio_read_ivar), + DEVMETHOD(bus_child_pnpinfo_str, mdio_child_pnpinfo_str), + DEVMETHOD(bus_child_location_str, mdio_child_location_str), + DEVMETHOD(bus_add_child, device_add_child_ordered), + DEVMETHOD(bus_hinted_child, mdio_hinted_child), + + /* MDIO access */ + DEVMETHOD(mdio_readreg, mdio_readreg), + DEVMETHOD(mdio_writereg, mdio_writereg), + + DEVMETHOD_END +}; + +driver_t mdio_driver = { + "mdio", + mdio_methods, + 0 +}; + +devclass_t mdio_devclass; + diff --git a/sys/dev/etherswitch/mdio.h b/sys/dev/etherswitch/mdio.h new file mode 100644 index 0000000..52eddbd --- /dev/null +++ b/sys/dev/etherswitch/mdio.h @@ -0,0 +1,35 @@ +/*- + * Copyright (c) 2011-2012 Stefan Bethke. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#ifndef _DEV_MII_MDIO_H_ +#define _DEV_MII_MDIO_H_ + +extern driver_t mdio_driver; +extern devclass_t mdio_devclass; + +#endif diff --git a/sys/dev/etherswitch/mdio_if.m b/sys/dev/etherswitch/mdio_if.m new file mode 100644 index 0000000..9aedd92 --- /dev/null +++ b/sys/dev/etherswitch/mdio_if.m @@ -0,0 +1,24 @@ +# $FreeBSD$ + +#include + +INTERFACE mdio; + +# +# Read register from device on MDIO bus +# +METHOD int readreg { + device_t dev; + int phy; + int reg; +}; + +# +# Write register to device on MDIO bus +# +METHOD int writereg { + device_t dev; + int phy; + int reg; + int val; +}; diff --git a/sys/dev/etherswitch/miiproxy.c b/sys/dev/etherswitch/miiproxy.c new file mode 100644 index 0000000..2791082 --- /dev/null +++ b/sys/dev/etherswitch/miiproxy.c @@ -0,0 +1,437 @@ +/*- + * Copyright (c) 2011-2012 Stefan Bethke. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "mdio_if.h" +#include "miibus_if.h" + + +MALLOC_DECLARE(M_MIIPROXY); +MALLOC_DEFINE(M_MIIPROXY, "miiproxy", "miiproxy data structures"); + +driver_t miiproxy_driver; +driver_t mdioproxy_driver; + +struct miiproxy_softc { + device_t parent; + device_t proxy; + device_t mdio; + miiproxy_attach_callback_t attach_callback; + void *attach_arg; +}; + +struct mdioproxy_softc { +}; + +/* + * The rendevous data structures and functions allow two device endpoints to + * match up, so that the proxy endpoint can be associated with a target + * endpoint. The proxy has to know the device name of the target that it + * wants to associate with, for example through a hint. The rendevous code + * makes no assumptions about the devices that want to meet. + */ +struct rendevous_entry; + +enum rendevous_op { + RENDEVOUS_ATTACH, + RENDEVOUS_DETACH +}; + +typedef int (*rendevous_callback_t)(enum rendevous_op, + struct rendevous_entry *); + +static SLIST_HEAD(rendevoushead, rendevous_entry) rendevoushead = + SLIST_HEAD_INITIALIZER(rendevoushead); + +struct rendevous_endpoint { + device_t device; + const char *name; + rendevous_callback_t callback; +}; + +struct rendevous_entry { + SLIST_ENTRY(rendevous_entry) entries; + struct rendevous_endpoint proxy; + struct rendevous_endpoint target; +}; + +/* + * Call the callback routines for both the proxy and the target. If either + * returns an error, undo the attachment. + */ +static int +rendevous_attach(struct rendevous_entry *e, struct rendevous_endpoint *ep) +{ + int error; + + error = e->proxy.callback(RENDEVOUS_ATTACH, e); + if (error == 0) + error = e->target.callback(RENDEVOUS_ATTACH, e); + if (error != 0) { + e->proxy.callback(RENDEVOUS_DETACH, e); + ep->device = NULL; + ep->callback = NULL; + } + return (error); +} + +/* + * Create an entry for the proxy in the rendevous list. The name parameter + * indicates the name of the device that is the target endpoint for this + * rendevous. The callback will be invoked as soon as the target is + * registered: either immediately if the target registered itself earlier, + * or once the target registers. + */ +static int +rendevous_register_proxy(device_t dev, const char *name, + rendevous_callback_t callback) +{ + struct rendevous_entry *e; + + KASSERT(callback != NULL, ("callback must not be NULL")); + SLIST_FOREACH(e, &rendevoushead, entries) { + if (strcmp(name, e->target.name) == 0) { + /* the target is already attached */ + e->proxy.name = device_get_nameunit(dev); + e->proxy.device = dev; + e->proxy.callback = callback; + return (rendevous_attach(e, &e->proxy)); + } + } + e = malloc(sizeof(*e), M_MIIPROXY, M_WAITOK | M_ZERO); + e->proxy.name = device_get_nameunit(dev); + e->proxy.device = dev; + e->proxy.callback = callback; + e->target.name = name; + SLIST_INSERT_HEAD(&rendevoushead, e, entries); + return (0); +} + +/* + * Create an entry in the rendevous list for the target. The callback will + * be called once the proxy has registered. + */ +static int +rendevous_register_target(device_t dev, rendevous_callback_t callback) +{ + struct rendevous_entry *e; + const char *name; + + KASSERT(callback != NULL, ("callback must not be NULL")); + name = device_get_nameunit(dev); + SLIST_FOREACH(e, &rendevoushead, entries) { + if (strcmp(name, e->target.name) == 0) { + e->target.device = dev; + e->target.callback = callback; + return (rendevous_attach(e, &e->target)); + } + } + e = malloc(sizeof(*e), M_MIIPROXY, M_WAITOK | M_ZERO); + e->target.name = name; + e->target.device = dev; + e->target.callback = callback; + SLIST_INSERT_HEAD(&rendevoushead, e, entries); + return (0); +} + +/* + * Remove the registration for the proxy. + */ +static int +rendevous_unregister_proxy(device_t dev) +{ + struct rendevous_entry *e; + int error = 0; + + SLIST_FOREACH(e, &rendevoushead, entries) { + if (e->proxy.device == dev) { + if (e->target.device == NULL) { + SLIST_REMOVE(&rendevoushead, e, rendevous_entry, entries); + free(e, M_MIIPROXY); + return (0); + } else { + e->proxy.callback(RENDEVOUS_DETACH, e); + e->target.callback(RENDEVOUS_DETACH, e); + } + e->proxy.device = NULL; + e->proxy.callback = NULL; + return (error); + } + } + return (ENOENT); +} + +/* + * Remove the registration for the target. + */ +static int +rendevous_unregister_target(device_t dev) +{ + struct rendevous_entry *e; + int error = 0; + + SLIST_FOREACH(e, &rendevoushead, entries) { + if (e->target.device == dev) { + if (e->proxy.device == NULL) { + SLIST_REMOVE(&rendevoushead, e, rendevous_entry, entries); + free(e, M_MIIPROXY); + return (0); + } else { + e->proxy.callback(RENDEVOUS_DETACH, e); + e->target.callback(RENDEVOUS_DETACH, e); + } + e->target.device = NULL; + e->target.callback = NULL; + return (error); + } + } + return (ENOENT); +} + +/* + * Functions of the proxy that is interposed between the ethernet interface + * driver and the miibus device. + */ + +static int +miiproxy_rendevous_callback(enum rendevous_op op, struct rendevous_entry *rendevous) +{ + struct miiproxy_softc *sc = device_get_softc(rendevous->proxy.device); + + switch (op) { + case RENDEVOUS_ATTACH: + sc->mdio = device_get_parent(rendevous->target.device); + (sc->attach_callback)(sc->attach_arg); + break; + case RENDEVOUS_DETACH: + sc->mdio = NULL; + /* detach miibus */ + } + return (0); +} + +static int +miiproxy_probe(device_t dev) +{ + device_set_desc(dev, "MII/MDIO proxy, MII side"); + + return (BUS_PROBE_SPECIFIC); +} + +static int +miiproxy_attach(device_t dev) +{ + /* + * The ethernet interface needs to call mii_attach_proxy() to pass + * the relevant parameters for rendevous with the MDIO target. + */ + return (bus_generic_attach(dev)); +} + +static int +miiproxy_detach(device_t dev) +{ + rendevous_unregister_proxy(dev); + bus_generic_detach(dev); + return (0); +} + +static int +miiproxy_readreg(device_t dev, int phy, int reg) +{ + struct miiproxy_softc *sc = device_get_softc(dev); + + if (sc->mdio != NULL) + return (MDIO_READREG(sc->mdio, phy, reg)); + return (-1); +} + +static int +miiproxy_writereg(device_t dev, int phy, int reg, int val) +{ + struct miiproxy_softc *sc = device_get_softc(dev); + + if (sc->mdio != NULL) + return (MDIO_WRITEREG(sc->mdio, phy, reg, val)); + return (-1); +} + +static void +miiproxy_statchg(device_t dev) +{ + MIIBUS_STATCHG(device_get_parent(dev)); +} + +static void +miiproxy_linkchg(device_t dev) +{ + MIIBUS_LINKCHG(device_get_parent(dev)); +} + +static void +miiproxy_mediainit(device_t dev) +{ + MIIBUS_MEDIAINIT(device_get_parent(dev)); +} + +/* + * Functions for the MDIO target device driver. + */ +static int +mdioproxy_rendevous_callback(enum rendevous_op op, struct rendevous_entry *rendevous) +{ + return (0); +} + +static void +mdioproxy_identify(driver_t *driver, device_t parent) +{ + device_t child; + + if (device_find_child(parent, driver->name, -1) == NULL) { + child = BUS_ADD_CHILD(parent, 0, driver->name, -1); + } +} + +static int +mdioproxy_probe(device_t dev) +{ + device_set_desc(dev, "MII/MDIO proxy, MDIO side"); + + return (BUS_PROBE_SPECIFIC); +} + +static int +mdioproxy_attach(device_t dev) +{ + rendevous_register_target(dev, mdioproxy_rendevous_callback); + return (bus_generic_attach(dev)); +} + +static int +mdioproxy_detach(device_t dev) +{ + rendevous_unregister_target(dev); + bus_generic_detach(dev); + return (0); +} + +/* + * Attach this proxy in place of miibus. The callback is called once all + * parts are in place, so that it can attach the miibus to the proxy device, + * and finish interface initialization. + */ +device_t +mii_attach_proxy(device_t dev, miiproxy_attach_callback_t cb, void *aa) +{ + struct miiproxy_softc *sc; + int error; + const char *name; + device_t miiproxy; + + if (resource_string_value(device_get_name(dev), + device_get_unit(dev), "mdio", &name) != 0) { + if (bootverbose) + printf("mii_attach_proxy: not attaching, no mdio" + " device hint for %s\n", device_get_nameunit(dev)); + return (NULL); + } + + miiproxy = device_add_child(dev, miiproxy_driver.name, -1); + error = bus_generic_attach(dev); + if (error != 0) { + device_printf(dev, "can't attach miiproxy\n"); + return (NULL); + } + sc = device_get_softc(miiproxy); + sc->parent = dev; + sc->proxy = miiproxy; + sc->attach_callback = cb; + sc->attach_arg = aa; + rendevous_register_proxy(miiproxy, name, miiproxy_rendevous_callback); + return (miiproxy); +} + +static device_method_t miiproxy_methods[] = { + /* device interface */ + DEVMETHOD(device_probe, miiproxy_probe), + DEVMETHOD(device_attach, miiproxy_attach), + DEVMETHOD(device_detach, miiproxy_detach), + DEVMETHOD(device_shutdown, bus_generic_shutdown), + + /* MII interface */ + DEVMETHOD(miibus_readreg, miiproxy_readreg), + DEVMETHOD(miibus_writereg, miiproxy_writereg), + DEVMETHOD(miibus_statchg, miiproxy_statchg), + DEVMETHOD(miibus_linkchg, miiproxy_linkchg), + DEVMETHOD(miibus_mediainit, miiproxy_mediainit), + + DEVMETHOD_END +}; + +static device_method_t mdioproxy_methods[] = { + /* device interface */ + DEVMETHOD(device_identify, mdioproxy_identify), + DEVMETHOD(device_probe, mdioproxy_probe), + DEVMETHOD(device_attach, mdioproxy_attach), + DEVMETHOD(device_detach, mdioproxy_detach), + DEVMETHOD(device_shutdown, bus_generic_shutdown), + + DEVMETHOD_END +}; + +DEFINE_CLASS_0(miiproxy, miiproxy_driver, miiproxy_methods, + sizeof(struct miiproxy_softc)); +DEFINE_CLASS_0(mdioproxy, mdioproxy_driver, mdioproxy_methods, + sizeof(struct mdioproxy_softc)); + +devclass_t miiproxy_devclass; +static devclass_t mdioproxy_devclass; + +DRIVER_MODULE(mdioproxy, mdio, mdioproxy_driver, mdioproxy_devclass, 0, 0); +DRIVER_MODULE(miibus, miiproxy, miibus_driver, miibus_devclass, 0, 0); +MODULE_VERSION(miiproxy, 1); +MODULE_DEPEND(miiproxy, miibus, 1, 1, 1); diff --git a/sys/dev/etherswitch/miiproxy.h b/sys/dev/etherswitch/miiproxy.h new file mode 100644 index 0000000..5b8ee7c --- /dev/null +++ b/sys/dev/etherswitch/miiproxy.h @@ -0,0 +1,39 @@ +/*- + * Copyright (c) 2011-2012 Stefan Bethke. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#ifndef _DEV_ETHERSWITCH_MIIPROXY_H_ +#define _DEV_ETHERSWITCH_MIIPROXY_H_ + +typedef void (*miiproxy_attach_callback_t)(void *); + +extern devclass_t miiproxy_devclass; +extern driver_t miiproxy_driver; + +device_t mii_attach_proxy(device_t dev, miiproxy_attach_callback_t cb, void *aa); + +#endif diff --git a/sys/mips/atheros/if_arge.c b/sys/mips/atheros/if_arge.c index 0154ae2..3dd2e15 100644 --- a/sys/mips/atheros/if_arge.c +++ b/sys/mips/atheros/if_arge.c @@ -72,8 +72,18 @@ __FBSDID("$FreeBSD$"); #include #include +#include + +#if defined(ARGE_MDIO) +#include +#include +#include "mdio_if.h" +#endif + + MODULE_DEPEND(arge, ether, 1, 1, 1); MODULE_DEPEND(arge, miibus, 1, 1, 1); +MODULE_VERSION(arge, 1); #include "miibus_if.h" @@ -102,6 +112,7 @@ typedef enum { #endif static int arge_attach(device_t); +static int arge_attach_finish(struct arge_softc *sc); static int arge_detach(device_t); static void arge_flush_ddr(struct arge_softc *); static int arge_ifmedia_upd(struct ifnet *); @@ -134,6 +145,8 @@ static void arge_intr(void *); static int arge_intr_filter(void *); static void arge_tick(void *); +static void arge_hinted_child(device_t bus, const char *dname, int dunit); + /* * ifmedia callbacks for multiPHY MAC */ @@ -160,6 +173,10 @@ static device_method_t arge_methods[] = { DEVMETHOD(miibus_writereg, arge_miibus_writereg), DEVMETHOD(miibus_statchg, arge_miibus_statchg), + /* bus interface */ + DEVMETHOD(bus_add_child, device_add_child_ordered), + DEVMETHOD(bus_hinted_child, arge_hinted_child), + DEVMETHOD_END }; @@ -174,6 +191,37 @@ static devclass_t arge_devclass; DRIVER_MODULE(arge, nexus, arge_driver, arge_devclass, 0, 0); DRIVER_MODULE(miibus, arge, miibus_driver, miibus_devclass, 0, 0); +#if defined(ARGE_MDIO) +static int argemdio_probe(device_t); +static int argemdio_attach(device_t); +static int argemdio_detach(device_t); + +/* + * Declare an additional, separate driver for accessing the MDIO bus. + */ +static device_method_t argemdio_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, argemdio_probe), + DEVMETHOD(device_attach, argemdio_attach), + DEVMETHOD(device_detach, argemdio_detach), + + /* bus interface */ + DEVMETHOD(bus_add_child, device_add_child_ordered), + + /* MDIO access */ + DEVMETHOD(mdio_readreg, arge_miibus_readreg), + DEVMETHOD(mdio_writereg, arge_miibus_writereg), +}; + +DEFINE_CLASS_0(argemdio, argemdio_driver, argemdio_methods, + sizeof(struct arge_softc)); +static devclass_t argemdio_devclass; + +DRIVER_MODULE(miiproxy, arge, miiproxy_driver, miiproxy_devclass, 0, 0); +DRIVER_MODULE(argemdio, nexus, argemdio_driver, argemdio_devclass, 0, 0); +DRIVER_MODULE(mdio, argemdio, mdio_driver, mdio_devclass, 0, 0); +#endif + /* * RedBoot passes MAC address to entry point as environment * variable. platfrom_start parses it and stores in this variable @@ -234,15 +282,22 @@ arge_attach_sysctl(device_t dev) #endif } +#if defined(ARGE_MDIO) +static void +arge_attach_proxy(void *aa) +{ + arge_attach_finish(aa); +} +#endif + static int arge_attach(device_t dev) { - uint8_t eaddr[ETHER_ADDR_LEN]; struct ifnet *ifp; struct arge_softc *sc; - int error = 0, rid, phymask; + int error = 0, rid; uint32_t reg, rnd; - int is_base_mac_empty, i, phys_total; + int is_base_mac_empty, i; uint32_t hint; long eeprom_mac_addr = 0; @@ -277,18 +332,18 @@ arge_attach(device_t dev) * Get which PHY of 5 available we should use for this unit */ if (resource_int_value(device_get_name(dev), device_get_unit(dev), - "phymask", &phymask) != 0) { + "phymask", &sc->arge_phymask) != 0) { /* * Use port 4 (WAN) for GE0. For any other port use * its PHY the same as its unit number */ if (sc->arge_mac_unit == 0) - phymask = (1 << 4); + sc->arge_phymask = (1 << 4); else /* Use all phys up to 4 */ - phymask = (1 << 4) - 1; + sc->arge_phymask = (1 << 4) - 1; - device_printf(dev, "No PHY specified, using mask %d\n", phymask); + device_printf(dev, "No PHY specified, using mask %d\n", sc->arge_phymask); } /* @@ -313,8 +368,6 @@ arge_attach(device_t dev) else sc->arge_duplex_mode = 0; - sc->arge_phymask = phymask; - mtx_init(&sc->arge_mtx, device_get_nameunit(dev), MTX_NETWORK_LOCK, MTX_DEF); callout_init_mtx(&sc->arge_stat_callout, &sc->arge_mtx, 0); @@ -323,7 +376,7 @@ arge_attach(device_t dev) /* Map control/status registers. */ sc->arge_rid = 0; sc->arge_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, - &sc->arge_rid, RF_ACTIVE); + &sc->arge_rid, RF_ACTIVE | RF_SHAREABLE); if (sc->arge_res == NULL) { device_printf(dev, "couldn't map memory\n"); @@ -371,8 +424,8 @@ arge_attach(device_t dev) is_base_mac_empty = 1; for (i = 0; i < ETHER_ADDR_LEN; i++) { - eaddr[i] = ar711_base_mac[i] & 0xff; - if (eaddr[i] != 0) + sc->arge_eaddr[i] = ar711_base_mac[i] & 0xff; + if (sc->arge_eaddr[i] != 0) is_base_mac_empty = 0; } @@ -385,16 +438,15 @@ arge_attach(device_t dev) "Generating random ethernet address.\n"); rnd = arc4random(); - eaddr[0] = 'b'; - eaddr[1] = 's'; - eaddr[2] = 'd'; - eaddr[3] = (rnd >> 24) & 0xff; - eaddr[4] = (rnd >> 16) & 0xff; - eaddr[5] = (rnd >> 8) & 0xff; + sc->arge_eaddr[0] = 'b'; + sc->arge_eaddr[1] = 's'; + sc->arge_eaddr[2] = 'd'; + sc->arge_eaddr[3] = (rnd >> 24) & 0xff; + sc->arge_eaddr[4] = (rnd >> 16) & 0xff; + sc->arge_eaddr[5] = (rnd >> 8) & 0xff; } - if (sc->arge_mac_unit != 0) - eaddr[5] += sc->arge_mac_unit; + sc->arge_eaddr[5] += sc->arge_mac_unit; if (arge_dma_alloc(sc) != 0) { error = ENXIO; @@ -423,19 +475,23 @@ arge_attach(device_t dev) ARGE_WRITE(sc, AR71XX_MAC_MAX_FRAME_LEN, 1536); +#if !defined(ARGE_MDIO) /* Reset MII bus */ ARGE_WRITE(sc, AR71XX_MAC_MII_CFG, MAC_MII_CFG_RESET); DELAY(100); ARGE_WRITE(sc, AR71XX_MAC_MII_CFG, MAC_MII_CFG_CLOCK_DIV_28); DELAY(100); +#endif /* * Set all Ethernet address registers to the same initial values * set all four addresses to 66-88-aa-cc-dd-ee */ - ARGE_WRITE(sc, AR71XX_MAC_STA_ADDR1, - (eaddr[2] << 24) | (eaddr[3] << 16) | (eaddr[4] << 8) | eaddr[5]); - ARGE_WRITE(sc, AR71XX_MAC_STA_ADDR2, (eaddr[0] << 8) | eaddr[1]); + ARGE_WRITE(sc, AR71XX_MAC_STA_ADDR1, (sc->arge_eaddr[2] << 24) + | (sc->arge_eaddr[3] << 16) | (sc->arge_eaddr[4] << 8) + | sc->arge_eaddr[5]); + ARGE_WRITE(sc, AR71XX_MAC_STA_ADDR2, (sc->arge_eaddr[0] << 8) + | sc->arge_eaddr[1]); ARGE_WRITE(sc, AR71XX_MAC_FIFO_CFG0, FIFO_CFG0_ALL << FIFO_CFG0_ENABLE_SHIFT); @@ -458,30 +514,44 @@ arge_attach(device_t dev) ARGE_WRITE(sc, AR71XX_MAC_FIFO_RX_FILTMASK, FIFO_RX_FILTMASK_DEFAULT); - /* - * Check if we have single-PHY MAC or multi-PHY - */ - phys_total = 0; - for (i = 0; i < ARGE_NPHY; i++) - if (phymask & (1 << i)) - phys_total ++; +#if defined(ARGE_MDIO) + sc->arge_miiproxy = mii_attach_proxy(sc->arge_dev, arge_attach_proxy, sc); + if (sc->arge_miiproxy == NULL) + return (arge_attach_finish(sc)); +#else + return (arge_attach_finish(sc)); +#endif +fail: + if (error) + arge_detach(dev); - if (phys_total == 0) { - error = EINVAL; - goto fail; - } + return (error); +} - if (phys_total == 1) { - /* Do MII setup. */ - error = mii_attach(dev, &sc->arge_miibus, ifp, - arge_ifmedia_upd, arge_ifmedia_sts, BMSR_DEFCAPMASK, - MII_PHY_ANY, MII_OFFSET_ANY, 0); - if (error != 0) { - device_printf(dev, "attaching PHYs failed\n"); - goto fail; +static int +arge_attach_finish(struct arge_softc *sc) +{ + int error, phy; + + device_printf(sc->arge_dev, "finishing attachment, phymask %04x" + ", proxy %s \n", sc->arge_phymask, sc->arge_miiproxy == NULL ? + "null" : "set"); + for (phy = 0; phy < ARGE_NPHY; phy++) { + if (((1 << phy) & sc->arge_phymask) != 0) { + error = mii_attach(sc->arge_miiproxy != NULL ? + sc->arge_miiproxy : sc->arge_dev, + &sc->arge_miibus, sc->arge_ifp, + arge_ifmedia_upd, arge_ifmedia_sts, + BMSR_DEFCAPMASK, phy, MII_OFFSET_ANY, 0); + if (error != 0) { + device_printf(sc->arge_dev, "unable to attach" + " PHY %d: %d\n", phy, error); + goto fail; + } } } - else { + if (sc->arge_miibus == NULL) { + /* no PHY, so use hard-coded values */ ifmedia_init(&sc->arge_ifmedia, 0, arge_multiphy_mediachange, arge_multiphy_mediastatus); @@ -494,24 +564,23 @@ arge_attach(device_t dev) } /* Call MI attach routine. */ - ether_ifattach(ifp, eaddr); + ether_ifattach(sc->arge_ifp, sc->arge_eaddr); /* Hook interrupt last to avoid having to lock softc */ - error = bus_setup_intr(dev, sc->arge_irq, INTR_TYPE_NET | INTR_MPSAFE, + error = bus_setup_intr(sc->arge_dev, sc->arge_irq, INTR_TYPE_NET | INTR_MPSAFE, arge_intr_filter, arge_intr, sc, &sc->arge_intrhand); if (error) { - device_printf(dev, "couldn't set up irq\n"); - ether_ifdetach(ifp); + device_printf(sc->arge_dev, "couldn't set up irq\n"); + ether_ifdetach(sc->arge_ifp); goto fail; } /* setup sysctl variables */ - arge_attach_sysctl(dev); - + arge_attach_sysctl(sc->arge_dev); fail: if (error) - arge_detach(dev); + arge_detach(sc->arge_dev); return (error); } @@ -542,6 +611,9 @@ arge_detach(device_t dev) if (sc->arge_miibus) device_delete_child(dev, sc->arge_miibus); + if (sc->arge_miiproxy) + device_delete_child(dev, sc->arge_miiproxy); + bus_generic_detach(dev); if (sc->arge_intrhand) @@ -592,6 +664,13 @@ arge_shutdown(device_t dev) return (0); } +static void +arge_hinted_child(device_t bus, const char *dname, int dunit) +{ + BUS_ADD_CHILD(bus, 0, dname, dunit); + device_printf(bus, "hinted child %s%d\n", dname, dunit); +} + static int arge_miibus_readreg(device_t dev, int phy, int reg) { @@ -600,16 +679,13 @@ arge_miibus_readreg(device_t dev, int phy, int reg) uint32_t addr = (phy << MAC_MII_PHY_ADDR_SHIFT) | (reg & MAC_MII_REG_MASK); - if ((sc->arge_phymask & (1 << phy)) == 0) - return (0); - mtx_lock(&miibus_mtx); - ARGE_MII_WRITE(AR71XX_MAC_MII_CMD, MAC_MII_CMD_WRITE); - ARGE_MII_WRITE(AR71XX_MAC_MII_ADDR, addr); - ARGE_MII_WRITE(AR71XX_MAC_MII_CMD, MAC_MII_CMD_READ); + ARGE_MDIO_WRITE(sc, AR71XX_MAC_MII_CMD, MAC_MII_CMD_WRITE); + ARGE_MDIO_WRITE(sc, AR71XX_MAC_MII_ADDR, addr); + ARGE_MDIO_WRITE(sc, AR71XX_MAC_MII_CMD, MAC_MII_CMD_READ); i = ARGE_MII_TIMEOUT; - while ((ARGE_MII_READ(AR71XX_MAC_MII_INDICATOR) & + while ((ARGE_MDIO_READ(sc, AR71XX_MAC_MII_INDICATOR) & MAC_MII_INDICATOR_BUSY) && (i--)) DELAY(5); @@ -620,8 +696,8 @@ arge_miibus_readreg(device_t dev, int phy, int reg) return (-1); } - result = ARGE_MII_READ(AR71XX_MAC_MII_STATUS) & MAC_MII_STATUS_MASK; - ARGE_MII_WRITE(AR71XX_MAC_MII_CMD, MAC_MII_CMD_WRITE); + result = ARGE_MDIO_READ(sc, AR71XX_MAC_MII_STATUS) & MAC_MII_STATUS_MASK; + ARGE_MDIO_WRITE(sc, AR71XX_MAC_MII_CMD, MAC_MII_CMD_WRITE); mtx_unlock(&miibus_mtx); ARGEDEBUG(sc, ARGE_DBG_MII, "%s: phy=%d, reg=%02x, value[%08x]=%04x\n", __func__, @@ -638,19 +714,15 @@ arge_miibus_writereg(device_t dev, int phy, int reg, int data) uint32_t addr = (phy << MAC_MII_PHY_ADDR_SHIFT) | (reg & MAC_MII_REG_MASK); - - if ((sc->arge_phymask & (1 << phy)) == 0) - return (-1); - ARGEDEBUG(sc, ARGE_DBG_MII, "%s: phy=%d, reg=%02x, value=%04x\n", __func__, phy, reg, data); mtx_lock(&miibus_mtx); - ARGE_MII_WRITE(AR71XX_MAC_MII_ADDR, addr); - ARGE_MII_WRITE(AR71XX_MAC_MII_CONTROL, data); + ARGE_MDIO_WRITE(sc, AR71XX_MAC_MII_ADDR, addr); + ARGE_MDIO_WRITE(sc, AR71XX_MAC_MII_CONTROL, data); i = ARGE_MII_TIMEOUT; - while ((ARGE_MII_READ(AR71XX_MAC_MII_INDICATOR) & + while ((ARGE_MDIO_READ(sc, AR71XX_MAC_MII_INDICATOR) & MAC_MII_INDICATOR_BUSY) && (i--)) DELAY(5); @@ -715,6 +787,8 @@ arge_set_pll(struct arge_softc *sc, int media, int duplex) uint32_t fifo_tx; int if_speed; + ARGEDEBUG(sc, ARGE_DBG_MII, "set_pll(%04x, %s)\n", media, + duplex == IFM_FDX ? "full" : "half"); cfg = ARGE_READ(sc, AR71XX_MAC_CFG2); cfg &= ~(MAC_CFG2_IFACE_MODE_1000 | MAC_CFG2_IFACE_MODE_10_100 @@ -1923,3 +1997,47 @@ arge_multiphy_mediastatus(struct ifnet *ifp, struct ifmediareq *ifmr) sc->arge_duplex_mode; } +#if defined(ARGE_MDIO) +static int +argemdio_probe(device_t dev) +{ + device_set_desc(dev, "Atheros AR71xx built-in ethernet interface, MDIO controller"); + return (0); +} + +static int +argemdio_attach(device_t dev) +{ + struct arge_softc *sc; + int error = 0; + + sc = device_get_softc(dev); + sc->arge_dev = dev; + sc->arge_mac_unit = device_get_unit(dev); + sc->arge_rid = 0; + sc->arge_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, + &sc->arge_rid, RF_ACTIVE | RF_SHAREABLE); + if (sc->arge_res == NULL) { + device_printf(dev, "couldn't map memory\n"); + error = ENXIO; + goto fail; + } + /* Reset MII bus */ + ARGE_WRITE(sc, AR71XX_MAC_MII_CFG, MAC_MII_CFG_RESET); + DELAY(100); + ARGE_WRITE(sc, AR71XX_MAC_MII_CFG, MAC_MII_CFG_CLOCK_DIV_28); + DELAY(100); + bus_generic_probe(dev); + bus_enumerate_hinted_children(dev); + error = bus_generic_attach(dev); +fail: + return (error); +} + +static int +argemdio_detach(device_t dev) +{ + return (0); +} + +#endif diff --git a/sys/mips/atheros/if_argevar.h b/sys/mips/atheros/if_argevar.h index c1df678..dc3d931 100644 --- a/sys/mips/atheros/if_argevar.h +++ b/sys/mips/atheros/if_argevar.h @@ -67,15 +67,10 @@ #define ARGE_CLEAR_BITS(sc, reg, bits) \ ARGE_WRITE(sc, reg, ARGE_READ(sc, (reg)) & ~(bits)) -/* - * MII registers access macros - */ -#define ARGE_MII_READ(reg) \ - *((volatile uint32_t *)MIPS_PHYS_TO_KSEG1((AR71XX_MII_BASE + reg))) - -#define ARGE_MII_WRITE(reg, val) \ - *((volatile uint32_t *)MIPS_PHYS_TO_KSEG1((AR71XX_MII_BASE + reg))) = (val) - +#define ARGE_MDIO_WRITE(_sc, _reg, _val) \ + ARGE_WRITE((_sc), (_reg), (_val)) +#define ARGE_MDIO_READ(_sc, _reg) \ + ARGE_READ((_sc), (_reg)) #define ARGE_DESC_EMPTY (1 << 31) #define ARGE_DESC_MORE (1 << 24) @@ -132,11 +127,14 @@ struct arge_softc { */ uint32_t arge_media_type; uint32_t arge_duplex_mode; + uint32_t arge_phymask; + uint8_t arge_eaddr[ETHER_ADDR_LEN]; struct resource *arge_res; int arge_rid; struct resource *arge_irq; void *arge_intrhand; device_t arge_miibus; + device_t arge_miiproxy; bus_dma_tag_t arge_parent_tag; bus_dma_tag_t arge_tag; struct mtx arge_mtx; @@ -148,7 +146,6 @@ struct arge_softc { int arge_detach; uint32_t arge_intr_status; int arge_mac_unit; - int arge_phymask; int arge_if_flags; uint32_t arge_debug; struct { --Apple-Mail=_213E4B8A-6009-4FF8-A23A-C6A4E1D4CE37--