summaryrefslogtreecommitdiffstats
path: root/sys/dev/etherswitch
diff options
context:
space:
mode:
authoradrian <adrian@FreeBSD.org>2012-05-11 20:53:20 +0000
committeradrian <adrian@FreeBSD.org>2012-05-11 20:53:20 +0000
commit98304d063a77cd8fd38bfb9edc6158f9243bd352 (patch)
tree7b7aa63c4a2ad92fa32a4ad029e2e68463df7b8a /sys/dev/etherswitch
parent8670397617a81b2ecea7375cbd0af3c77821459b (diff)
downloadFreeBSD-src-98304d063a77cd8fd38bfb9edc6158f9243bd352.zip
FreeBSD-src-98304d063a77cd8fd38bfb9edc6158f9243bd352.tar.gz
Commit the first pass of the etherswitch support.
This is designed to support the very basic ethernet switch chip behaviour, specifically: * accessing switch register space; * accessing per-PHY registers (for switches that actually expose PHYs); * basic vlan group support, which applies for the rtl8366 driver but not for the atheros switches. This also includes initial support for: * rtl8366rb support - which is a 10/100/1000 switch which supports vlan groups; * Initial Atheros AR8316 switch support - which is a 10/100/1000 switch which supports an alternate vlan configuration (so the vlan group methods are stubbed.) The general idea here is that the switch driver may speak to a variety of backend busses (mdio, i2c, spi, whatever) and expose: * If applicable, one or more MDIO busses which ethernet interfaces can then attach PHYs to via miiproxy/mdioproxy; * exposes miibusses, one for each port at the moment, so .. * .. a PHY can be exposed on each miibus, for each switch port, with all of the existing MII/ifnet framework. However: * The ifnet is manually created for now, and it isn't linked into the interface list, nor can you (currently) send/receive frames on this ifnet. At some point in the future there may be _some_ support for this, for switches with a multi-port, isolated mode. * I'm still in the process of sorting out correct(er) locking. TODO: * ray's switch code in zrouter (zrouter.org) includes a much more developed newbus API that covers the various switch methods, as well as a capability API so drivers, the switch layer and the userland utility can properly control the subset of supported features. The plan is to sort that out later, once the rest of ray's switch drivers are brought over and extended to export MII busses and PHYs. Submitted by: Stefan Bethke <stb@lassitu.de> Reviewed by: ray
Diffstat (limited to 'sys/dev/etherswitch')
-rw-r--r--sys/dev/etherswitch/arswitch/arswitch.c557
-rw-r--r--sys/dev/etherswitch/arswitch/arswitch_8216.c92
-rw-r--r--sys/dev/etherswitch/arswitch/arswitch_8216.h34
-rw-r--r--sys/dev/etherswitch/arswitch/arswitch_8226.c92
-rw-r--r--sys/dev/etherswitch/arswitch/arswitch_8226.h34
-rw-r--r--sys/dev/etherswitch/arswitch/arswitch_8316.c144
-rw-r--r--sys/dev/etherswitch/arswitch/arswitch_8316.h34
-rw-r--r--sys/dev/etherswitch/arswitch/arswitch_phy.c136
-rw-r--r--sys/dev/etherswitch/arswitch/arswitch_phy.h34
-rw-r--r--sys/dev/etherswitch/arswitch/arswitch_reg.c180
-rw-r--r--sys/dev/etherswitch/arswitch/arswitch_reg.h44
-rw-r--r--sys/dev/etherswitch/arswitch/arswitchreg.h274
-rw-r--r--sys/dev/etherswitch/arswitch/arswitchvar.h91
-rw-r--r--sys/dev/etherswitch/etherswitch.c257
-rw-r--r--sys/dev/etherswitch/etherswitch.h70
-rw-r--r--sys/dev/etherswitch/etherswitch_if.m86
-rw-r--r--sys/dev/etherswitch/rtl8366/rtl8366rb.c755
-rw-r--r--sys/dev/etherswitch/rtl8366/rtl8366rbvar.h176
18 files changed, 3090 insertions, 0 deletions
diff --git a/sys/dev/etherswitch/arswitch/arswitch.c b/sys/dev/etherswitch/arswitch/arswitch.c
new file mode 100644
index 0000000..4130b36
--- /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 0000000..eb49aba
--- /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 0000000..1795a20
--- /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 0000000..336120a
--- /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 0000000..f430e12
--- /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 0000000..ebf276b
--- /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 0000000..ceb8ae7
--- /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 0000000..283cdb6
--- /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 0000000..a3b7627
--- /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 0000000..0c0e915
--- /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 0000000..fdac59f
--- /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 0000000..3717573
--- /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 0000000..6f9d59e
--- /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 0000000..a216289
--- /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 0000000..8b02f69
--- /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 0000000..7429c04
--- /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 0000000..9b64851
--- /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 0000000..4f3d1ba
--- /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
OpenPOWER on IntegriCloud