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