summaryrefslogtreecommitdiffstats
path: root/sys/dev
diff options
context:
space:
mode:
Diffstat (limited to 'sys/dev')
-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