Skip site navigation (1)Skip section navigation (2)
Date:      Sat, 21 Jan 2012 00:08:34 +0100
From:      Stefan Bethke <stb@lassitu.de>
To:        FreeBSD-arch <freebsd-arch@freebsd.org>
Subject:   Re: Extending sys/dev/mii
Message-ID:  <66DDA0A2-F878-43FF-8824-54868F493B18@lassitu.de>
In-Reply-To: <20120111193738.GB44286@alchemy.franken.de>
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> <F60B2B70-049F-4497-BBA8-3C421088C1EA@lassitu.de> <20120108130039.GG88161@alchemy.franken.de> <23477898-8D85-498C-8E30-192810BD68A8@lassitu.de> <20120111193738.GB44286@alchemy.franken.de>

next in thread | previous in thread | raw e-mail | index | archive | help

[-- Attachment #1 --]
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

-- 
Stefan Bethke <stb@lassitu.de>   Fon +49 151 14070811



[-- Attachment #2 --]
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 <sys/param.h>
+#include <sys/bus.h>
+#include <sys/systm.h>
+
+#include <dev/etherswitch/mdio.h>
+
+#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 <sys/bus.h>
+
+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 <sys/param.h>
+#include <sys/bus.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/socket.h>
+#include <sys/sockio.h>
+#include <sys/systm.h>
+
+#include <net/if.h>
+#include <net/if_arp.h>
+#include <net/ethernet.h>
+#include <net/if_dl.h>
+#include <net/if_media.h>
+#include <net/if_types.h>
+
+#include <dev/etherswitch/miiproxy.h>
+#include <dev/mii/mii.h>
+#include <dev/mii/miivar.h>
+
+#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 <dev/pci/pcireg.h>
 #include <dev/pci/pcivar.h>
 
+#include <opt_arge.h>
+
+#if defined(ARGE_MDIO)
+#include <dev/etherswitch/mdio.h>
+#include <dev/etherswitch/miiproxy.h>
+#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 {

Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?66DDA0A2-F878-43FF-8824-54868F493B18>