diff --git a/sys/dev/etherswitch/arswitch/arswitch.c b/sys/dev/etherswitch/arswitch/arswitch.c
new file mode 100644
index 000000000000..4130b369b696
--- /dev/null
+++ b/sys/dev/etherswitch/arswitch/arswitch.c
@@ -0,0 +1,557 @@
+/*-
+ * Copyright (c) 2011-2012 Stefan Bethke.
+ * Copyright (c) 2012 Adrian Chadd.
+ * 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/errno.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/socket.h>
+#include <sys/sockio.h>
+#include <sys/sysctl.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 <machine/bus.h>
+#include <dev/iicbus/iic.h>
+#include <dev/iicbus/iiconf.h>
+#include <dev/iicbus/iicbus.h>
+#include <dev/mii/mii.h>
+#include <dev/mii/miivar.h>
+#include <dev/etherswitch/mdio.h>
+
+#include <dev/etherswitch/etherswitch.h>
+
+#include <dev/etherswitch/arswitch/arswitchreg.h>
+#include <dev/etherswitch/arswitch/arswitchvar.h>
+#include <dev/etherswitch/arswitch/arswitch_reg.h>
+#include <dev/etherswitch/arswitch/arswitch_phy.h>
+
+#include <dev/etherswitch/arswitch/arswitch_8216.h>
+#include <dev/etherswitch/arswitch/arswitch_8226.h>
+#include <dev/etherswitch/arswitch/arswitch_8316.h>
+
+#include "mdio_if.h"
+#include "miibus_if.h"
+#include "etherswitch_if.h"
+
+#if	defined(DEBUG)
+static SYSCTL_NODE(_debug, OID_AUTO, arswitch, CTLFLAG_RD, 0, "arswitch");
+#endif
+
+static inline int arswitch_portforphy(int phy);
+static void arswitch_tick(void *arg);
+static int arswitch_ifmedia_upd(struct ifnet *);
+static void arswitch_ifmedia_sts(struct ifnet *, struct ifmediareq *);
+
+static void
+arswitch_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
+arswitch_probe(device_t dev)
+{
+	struct arswitch_softc *sc;
+	uint32_t id;
+	char *chipname, desc[256];
+
+	sc = device_get_softc(dev);
+	bzero(sc, sizeof(*sc));
+	sc->page = -1;
+	id = arswitch_readreg(dev, AR8X16_REG_MASK_CTRL);
+	switch ((id & AR8X16_MASK_CTRL_VER_MASK) >>
+	    AR8X16_MASK_CTRL_VER_SHIFT) {
+	case 1:
+		chipname = "AR8216";
+		sc->sc_switchtype = AR8X16_SWITCH_AR8216;
+		break;
+	case 2:
+		chipname = "AR8226";
+		sc->sc_switchtype = AR8X16_SWITCH_AR8226;
+		break;
+	case 16:
+		chipname = "AR8316";
+		sc->sc_switchtype = AR8X16_SWITCH_AR8316;
+		break;
+	default:
+		chipname = NULL;
+	}
+	DPRINTF(dev, "chipname=%s, rev=%02x\n", chipname,
+	    id & AR8X16_MASK_CTRL_REV_MASK);
+	if (chipname != NULL) {
+		snprintf(desc, sizeof(desc),
+		    "Atheros %s Ethernet Switch",
+		    chipname);
+		device_set_desc_copy(dev, desc);
+		return (BUS_PROBE_DEFAULT);
+	}
+	return (ENXIO);
+}
+
+static int
+arswitch_attach_phys(struct arswitch_softc *sc)
+{
+	int phy, err = 0;
+	char name[IFNAMSIZ];
+
+	/* PHYs need an interface, so we generate a dummy one */
+	snprintf(name, IFNAMSIZ, "%sport", device_get_nameunit(sc->sc_dev));
+	for (phy = 0; phy < sc->numphys; phy++) {
+		sc->ifp[phy] = if_alloc(IFT_ETHER);
+		sc->ifp[phy]->if_softc = sc;
+		sc->ifp[phy]->if_flags |= IFF_UP | IFF_BROADCAST |
+		    IFF_DRV_RUNNING | IFF_SIMPLEX;
+		sc->ifname[phy] = malloc(strlen(name)+1, M_DEVBUF, M_WAITOK);
+		bcopy(name, sc->ifname[phy], strlen(name)+1);
+		if_initname(sc->ifp[phy], sc->ifname[phy],
+		    arswitch_portforphy(phy));
+		err = mii_attach(sc->sc_dev, &sc->miibus[phy], sc->ifp[phy],
+		    arswitch_ifmedia_upd, arswitch_ifmedia_sts, \
+		    BMSR_DEFCAPMASK, phy, MII_OFFSET_ANY, 0);
+		DPRINTF(sc->sc_dev, "%s attached to pseudo interface %s\n",
+		    device_get_nameunit(sc->miibus[phy]),
+		    sc->ifp[phy]->if_xname);
+		if (err != 0) {
+			device_printf(sc->sc_dev,
+			    "attaching PHY %d failed\n",
+			    phy);
+		}
+	}
+	return (err);
+}
+
+static int
+arswitch_attach(device_t dev)
+{
+	struct arswitch_softc *sc;
+	int err = 0;
+
+	sc = device_get_softc(dev);
+
+	/* sc->sc_switchtype is already decided in arswitch_probe() */
+	sc->sc_dev = dev;
+	mtx_init(&sc->sc_mtx, "arswitch", NULL, MTX_DEF);
+	sc->page = -1;
+	strlcpy(sc->info.es_name, device_get_desc(dev),
+	    sizeof(sc->info.es_name));
+
+	/*
+	 * Attach switch related functions
+	 */
+	if (AR8X16_IS_SWITCH(sc, AR8216))
+		ar8216_attach(sc);
+	else if (AR8X16_IS_SWITCH(sc, AR8226))
+		ar8226_attach(sc);
+	else if (AR8X16_IS_SWITCH(sc, AR8316))
+		ar8316_attach(sc);
+	else
+		return (ENXIO);
+
+	/*
+	 * XXX these two should be part of the switch attach function
+	 */
+	sc->info.es_nports = 5; /* XXX technically 6, but 6th not used */
+	sc->info.es_nvlangroups = 16;
+
+	/* XXX Defaults for externally connected AR8316 */
+	sc->numphys = 4;
+	sc->phy4cpu = 1;
+	sc->is_rgmii = 1;
+	sc->is_gmii = 0;
+
+	(void) resource_int_value(device_get_name(dev), device_get_unit(dev),
+	    "numphys", &sc->numphys);
+	(void) resource_int_value(device_get_name(dev), device_get_unit(dev),
+	    "phy4cpu", &sc->phy4cpu);
+	(void) resource_int_value(device_get_name(dev), device_get_unit(dev),
+	    "is_rgmii", &sc->is_rgmii);
+	(void) resource_int_value(device_get_name(dev), device_get_unit(dev),
+	    "is_gmii", &sc->is_gmii);
+
+#ifdef NOTYET
+	arswitch_writereg(dev, AR8X16_REG_MASK_CTRL,
+	    AR8X16_MASK_CTRL_SOFT_RESET);
+	DELAY(1000);
+	if (arswitch_readreg(dev, AR8X16_REG_MASK_CTRL) &
+	    AR8X16_MASK_CTRL_SOFT_RESET) {
+		device_printf(dev, "unable to reset switch\n");
+		return (ENXIO);
+	}
+	arswitch_modifyreg(dev, AR8X16_REG_GLOBAL_CTRL,
+	    AR8X16_FLOOD_MASK_BCAST_TO_CPU,
+	    AR8X16_FLOOD_MASK_BCAST_TO_CPU);
+#endif
+
+	err = sc->hal.arswitch_hw_setup(sc);
+	if (err != 0)
+		return (err);
+
+	err = sc->hal.arswitch_hw_global_setup(sc);
+	if (err != 0)
+		return (err);
+
+	/*
+	 * Attach the PHYs and complete the bus enumeration.
+	 */
+	err = arswitch_attach_phys(sc);
+	if (err != 0)
+		return (err);
+
+	bus_generic_probe(dev);
+	bus_enumerate_hinted_children(dev);
+	err = bus_generic_attach(dev);
+	if (err != 0)
+		return (err);
+	
+	callout_init_mtx(&sc->callout_tick, &sc->sc_mtx, 0);
+	arswitch_tick(sc);
+	
+	return (err);
+}
+
+static int
+arswitch_detach(device_t dev)
+{
+	struct arswitch_softc *sc = device_get_softc(dev);
+	int i;
+
+	callout_drain(&sc->callout_tick);
+
+	for (i=0; i < sc->numphys; i++) {
+		if (sc->miibus[i] != NULL)
+			device_delete_child(dev, sc->miibus[i]);
+		if (sc->ifp[i] != NULL)
+			if_free(sc->ifp[i]);
+		free(sc->ifname[i], M_DEVBUF);
+	}
+
+	bus_generic_detach(dev);
+	mtx_destroy(&sc->sc_mtx);
+
+	return (0);
+}
+
+/*
+ * Convert PHY number to port number. PHY0 is connected to port 1, PHY1 to
+ * port 2, etc.
+ */
+static inline int
+arswitch_portforphy(int phy)
+{
+	return (phy+1);
+}
+
+static inline struct mii_data *
+arswitch_miiforport(struct arswitch_softc *sc, int port)
+{
+	int phy = port-1;
+
+	if (phy < 0 || phy >= sc->numphys)
+		return (NULL);
+	return (device_get_softc(sc->miibus[phy]));
+}
+
+static inline struct ifnet *
+arswitch_ifpforport(struct arswitch_softc *sc, int port)
+{
+	int phy = port-1;
+
+	if (phy < 0 || phy >= sc->numphys)
+		return (NULL);
+	return (sc->ifp[phy]);
+}
+
+/*
+ * Convert port status to ifmedia.
+ */
+static void
+arswitch_update_ifmedia(int portstatus, u_int *media_status, u_int *media_active)
+{
+	*media_active = IFM_ETHER;
+	*media_status = IFM_AVALID;
+
+	if ((portstatus & AR8X16_PORT_STS_LINK_UP) != 0)
+		*media_status |= IFM_ACTIVE;
+	else {
+		*media_active |= IFM_NONE;
+		return;
+	}
+	switch (portstatus & AR8X16_PORT_STS_SPEED_MASK) {
+	case AR8X16_PORT_STS_SPEED_10:
+		*media_active |= IFM_10_T;
+		break;
+	case AR8X16_PORT_STS_SPEED_100:
+		*media_active |= IFM_100_TX;
+		break;
+	case AR8X16_PORT_STS_SPEED_1000:
+		*media_active |= IFM_1000_T;
+		break;
+	}
+	if ((portstatus & AR8X16_PORT_STS_DUPLEX) == 0)
+		*media_active |= IFM_FDX;
+	else
+		*media_active |= IFM_HDX;
+	if ((portstatus & AR8X16_PORT_STS_TXFLOW) != 0)
+		*media_active |= IFM_ETH_TXPAUSE;
+	if ((portstatus & AR8X16_PORT_STS_RXFLOW) != 0)
+		*media_active |= IFM_ETH_RXPAUSE;
+}
+
+/*
+ * Poll the status for all PHYs.  We're using the switch port status because
+ * thats a lot quicker to read than talking to all the PHYs.  Care must be
+ * taken that the resulting ifmedia_active is identical to what the PHY will
+ * compute, or gratuitous link status changes will occur whenever the PHYs
+ * update function is called.
+ */
+static void
+arswitch_miipollstat(struct arswitch_softc *sc)
+{
+	int i;
+	struct mii_data *mii;
+	struct mii_softc *miisc;
+	int portstatus;
+
+	for (i = 0; i < sc->numphys; i++) {
+		if (sc->miibus[i] == NULL)
+			continue;
+		mii = device_get_softc(sc->miibus[i]);
+		portstatus = arswitch_readreg(sc->sc_dev,
+		    AR8X16_REG_PORT_STS(arswitch_portforphy(i)));
+#if 0
+		DPRINTF(sc->sc_dev, "p[%d]=%b\n",
+		    arge_portforphy(i),
+		    portstatus,
+		    "\20\3TXMAC\4RXMAC\5TXFLOW\6RXFLOW\7"
+		    "DUPLEX\11LINK_UP\12LINK_AUTO\13LINK_PAUSE");
+#endif
+		arswitch_update_ifmedia(portstatus, &mii->mii_media_status,
+		    &mii->mii_media_active);
+		LIST_FOREACH(miisc, &mii->mii_phys, mii_list) {
+			if (IFM_INST(mii->mii_media.ifm_cur->ifm_media) !=
+			    miisc->mii_inst)
+				continue;
+			mii_phy_update(miisc, MII_POLLSTAT);
+		}
+	}
+}
+
+static void
+arswitch_tick(void *arg)
+{
+	struct arswitch_softc *sc = arg;
+
+	arswitch_miipollstat(sc);
+	callout_reset(&sc->callout_tick, hz, arswitch_tick, sc);
+}
+
+static etherswitch_info_t *
+arswitch_getinfo(device_t dev)
+{
+	struct arswitch_softc *sc = device_get_softc(dev);
+	
+	return (&sc->info);
+}
+
+static int
+arswitch_getport(device_t dev, etherswitch_port_t *p)
+{
+	struct arswitch_softc *sc = device_get_softc(dev);
+	struct mii_data *mii;
+	struct ifmediareq *ifmr = &p->es_ifmr;
+	int err;
+	
+	if (p->es_port < 0 || p->es_port >= AR8X16_NUM_PORTS)
+		return (ENXIO);
+	p->es_vlangroup = 0;
+
+	mii = arswitch_miiforport(sc, p->es_port);
+	if (p->es_port == 0) {
+		/* fill in fixed values for CPU port */
+		ifmr->ifm_count = 0;
+		ifmr->ifm_current = ifmr->ifm_active =
+		    IFM_ETHER | IFM_1000_T | IFM_FDX;
+		ifmr->ifm_mask = 0;
+		ifmr->ifm_status = IFM_ACTIVE | IFM_AVALID;
+	} else if (mii != NULL) {
+		err = ifmedia_ioctl(mii->mii_ifp, &p->es_ifr,
+		    &mii->mii_media, SIOCGIFMEDIA);
+		if (err)
+			return (err);
+	} else {
+		return (ENXIO);
+	}
+	return (0);
+}
+
+/*
+ * XXX doesn't yet work?
+ */
+static int
+arswitch_setport(device_t dev, etherswitch_port_t *p)
+{
+	int err;
+	struct arswitch_softc *sc;
+	struct ifmedia *ifm;
+	struct mii_data *mii;
+	struct ifnet *ifp;
+
+	/*
+	 * XXX check the sc numphys, or the #define ?
+	 */
+	if (p->es_port < 0 || p->es_port >= AR8X16_NUM_PHYS)
+		return (ENXIO);
+
+	sc = device_get_softc(dev);
+
+	/*
+	 * XXX TODO: don't set the CPU port?
+	 */
+
+	mii = arswitch_miiforport(sc, p->es_port);
+	if (mii == NULL)
+		return (ENXIO);
+
+	ifp = arswitch_ifpforport(sc, p->es_port);
+
+	ifm = &mii->mii_media;
+	err = ifmedia_ioctl(ifp, &p->es_ifr, ifm, SIOCSIFMEDIA);
+	return (err);
+}
+
+static int
+arswitch_getvgroup(device_t dev, etherswitch_vlangroup_t *vg)
+{
+
+	/* XXX not implemented yet */
+	vg->es_vid = 0;
+	vg->es_member_ports = 0;
+	vg->es_untagged_ports = 0;
+	vg->es_fid = 0;
+	return (0);
+}
+
+static int
+arswitch_setvgroup(device_t dev, etherswitch_vlangroup_t *vg)
+{
+
+	/* XXX not implemented yet */
+	return (0);
+}
+
+static void
+arswitch_statchg(device_t dev)
+{
+
+	DPRINTF(dev, "%s\n", __func__);
+}
+
+static int
+arswitch_ifmedia_upd(struct ifnet *ifp)
+{
+	struct arswitch_softc *sc = ifp->if_softc;
+	struct mii_data *mii = arswitch_miiforport(sc, ifp->if_dunit);
+
+	if (mii == NULL)
+		return (ENXIO);
+	mii_mediachg(mii);
+	return (0);
+}
+
+static void
+arswitch_ifmedia_sts(struct ifnet *ifp, struct ifmediareq *ifmr)
+{
+	struct arswitch_softc *sc = ifp->if_softc;
+	struct mii_data *mii = arswitch_miiforport(sc, ifp->if_dunit);
+
+	DPRINTF(sc->sc_dev, "%s\n", __func__);
+
+	if (mii == NULL)
+		return;
+	mii_pollstat(mii);
+	ifmr->ifm_active = mii->mii_media_active;
+	ifmr->ifm_status = mii->mii_media_status;
+}
+
+static device_method_t arswitch_methods[] = {
+	/* Device interface */
+	DEVMETHOD(device_identify,	arswitch_identify),
+	DEVMETHOD(device_probe,		arswitch_probe),
+	DEVMETHOD(device_attach,	arswitch_attach),
+	DEVMETHOD(device_detach,	arswitch_detach),
+	
+	/* bus interface */
+	DEVMETHOD(bus_add_child,	device_add_child_ordered),
+	
+	/* MII interface */
+	DEVMETHOD(miibus_readreg,	arswitch_readphy),
+	DEVMETHOD(miibus_writereg,	arswitch_writephy),
+	DEVMETHOD(miibus_statchg,	arswitch_statchg),
+
+	/* MDIO interface */
+	DEVMETHOD(mdio_readreg,		arswitch_readphy),
+	DEVMETHOD(mdio_writereg,	arswitch_writephy),
+
+	/* etherswitch interface */
+	DEVMETHOD(etherswitch_getinfo,	arswitch_getinfo),
+	DEVMETHOD(etherswitch_readreg,	arswitch_readreg),
+	DEVMETHOD(etherswitch_writereg,	arswitch_writereg),
+	DEVMETHOD(etherswitch_readphyreg,	arswitch_readphy),
+	DEVMETHOD(etherswitch_writephyreg,	arswitch_writephy),
+	DEVMETHOD(etherswitch_getport,	arswitch_getport),
+	DEVMETHOD(etherswitch_setport,	arswitch_setport),
+	DEVMETHOD(etherswitch_getvgroup,	arswitch_getvgroup),
+	DEVMETHOD(etherswitch_setvgroup,	arswitch_setvgroup),
+
+	DEVMETHOD_END
+};
+
+DEFINE_CLASS_0(arswitch, arswitch_driver, arswitch_methods,
+    sizeof(struct arswitch_softc));
+static devclass_t arswitch_devclass;
+
+DRIVER_MODULE(arswitch, mdio, arswitch_driver, arswitch_devclass, 0, 0);
+DRIVER_MODULE(miibus, arswitch, miibus_driver, miibus_devclass, 0, 0);
+DRIVER_MODULE(mdio, arswitch, mdio_driver, mdio_devclass, 0, 0);
+DRIVER_MODULE(etherswitch, arswitch, etherswitch_driver, etherswitch_devclass, 0, 0);
+MODULE_VERSION(arswitch, 1);
+MODULE_DEPEND(arswitch, miibus, 1, 1, 1); /* XXX which versions? */
+MODULE_DEPEND(arswitch, etherswitch, 1, 1, 1); /* XXX which versions? */
diff --git a/sys/dev/etherswitch/arswitch/arswitch_8216.c b/sys/dev/etherswitch/arswitch/arswitch_8216.c
new file mode 100644
index 000000000000..eb49abad1414
--- /dev/null
+++ b/sys/dev/etherswitch/arswitch/arswitch_8216.c
@@ -0,0 +1,92 @@
+/*-
+ * Copyright (c) 2011-2012 Stefan Bethke.
+ * Copyright (c) 2012 Adrian Chadd.
+ * 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/errno.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/socket.h>
+#include <sys/sockio.h>
+#include <sys/sysctl.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 <machine/bus.h>
+#include <dev/iicbus/iic.h>
+#include <dev/iicbus/iiconf.h>
+#include <dev/iicbus/iicbus.h>
+#include <dev/mii/mii.h>
+#include <dev/mii/miivar.h>
+#include <dev/etherswitch/mdio.h>
+
+#include <dev/etherswitch/etherswitch.h>
+
+#include <dev/etherswitch/arswitch/arswitchreg.h>
+#include <dev/etherswitch/arswitch/arswitchvar.h>
+#include <dev/etherswitch/arswitch/arswitch_reg.h>
+#include <dev/etherswitch/arswitch/arswitch_8216.h>
+
+#include "mdio_if.h"
+#include "miibus_if.h"
+#include "etherswitch_if.h"
+
+/*
+ * AR8216 specific functions
+ */
+static int
+ar8216_hw_setup(struct arswitch_softc *sc)
+{
+
+	return (0);
+}
+
+/*
+ * Initialise other global values, for the AR8216.
+ */
+static int
+ar8216_hw_global_setup(struct arswitch_softc *sc)
+{
+
+	return (0);
+}
+
+void
+ar8216_attach(struct arswitch_softc *sc)
+{
+
+	sc->hal.arswitch_hw_setup = ar8216_hw_setup;
+	sc->hal.arswitch_hw_global_setup = ar8216_hw_global_setup;
+}
diff --git a/sys/dev/etherswitch/arswitch/arswitch_8216.h b/sys/dev/etherswitch/arswitch/arswitch_8216.h
new file mode 100644
index 000000000000..1795a20b921e
--- /dev/null
+++ b/sys/dev/etherswitch/arswitch/arswitch_8216.h
@@ -0,0 +1,34 @@
+/*-
+ * 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	__ARSWITCH_8216_H__
+#define	__ARSWITCH_8216_H__
+
+extern	void ar8216_attach(struct arswitch_softc *sc);
+
+#endif	/* __ARSWITCH_8216_H__ */
+
diff --git a/sys/dev/etherswitch/arswitch/arswitch_8226.c b/sys/dev/etherswitch/arswitch/arswitch_8226.c
new file mode 100644
index 000000000000..336120a7bffc
--- /dev/null
+++ b/sys/dev/etherswitch/arswitch/arswitch_8226.c
@@ -0,0 +1,92 @@
+/*-
+ * Copyright (c) 2011-2012 Stefan Bethke.
+ * Copyright (c) 2012 Adrian Chadd.
+ * 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/errno.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/socket.h>
+#include <sys/sockio.h>
+#include <sys/sysctl.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 <machine/bus.h>
+#include <dev/iicbus/iic.h>
+#include <dev/iicbus/iiconf.h>
+#include <dev/iicbus/iicbus.h>
+#include <dev/mii/mii.h>
+#include <dev/mii/miivar.h>
+#include <dev/etherswitch/mdio.h>
+
+#include <dev/etherswitch/etherswitch.h>
+
+#include <dev/etherswitch/arswitch/arswitchreg.h>
+#include <dev/etherswitch/arswitch/arswitchvar.h>
+#include <dev/etherswitch/arswitch/arswitch_phy.h>
+#include <dev/etherswitch/arswitch/arswitch_8226.h>
+
+#include "mdio_if.h"
+#include "miibus_if.h"
+#include "etherswitch_if.h"
+
+/*
+ * AR8226 specific functions
+ */
+static int
+ar8226_hw_setup(struct arswitch_softc *sc)
+{
+
+	return (0);
+}
+
+/*
+ * Initialise other global values, for the AR8226.
+ */
+static int
+ar8226_hw_global_setup(struct arswitch_softc *sc)
+{
+
+	return (0);
+}
+
+void
+ar8226_attach(struct arswitch_softc *sc)
+{
+
+	sc->hal.arswitch_hw_setup = ar8226_hw_setup;
+	sc->hal.arswitch_hw_global_setup = ar8226_hw_global_setup;
+}
diff --git a/sys/dev/etherswitch/arswitch/arswitch_8226.h b/sys/dev/etherswitch/arswitch/arswitch_8226.h
new file mode 100644
index 000000000000..f430e1200f8c
--- /dev/null
+++ b/sys/dev/etherswitch/arswitch/arswitch_8226.h
@@ -0,0 +1,34 @@
+/*-
+ * 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	__ARSWITCH_8226_H__
+#define	__ARSWITCH_8226_H__
+
+extern	void ar8226_attach(struct arswitch_softc *sc);
+
+#endif	/* __ARSWITCH_8226_H__ */
+
diff --git a/sys/dev/etherswitch/arswitch/arswitch_8316.c b/sys/dev/etherswitch/arswitch/arswitch_8316.c
new file mode 100644
index 000000000000..ebf276be4ded
--- /dev/null
+++ b/sys/dev/etherswitch/arswitch/arswitch_8316.c
@@ -0,0 +1,144 @@
+/*-
+ * Copyright (c) 2011-2012 Stefan Bethke.
+ * Copyright (c) 2012 Adrian Chadd.
+ * 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/errno.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/socket.h>
+#include <sys/sockio.h>
+#include <sys/sysctl.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 <machine/bus.h>
+#include <dev/iicbus/iic.h>
+#include <dev/iicbus/iiconf.h>
+#include <dev/iicbus/iicbus.h>
+#include <dev/mii/mii.h>
+#include <dev/mii/miivar.h>
+#include <dev/etherswitch/mdio.h>
+
+#include <dev/etherswitch/etherswitch.h>
+
+#include <dev/etherswitch/arswitch/arswitchreg.h>
+#include <dev/etherswitch/arswitch/arswitchvar.h>
+#include <dev/etherswitch/arswitch/arswitch_reg.h>
+#include <dev/etherswitch/arswitch/arswitch_8316.h>
+
+#include "mdio_if.h"
+#include "miibus_if.h"
+#include "etherswitch_if.h"
+
+/*
+ * AR8316 specific functions
+ */
+static int
+ar8316_hw_setup(struct arswitch_softc *sc)
+{
+
+	/*
+	 * Configure the switch mode based on whether:
+	 *
+	 * + The switch port is GMII/RGMII;
+	 * + Port 4 is either connected to the CPU or to the internal switch.
+	 */
+	if (sc->is_rgmii && sc->phy4cpu) {
+		arswitch_writereg(sc->sc_dev, AR8X16_REG_MODE,
+		    AR8X16_MODE_RGMII_PORT4_ISO);
+		device_printf(sc->sc_dev,
+		    "%s: MAC port == RGMII, port 4 = dedicated PHY\n",
+		    __func__);
+	} else if (sc->is_rgmii) {
+		arswitch_writereg(sc->sc_dev, AR8X16_REG_MODE,
+		    AR8X16_MODE_RGMII_PORT4_SWITCH);
+		device_printf(sc->sc_dev,
+		    "%s: MAC port == RGMII, port 4 = switch port\n",
+		    __func__);
+	} else if (sc->is_gmii) {
+		arswitch_writereg(sc->sc_dev, AR8X16_REG_MODE,
+		    AR8X16_MODE_GMII);
+		device_printf(sc->sc_dev, "%s: MAC port == GMII\n", __func__);
+	} else {
+		device_printf(sc->sc_dev, "%s: unknown switch PHY config\n",
+		    __func__);
+		return (ENXIO);
+	}
+
+	DELAY(1000);	/* 1ms wait for things to settle */
+
+	/*
+	 * If port 4 is RGMII, force workaround
+	 */
+	if (sc->is_rgmii && sc->phy4cpu) {
+		device_printf(sc->sc_dev,
+		    "%s: port 4 RGMII workaround\n",
+		    __func__);
+
+		/* work around for phy4 rgmii mode */
+		arswitch_writedbg(sc->sc_dev, 4, 0x12, 0x480c);
+		/* rx delay */
+		arswitch_writedbg(sc->sc_dev, 4, 0x0, 0x824e);
+		/* tx delay */
+		arswitch_writedbg(sc->sc_dev, 4, 0x5, 0x3d47);
+		DELAY(1000);	/* 1ms, again to let things settle */
+	}
+
+	return (0);
+}
+
+/*
+ * Initialise other global values, for the AR8316.
+ */
+static int
+ar8316_hw_global_setup(struct arswitch_softc *sc)
+{
+
+	arswitch_writereg(sc->sc_dev, 0x38, 0xc000050e);
+	arswitch_writereg(sc->sc_dev, AR8X16_REG_FLOOD_MASK, 0x003f003f);
+	arswitch_modifyreg(sc->sc_dev, AR8X16_REG_GLOBAL_CTRL,
+	    AR8316_GLOBAL_CTRL_MTU_MASK, 9018 + 8 + 2);
+
+	return (0);
+}
+
+void
+ar8316_attach(struct arswitch_softc *sc)
+{
+
+	sc->hal.arswitch_hw_setup = ar8316_hw_setup;
+	sc->hal.arswitch_hw_global_setup = ar8316_hw_global_setup;
+}
diff --git a/sys/dev/etherswitch/arswitch/arswitch_8316.h b/sys/dev/etherswitch/arswitch/arswitch_8316.h
new file mode 100644
index 000000000000..ceb8ae73ff8c
--- /dev/null
+++ b/sys/dev/etherswitch/arswitch/arswitch_8316.h
@@ -0,0 +1,34 @@
+/*-
+ * 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	__ARSWITCH_8316_H__
+#define	__ARSWITCH_8316_H__
+
+extern	void ar8316_attach(struct arswitch_softc *sc);
+
+#endif	/* __ARSWITCH_8316_H__ */
+
diff --git a/sys/dev/etherswitch/arswitch/arswitch_phy.c b/sys/dev/etherswitch/arswitch/arswitch_phy.c
new file mode 100644
index 000000000000..283cdb67bd9a
--- /dev/null
+++ b/sys/dev/etherswitch/arswitch/arswitch_phy.c
@@ -0,0 +1,136 @@
+/*-
+ * Copyright (c) 2011-2012 Stefan Bethke.
+ * Copyright (c) 2012 Adrian Chadd.
+ * 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/errno.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/socket.h>
+#include <sys/sockio.h>
+#include <sys/sysctl.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 <machine/bus.h>
+#include <dev/iicbus/iic.h>
+#include <dev/iicbus/iiconf.h>
+#include <dev/iicbus/iicbus.h>
+#include <dev/mii/mii.h>
+#include <dev/mii/miivar.h>
+#include <dev/etherswitch/mdio.h>
+
+#include <dev/etherswitch/etherswitch.h>
+
+#include <dev/etherswitch/arswitch/arswitchreg.h>
+#include <dev/etherswitch/arswitch/arswitchvar.h>
+
+#include <dev/etherswitch/arswitch/arswitch_reg.h>
+#include <dev/etherswitch/arswitch/arswitch_phy.h>
+
+#include "mdio_if.h"
+#include "miibus_if.h"
+#include "etherswitch_if.h"
+
+#if	defined(DEBUG)
+static SYSCTL_NODE(_debug, OID_AUTO, arswitch, CTLFLAG_RD, 0, "arswitch");
+#endif
+
+/*
+ * access PHYs integrated into the switch chip through the switch's MDIO
+ * control register.
+ */
+int
+arswitch_readphy(device_t dev, int phy, int reg)
+{
+	uint32_t data = 0, ctrl;
+	int err, timeout;
+
+	if (phy < 0 || phy >= 32)
+		return (ENXIO);
+	if (reg < 0 || reg >= 32)
+		return (ENXIO);
+	err = arswitch_writereg_msb(dev, AR8X16_REG_MDIO_CTRL,
+	    AR8X16_MDIO_CTRL_BUSY | AR8X16_MDIO_CTRL_MASTER_EN |
+	    AR8X16_MDIO_CTRL_CMD_READ |
+	    (phy << AR8X16_MDIO_CTRL_PHY_ADDR_SHIFT) |
+	    (reg << AR8X16_MDIO_CTRL_REG_ADDR_SHIFT));
+	DEVERR(dev, err, "arswitch_readphy()=%d: phy=%d.%02x\n", phy, reg);
+	if (err != 0)
+		return (-1);
+	for (timeout = 100; timeout--; ) {
+		ctrl = arswitch_readreg_msb(dev, AR8X16_REG_MDIO_CTRL);
+		if ((ctrl & AR8X16_MDIO_CTRL_BUSY) == 0)
+			break;
+	}
+	if (timeout < 0)
+		err = EIO;
+	data = arswitch_readreg_lsb(dev, AR8X16_REG_MDIO_CTRL) &
+	    AR8X16_MDIO_CTRL_DATA_MASK;
+	return (data);
+}
+
+int
+arswitch_writephy(device_t dev, int phy, int reg, int data)
+{
+	uint32_t ctrl;
+	int err, timeout;
+	
+	if (reg < 0 || reg >= 32)
+		return (ENXIO);
+	err = arswitch_writereg_lsb(dev, AR8X16_REG_MDIO_CTRL,
+	    (data & AR8X16_MDIO_CTRL_DATA_MASK));
+	DEVERR(dev, err, "arswitch_writephy()=%d: phy=%d.%02x\n", phy, reg);
+	if (err != 0)
+		return (err);
+	err = arswitch_writereg_msb(dev, AR8X16_REG_MDIO_CTRL,
+	    AR8X16_MDIO_CTRL_BUSY |
+	    AR8X16_MDIO_CTRL_MASTER_EN |
+	    AR8X16_MDIO_CTRL_CMD_WRITE |
+	    (phy << AR8X16_MDIO_CTRL_PHY_ADDR_SHIFT) |
+	    (reg << AR8X16_MDIO_CTRL_REG_ADDR_SHIFT));
+	DEVERR(dev, err, "arswitch_writephy()=%d: phy=%d.%02x\n", phy, reg);
+	if (err != 0)
+		return (err);
+	for (timeout = 100; timeout--; ) {
+		ctrl = arswitch_readreg(dev, AR8X16_REG_MDIO_CTRL);
+		if ((ctrl & AR8X16_MDIO_CTRL_BUSY) == 0)
+			break;
+	}
+	if (timeout < 0)
+		err = EIO;
+	DEVERR(dev, err, "arswitch_writephy()=%d: phy=%d.%02x\n", phy, reg);
+	return (err);
+}
diff --git a/sys/dev/etherswitch/arswitch/arswitch_phy.h b/sys/dev/etherswitch/arswitch/arswitch_phy.h
new file mode 100644
index 000000000000..a3b762746df1
--- /dev/null
+++ b/sys/dev/etherswitch/arswitch/arswitch_phy.h
@@ -0,0 +1,34 @@
+/*-
+ * 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	__ARSWITCH_PHY_H__
+#define	__ARSWITCH_PHY_H__
+
+extern	int arswitch_readphy(device_t dev, int phy, int reg);
+extern	int arswitch_writephy(device_t dev, int phy, int reg, int data);
+
+#endif	/* __ARSWITCH_PHY_H__ */
diff --git a/sys/dev/etherswitch/arswitch/arswitch_reg.c b/sys/dev/etherswitch/arswitch/arswitch_reg.c
new file mode 100644
index 000000000000..0c0e9153b2ed
--- /dev/null
+++ b/sys/dev/etherswitch/arswitch/arswitch_reg.c
@@ -0,0 +1,180 @@
+/*-
+ * 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/errno.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/socket.h>
+#include <sys/sockio.h>
+#include <sys/sysctl.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 <machine/bus.h>
+#include <dev/iicbus/iic.h>
+#include <dev/iicbus/iiconf.h>
+#include <dev/iicbus/iicbus.h>
+#include <dev/mii/mii.h>
+#include <dev/mii/miivar.h>
+#include <dev/etherswitch/mdio.h>
+
+#include <dev/etherswitch/etherswitch.h>
+
+#include <dev/etherswitch/arswitch/arswitchreg.h>
+#include <dev/etherswitch/arswitch/arswitchvar.h>
+#include <dev/etherswitch/arswitch/arswitch_reg.h>
+
+#include "mdio_if.h"
+#include "miibus_if.h"
+#include "etherswitch_if.h"
+
+static inline void
+arswitch_split_setpage(device_t dev, uint32_t addr, uint16_t *phy,
+    uint16_t *reg)
+{
+	struct arswitch_softc *sc = device_get_softc(dev);
+	uint16_t page;
+
+	page = ((addr) >> 9) & 0xffff;
+	*phy = (((addr) >> 6) & 0x07) | 0x10;
+	*reg = ((addr) >> 1) & 0x1f;
+
+	if (sc->page != page) {
+		MDIO_WRITEREG(device_get_parent(dev), 0x18, 0, page);
+		sc->page = page;
+	}
+}
+
+/*
+ * Read half a register.  Some of the registers define control bits, and
+ * the sequence of half-word accesses matters.  The register addresses
+ * are word-even (mod 4).
+ */
+static inline int
+arswitch_readreg16(device_t dev, int addr)
+{
+	uint16_t phy, reg;
+	
+	arswitch_split_setpage(dev, addr, &phy, &reg);
+	return (MDIO_READREG(device_get_parent(dev), phy, reg));
+}
+
+/*
+ * XXX NOTE:
+ *
+ * This may not work for AR7240 series embedded switches -
+ * the per-PHY register space doesn't seem to be exposed.
+ *
+ * In that instance, it may be required to speak via
+ * the internal switch PHY MDIO bus indirection.
+ */
+void
+arswitch_writedbg(device_t dev, int phy, uint16_t dbg_addr,
+    uint16_t dbg_data)
+{
+	(void) MDIO_WRITEREG(device_get_parent(dev), phy,
+	    MII_ATH_DBG_ADDR, dbg_addr);
+	(void) MDIO_WRITEREG(device_get_parent(dev), phy,
+	    MII_ATH_DBG_DATA, dbg_data);
+}
+
+/*
+ * Write half a register
+ */
+static inline int
+arswitch_writereg16(device_t dev, int addr, int data)
+{
+	uint16_t phy, reg;
+	
+	arswitch_split_setpage(dev, addr, &phy, &reg);
+	return (MDIO_WRITEREG(device_get_parent(dev), phy, reg, data));
+}
+
+int
+arswitch_readreg_lsb(device_t dev, int addr)
+{
+
+	return (arswitch_readreg16(dev, addr));
+}
+
+int
+arswitch_readreg_msb(device_t dev, int addr)
+{
+
+	return (arswitch_readreg16(dev, addr + 2) << 16);
+}
+
+int
+arswitch_writereg_lsb(device_t dev, int addr, int data)
+{
+
+	return (arswitch_writereg16(dev, addr, data & 0xffff));
+}
+
+int
+arswitch_writereg_msb(device_t dev, int addr, int data)
+{
+
+	return (arswitch_writereg16(dev, addr + 2, data >> 16));
+}
+
+int
+arswitch_readreg(device_t dev, int addr)
+{
+
+	return (arswitch_readreg_lsb(dev, addr) |
+	    arswitch_readreg_msb(dev, addr));
+}
+
+int
+arswitch_writereg(device_t dev, int addr, int value)
+{
+
+	/* XXX Check the first write too? */
+	arswitch_writereg_lsb(dev, addr, value);
+	return (arswitch_writereg_msb(dev, addr, value));
+}
+
+int
+arswitch_modifyreg(device_t dev, int addr, int mask, int set)
+{
+	int value;
+	
+	value = arswitch_readreg(dev, addr);
+	value &= ~mask;
+	value |= set;
+	return (arswitch_writereg(dev, addr, value));
+}
diff --git a/sys/dev/etherswitch/arswitch/arswitch_reg.h b/sys/dev/etherswitch/arswitch/arswitch_reg.h
new file mode 100644
index 000000000000..fdac59f9d786
--- /dev/null
+++ b/sys/dev/etherswitch/arswitch/arswitch_reg.h
@@ -0,0 +1,44 @@
+/*-
+ * 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	__ARSWITCH_REG_H__
+#define	__ARSWITCH_REG_H__
+
+extern	void arswitch_writedbg(device_t dev, int phy, uint16_t dbg_addr,
+	    uint16_t dbg_data);
+
+extern	int arswitch_readreg(device_t dev, int addr);
+extern	int arswitch_writereg(device_t dev, int addr, int value);
+extern	int arswitch_modifyreg(device_t dev, int addr, int mask, int set);
+
+extern	int arswitch_readreg_lsb(device_t dev, int addr);
+extern	int arswitch_readreg_msb(device_t dev, int addr);
+
+extern	int arswitch_writereg_lsb(device_t dev, int addr, int data);
+extern	int arswitch_writereg_msb(device_t dev, int addr, int data);
+
+#endif	/* __ARSWITCH_REG_H__ */
diff --git a/sys/dev/etherswitch/arswitch/arswitchreg.h b/sys/dev/etherswitch/arswitch/arswitchreg.h
new file mode 100644
index 000000000000..37175738d3cf
--- /dev/null
+++ b/sys/dev/etherswitch/arswitch/arswitchreg.h
@@ -0,0 +1,274 @@
+/*-
+ * Copyright (c) 2011 Aleksandr Rybalko.
+ * 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 __AR8X16_SWITCHREG_H__
+#define	__AR8X16_SWITCHREG_H__
+
+/* Atheros specific MII registers */
+#define	MII_ATH_DBG_ADDR		0x1d
+#define	MII_ATH_DBG_DATA		0x1e
+
+#define	AR8X16_REG_MASK_CTRL		0x0000
+#define		AR8X16_MASK_CTRL_REV_MASK	0x000000ff
+#define		AR8X16_MASK_CTRL_VER_MASK	0x0000ff00
+#define		AR8X16_MASK_CTRL_VER_SHIFT	8
+#define		AR8X16_MASK_CTRL_SOFT_RESET	(1 << 31)
+
+#define	AR8X16_REG_MODE			0x0008
+/* DIR-615 E4 U-Boot */
+#define		AR8X16_MODE_DIR_615_UBOOT	0x8d1003e0
+/* From Ubiquiti RSPRO */
+#define		AR8X16_MODE_RGMII_PORT4_ISO	0x81461bea
+#define		AR8X16_MODE_RGMII_PORT4_SWITCH	0x01261be2
+/* AVM Fritz!Box 7390 */
+#define		AR8X16_MODE_GMII		0x010e5b71
+/* from avm_cpmac/linux_ar_reg.h */
+#define		AR8X16_MODE_RESERVED		0x000e1b20
+#define		AR8X16_MODE_MAC0_GMII_EN	(1u <<  0)
+#define		AR8X16_MODE_MAC0_RGMII_EN	(1u <<  1)
+#define		AR8X16_MODE_PHY4_GMII_EN	(1u <<  2)
+#define		AR8X16_MODE_PHY4_RGMII_EN	(1u <<  3)
+#define		AR8X16_MODE_MAC0_MAC_MODE	(1u <<  4)
+#define		AR8X16_MODE_RGMII_RXCLK_DELAY_EN (1u <<  6)
+#define		AR8X16_MODE_RGMII_TXCLK_DELAY_EN (1u <<  7)
+#define		AR8X16_MODE_MAC5_MAC_MODE	(1u << 14)
+#define		AR8X16_MODE_MAC5_PHY_MODE	(1u << 15)
+#define		AR8X16_MODE_TXDELAY_S0		(1u << 21)
+#define		AR8X16_MODE_TXDELAY_S1		(1u << 22)
+#define		AR8X16_MODE_RXDELAY_S0		(1u << 23)
+#define		AR8X16_MODE_LED_OPEN_EN		(1u << 24)
+#define		AR8X16_MODE_SPI_EN		(1u << 25)
+#define		AR8X16_MODE_RXDELAY_S1		(1u << 26)
+#define		AR8X16_MODE_POWER_ON_SEL	(1u << 31)
+
+#define	AR8X16_REG_ISR			0x0010
+#define	AR8X16_REG_IMR			0x0014
+
+#define	AR8X16_REG_SW_MAC_ADDR0		0x0020
+#define	AR8X16_REG_SW_MAC_ADDR1		0x0024
+
+#define	AR8X16_REG_FLOOD_MASK		0x002c
+#define		AR8X16_FLOOD_MASK_BCAST_TO_CPU	(1 << 26)
+
+#define	AR8X16_REG_GLOBAL_CTRL		0x0030
+#define		AR8216_GLOBAL_CTRL_MTU_MASK	0x00000fff
+#define		AR8316_GLOBAL_CTRL_MTU_MASK	0x00007fff
+#define		AR8236_GLOBAL_CTRL_MTU_MASK	0x00007fff
+
+#define	AR8X16_REG_VLAN_CTRL			0x0040
+#define		AR8X16_VLAN_OP			0x00000007
+#define		AR8X16_VLAN_OP_NOOP		0x0
+#define		AR8X16_VLAN_OP_FLUSH		0x1
+#define		AR8X16_VLAN_OP_LOAD		0x2
+#define		AR8X16_VLAN_OP_PURGE		0x3
+#define		AR8X16_VLAN_OP_REMOVE_PORT	0x4
+#define		AR8X16_VLAN_OP_GET_NEXT		0x5
+#define		AR8X16_VLAN_OP_GET		0x6
+#define		AR8X16_VLAN_ACTIVE		(1 << 3)
+#define		AR8X16_VLAN_FULL		(1 << 4)
+#define		AR8X16_VLAN_PORT		0x00000f00
+#define		AR8X16_VLAN_PORT_SHIFT		8
+#define		AR8X16_VLAN_VID			0x0fff0000
+#define		AR8X16_VLAN_VID_SHIFT		16
+#define		AR8X16_VLAN_PRIO		0x70000000
+#define		AR8X16_VLAN_PRIO_SHIFT		28
+#define		AR8X16_VLAN_PRIO_EN		(1 << 31)
+
+#define	AR8X16_REG_VLAN_DATA		0x0044
+#define		AR8X16_VLAN_MEMBER		0x000003ff
+#define		AR8X16_VLAN_VALID		(1 << 11)
+
+#define	AR8X16_REG_ARL_CTRL0		0x0050
+#define	AR8X16_REG_ARL_CTRL1		0x0054
+#define	AR8X16_REG_ARL_CTRL2		0x0058
+
+#define	AR8X16_REG_AT_CTRL		0x005c
+#define		AR8X16_AT_CTRL_ARP_EN		(1 << 20)
+
+#define	AR8X16_REG_IP_PRIORITY_1     	0x0060
+#define	AR8X16_REG_IP_PRIORITY_2     	0x0064
+#define	AR8X16_REG_IP_PRIORITY_3     	0x0068
+#define	AR8X16_REG_IP_PRIORITY_4     	0x006C
+
+#define	AR8X16_REG_TAG_PRIO		0x0070
+
+#define	AR8X16_REG_SERVICE_TAG		0x0074
+#define		AR8X16_SERVICE_TAG_MASK		0x0000ffff
+
+#define	AR8X16_REG_CPU_PORT		0x0078
+#define		AR8X16_MIRROR_PORT_SHIFT	4
+#define		AR8X16_CPU_PORT_EN		(1 << 8)
+
+#define	AR8X16_REG_MIB_FUNC0		0x0080
+#define		AR8X16_MIB_TIMER_MASK		0x0000ffff
+#define		AR8X16_MIB_AT_HALF_EN		(1 << 16)
+#define		AR8X16_MIB_BUSY			(1 << 17)
+#define		AR8X16_MIB_FUNC_SHIFT		24
+#define		AR8X16_MIB_FUNC_NO_OP		0x0
+#define		AR8X16_MIB_FUNC_FLUSH		0x1
+#define		AR8X16_MIB_FUNC_CAPTURE		0x3
+#define		AR8X16_MIB_FUNC_XXX		(1 << 30) /* 0x40000000 */
+
+#define	AR8X16_REG_MDIO_HIGH_ADDR	0x0094
+
+#define	AR8X16_REG_MDIO_CTRL		0x0098
+#define		AR8X16_MDIO_CTRL_DATA_MASK	0x0000ffff
+#define		AR8X16_MDIO_CTRL_REG_ADDR_SHIFT	16
+#define		AR8X16_MDIO_CTRL_PHY_ADDR_SHIFT	21
+#define		AR8X16_MDIO_CTRL_CMD_WRITE	0
+#define		AR8X16_MDIO_CTRL_CMD_READ	(1 << 27)
+#define		AR8X16_MDIO_CTRL_MASTER_EN	(1 << 30)
+#define		AR8X16_MDIO_CTRL_BUSY		(1 << 31)
+
+#define	AR8X16_REG_PORT_BASE(_p)	(0x0100 + (_p) * 0x0100)
+
+#define	AR8X16_REG_PORT_STS(_p)		(AR8X16_REG_PORT_BASE((_p)) + 0x0000)
+#define		AR8X16_PORT_STS_SPEED_MASK	0x00000003
+#define		AR8X16_PORT_STS_SPEED_10	0
+#define		AR8X16_PORT_STS_SPEED_100	1
+#define		AR8X16_PORT_STS_SPEED_1000	2
+#define		AR8X16_PORT_STS_TXMAC		(1 << 2)
+#define		AR8X16_PORT_STS_RXMAC		(1 << 3)
+#define		AR8X16_PORT_STS_TXFLOW		(1 << 4)
+#define		AR8X16_PORT_STS_RXFLOW		(1 << 5)
+#define		AR8X16_PORT_STS_DUPLEX		(1 << 6)
+#define		AR8X16_PORT_STS_LINK_UP		(1 << 8)
+#define		AR8X16_PORT_STS_LINK_AUTO	(1 << 9)
+#define		AR8X16_PORT_STS_LINK_PAUSE	(1 << 10)
+
+#define	AR8X16_REG_PORT_CTRL(_p)	(AR8X16_REG_PORT_BASE((_p)) + 0x0004)
+#define		AR8X16_PORT_CTRL_STATE_MASK	0x00000007
+#define		AR8X16_PORT_CTRL_STATE_DISABLED	0
+#define		AR8X16_PORT_CTRL_STATE_BLOCK	1
+#define		AR8X16_PORT_CTRL_STATE_LISTEN	2
+#define		AR8X16_PORT_CTRL_STATE_LEARN	3
+#define		AR8X16_PORT_CTRL_STATE_FORWARD	4
+#define		AR8X16_PORT_CTRL_LEARN_LOCK	(1 << 7)
+#define		AR8X16_PORT_CTRL_EGRESS_VLAN_MODE_SHIFT 8
+#define		AR8X16_PORT_CTRL_EGRESS_VLAN_MODE_KEEP	0
+#define		AR8X16_PORT_CTRL_EGRESS_VLAN_MODE_STRIP 1
+#define		AR8X16_PORT_CTRL_EGRESS_VLAN_MODE_ADD 2
+#define		AR8X16_PORT_CTRL_EGRESS_VLAN_MODE_DOUBLE_TAG 3
+#define		AR8X16_PORT_CTRL_IGMP_SNOOP	(1 << 10)
+#define		AR8X16_PORT_CTRL_HEADER		(1 << 11)
+#define		AR8X16_PORT_CTRL_MAC_LOOP	(1 << 12)
+#define		AR8X16_PORT_CTRL_SINGLE_VLAN	(1 << 13)
+#define		AR8X16_PORT_CTRL_LEARN		(1 << 14)
+#define		AR8X16_PORT_CTRL_DOUBLE_TAG	(1 << 15)
+#define		AR8X16_PORT_CTRL_MIRROR_TX	(1 << 16)
+#define		AR8X16_PORT_CTRL_MIRROR_RX	(1 << 17)
+
+#define	AR8X16_REG_PORT_VLAN(_p)	(AR8X16_REG_PORT_BASE((_p)) + 0x0008)
+
+#define		AR8X16_PORT_VLAN_DEFAULT_ID_SHIFT	0
+#define		AR8X16_PORT_VLAN_DEST_PORTS_SHIFT	16
+#define		AR8X16_PORT_VLAN_MODE_MASK		0xc0000000
+#define		AR8X16_PORT_VLAN_MODE_SHIFT		30
+#define		AR8X16_PORT_VLAN_MODE_PORT_ONLY		0
+#define		AR8X16_PORT_VLAN_MODE_PORT_FALLBACK	1
+#define		AR8X16_PORT_VLAN_MODE_VLAN_ONLY		2
+#define		AR8X16_PORT_VLAN_MODE_SECURE		3
+
+#define	AR8X16_REG_PORT_RATE_LIM(_p)	(AR8X16_REG_PORT_BASE((_p)) + 0x000c)
+#define		AR8X16_PORT_RATE_LIM_128KB	0
+#define		AR8X16_PORT_RATE_LIM_256KB	1
+#define		AR8X16_PORT_RATE_LIM_512KB	2
+#define		AR8X16_PORT_RATE_LIM_1MB	3
+#define		AR8X16_PORT_RATE_LIM_2MB	4
+#define		AR8X16_PORT_RATE_LIM_4MB	5
+#define		AR8X16_PORT_RATE_LIM_8MB	6
+#define		AR8X16_PORT_RATE_LIM_16MB	7
+#define		AR8X16_PORT_RATE_LIM_32MB	8
+#define		AR8X16_PORT_RATE_LIM_64MB	9
+#define		AR8X16_PORT_RATE_LIM_IN_EN	(1 << 24)
+#define		AR8X16_PORT_RATE_LIM_OUT_EN	(1 << 23)
+#define		AR8X16_PORT_RATE_LIM_IN_MASK	0x000f0000
+#define		AR8X16_PORT_RATE_LIM_IN_SHIFT	16
+#define		AR8X16_PORT_RATE_LIM_OUT_MASK	0x0000000f
+#define		AR8X16_PORT_RATE_LIM_OUT_SHIFT	0
+
+#define	AR8X16_REG_PORT_PRIORITY(_p)	(AR8X16_REG_PORT_BASE((_p)) + 0x0010)
+
+#define	AR8X16_REG_STATS_BASE(_p)	(0x20000 + (_p) * 0x100)
+
+#define	AR8X16_STATS_RXBROAD		0x0000
+#define	AR8X16_STATS_RXPAUSE		0x0004
+#define	AR8X16_STATS_RXMULTI		0x0008
+#define	AR8X16_STATS_RXFCSERR		0x000c
+#define	AR8X16_STATS_RXALIGNERR		0x0010
+#define	AR8X16_STATS_RXRUNT		0x0014
+#define	AR8X16_STATS_RXFRAGMENT		0x0018
+#define	AR8X16_STATS_RX64BYTE		0x001c
+#define	AR8X16_STATS_RX128BYTE		0x0020
+#define	AR8X16_STATS_RX256BYTE		0x0024
+#define	AR8X16_STATS_RX512BYTE		0x0028
+#define	AR8X16_STATS_RX1024BYTE		0x002c
+#define	AR8X16_STATS_RX1518BYTE		0x0030
+#define	AR8X16_STATS_RXMAXBYTE		0x0034
+#define	AR8X16_STATS_RXTOOLONG		0x0038
+#define	AR8X16_STATS_RXGOODBYTE		0x003c
+#define	AR8X16_STATS_RXBADBYTE		0x0044
+#define	AR8X16_STATS_RXOVERFLOW		0x004c
+#define	AR8X16_STATS_FILTERED		0x0050
+#define	AR8X16_STATS_TXBROAD		0x0054
+#define	AR8X16_STATS_TXPAUSE		0x0058
+#define	AR8X16_STATS_TXMULTI		0x005c
+#define	AR8X16_STATS_TXUNDERRUN		0x0060
+#define	AR8X16_STATS_TX64BYTE		0x0064
+#define	AR8X16_STATS_TX128BYTE		0x0068
+#define	AR8X16_STATS_TX256BYTE		0x006c
+#define	AR8X16_STATS_TX512BYTE		0x0070
+#define	AR8X16_STATS_TX1024BYTE		0x0074
+#define	AR8X16_STATS_TX1518BYTE		0x0078
+#define	AR8X16_STATS_TXMAXBYTE		0x007c
+#define	AR8X16_STATS_TXOVERSIZE		0x0080
+#define	AR8X16_STATS_TXBYTE		0x0084
+#define	AR8X16_STATS_TXCOLLISION	0x008c
+#define	AR8X16_STATS_TXABORTCOL		0x0090
+#define	AR8X16_STATS_TXMULTICOL		0x0094
+#define	AR8X16_STATS_TXSINGLECOL	0x0098
+#define	AR8X16_STATS_TXEXCDEFER		0x009c
+#define	AR8X16_STATS_TXDEFER		0x00a0
+#define	AR8X16_STATS_TXLATECOL		0x00a4
+
+#define	AR8X16_PORT_CPU			0
+#define	AR8X16_NUM_PORTS		6
+#define	AR8X16_NUM_PHYS			5
+#define	AR8X16_MAGIC			0xc000050e
+
+#define	AR8X16_PHY_ID1			0x004d
+#define	AR8X16_PHY_ID2			0xd041
+
+#define	AR8X16_PORT_MASK(_port)		(1 << (_port))
+#define	AR8X16_PORT_MASK_ALL		((1<<AR8X16_NUM_PORTS)-1)
+#define	AR8X16_PORT_MASK_BUT(_port)	(AR8X16_PORT_MASK_ALL & ~(1 << (_port)))
+
+#define	AR8X16_MAX_VLANS		16
+
+#endif /* __AR8X16_SWITCHREG_H__ */
+
diff --git a/sys/dev/etherswitch/arswitch/arswitchvar.h b/sys/dev/etherswitch/arswitch/arswitchvar.h
new file mode 100644
index 000000000000..6f9d59ecf0bf
--- /dev/null
+++ b/sys/dev/etherswitch/arswitch/arswitchvar.h
@@ -0,0 +1,91 @@
+/*-
+ * 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	__ARSWITCHVAR_H__
+#define	__ARSWITCHVAR_H__
+
+typedef enum {
+	AR8X16_SWITCH_NONE,
+	AR8X16_SWITCH_AR8216,
+	AR8X16_SWITCH_AR8226,
+	AR8X16_SWITCH_AR8316,
+} ar8x16_switch_type;
+
+/*
+ * XXX TODO: start using this where required
+ */
+#define	AR8X16_IS_SWITCH(_sc, _type) \
+	    (!!((_sc)->sc_switchtype == AR8X16_SWITCH_ ## _type))
+
+struct arswitch_softc;
+
+struct arswitch_softc {
+	struct mtx	sc_mtx;		/* serialize access to softc */
+	device_t	sc_dev;
+	int		phy4cpu;	/* PHY4 is connected to the CPU */
+	int		numphys;	/* PHYs we manage */
+	int		is_rgmii;	/* PHY mode is RGMII (XXX which PHY?) */
+	int		is_gmii;	/* PHY mode is GMII (XXX which PHY?) */
+	int		page;
+	ar8x16_switch_type	sc_switchtype;
+	char		*ifname[AR8X16_NUM_PHYS];
+	device_t	miibus[AR8X16_NUM_PHYS];
+	struct ifnet	*ifp[AR8X16_NUM_PHYS];
+	struct callout	callout_tick;
+	etherswitch_info_t info;
+
+	struct {
+		int (* arswitch_hw_setup) (struct arswitch_softc *);
+		int (* arswitch_hw_global_setup) (struct arswitch_softc *);
+	} hal;
+};
+
+#define	ARSWITCH_LOCK(_sc)			\
+	    mtx_lock(&(_sc)->sc_mtx)
+#define	ARSWITCH_UNLOCK(_sc)			\
+	    mtx_unlock(&(_sc)->sc_mtx)
+#define	ARSWITCH_LOCK_ASSERT(_sc, _what)	\
+	    mtx_assert(&(_s)c->sc_mtx, (_what))
+#define	ARSWITCH_TRYLOCK(_sc)			\
+	    mtx_trylock(&(_sc)->sc_mtx)
+
+#if defined(DEBUG)
+#define DPRINTF(dev, args...) device_printf(dev, args)
+#define DEVERR(dev, err, fmt, args...) do { \
+		if (err != 0) device_printf(dev, fmt, err, args); \
+	} while (0)
+#define DEBUG_INCRVAR(var)	do { \
+		var++; \
+	} while (0)
+#else
+#define DPRINTF(dev, args...)
+#define DEVERR(dev, err, fmt, args...)
+#define DEBUG_INCRVAR(var)
+#endif
+
+#endif	/* __ARSWITCHVAR_H__ */
+
diff --git a/sys/dev/etherswitch/etherswitch.c b/sys/dev/etherswitch/etherswitch.c
new file mode 100644
index 000000000000..a2162899f9fc
--- /dev/null
+++ b/sys/dev/etherswitch/etherswitch.c
@@ -0,0 +1,257 @@
+/*-
+ * 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/conf.h>
+#include <sys/fcntl.h>
+#include <sys/lock.h>
+#include <sys/kernel.h>
+#include <sys/malloc.h>
+#include <sys/module.h>
+#include <sys/socket.h>
+#include <sys/sx.h>
+#include <sys/systm.h>
+#include <sys/uio.h>
+
+#include <net/if.h>
+
+#include <dev/etherswitch/etherswitch.h>
+
+#include "etherswitch_if.h"
+
+#define BUFSIZE 1024
+
+struct etherswitch_softc {
+	device_t sc_dev;
+	int sc_count;
+
+	struct cdev *sc_devnode;
+	struct sx sc_lock;
+};
+
+#define	SWITCH_LOCK(sc)			sx_xlock(&(sc)->sc_lock)
+#define	SWITCH_UNLOCK(sc)			sx_xunlock(&(sc)->sc_lock)
+
+static int etherswitch_probe(device_t);
+static int etherswitch_attach(device_t);
+static int etherswitch_detach(device_t);
+static void etherswitch_identify(driver_t *driver, device_t parent);
+
+devclass_t etherswitch_devclass;
+
+static device_method_t etherswitch_methods[] = {
+	/* device interface */
+	DEVMETHOD(device_identify,	etherswitch_identify),
+	DEVMETHOD(device_probe,		etherswitch_probe),
+	DEVMETHOD(device_attach,	etherswitch_attach),
+	DEVMETHOD(device_detach,	etherswitch_detach),
+
+	{ 0, 0 }
+};
+
+driver_t etherswitch_driver = {
+	"etherswitch",
+	etherswitch_methods,
+	sizeof(struct etherswitch_softc),
+};
+
+static	d_open_t	etherswitchopen;
+static	d_close_t	etherswitchclose;
+static	d_write_t	etherswitchwrite;
+static	d_read_t	etherswitchread;
+static	d_ioctl_t	etherswitchioctl;
+
+static struct cdevsw etherswitch_cdevsw = {
+	.d_version =	D_VERSION,
+	.d_flags =	D_TRACKCLOSE,
+	.d_open =	etherswitchopen,
+	.d_close =	etherswitchclose,
+	.d_read =	etherswitchread,
+	.d_write =	etherswitchwrite,
+	.d_ioctl =	etherswitchioctl,
+	.d_name =	"etherswitch",
+};
+
+static void
+etherswitch_identify(driver_t *driver, device_t parent)
+{
+	if (device_find_child(parent, "etherswitch", -1) == NULL)
+		BUS_ADD_CHILD(parent, 0, "etherswitch", -1);
+}
+
+static int
+etherswitch_probe(device_t dev)
+{
+	device_set_desc(dev, "Switch controller");
+
+	return (0);
+}
+	
+static int
+etherswitch_attach(device_t dev)
+{
+	struct etherswitch_softc *sc = (struct etherswitch_softc *)device_get_softc(dev);
+
+	sc->sc_dev = dev;
+	sx_init(&sc->sc_lock, "etherswitch");
+	sc->sc_devnode = make_dev(&etherswitch_cdevsw, device_get_unit(dev),
+			UID_ROOT, GID_WHEEL,
+			0600, "etherswitch%d", device_get_unit(dev));
+	if (sc->sc_devnode == NULL) {
+		device_printf(dev, "failed to create character device\n");
+		sx_destroy(&sc->sc_lock);
+		return (ENXIO);
+	}
+	sc->sc_devnode->si_drv1 = sc;
+
+	return (0);
+}
+
+static int
+etherswitch_detach(device_t dev)
+{
+	struct etherswitch_softc *sc = (struct etherswitch_softc *)device_get_softc(dev);
+
+	if (sc->sc_devnode)
+		destroy_dev(sc->sc_devnode);
+	sx_destroy(&sc->sc_lock);
+
+	return (0);
+}
+
+static int
+etherswitchopen(struct cdev *dev, int flags, int fmt, struct thread *td)
+{
+	struct etherswitch_softc *sc = dev->si_drv1;
+
+	SWITCH_LOCK(sc);
+	if (sc->sc_count > 0) {
+		SWITCH_UNLOCK(sc);
+		return (EBUSY);
+	}
+
+	sc->sc_count++;
+	SWITCH_UNLOCK(sc);
+
+	return (0);
+}
+
+static int
+etherswitchclose(struct cdev *dev, int flags, int fmt, struct thread *td)
+{
+	struct etherswitch_softc *sc = dev->si_drv1;
+
+	SWITCH_LOCK(sc);
+	if (sc->sc_count == 0) {
+		SWITCH_UNLOCK(sc);
+		return (EINVAL);
+	}
+
+	sc->sc_count--;
+
+	if (sc->sc_count < 0)
+		panic("%s: etherswitch_count < 0!", __func__);
+	SWITCH_UNLOCK(sc);
+
+	return (0);
+}
+
+static int
+etherswitchwrite(struct cdev *dev, struct uio * uio, int ioflag)
+{
+	return (EINVAL);
+}
+
+static int
+etherswitchread(struct cdev *dev, struct uio * uio, int ioflag)
+{
+	return (EINVAL);
+}
+
+static int
+etherswitchioctl(struct cdev *cdev, u_long cmd, caddr_t data, int flags, struct thread *td)
+{
+	struct etherswitch_softc *sc = cdev->si_drv1;
+	device_t dev = sc->sc_dev;
+	device_t etherswitch = device_get_parent(dev);
+	etherswitch_info_t *info;
+	etherswitch_reg_t *reg;
+	etherswitch_phyreg_t *phyreg;
+	int error = 0;
+
+	switch (cmd) {
+	case IOETHERSWITCHGETINFO:
+		info = ETHERSWITCH_GETINFO(etherswitch);
+		bcopy(info, data, sizeof(etherswitch_info_t));
+		break;
+		
+	case IOETHERSWITCHGETREG:
+		reg = (etherswitch_reg_t *)data;
+		reg->val = ETHERSWITCH_READREG(etherswitch, reg->reg);
+		break;
+	
+	case IOETHERSWITCHSETREG:
+		reg = (etherswitch_reg_t *)data;
+		error = ETHERSWITCH_WRITEREG(etherswitch, reg->reg, reg->val);
+		break;
+
+	case IOETHERSWITCHGETPORT:
+		error = ETHERSWITCH_GETPORT(etherswitch, (etherswitch_port_t *)data);
+		break;
+
+	case IOETHERSWITCHSETPORT:
+		error = ETHERSWITCH_SETPORT(etherswitch, (etherswitch_port_t *)data);
+		break;
+
+	case IOETHERSWITCHGETVLANGROUP:
+		error = ETHERSWITCH_GETVGROUP(etherswitch, (etherswitch_vlangroup_t *)data);
+		break;
+
+	case IOETHERSWITCHSETVLANGROUP:
+		error = ETHERSWITCH_SETVGROUP(etherswitch, (etherswitch_vlangroup_t *)data);
+		break;
+
+	case IOETHERSWITCHGETPHYREG:
+		phyreg = (etherswitch_phyreg_t *)data;
+		phyreg->val = ETHERSWITCH_READPHYREG(etherswitch, phyreg->phy, phyreg->reg);
+		break;
+	
+	case IOETHERSWITCHSETPHYREG:
+		phyreg = (etherswitch_phyreg_t *)data;
+		error = ETHERSWITCH_WRITEPHYREG(etherswitch, phyreg->phy, phyreg->reg, phyreg->val);
+		break;
+
+	default:
+		error = ENOTTY;
+	}
+
+	return (error);
+}
+
+MODULE_VERSION(etherswitch, 1);
diff --git a/sys/dev/etherswitch/etherswitch.h b/sys/dev/etherswitch/etherswitch.h
new file mode 100644
index 000000000000..8b02f697f2b4
--- /dev/null
+++ b/sys/dev/etherswitch/etherswitch.h
@@ -0,0 +1,70 @@
+/*
+ * $FreeBSD$
+ */
+
+#ifndef __SYS_DEV_ETHERSWITCH_ETHERSWITCH_H
+#define __SYS_DEV_ETHERSWITCH_ETHERSWITCH_H
+
+#include <sys/ioccom.h>
+
+#ifdef _KERNEL
+extern devclass_t       etherswitch_devclass;
+extern driver_t         etherswitch_driver;
+#endif /* _KERNEL */
+
+struct etherswitch_reg {
+	uint16_t	reg;
+	uint16_t	val;
+};
+typedef struct etherswitch_reg etherswitch_reg_t;
+
+struct etherswitch_phyreg {
+	uint16_t	phy;
+	uint16_t	reg;
+	uint16_t	val;
+};
+typedef struct etherswitch_phyreg etherswitch_phyreg_t;
+
+#define ETHERSWITCH_NAMEMAX	64
+
+struct etherswitch_info {
+	int		es_nports;
+	int		es_nvlangroups;
+	char		es_name[ETHERSWITCH_NAMEMAX];
+};
+typedef struct etherswitch_info etherswitch_info_t;
+
+struct etherswitch_port {
+	int		es_port;
+	int		es_vlangroup;
+	union {
+		struct ifreq		es_uifr;
+		struct ifmediareq	es_uifmr;
+	} es_ifu;
+#define es_ifr		es_ifu.es_uifr
+#define es_ifmr		es_ifu.es_uifmr
+};
+typedef struct etherswitch_port etherswitch_port_t;
+
+struct etherswitch_vlangroup {
+	int		es_vlangroup;
+	int		es_vid;
+	int		es_member_ports;
+	int		es_untagged_ports;
+	int		es_fid;
+};
+typedef struct etherswitch_vlangroup etherswitch_vlangroup_t;
+
+#define ETHERSWITCH_PORTMASK(_port)	(1 << (_port))
+
+#define IOETHERSWITCHGETINFO		_IOR('i', 1, etherswitch_info_t)
+#define IOETHERSWITCHGETREG		_IOWR('i', 2, etherswitch_reg_t)
+#define IOETHERSWITCHSETREG		_IOW('i', 3, etherswitch_reg_t)
+#define IOETHERSWITCHGETPORT		_IOWR('i', 4, etherswitch_port_t)
+#define IOETHERSWITCHSETPORT		_IOW('i', 5, etherswitch_port_t)
+#define IOETHERSWITCHGETVLANGROUP	_IOWR('i', 6, etherswitch_vlangroup_t)
+#define IOETHERSWITCHSETVLANGROUP	_IOW('i', 7, etherswitch_vlangroup_t)
+#define IOETHERSWITCHGETPHYREG		_IOWR('i', 8, etherswitch_phyreg_t)
+#define IOETHERSWITCHSETPHYREG		_IOW('i', 9, etherswitch_phyreg_t)
+
+#endif
diff --git a/sys/dev/etherswitch/etherswitch_if.m b/sys/dev/etherswitch/etherswitch_if.m
new file mode 100644
index 000000000000..7429c04c946a
--- /dev/null
+++ b/sys/dev/etherswitch/etherswitch_if.m
@@ -0,0 +1,86 @@
+# $FreeBSD$
+
+#include <sys/bus.h>
+
+# Needed for ifreq/ifmediareq
+#include <sys/socket.h>
+#include <net/if.h>
+
+#include <dev/etherswitch/etherswitch.h>
+
+INTERFACE etherswitch;
+
+#
+# Return device info
+#
+METHOD etherswitch_info_t* getinfo {
+	device_t	dev;
+}
+
+#
+# Read switch register
+#
+METHOD int readreg {
+	device_t	dev;
+	int		reg;
+};
+
+#
+# Write switch register
+#
+METHOD int writereg {
+	device_t	dev;
+	int		reg;
+	int		value;
+};
+
+#
+# Read PHY register
+#
+METHOD int readphyreg {
+	device_t	dev;
+	int		phy;
+	int		reg;
+};
+
+#
+# Write PHY register
+#
+METHOD int writephyreg {
+	device_t	dev;
+	int		phy;
+	int		reg;
+	int		value;
+};
+
+#
+# Get port configuration
+#
+METHOD int getport {
+	device_t	dev;
+	etherswitch_port_t *vg;
+}
+
+#
+# Set port configuration
+#
+METHOD int setport {
+	device_t	dev;
+	etherswitch_port_t *vg;
+}
+
+#
+# Get VLAN group configuration
+#
+METHOD int getvgroup {
+	device_t	dev;
+	etherswitch_vlangroup_t *vg;
+}
+
+#
+# Set VLAN group configuration
+#
+METHOD int setvgroup {
+	device_t	dev;
+	etherswitch_vlangroup_t *vg;
+}
diff --git a/sys/dev/etherswitch/rtl8366/rtl8366rb.c b/sys/dev/etherswitch/rtl8366/rtl8366rb.c
new file mode 100644
index 000000000000..9b648515f5d6
--- /dev/null
+++ b/sys/dev/etherswitch/rtl8366/rtl8366rb.c
@@ -0,0 +1,755 @@
+/*-
+ * 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/errno.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/socket.h>
+#include <sys/sockio.h>
+#include <sys/sysctl.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 <machine/bus.h>
+#include <dev/iicbus/iic.h>
+#include <dev/iicbus/iiconf.h>
+#include <dev/iicbus/iicbus.h>
+#include <dev/mii/mii.h>
+#include <dev/mii/miivar.h>
+
+#include <dev/etherswitch/etherswitch.h>
+#include <dev/etherswitch/rtl8366/rtl8366rbvar.h>
+
+#include "iicbus_if.h"
+#include "miibus_if.h"
+#include "etherswitch_if.h"
+
+
+struct rtl8366rb_softc {
+	struct mtx	sc_mtx;		/* serialize access to softc */
+	int		smi_acquired;	/* serialize access to SMI/I2C bus */
+	struct mtx	callout_mtx;	/* serialize callout */
+	device_t	dev;
+	char		*ifname[RTL8366RB_NUM_PHYS];
+	device_t	miibus[RTL8366RB_NUM_PHYS];
+	struct ifnet	*ifp[RTL8366RB_NUM_PHYS];
+	struct callout	callout_tick;
+};
+
+static etherswitch_info_t etherswitch_info = {
+	.es_nports =		6,
+	.es_nvlangroups =	16,
+	.es_name =			"Realtek RTL8366RB"
+};
+
+#define RTL_LOCK(_sc)	mtx_lock(&(_sc)->sc_mtx)
+#define RTL_UNLOCK(_sc)	mtx_unlock(&(_sc)->sc_mtx)
+#define RTL_LOCK_ASSERT(_sc, _what)	mtx_assert(&(_s)c->sc_mtx, (_what))
+#define RTL_TRYLOCK(_sc)	mtx_trylock(&(_sc)->sc_mtx)
+
+#define RTL_WAITOK	0
+#define	RTL_NOWAIT	1
+
+#define RTL_SMI_ACQUIRED	1
+#define RTL_SMI_ACQUIRED_ASSERT(_sc) \
+	KASSERT((_sc)->smi_acquired == RTL_SMI_ACQUIRED, ("smi must be acquired @%s", __FUNCTION__))
+
+#if defined(DEBUG)
+#define DPRINTF(dev, args...) device_printf(dev, args)
+#define DEVERR(dev, err, fmt, args...) do { \
+		if (err != 0) device_printf(dev, fmt, err, args); \
+	} while (0)
+#define DEBUG_INCRVAR(var)	do { \
+		var++; \
+	} while (0)
+
+static int callout_blocked = 0;
+static int iic_select_retries = 0;
+static int phy_access_retries = 0;
+static SYSCTL_NODE(_debug, OID_AUTO, rtl8366rb, CTLFLAG_RD, 0, "rtl8366rb");
+SYSCTL_INT(_debug_rtl8366rb, OID_AUTO, callout_blocked, CTLFLAG_RW, &callout_blocked, 0,
+	"number of times the callout couldn't acquire the bus");
+SYSCTL_INT(_debug_rtl8366rb, OID_AUTO, iic_select_retries, CTLFLAG_RW, &iic_select_retries, 0,
+	"number of times the I2C bus selection had to be retried");
+SYSCTL_INT(_debug_rtl8366rb, OID_AUTO, phy_access_retries, CTLFLAG_RW, &phy_access_retries, 0,
+	"number of times PHY register access had to be retried");
+#else
+#define DPRINTF(dev, args...)
+#define DEVERR(dev, err, fmt, args...)
+#define DEBUG_INCRVAR(var)
+#endif
+
+static int smi_probe(device_t dev);
+static int smi_read(device_t dev, uint16_t addr, uint16_t *data, int sleep);
+static int smi_write(device_t dev, uint16_t addr, uint16_t data, int sleep);
+static int smi_rmw(device_t dev, uint16_t addr, uint16_t mask, uint16_t data, int sleep);
+static void rtl8366rb_tick(void *arg);
+static int rtl8366rb_ifmedia_upd(struct ifnet *);
+static void rtl8366rb_ifmedia_sts(struct ifnet *, struct ifmediareq *);
+
+static void
+rtl8366rb_identify(driver_t *driver, device_t parent)
+{
+	device_t child;
+	struct iicbus_ivar *devi;
+
+	if (device_find_child(parent, "rtl8366rb", -1) == NULL) {
+		child = BUS_ADD_CHILD(parent, 0, "rtl8366rb", -1);
+		devi = IICBUS_IVAR(child);
+		devi->addr = RTL8366RB_IIC_ADDR;
+	}
+}
+
+static int
+rtl8366rb_probe(device_t dev)
+{
+	if (smi_probe(dev) != 0)
+		return (ENXIO);
+	device_set_desc(dev, "RTL8366RB Ethernet Switch Controller");
+	return (BUS_PROBE_DEFAULT);
+}
+
+static void
+rtl8366rb_init(device_t dev)
+{
+	/* Initialisation for TL-WR1043ND */
+	smi_rmw(dev, RTL8366RB_RCR,
+		RTL8366RB_RCR_HARD_RESET,
+		RTL8366RB_RCR_HARD_RESET, RTL_WAITOK);
+	DELAY(100000);
+	/* Enable 16 VLAN mode */
+	smi_rmw(dev, RTL8366RB_SGCR,
+		RTL8366RB_SGCR_EN_VLAN | RTL8366RB_SGCR_EN_VLAN_4KTB,
+		RTL8366RB_SGCR_EN_VLAN, RTL_WAITOK);
+	/* remove port 0 form VLAN 0 */
+	smi_rmw(dev, RTL8366RB_VMCR(RTL8366RB_VMCR_MU_REG, 0),
+		(1 << 0), 0, RTL_WAITOK);
+	/* add port 0 untagged and port 5 tagged to VLAN 1 */
+	smi_rmw(dev, RTL8366RB_VMCR(RTL8366RB_VMCR_MU_REG, 1),
+		((1 << 5 | 1 << 0) << RTL8366RB_VMCR_MU_MEMBER_SHIFT)
+			| ((1 << 5 | 1 << 0) << RTL8366RB_VMCR_MU_UNTAG_SHIFT),
+		((1 << 5 | 1 << 0) << RTL8366RB_VMCR_MU_MEMBER_SHIFT
+			| ((1 << 0) << RTL8366RB_VMCR_MU_UNTAG_SHIFT)),
+		RTL_WAITOK);
+	/* set PVLAN 1 for port 0 */
+	smi_rmw(dev, RTL8366RB_PVCR_REG(0),
+		RTL8366RB_PVCR_VAL(0, RTL8366RB_PVCR_PORT_MASK),
+		RTL8366RB_PVCR_VAL(0, 1), RTL_WAITOK);
+}
+
+static int
+rtl8366rb_attach(device_t dev)
+{
+	uint16_t rev = 0;
+	struct rtl8366rb_softc *sc;
+	char name[IFNAMSIZ];
+	int err = 0;
+	int i;
+
+	sc = device_get_softc(dev);
+	bzero(sc, sizeof(*sc));
+	sc->dev = dev;
+	mtx_init(&sc->sc_mtx, "rtl8366rb", NULL, MTX_DEF);
+	sc->smi_acquired = 0;
+	mtx_init(&sc->callout_mtx, "rtl8366rbcallout", NULL, MTX_DEF);
+
+	rtl8366rb_init(dev);
+	smi_read(dev, RTL8366RB_CVCR, &rev, RTL_WAITOK);
+	device_printf(dev, "rev. %d\n", rev & 0x000f);
+
+	/* attach miibus and phys */
+	/* PHYs need an interface, so we generate a dummy one */
+	for (i = 0; i < RTL8366RB_NUM_PHYS; i++) {
+		sc->ifp[i] = if_alloc(IFT_ETHER);
+		sc->ifp[i]->if_softc = sc;
+		sc->ifp[i]->if_flags |= IFF_UP | IFF_BROADCAST | IFF_DRV_RUNNING
+			| IFF_SIMPLEX;
+		snprintf(name, IFNAMSIZ, "%sport", device_get_nameunit(dev));
+		sc->ifname[i] = malloc(strlen(name)+1, M_DEVBUF, M_WAITOK);
+		bcopy(name, sc->ifname[i], strlen(name)+1);
+		if_initname(sc->ifp[i], sc->ifname[i], i);
+		err = mii_attach(dev, &sc->miibus[i], sc->ifp[i], rtl8366rb_ifmedia_upd, \
+			rtl8366rb_ifmedia_sts, BMSR_DEFCAPMASK, \
+			i, MII_OFFSET_ANY, 0);
+		if (err != 0) {
+			device_printf(dev, "attaching PHY %d failed\n", i);
+			return (err);
+		}
+	}
+
+	bus_generic_probe(dev);
+	bus_enumerate_hinted_children(dev);
+	err = bus_generic_attach(dev);
+	if (err != 0)
+		return (err);
+	
+	callout_init_mtx(&sc->callout_tick, &sc->callout_mtx, 0);
+	rtl8366rb_tick(sc);
+	
+	return (err);
+}
+
+static int
+rtl8366rb_detach(device_t dev)
+{
+	struct rtl8366rb_softc *sc = device_get_softc(dev);
+	int i;
+
+	for (i=0; i < RTL8366RB_NUM_PHYS; i++) {
+		if (sc->miibus[i])
+			device_delete_child(dev, sc->miibus[i]);
+		if (sc->ifp[i] != NULL)
+			if_free(sc->ifp[i]);
+		free(sc->ifname[i], M_DEVBUF);
+	}
+	bus_generic_detach(dev);
+	callout_drain(&sc->callout_tick);
+	mtx_destroy(&sc->callout_mtx);
+	mtx_destroy(&sc->sc_mtx);
+
+	return (0);
+}
+
+static void
+rtl8366rb_update_ifmedia(int portstatus, u_int *media_status, u_int *media_active)
+{
+	*media_active = IFM_ETHER;
+	*media_status = IFM_AVALID;
+	if ((portstatus & RTL8366RB_PLSR_LINK) != 0)
+		*media_status |= IFM_ACTIVE;
+	else {
+		*media_active |= IFM_NONE;
+		return;
+	}
+	switch (portstatus & RTL8366RB_PLSR_SPEED_MASK) {
+	case RTL8366RB_PLSR_SPEED_10:
+		*media_active |= IFM_10_T;
+		break;
+	case RTL8366RB_PLSR_SPEED_100:
+		*media_active |= IFM_100_TX;
+		break;
+	case RTL8366RB_PLSR_SPEED_1000:
+		*media_active |= IFM_1000_T;
+		break;
+	}
+	if ((portstatus & RTL8366RB_PLSR_FULLDUPLEX) == 0)
+		*media_active |= IFM_FDX;
+	else
+		*media_active |= IFM_HDX;
+	if ((portstatus & RTL8366RB_PLSR_TXPAUSE) != 0)
+		*media_active |= IFM_ETH_TXPAUSE;
+	if ((portstatus & RTL8366RB_PLSR_RXPAUSE) != 0)
+		*media_active |= IFM_ETH_RXPAUSE;
+}
+
+static void
+rtl833rb_miipollstat(struct rtl8366rb_softc *sc)
+{
+	int i;
+	struct mii_data *mii;
+	struct mii_softc *miisc;
+	uint16_t value;
+	int portstatus;
+
+	for (i = 0; i < RTL8366RB_NUM_PHYS; i++) {
+		mii = device_get_softc(sc->miibus[i]);
+		if ((i % 2) == 0) {
+			if (smi_read(sc->dev, RTL8366RB_PLSR_BASE + i/2, &value, RTL_NOWAIT) != 0) {
+				DEBUG_INCRVAR(callout_blocked);
+				return;
+			}
+			portstatus = value & 0xff;
+		} else {
+			portstatus = (value >> 8) & 0xff;
+		}
+		rtl8366rb_update_ifmedia(portstatus, &mii->mii_media_status, &mii->mii_media_active);
+		LIST_FOREACH(miisc, &mii->mii_phys, mii_list) {
+			if (IFM_INST(mii->mii_media.ifm_cur->ifm_media) != miisc->mii_inst)
+				continue;
+			mii_phy_update(miisc, MII_POLLSTAT);
+		}
+	}	
+}
+
+static void
+rtl8366rb_tick(void *arg)
+{
+	struct rtl8366rb_softc *sc = arg;
+
+	rtl833rb_miipollstat(sc);
+	callout_reset(&sc->callout_tick, hz, rtl8366rb_tick, sc);
+}
+
+static int
+smi_probe(device_t dev)
+{
+	device_t iicbus, iicha;
+	int err, i;
+	uint16_t chipid;
+	char bytes[2];
+	int xferd;
+
+	bytes[0] = RTL8366RB_CIR & 0xff;
+	bytes[1] = (RTL8366RB_CIR >> 8) & 0xff;
+	iicbus = device_get_parent(dev);
+	iicha = device_get_parent(iicbus);
+	iicbus_reset(iicbus, IIC_FASTEST, RTL8366RB_IIC_ADDR, NULL);
+	for (i=3; i--; ) {
+		IICBUS_STOP(iicha);
+		/*
+		 * we go directly to the host adapter because iicbus.c
+		 * only issues a stop on a bus that was successfully started.
+		 */
+	}
+	err = iicbus_request_bus(iicbus, dev, IIC_WAIT);
+	if (err != 0)
+		goto out;
+	err = iicbus_start(iicbus, RTL8366RB_IIC_ADDR | RTL_IICBUS_READ, RTL_IICBUS_TIMEOUT);
+	if (err != 0)
+		goto out;
+	err = iicbus_write(iicbus, bytes, 2, &xferd, RTL_IICBUS_TIMEOUT);
+	if (err != 0)
+		goto out;
+	err = iicbus_read(iicbus, bytes, 2, &xferd, IIC_LAST_READ, 0);
+	if (err != 0)
+		goto out;
+	chipid = ((bytes[1] & 0xff) << 8) | (bytes[0] & 0xff);
+	DPRINTF(dev, "chip id 0x%04x\n", chipid);
+	if (chipid != RTL8366RB_CIR_ID8366RB)
+		err = ENXIO;
+out:
+	iicbus_stop(iicbus);
+	iicbus_release_bus(iicbus, dev);
+	return (err == 0 ? 0 : ENXIO);
+}
+
+static int
+smi_acquire(struct rtl8366rb_softc *sc, int sleep)
+{
+	int r = 0;
+	if (sleep == RTL_WAITOK)
+		RTL_LOCK(sc);
+	else
+		if (RTL_TRYLOCK(sc) == 0)
+			return (EWOULDBLOCK);
+	if (sc->smi_acquired == RTL_SMI_ACQUIRED)
+		r = EBUSY;
+	else {
+		r = iicbus_request_bus(device_get_parent(sc->dev), sc->dev, \
+			sleep == RTL_WAITOK ? IIC_WAIT : IIC_DONTWAIT);
+		if (r == 0)
+			sc->smi_acquired = RTL_SMI_ACQUIRED;
+	}
+	RTL_UNLOCK(sc);
+	return (r);
+}
+
+static int
+smi_release(struct rtl8366rb_softc *sc, int sleep)
+{
+	if (sleep == RTL_WAITOK)
+		RTL_LOCK(sc);
+	else
+		if (RTL_TRYLOCK(sc) == 0)
+			return (EWOULDBLOCK);
+	RTL_SMI_ACQUIRED_ASSERT(sc);
+	iicbus_release_bus(device_get_parent(sc->dev), sc->dev);
+	sc->smi_acquired = 0;
+	RTL_UNLOCK(sc);
+	return (0);
+}
+
+static int
+smi_select(device_t dev, int op, int sleep)
+{
+	int err, i;
+	device_t iicbus = device_get_parent(dev);
+	struct iicbus_ivar *devi = IICBUS_IVAR(dev);
+	int slave = devi->addr;
+
+	RTL_SMI_ACQUIRED_ASSERT((struct rtl8366rb_softc *)device_get_softc(dev));
+	/*
+	 * The chip does not use clock stretching when it is busy,
+	 * instead ignoring the command. Retry a few times.
+	 */
+	for (i = RTL_IICBUS_RETRIES; i--; ) {
+		err = iicbus_start(iicbus, slave | op, RTL_IICBUS_TIMEOUT);
+		if (err != IIC_ENOACK)
+			break;
+		if (sleep == RTL_WAITOK) {
+			DEBUG_INCRVAR(iic_select_retries);
+			pause("smi_select", RTL_IICBUS_RETRY_SLEEP);
+		} else
+			break;
+	}
+	return (err);
+}
+
+static int
+smi_read_locked(struct rtl8366rb_softc *sc, uint16_t addr, uint16_t *data, int sleep)
+{
+	int err;
+	device_t iicbus = device_get_parent(sc->dev);
+	char bytes[2];
+	int xferd;
+
+	RTL_SMI_ACQUIRED_ASSERT(sc);
+	bytes[0] = addr & 0xff;
+	bytes[1] = (addr >> 8) & 0xff;
+	err = smi_select(sc->dev, RTL_IICBUS_READ, sleep);
+	if (err != 0)
+		goto out;
+	err = iicbus_write(iicbus, bytes, 2, &xferd, RTL_IICBUS_TIMEOUT);
+	if (err != 0)
+		goto out;
+	err = iicbus_read(iicbus, bytes, 2, &xferd, IIC_LAST_READ, 0);
+	if (err != 0)
+		goto out;
+	*data = ((bytes[1] & 0xff) << 8) | (bytes[0] & 0xff);
+
+out:
+	iicbus_stop(iicbus);
+	return (err);
+}
+
+static int
+smi_write_locked(struct rtl8366rb_softc *sc, uint16_t addr, uint16_t data, int sleep)
+{
+	int err;
+	device_t iicbus = device_get_parent(sc->dev);
+	char bytes[4];
+	int xferd;
+
+	RTL_SMI_ACQUIRED_ASSERT(sc);
+	bytes[0] = addr & 0xff;
+	bytes[1] = (addr >> 8) & 0xff;
+	bytes[2] = data & 0xff;
+	bytes[3] = (data >> 8) & 0xff;
+
+	err = smi_select(sc->dev, RTL_IICBUS_WRITE, sleep);
+	if (err == 0)
+		err = iicbus_write(iicbus, bytes, 4, &xferd, RTL_IICBUS_TIMEOUT);
+	iicbus_stop(iicbus);
+
+	return (err);
+}
+
+static int
+smi_read(device_t dev, uint16_t addr, uint16_t *data, int sleep)
+{
+	struct rtl8366rb_softc *sc = device_get_softc(dev);
+	int err;
+
+	err = smi_acquire(sc, sleep);
+	if (err != 0)
+		return (EBUSY);
+	err = smi_read_locked(sc, addr, data, sleep);
+	smi_release(sc, sleep);
+	DEVERR(dev, err, "smi_read()=%d: addr=%04x\n", addr);
+	return (err == 0 ? 0 : EIO);
+}
+
+static int
+smi_write(device_t dev, uint16_t addr, uint16_t data, int sleep)
+{
+	struct rtl8366rb_softc *sc = device_get_softc(dev);
+	int err;
+	
+	err = smi_acquire(sc, sleep);
+	if (err != 0)
+		return (EBUSY);
+	err = smi_write_locked(sc, addr, data, sleep);
+	smi_release(sc, sleep);
+	DEVERR(dev, err, "smi_write()=%d: addr=%04x\n", addr);
+	return (err == 0 ? 0 : EIO);
+}
+
+static int
+smi_rmw(device_t dev, uint16_t addr, uint16_t mask, uint16_t data, int sleep)
+{
+	struct rtl8366rb_softc *sc = device_get_softc(dev);
+	int err;
+	uint16_t oldv, newv;
+	
+	err = smi_acquire(sc, sleep);
+	if (err != 0)
+		return (EBUSY);
+	if (err == 0) {
+		err = smi_read_locked(sc, addr, &oldv, sleep);
+		if (err == 0) {
+			newv = oldv & ~mask;
+			newv |= data & mask;
+			if (newv != oldv)
+				err = smi_write_locked(sc, addr, newv, sleep);
+		}
+	}
+	smi_release(sc, sleep);
+	DEVERR(dev, err, "smi_rmw()=%d: addr=%04x\n", addr);
+	return (err == 0 ? 0 : EIO);
+}
+
+static etherswitch_info_t *
+rtl_getinfo(device_t dev)
+{
+	return (&etherswitch_info);
+}
+
+static int
+rtl_readreg(device_t dev, int reg)
+{
+	uint16_t data = 0;
+
+	smi_read(dev, reg, &data, RTL_WAITOK);
+	return (data);
+}
+
+static int
+rtl_writereg(device_t dev, int reg, int value)
+{
+	return (smi_write(dev, reg, value, RTL_WAITOK));
+}
+
+static int
+rtl_getport(device_t dev, etherswitch_port_t *p)
+{
+	struct rtl8366rb_softc *sc;
+	struct ifmedia *ifm;
+	struct mii_data *mii;
+	struct ifmediareq *ifmr = &p->es_ifmr;
+	uint16_t v;
+	int err;
+	
+	if (p->es_port < 0 || p->es_port >= RTL8366RB_NUM_PORTS)
+		return (ENXIO);
+	p->es_vlangroup = RTL8366RB_PVCR_GET(p->es_port,
+		rtl_readreg(dev, RTL8366RB_PVCR_REG(p->es_port)));
+	
+	if (p->es_port < RTL8366RB_NUM_PHYS) {
+		sc = device_get_softc(dev);
+		mii = device_get_softc(sc->miibus[p->es_port]);
+		ifm = &mii->mii_media;
+		err = ifmedia_ioctl(sc->ifp[p->es_port], &p->es_ifr, ifm, SIOCGIFMEDIA);
+		if (err)
+			return (err);
+	} else {
+		/* fill in fixed values for CPU port */
+		ifmr->ifm_count = 0;
+		smi_read(dev, RTL8366RB_PLSR_BASE + (RTL8366RB_NUM_PHYS)/2, &v, RTL_WAITOK);
+		v = v >> (8 * ((RTL8366RB_NUM_PHYS) % 2));
+		rtl8366rb_update_ifmedia(v, &ifmr->ifm_status, &ifmr->ifm_active);
+		ifmr->ifm_current = ifmr->ifm_active;
+		ifmr->ifm_mask = 0;
+		ifmr->ifm_status = IFM_ACTIVE | IFM_AVALID;
+	}
+	return (0);
+}
+
+static int
+rtl_setport(device_t dev, etherswitch_port_t *p)
+{
+	int err;
+	struct rtl8366rb_softc *sc;
+	struct ifmedia *ifm;
+	struct mii_data *mii;
+
+	if (p->es_port < 0 || p->es_port >= RTL8366RB_NUM_PHYS)
+		return (ENXIO);
+	err = smi_rmw(dev, RTL8366RB_PVCR_REG(p->es_port),
+		RTL8366RB_PVCR_VAL(p->es_port, RTL8366RB_PVCR_PORT_MASK),
+		RTL8366RB_PVCR_VAL(p->es_port, p->es_vlangroup), RTL_WAITOK);
+	if (err)
+		return (err);
+	sc = device_get_softc(dev);
+	mii = device_get_softc(sc->miibus[p->es_port]);
+	ifm = &mii->mii_media;
+	err = ifmedia_ioctl(sc->ifp[p->es_port], &p->es_ifr, ifm, SIOCSIFMEDIA);
+	return (err);
+}
+
+static int
+rtl_getvgroup(device_t dev, etherswitch_vlangroup_t *vg)
+{
+	uint16_t vmcr[3];
+	int i;
+	
+	for (i=0; i<3; i++)
+		vmcr[i] = rtl_readreg(dev, RTL8366RB_VMCR(i, vg->es_vlangroup));
+		
+	vg->es_vid = RTL8366RB_VMCR_VID(vmcr);
+	vg->es_member_ports = RTL8366RB_VMCR_MEMBER(vmcr);
+	vg->es_untagged_ports = RTL8366RB_VMCR_UNTAG(vmcr);
+	vg->es_fid = RTL8366RB_VMCR_FID(vmcr);
+	return (0);
+}
+
+static int
+rtl_setvgroup(device_t dev, etherswitch_vlangroup_t *vg)
+{
+	int g = vg->es_vlangroup;
+
+	rtl_writereg(dev, RTL8366RB_VMCR(RTL8366RB_VMCR_DOT1Q_REG, g),
+		(vg->es_vid << RTL8366RB_VMCR_DOT1Q_VID_SHIFT) & RTL8366RB_VMCR_DOT1Q_VID_MASK);
+	rtl_writereg(dev, RTL8366RB_VMCR(RTL8366RB_VMCR_MU_REG, g),
+		((vg->es_member_ports << RTL8366RB_VMCR_MU_MEMBER_SHIFT) & RTL8366RB_VMCR_MU_MEMBER_MASK) |
+		((vg->es_untagged_ports << RTL8366RB_VMCR_MU_UNTAG_SHIFT) & RTL8366RB_VMCR_MU_UNTAG_MASK));
+	rtl_writereg(dev, RTL8366RB_VMCR(RTL8366RB_VMCR_FID_REG, g),
+		vg->es_fid);
+	return (0);
+}
+
+static int
+rtl_readphy(device_t dev, int phy, int reg)
+{
+	struct rtl8366rb_softc *sc = device_get_softc(dev);
+	uint16_t data = 0;
+	int err, i, sleep;
+
+	if (phy < 0 || phy >= RTL8366RB_NUM_PHYS)
+		return (ENXIO);
+	if (reg < 0 || reg >= RTL8366RB_NUM_PHY_REG)
+		return (ENXIO);
+	sleep = RTL_WAITOK;
+	err = smi_acquire(sc, sleep);
+	if (err != 0)
+		return (EBUSY);
+	for (i = RTL_IICBUS_RETRIES; i--; ) {
+		err = smi_write_locked(sc, RTL8366RB_PACR, RTL8366RB_PACR_READ, sleep);
+		if (err == 0)
+			err = smi_write_locked(sc, RTL8366RB_PHYREG(phy, 0, reg), 0, sleep);
+		if (err == 0) {
+			err = smi_read_locked(sc, RTL8366RB_PADR, &data, sleep);
+			break;
+		}
+		DEBUG_INCRVAR(phy_access_retries);
+		DPRINTF(dev, "rtl_readphy(): chip not responsive, retrying %d more times\n", i);
+		pause("rtl_readphy", RTL_IICBUS_RETRY_SLEEP);
+	}
+	smi_release(sc, sleep);
+	DEVERR(dev, err, "rtl_readphy()=%d: phy=%d.%02x\n", phy, reg);
+	return (data);
+}
+
+static int
+rtl_writephy(device_t dev, int phy, int reg, int data)
+{
+	struct rtl8366rb_softc *sc = device_get_softc(dev);
+	int err, i, sleep;
+	
+	if (phy < 0 || phy >= RTL8366RB_NUM_PHYS)
+		return (ENXIO);
+	if (reg < 0 || reg >= RTL8366RB_NUM_PHY_REG)
+		return (ENXIO);
+	sleep = RTL_WAITOK;
+	err = smi_acquire(sc, sleep);
+	if (err != 0)
+		return (EBUSY);
+	for (i = RTL_IICBUS_RETRIES; i--; ) {
+		err = smi_write_locked(sc, RTL8366RB_PACR, RTL8366RB_PACR_WRITE, sleep);
+		if (err == 0)
+			err = smi_write_locked(sc, RTL8366RB_PHYREG(phy, 0, reg), data, sleep);
+		if (err == 0) {
+			break;
+		}
+		DEBUG_INCRVAR(phy_access_retries);
+		DPRINTF(dev, "rtl_writephy(): chip not responsive, retrying %d more tiems\n", i);
+		pause("rtl_writephy", RTL_IICBUS_RETRY_SLEEP);
+	}
+	smi_release(sc, sleep);
+	DEVERR(dev, err, "rtl_writephy()=%d: phy=%d.%02x\n", phy, reg);
+	return (err == 0 ? 0 : EIO);
+}
+
+static int
+rtl8366rb_ifmedia_upd(struct ifnet *ifp)
+{
+	struct rtl8366rb_softc *sc = ifp->if_softc;
+	struct mii_data *mii = device_get_softc(sc->miibus[ifp->if_dunit]);
+	
+	mii_mediachg(mii);
+	return (0);
+}
+
+static void
+rtl8366rb_ifmedia_sts(struct ifnet *ifp, struct ifmediareq *ifmr)
+{
+	struct rtl8366rb_softc *sc = ifp->if_softc;
+	struct mii_data *mii = device_get_softc(sc->miibus[ifp->if_dunit]);
+
+	mii_pollstat(mii);
+	ifmr->ifm_active = mii->mii_media_active;
+	ifmr->ifm_status = mii->mii_media_status;
+}
+
+
+static device_method_t rtl8366rb_methods[] = {
+	/* Device interface */
+	DEVMETHOD(device_identify,	rtl8366rb_identify),
+	DEVMETHOD(device_probe,		rtl8366rb_probe),
+	DEVMETHOD(device_attach,	rtl8366rb_attach),
+	DEVMETHOD(device_detach,	rtl8366rb_detach),
+	
+	/* bus interface */
+	DEVMETHOD(bus_add_child,	device_add_child_ordered),
+	
+	/* MII interface */
+	DEVMETHOD(miibus_readreg,	rtl_readphy),
+	DEVMETHOD(miibus_writereg,	rtl_writephy),
+
+	/* etherswitch interface */
+	DEVMETHOD(etherswitch_getinfo,	rtl_getinfo),
+	DEVMETHOD(etherswitch_readreg,	rtl_readreg),
+	DEVMETHOD(etherswitch_writereg,	rtl_writereg),
+	DEVMETHOD(etherswitch_readphyreg,	rtl_readphy),
+	DEVMETHOD(etherswitch_writephyreg,	rtl_writephy),
+	DEVMETHOD(etherswitch_getport,	rtl_getport),
+	DEVMETHOD(etherswitch_setport,	rtl_setport),
+	DEVMETHOD(etherswitch_getvgroup,	rtl_getvgroup),
+	DEVMETHOD(etherswitch_setvgroup,	rtl_setvgroup),
+
+	DEVMETHOD_END
+};
+
+DEFINE_CLASS_0(rtl8366rb, rtl8366rb_driver, rtl8366rb_methods,
+    sizeof(struct rtl8366rb_softc));
+static devclass_t rtl8366rb_devclass;
+
+DRIVER_MODULE(rtl8366rb, iicbus, rtl8366rb_driver, rtl8366rb_devclass, 0, 0);
+DRIVER_MODULE(miibus, rtl8366rb, miibus_driver, miibus_devclass, 0, 0);
+DRIVER_MODULE(etherswitch, rtl8366rb, etherswitch_driver, etherswitch_devclass, 0, 0);
+MODULE_VERSION(rtl8366rb, 1);
+MODULE_DEPEND(rtl8366rb, iicbus, 1, 1, 1); /* XXX which versions? */
+MODULE_DEPEND(rtl8366rb, miibus, 1, 1, 1); /* XXX which versions? */
+MODULE_DEPEND(rtl8366rb, etherswitch, 1, 1, 1); /* XXX which versions? */
diff --git a/sys/dev/etherswitch/rtl8366/rtl8366rbvar.h b/sys/dev/etherswitch/rtl8366/rtl8366rbvar.h
new file mode 100644
index 000000000000..4f3d1bae044b
--- /dev/null
+++ b/sys/dev/etherswitch/rtl8366/rtl8366rbvar.h
@@ -0,0 +1,176 @@
+/*-
+ * 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_RTL8366RBVAR_H_
+#define	_DEV_ETHERSWITCH_RTL8366RBVAR_H_
+
+#define RTL8366RB_IIC_ADDR	0xa8
+#define RTL_IICBUS_TIMEOUT	100	/* us */
+#define RTL_IICBUS_READ		1
+#define	RTL_IICBUS_WRITE	0
+/* number of times to try and select the chip on the I2C bus */
+#define RTL_IICBUS_RETRIES	3
+#define RTL_IICBUS_RETRY_SLEEP	(hz/1000)
+
+/* Register definitions */
+
+/* Switch Global Configuration */
+#define RTL8366RB_SGCR				0x0000
+#define RTL8366RB_SGCR_EN_BC_STORM_CTRL		0x0001
+#define RTL8366RB_SGCR_MAX_LENGTH_MASK		0x0030
+#define RTL8366RB_SGCR_MAX_LENGTH_1522		0x0000
+#define RTL8366RB_SGCR_MAX_LENGTH_1536		0x0010
+#define RTL8366RB_SGCR_MAX_LENGTH_1552		0x0020
+#define RTL8366RB_SGCR_MAX_LENGTH_9216		0x0030
+#define RTL8366RB_SGCR_EN_VLAN			0x2000
+#define RTL8366RB_SGCR_EN_VLAN_4KTB		0x4000
+#define RTL8366RB_SGCR_EN_QOS			0x8000
+
+/* Port Enable Control: DISABLE_PORT[5:0] */
+#define RTL8366RB_PECR				0x0001
+
+/* Switch Security Control 0: DIS_LEARN[5:0] */
+#define RTL8366RB_SSCR0				0x0002
+
+/* Switch Security Control 1: DIS_AGE[5:0] */
+#define RTL8366RB_SSCR1				0x0003
+
+/* Switch Security Control 2 */
+#define RTL8366RB_SSCR2				0x0004
+#define RTL8366RB_SSCR2_DROP_UNKNOWN_DA		0x0001
+
+/* Port Link Status: two ports per register */
+#define RTL8366RB_PLSR_BASE			0x0014
+#define RTL8366RB_PLSR_SPEED_MASK	0x03
+#define RTL8366RB_PLSR_SPEED_10		0x00
+#define RTL8366RB_PLSR_SPEED_100	0x01
+#define RTL8366RB_PLSR_SPEED_1000	0x02
+#define RTL8366RB_PLSR_FULLDUPLEX	0x08
+#define RTL8366RB_PLSR_LINK		0x10
+#define RTL8366RB_PLSR_TXPAUSE		0x20
+#define RTL8366RB_PLSR_RXPAUSE		0x40
+#define RTL8366RB_PLSR_NO_AUTO		0x80
+
+/* VLAN Member Configuration, 3 registers per VLAN */
+#define RTL8366RB_VMCR_BASE			0x0020
+#define RTL8366RB_VMCR_MULT		3
+#define RTL8366RB_VMCR_DOT1Q_REG	0
+#define RTL8366RB_VMCR_DOT1Q_VID_SHIFT	0
+#define RTL8366RB_VMCR_DOT1Q_VID_MASK	0x0fff
+#define RTL8366RB_VMCR_DOT1Q_PCP_SHIFT	12
+#define RTL8366RB_VMCR_DOT1Q_PCP_MASK	0x7000
+#define RTL8366RB_VMCR_MU_REG		1
+#define RTL8366RB_VMCR_MU_MEMBER_SHIFT	0
+#define RTL8366RB_VMCR_MU_MEMBER_MASK	0x00ff
+#define RTL8366RB_VMCR_MU_UNTAG_SHIFT	8
+#define RTL8366RB_VMCR_MU_UNTAG_MASK	0xff00
+#define RTL8366RB_VMCR_FID_REG		2
+#define RTL8366RB_VMCR_FID_FID_SHIFT	0
+#define RTL8366RB_VMCR_FID_FID_MASK	0x0007
+#define RTL8366RB_VMCR(_reg, _vlan) \
+	(RTL8366RB_VMCR_BASE + _reg + _vlan * RTL8366RB_VMCR_MULT)
+/* VLAN Identifier */
+#define RTL8366RB_VMCR_VID(_r) \
+	(_r[RTL8366RB_VMCR_DOT1Q_REG] & RTL8366RB_VMCR_DOT1Q_VID_MASK)
+/* Priority Code Point */
+#define RTL8366RB_VMCR_PCP(_r) \
+	((_r[RTL8366RB_VMCR_DOT1Q_REG] & RTL8366RB_VMCR_DOT1Q_PCP_MASK) \
+	>> RTL8366RB_VMCR_DOT1Q_PCP_SHIFT)
+/* Member ports */
+#define RTL8366RB_VMCR_MEMBER(_r) \
+	(_r[RTL8366RB_VMCR_MU_REG] & RTL8366RB_VMCR_MU_MEMBER_MASK)
+/* Untagged ports */
+#define RTL8366RB_VMCR_UNTAG(_r) \
+	((_r[RTL8366RB_VMCR_MU_REG] & RTL8366RB_VMCR_MU_UNTAG_MASK) \
+	>> RTL8366RB_VMCR_MU_UNTAG_SHIFT)
+/* Forwarding ID */
+#define RTL8366RB_VMCR_FID(_r) \
+	(_r[RTL8366RB_VMCR_FID_REG] & RTL8366RB_VMCR_FID_FID_MASK)
+
+/*
+ * Port VLAN Control, 4 ports per register
+ * Determines the VID for untagged ingress frames through
+ * index into VMC.
+ */
+#define RTL8366RB_PVCR_BASE			0x0063
+#define RTL8366RB_PVCR_PORT_SHIFT	4
+#define RTL8366RB_PVCR_PORT_PERREG	(16 / RTL8366RB_PVCR_PORT_SHIFT)
+#define RTL8366RB_PVCR_PORT_MASK	0x000f
+#define RTL8366RB_PVCR_REG(_port) \
+	(RTL8366RB_PVCR_BASE + _port / (RTL8366RB_PVCR_PORT_PERREG))
+#define RTL8366RB_PVCR_VAL(_port, _pvlan) \
+	((_pvlan & RTL8366RB_PVCR_PORT_MASK) << \
+	((_port % RTL8366RB_PVCR_PORT_PERREG) * RTL8366RB_PVCR_PORT_SHIFT))
+#define RTL8366RB_PVCR_GET(_port, _val) \
+	(((_val) >> ((_port % RTL8366RB_PVCR_PORT_PERREG) * RTL8366RB_PVCR_PORT_SHIFT)) & RTL8366RB_PVCR_PORT_MASK)
+
+/* Reset Control */
+#define RTL8366RB_RCR				0x0100
+#define RTL8366RB_RCR_HARD_RESET	0x0001
+#define RTL8366RB_RCR_SOFT_RESET	0x0002
+
+/* Chip Version Control: CHIP_VER[3:0] */
+#define RTL8366RB_CVCR				0x050A
+/* Chip Identifier */
+#define RTL8366RB_CIR				0x0509
+#define RTL8366RB_CIR_ID8366RB		0x5937
+
+/* VLAN Ingress Control 2: [5:0] */
+#define RTL8366RB_VIC2R				0x037f
+
+/* MIB registers */
+#define RTL8366RB_MCNT_BASE			0x1000
+#define RTL8366RB_MCTLR				0x13f0
+#define RTL8366RB_MCTLR_BUSY		0x0001
+#define RTL8366RB_MCTLR_RESET		0x0002
+#define RTL8366RB_MCTLR_RESET_PORT_MASK	0x00fc
+#define RTL8366RB_MCTLR_RESET_ALL	0x0800
+
+#define RTL8366RB_MCNT(_port, _r) \
+	(RTL8366RB_MCNT_BASE + 0x50 * (_port) + (_r))
+#define RTL8366RB_MCTLR_RESET_PORT(_p) \
+	(1 << ((_p) + 2))
+
+/* PHY Access Control */
+#define RTL8366RB_PACR				0x8000
+#define RTL8366RB_PACR_WRITE		0x0000
+#define RTL8366RB_PACR_READ			0x0001
+
+/* PHY Access Data */
+#define	RTL8366RB_PADR				0x8002
+
+#define RTL8366RB_PHYREG(phy, page, reg) \
+	(RTL8366RB_PACR | (1 << (((phy) & 0x1f) + 9)) | (((page) & 0xf) << 5) | ((reg) & 0x1f))
+
+/* general characteristics of the chip */
+#define RTL8366RB_NUM_PORTS			6
+#define RTL8366RB_NUM_PHYS			(RTL8366RB_NUM_PORTS-1)
+#define RTL8366RB_NUM_VLANS			16
+#define RTL8366RB_NUM_PHY_REG			32
+
+#endif