Page Menu
Home
FreeBSD
Search
Configure Global Search
Log In
Files
F132016299
D3902.id9412.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
30 KB
Referenced Files
None
Subscribers
None
D3902.id9412.diff
View Options
Index: sys/dev/etherswitch/e6000sw/e6000sw.c
===================================================================
--- /dev/null
+++ sys/dev/etherswitch/e6000sw/e6000sw.c
@@ -0,0 +1,974 @@
+/*-
+ * Copyright (c) 2015 Semihalf.
+ * Copyright (c) 2015 Stormshield.
+ * 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.
+ */
+
+#include <sys/cdefs.h>
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/sockio.h>
+#include <sys/kernel.h>
+#include <sys/kthread.h>
+#include <sys/socket.h>
+#include <sys/module.h>
+#include <sys/errno.h>
+#include <sys/bus.h>
+#include <sys/conf.h>
+#include <sys/uio.h>
+#include <sys/fcntl.h>
+
+#include <net/if.h>
+#include <net/if_media.h>
+#include <net/if_types.h>
+
+#include <machine/bus.h>
+#include <machine/resource.h>
+
+#include <arm/mv/mvwin.h>
+#include <arm/mv/mvreg.h>
+#include <arm/mv/mvvar.h>
+
+#include <dev/etherswitch/etherswitch.h>
+#include <dev/etherswitch/mdio.h>
+#include <dev/mii/mii.h>
+#include <dev/mii/miivar.h>
+#include <dev/mge/if_mgevar.h>
+
+#include "e6000swreg.h"
+#include "etherswitch_if.h"
+#include "miibus_if.h"
+#include "mdio_if.h"
+
+MALLOC_DECLARE(M_E6000SW);
+MALLOC_DEFINE(M_E6000SW, "e6000sw", "e6000sw switch");
+
+#define E6000SW_LOCK(_sc) \
+ sx_xlock(&(_sc)->sx)
+#define E6000SW_UNLOCK(_sc) \
+ sx_unlock(&(_sc)->sx)
+#define E6000SW_LOCK_ASSERT(_sc, _what) \
+ sx_assert(&(_sc)->sx, (_what))
+#define E6000SW_TRYLOCK(_sc) \
+ sx_tryxlock(&(_sc)->sx)
+
+typedef struct e6000sw_softc {
+ device_t dev;
+
+ struct sx sx;
+ struct ifnet *ifp[E6000SW_NUM_PHYS];
+ char *ifname[E6000SW_NUM_PHYS];
+ device_t miibus[E6000SW_NUM_PHYS];
+ struct mii_data *mii[E6000SW_NUM_PHYS];
+ struct callout tick_callout;
+
+ uint32_t cpuports_mask;
+
+ int vid[E6000SW_NUM_VGROUPS];
+ int members[E6000SW_NUM_VGROUPS];
+ int vgroup[E6000SW_NUM_PORTS];
+} e6000sw_softc_t;
+
+static etherswitch_info_t etherswitch_info = {
+ .es_nports = E6000SW_NUM_PORTS,
+ .es_nvlangroups = E6000SW_NUM_VGROUPS,
+ .es_name = "Marvell 6000 series switch"
+};
+
+static void e6000sw_identify(driver_t *driver, device_t parent);
+static int e6000sw_probe(device_t dev);
+static int e6000sw_attach(device_t dev);
+static int e6000sw_detach(device_t dev);
+static int e6000sw_readphy(device_t dev, int phy, int reg);
+static int e6000sw_writephy(device_t dev, int phy, int reg, int data);
+static etherswitch_info_t* e6000sw_getinfo(device_t dev);
+static void e6000sw_lock(device_t dev);
+static void e6000sw_unlock(device_t dev);
+static int e6000sw_getport(device_t dev, etherswitch_port_t *p);
+static int e6000sw_setport(device_t dev, etherswitch_port_t *p);
+static int e6000sw_readreg_wrapper(device_t dev, int addr_reg);
+static int e6000sw_writereg_wrapper(device_t dev, int addr_reg, int val);
+static int e6000sw_readphy_wrapper(device_t dev, int phy, int reg);
+static int e6000sw_writephy_wrapper(device_t dev, int phy, int reg, int data);
+static int e6000sw_getvgroup_wrapper(device_t dev, etherswitch_vlangroup_t *vg);
+static int e6000sw_setvgroup_wrapper(device_t dev, etherswitch_vlangroup_t *vg);
+static int e6000sw_setvgroup(device_t dev, etherswitch_vlangroup_t *vg);
+static int e6000sw_getvgroup(device_t dev, etherswitch_vlangroup_t *vg);
+static void e6000sw_setup(device_t dev, e6000sw_softc_t *sc);
+static void e6000sw_port_vlan_conf(e6000sw_softc_t *sc);
+static void e6000sw_tick(void *arg);
+static void e6000sw_set_atustat(device_t dev, e6000sw_softc_t *sc, int bin,
+ int flag);
+static int e6000sw_atu_flush(device_t dev, e6000sw_softc_t *sc, int flag);
+static __inline void e6000sw_writereg(e6000sw_softc_t *sc, int addr, int reg,
+ int val);
+static __inline uint32_t e6000sw_readreg(e6000sw_softc_t *sc, int addr,
+ int reg);
+static int e6000sw_ifmedia_upd(struct ifnet *ifp);
+static void e6000sw_ifmedia_sts(struct ifnet *ifp, struct ifmediareq *ifmr);
+static int e6000sw_atu_mac_table(device_t dev, e6000sw_softc_t *sc, struct
+ atu_opt *atu, int flag);
+static int e6000sw_get_pvid(e6000sw_softc_t *sc, int port, int *pvid);
+static int e6000sw_set_pvid(e6000sw_softc_t *sc, int port, int pvid);
+static __inline int e6000sw_cpuport(e6000sw_softc_t *sc, int port);
+static __inline struct mii_data *e6000sw_miiforphy(e6000sw_softc_t *sc,
+ unsigned int phy);
+
+static struct proc *e6000sw_kproc;
+
+static device_method_t e6000sw_methods[] = {
+ /* device interface */
+ DEVMETHOD(device_identify, e6000sw_identify),
+ DEVMETHOD(device_probe, e6000sw_probe),
+ DEVMETHOD(device_attach, e6000sw_attach),
+ DEVMETHOD(device_detach, e6000sw_detach),
+
+ /* bus interface */
+ DEVMETHOD(bus_add_child, device_add_child_ordered),
+
+ /* mii interface */
+ DEVMETHOD(miibus_readreg, e6000sw_readphy),
+ DEVMETHOD(miibus_writereg, e6000sw_writephy),
+
+ /* etherswitch interface */
+ DEVMETHOD(etherswitch_getinfo, e6000sw_getinfo),
+ DEVMETHOD(etherswitch_lock, e6000sw_lock),
+ DEVMETHOD(etherswitch_unlock, e6000sw_unlock),
+ DEVMETHOD(etherswitch_getport, e6000sw_getport),
+ DEVMETHOD(etherswitch_setport, e6000sw_setport),
+ DEVMETHOD(etherswitch_readreg, e6000sw_readreg_wrapper),
+ DEVMETHOD(etherswitch_writereg, e6000sw_writereg_wrapper),
+ DEVMETHOD(etherswitch_readphyreg, e6000sw_readphy_wrapper),
+ DEVMETHOD(etherswitch_writephyreg, e6000sw_writephy_wrapper),
+ DEVMETHOD(etherswitch_setvgroup, e6000sw_setvgroup_wrapper),
+ DEVMETHOD(etherswitch_getvgroup, e6000sw_getvgroup_wrapper),
+
+ DEVMETHOD_END
+};
+
+static devclass_t e6000sw_devclass;
+
+DEFINE_CLASS_0(e6000sw, e6000sw_driver, e6000sw_methods,
+ sizeof(e6000sw_softc_t));
+
+DRIVER_MODULE(e6000sw, mdio, e6000sw_driver, e6000sw_devclass, 0, 0);
+DRIVER_MODULE(etherswitch, e6000sw, etherswitch_driver, etherswitch_devclass, 0,
+ 0);
+DRIVER_MODULE(miibus, e6000sw, miibus_driver, miibus_devclass, 0, 0);
+MODULE_DEPEND(e6000sw, mdio, 1, 1, 1);
+
+static void
+e6000sw_identify(driver_t *driver, device_t parent)
+{
+
+ if (device_find_child(parent, "e6000sw", -1) == NULL)
+ BUS_ADD_CHILD(parent, 0, "e6000sw", -1);
+}
+
+static int
+e6000sw_probe(device_t dev)
+{
+ e6000sw_softc_t *sc;
+ const char *description;
+ unsigned int id;
+
+ sc = device_get_softc(dev);
+ bzero(sc, sizeof(e6000sw_softc_t));
+ sc->dev = dev;
+ /* Lock is necessary due to assertions. */
+ sx_init(&sc->sx, "e6000sw");
+ E6000SW_LOCK(sc);
+
+ id = e6000sw_readreg(sc, REG_PORT(0), SWITCH_ID);
+
+ switch (id & 0xfff0) {
+ case 0x3520:
+ description = "Marvell 88E6352";
+ break;
+ case 0x1720:
+ description = "Marvell 88E6172";
+ break;
+ case 0x1760:
+ description = "Marvell 88E6176";
+ break;
+ default:
+ E6000SW_UNLOCK(sc);
+ sx_destroy(&sc->sx);
+ device_printf(dev, "Unrecognized device.\n");
+ return (ENXIO);
+ }
+
+ device_set_desc(dev, description);
+
+ E6000SW_UNLOCK(sc);
+
+ return (BUS_PROBE_DEFAULT);
+}
+
+static int
+e6000sw_attach(device_t dev)
+{
+ e6000sw_softc_t *sc;
+ int phy, err, port;
+ char name[IFNAMSIZ];
+
+ err = 0;
+ sc = device_get_softc(dev);
+ E6000SW_LOCK(sc);
+ sc->cpuports_mask = E6000SW_CPUPORTS_MASK;
+ for (port = 0; port < E6000SW_NUM_PORTS; port++)
+ sc->vgroup[port] = E6000SW_PORT_NO_VGROUP;
+ e6000sw_setup(dev, sc);
+
+ snprintf(name, IFNAMSIZ, "%sport", device_get_nameunit(sc->dev));
+ for (phy = 0; phy < E6000SW_NUM_PHYS; phy++) {
+ sc->ifp[phy] = if_alloc(IFT_ETHER);
+ if (sc->ifp[phy] == NULL)
+ goto out_fail;
+ 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_E6000SW, M_WAITOK);
+ if (sc->ifname[phy] == NULL)
+ goto out_fail;
+ bcopy(name, sc->ifname[phy], strlen(name) + 1);
+ if_initname(sc->ifp[phy], sc->ifname[phy], phy);
+ err = mii_attach(sc->dev, &sc->miibus[phy], sc->ifp[phy],
+ e6000sw_ifmedia_upd, e6000sw_ifmedia_sts, BMSR_DEFCAPMASK,
+ phy, MII_OFFSET_ANY, 0);
+ if (err != 0) {
+ device_printf(sc->dev,
+ "attaching PHY %d failed\n",
+ phy);
+ goto out_fail;
+ }
+ sc->mii[phy] = device_get_softc(sc->miibus[phy]);
+ }
+ E6000SW_UNLOCK(sc);
+
+ bus_generic_probe(dev);
+ bus_enumerate_hinted_children(dev);
+ bus_generic_attach(dev);
+
+ kproc_create(e6000sw_tick, sc, &e6000sw_kproc, 0, 0,
+ "e6000sw tick kproc");
+
+ return (0);
+
+out_fail:
+ e6000sw_detach(dev);
+
+ return (ENXIO);
+}
+
+static __inline void
+e6000sw_poll_done(e6000sw_softc_t *sc)
+{
+
+ while (e6000sw_readreg(sc, REG_GLOBAL2, PHY_CMD) &
+ (1 << PHY_CMD_SMI_BUSY))
+ continue;
+}
+
+
+/*
+ * PHY registers are paged. Put page index in reg 22 (accessible from every
+ * page), then access specific register.
+ */
+static int
+e6000sw_readphy(device_t dev, int phy, int reg)
+{
+ e6000sw_softc_t *sc;
+ uint32_t val;
+
+ sc = device_get_softc(dev);
+ val = 0;
+
+ if (phy >= E6000SW_NUM_PHYS || reg >= E6000SW_NUM_PHY_REGS) {
+ device_printf(dev, "Wrong register address.\n");
+ return (EINVAL);
+ }
+
+ E6000SW_LOCK_ASSERT(sc, SA_XLOCKED);
+
+ e6000sw_poll_done(sc);
+ val |= 1 << PHY_CMD_SMI_BUSY;
+ val |= PHY_CMD_MODE_MDIO << PHY_CMD_MODE;
+ val |= PHY_CMD_OPCODE_READ << PHY_CMD_OPCODE;
+ val |= (reg << PHY_CMD_REG_ADDR) & PHY_CMD_REG_ADDR_MASK;
+ val |= (phy << PHY_CMD_DEV_ADDR) & PHY_CMD_DEV_ADDR_MASK;
+ e6000sw_writereg(sc, REG_GLOBAL2, SMI_PHY_CMD_REG, val);
+ e6000sw_poll_done(sc);
+ val = e6000sw_readreg(sc, REG_GLOBAL2, SMI_PHY_DATA_REG)
+ & PHY_DATA_MASK;
+
+ return (val);
+}
+
+static int
+e6000sw_writephy(device_t dev, int phy, int reg, int data)
+{
+ e6000sw_softc_t *sc;
+ uint32_t val;
+
+ sc = device_get_softc(dev);
+ val = 0;
+
+ if (phy >= E6000SW_NUM_PHYS || reg >= E6000SW_NUM_PHY_REGS) {
+ device_printf(dev, "Wrong register address.\n");
+ return (EINVAL);
+ }
+
+ E6000SW_LOCK_ASSERT(sc, SA_XLOCKED);
+
+ e6000sw_poll_done(sc);
+ val |= PHY_CMD_MODE_MDIO << PHY_CMD_MODE;
+ val |= 1 << PHY_CMD_SMI_BUSY;
+ val |= PHY_CMD_OPCODE_WRITE << PHY_CMD_OPCODE;
+ val |= (reg << PHY_CMD_REG_ADDR) & PHY_CMD_REG_ADDR_MASK;
+ val |= (phy << PHY_CMD_DEV_ADDR) & PHY_CMD_DEV_ADDR_MASK;
+ e6000sw_writereg(sc, REG_GLOBAL2, SMI_PHY_DATA_REG,
+ data & PHY_DATA_MASK);
+ e6000sw_writereg(sc, REG_GLOBAL2, SMI_PHY_CMD_REG, val);
+ e6000sw_poll_done(sc);
+
+ return (0);
+}
+
+static int
+e6000sw_detach(device_t dev)
+{
+ int phy;
+ e6000sw_softc_t *sc;
+
+ sc = device_get_softc(dev);
+ bus_generic_detach(dev);
+ sx_destroy(&sc->sx);
+ for (phy = 0; phy < E6000SW_NUM_PHYS; phy++) {
+ if (sc->miibus[phy] != NULL)
+ device_delete_child(dev, sc->miibus[phy]);
+ if (sc->ifp[phy] != NULL)
+ if_free(sc->ifp[phy]);
+ if (sc->ifname[phy] != NULL)
+ free(sc->ifname[phy], M_E6000SW);
+ }
+
+ return (0);
+}
+
+static etherswitch_info_t*
+e6000sw_getinfo(device_t dev)
+{
+
+ return (ðerswitch_info);
+}
+
+static void
+e6000sw_lock(device_t dev)
+{
+ struct e6000sw_softc *sc;
+
+ sc = device_get_softc(dev);
+
+ E6000SW_LOCK_ASSERT(sc, SA_UNLOCKED);
+ E6000SW_LOCK(sc);
+}
+
+static void
+e6000sw_unlock(device_t dev)
+{
+ struct e6000sw_softc *sc;
+
+ sc = device_get_softc(dev);
+
+ E6000SW_LOCK_ASSERT(sc, SA_XLOCKED);
+ E6000SW_UNLOCK(sc);
+}
+
+static int
+e6000sw_getport(device_t dev, etherswitch_port_t *p)
+{
+ struct mii_data *mii;
+ int err;
+ struct ifmediareq *ifmr;
+
+ err = 0;
+ e6000sw_softc_t *sc = device_get_softc(dev);
+ E6000SW_LOCK_ASSERT(sc, SA_UNLOCKED);
+
+ E6000SW_LOCK(sc);
+
+ if (p->es_port >= E6000SW_NUM_PORTS ||
+ p->es_port < 0) {
+ err = EINVAL;
+ goto out;
+ }
+
+ e6000sw_get_pvid(sc, p->es_port, &p->es_pvid);
+
+ if (e6000sw_cpuport(sc, p->es_port)) {
+ p->es_flags |= ETHERSWITCH_PORT_CPU;
+ ifmr = &p->es_ifmr;
+ ifmr->ifm_status = IFM_ACTIVE | IFM_AVALID;
+ ifmr->ifm_count = 0;
+ ifmr->ifm_current = ifmr->ifm_active =
+ IFM_ETHER | IFM_1000_T | IFM_FDX;
+ ifmr->ifm_mask = 0;
+ } else {
+ mii = e6000sw_miiforphy(sc, p->es_port);
+ err = ifmedia_ioctl(mii->mii_ifp, &p->es_ifr,
+ &mii->mii_media, SIOCGIFMEDIA);
+ }
+
+out:
+ E6000SW_UNLOCK(sc);
+ return (err);
+}
+
+static int
+e6000sw_setport(device_t dev, etherswitch_port_t *p)
+{
+ e6000sw_softc_t *sc;
+ int err;
+ struct mii_data *mii;
+
+ err = 0;
+ sc = device_get_softc(dev);
+ E6000SW_LOCK_ASSERT(sc, SA_UNLOCKED);
+
+ E6000SW_LOCK(sc);
+
+ if (p->es_port >= E6000SW_NUM_PORTS ||
+ p->es_port < 0) {
+ err = EINVAL;
+ goto out;
+ }
+
+ if (p->es_pvid != 0)
+ e6000sw_set_pvid(sc, p->es_port, p->es_pvid);
+ if (!e6000sw_cpuport(sc, p->es_port)) {
+ mii = e6000sw_miiforphy(sc, p->es_port);
+ err = ifmedia_ioctl(mii->mii_ifp, &p->es_ifr, &mii->mii_media,
+ SIOCSIFMEDIA);
+ }
+
+out:
+ E6000SW_UNLOCK(sc);
+ return (err);
+}
+
+/*
+ * Registers in this switch are divided into sections, specified in
+ * documentation. So as to access any of them, section index and reg index
+ * is necessary. etherswitchcfg uses only one variable, so indexes were
+ * compressed into addr_reg: 32 * section_index + reg_index.
+ */
+static int
+e6000sw_readreg_wrapper(device_t dev, int addr_reg)
+{
+
+ if ((addr_reg > (REG_GLOBAL2 * 32 + REG_NUM_MAX)) ||
+ (addr_reg < (REG_PORT(0) * 32))) {
+ device_printf(dev, "Wrong register address.\n");
+ return (EINVAL);
+ }
+
+ return (e6000sw_readreg(device_get_softc(dev), addr_reg / 32,
+ addr_reg % 32));
+}
+
+static int
+e6000sw_writereg_wrapper(device_t dev, int addr_reg, int val)
+{
+
+ if ((addr_reg > (REG_GLOBAL2 * 32 + REG_NUM_MAX)) ||
+ (addr_reg < (REG_PORT(0) * 32))) {
+ device_printf(dev, "Wrong register address.\n");
+ return (EINVAL);
+ }
+ e6000sw_writereg(device_get_softc(dev), addr_reg / 5,
+ addr_reg % 32, val);
+
+ return (0);
+}
+
+/*
+ * These wrappers are necessary because PHY accesses from etherswitchcfg
+ * need to be synchronized with locks, while miibus PHY accesses do not.
+ */
+static int
+e6000sw_readphy_wrapper(device_t dev, int phy, int reg)
+{
+ e6000sw_softc_t *sc;
+ int ret;
+
+ sc = device_get_softc(dev);
+ E6000SW_LOCK_ASSERT(sc, SA_UNLOCKED);
+
+ E6000SW_LOCK(sc);
+ ret = e6000sw_readphy(dev, phy, reg);
+ E6000SW_UNLOCK(sc);
+
+ return (ret);
+}
+
+static int
+e6000sw_writephy_wrapper(device_t dev, int phy, int reg, int data)
+{
+ e6000sw_softc_t *sc;
+ int ret;
+
+ sc = device_get_softc(dev);
+ E6000SW_LOCK_ASSERT(sc, SA_UNLOCKED);
+
+ E6000SW_LOCK(sc);
+ ret = e6000sw_writephy(dev, phy, reg, data);
+ E6000SW_UNLOCK(sc);
+
+ return (ret);
+}
+
+/*
+ * setvgroup/getvgroup called from etherswitchfcg need to be locked,
+ * while internal calls do not.
+ */
+static int
+e6000sw_setvgroup_wrapper(device_t dev, etherswitch_vlangroup_t *vg)
+{
+ e6000sw_softc_t *sc;
+ int ret;
+
+ sc = device_get_softc(dev);
+ E6000SW_LOCK_ASSERT(sc, SA_UNLOCKED);
+
+ E6000SW_LOCK(sc);
+ ret = e6000sw_setvgroup(dev, vg);
+ E6000SW_UNLOCK(sc);
+
+ return (ret);
+}
+
+static int
+e6000sw_getvgroup_wrapper(device_t dev, etherswitch_vlangroup_t *vg)
+{
+ e6000sw_softc_t *sc;
+ int ret;
+
+ sc = device_get_softc(dev);
+ E6000SW_LOCK_ASSERT(sc, SA_UNLOCKED);
+
+ E6000SW_LOCK(sc);
+ ret = e6000sw_getvgroup(dev, vg);
+ E6000SW_UNLOCK(sc);
+
+ return (ret);
+}
+
+static __inline void
+e6000sw_flush_port(e6000sw_softc_t *sc, int port)
+{
+ uint32_t reg;
+
+ reg = e6000sw_readreg(sc, REG_PORT(port),
+ PORT_VLAN_MAP);
+ reg &= ~PORT_VLAN_MAP_TABLE_MASK;
+ reg &= ~PORT_VLAN_MAP_FID_MASK;
+ e6000sw_writereg(sc, REG_PORT(port),
+ PORT_VLAN_MAP, reg);
+ if (sc->vgroup[port] != E6000SW_PORT_NO_VGROUP) {
+ /*
+ * If port belonged somewhere, owner-group
+ * should have its entry removed.
+ */
+ sc->members[sc->vgroup[port]] &= ~(1 << port);
+ sc->vgroup[port] = E6000SW_PORT_NO_VGROUP;
+ }
+}
+
+static __inline void
+e6000sw_port_assign_vgroup(e6000sw_softc_t *sc, int port, int fid, int vgroup,
+ int members)
+{
+ uint32_t reg;
+
+ reg = e6000sw_readreg(sc, REG_PORT(port),
+ PORT_VLAN_MAP);
+ reg &= ~PORT_VLAN_MAP_TABLE_MASK;
+ reg &= ~PORT_VLAN_MAP_FID_MASK;
+ reg |= members & ~(1 << port);
+ reg |= (fid << PORT_VLAN_MAP_FID) & PORT_VLAN_MAP_FID_MASK;
+ e6000sw_writereg(sc, REG_PORT(port), PORT_VLAN_MAP,
+ reg);
+ sc->vgroup[port] = vgroup;
+}
+
+static int
+e6000sw_setvgroup(device_t dev, etherswitch_vlangroup_t *vg)
+{
+ e6000sw_softc_t *sc;
+ int port, fid;
+
+ sc = device_get_softc(dev);
+ E6000SW_LOCK_ASSERT(sc, SA_XLOCKED);
+
+ if (vg->es_vlangroup >= E6000SW_NUM_VGROUPS)
+ return (EINVAL);
+ if (vg->es_member_ports != vg->es_untagged_ports) {
+ device_printf(dev, "Tagged ports not supported.\n");
+ return (EINVAL);
+ }
+
+ vg->es_untagged_ports &= PORT_VLAN_MAP_TABLE_MASK;
+ fid = vg->es_vlangroup + 1;
+ for (port = 0; port < E6000SW_NUM_PORTS; port++) {
+ if ((sc->members[vg->es_vlangroup] & (1 << port)) ||
+ (vg->es_untagged_ports & (1 << port)))
+ e6000sw_flush_port(sc, port);
+ if (vg->es_untagged_ports & (1 << port))
+ e6000sw_port_assign_vgroup(sc, port, fid,
+ vg->es_vlangroup, vg->es_untagged_ports);
+ }
+ sc->vid[vg->es_vlangroup] = vg->es_vid;
+ sc->members[vg->es_vlangroup] = vg->es_untagged_ports;
+
+ return (0);
+}
+
+static int
+e6000sw_getvgroup(device_t dev, etherswitch_vlangroup_t *vg)
+{
+ e6000sw_softc_t *sc;
+
+ sc = device_get_softc(dev);
+ E6000SW_LOCK_ASSERT(sc, SA_XLOCKED);
+
+ if (vg->es_vlangroup >= E6000SW_NUM_VGROUPS)
+ return (EINVAL);
+ vg->es_untagged_ports = vg->es_member_ports =
+ sc->members[vg->es_vlangroup];
+ vg->es_vid = ETHERSWITCH_VID_VALID;
+
+ return (0);
+}
+
+static __inline struct mii_data*
+e6000sw_miiforphy(e6000sw_softc_t *sc, unsigned int phy)
+{
+
+ if (phy >= E6000SW_NUM_PHYS)
+ return (NULL);
+
+ return (device_get_softc(sc->miibus[phy]));
+}
+
+static int
+e6000sw_ifmedia_upd(struct ifnet *ifp)
+{
+ e6000sw_softc_t *sc;
+ struct mii_data *mii;
+
+ sc = ifp->if_softc;
+ mii = e6000sw_miiforphy(sc, ifp->if_dunit);
+ if (mii == NULL)
+ return (ENXIO);
+ mii_mediachg(mii);
+
+ return (0);
+}
+
+static void
+e6000sw_ifmedia_sts(struct ifnet *ifp, struct ifmediareq *ifmr)
+{
+ e6000sw_softc_t *sc;
+ struct mii_data *mii;
+
+ sc = ifp->if_softc;
+ mii = e6000sw_miiforphy(sc, ifp->if_dunit);
+
+ if (mii == NULL)
+ return;
+
+ mii_pollstat(mii);
+ ifmr->ifm_active = mii->mii_media_active;
+ ifmr->ifm_status = mii->mii_media_status;
+}
+
+static __inline uint32_t
+e6000sw_readreg(e6000sw_softc_t *sc, int addr, int reg)
+{
+
+ E6000SW_LOCK_ASSERT(sc, SA_XLOCKED);
+
+ return (MDIO_READREG(device_get_parent(sc->dev), addr, reg));
+}
+
+static __inline void
+e6000sw_writereg(e6000sw_softc_t *sc, int addr, int reg, int val)
+{
+
+ E6000SW_LOCK_ASSERT(sc, SA_XLOCKED);
+
+ MDIO_WRITEREG(device_get_parent(sc->dev), addr, reg, val);
+}
+
+static __inline int
+e6000sw_cpuport(e6000sw_softc_t *sc, int port)
+{
+
+ return (sc->cpuports_mask & (1 << port));
+}
+
+static __inline int
+e6000sw_set_pvid(e6000sw_softc_t *sc, int port, int pvid)
+{
+
+ e6000sw_writereg(sc, REG_PORT(port), PORT_VID, pvid &
+ PORT_VID_DEF_VID_MASK);
+
+ return (0);
+}
+
+static __inline int
+e6000sw_get_pvid(e6000sw_softc_t *sc, int port, int *pvid)
+{
+
+ if (pvid == NULL)
+ return (ENXIO);
+
+ *pvid = e6000sw_readreg(sc, REG_PORT(port), PORT_VID) &
+ PORT_VID_DEF_VID_MASK;
+
+ return (0);
+}
+
+static void
+e6000sw_tick (void *arg)
+{
+ e6000sw_softc_t *sc;
+ struct mii_softc *miisc;
+ int i;
+
+ sc = arg;
+
+ E6000SW_LOCK_ASSERT(sc, SA_UNLOCKED);
+ for (;;) {
+ E6000SW_LOCK(sc);
+ for (i = 0; i < E6000SW_NUM_PHYS; i++) {
+ mii_tick(sc->mii[i]);
+ LIST_FOREACH(miisc, &sc->mii[i]->mii_phys, mii_list) {
+ if (IFM_INST(sc->mii[i]->mii_media.ifm_cur->ifm_media)
+ != miisc->mii_inst)
+ continue;
+ mii_phy_update(miisc, MII_POLLSTAT);
+ }
+ }
+ E6000SW_UNLOCK(sc);
+ pause("e6000sw tick", 1000);
+ }
+}
+
+static void
+e6000sw_setup(device_t dev, e6000sw_softc_t *sc)
+{
+ uint16_t atu_ctrl, atu_age;
+
+ /* Set aging time */
+ e6000sw_writereg(sc, REG_GLOBAL, ATU_CONTROL,
+ (E6000SW_DEFAULT_AGETIME << ATU_CONTROL_AGETIME) |
+ (1 << ATU_CONTROL_LEARN2ALL));
+
+ /* Send all with specific mac address to cpu port */
+ e6000sw_writereg(sc, REG_GLOBAL2, MGMT_EN_2x, MGMT_EN_ALL);
+ e6000sw_writereg(sc, REG_GLOBAL2, MGMT_EN_0x, MGMT_EN_ALL);
+
+ /* Disable Remote Managment */
+ e6000sw_writereg(sc, REG_GLOBAL, SWITCH_GLOBAL_CONTROL2, 0);
+
+ /* Disable loopback filter and flow control messages */
+ e6000sw_writereg(sc, REG_GLOBAL2, SWITCH_MGMT,
+ SWITCH_MGMT_PRI_MASK |
+ (1 << SWITCH_MGMT_RSVD2CPU) |
+ SWITCH_MGMT_FC_PRI_MASK |
+ (1 << SWITCH_MGMT_FORCEFLOW));
+
+ /* Set VLAN configuration */
+ e6000sw_port_vlan_conf(sc);
+
+ e6000sw_atu_flush(dev, sc, NO_OPERATION);
+ e6000sw_atu_mac_table(dev, sc, NULL, NO_OPERATION);
+ e6000sw_set_atustat(dev, sc, 0, COUNT_ALL);
+
+ /* Set ATU AgeTime to 15 seconds */
+ atu_age = 1;
+
+ atu_ctrl = e6000sw_readreg(sc, REG_GLOBAL, ATU_CONTROL);
+
+ /* Set new AgeTime field */
+ atu_ctrl &= ~ATU_CONTROL_AGETIME_MASK;
+ e6000sw_writereg(sc, REG_GLOBAL, ATU_CONTROL, atu_ctrl |
+ (atu_age << ATU_CONTROL_AGETIME));
+}
+
+static void
+e6000sw_port_vlan_conf(e6000sw_softc_t *sc)
+{
+ int port, ret;
+ etherswitch_vlangroup_t vg;
+ device_t dev;
+
+ dev = sc->dev;
+ /* Disable all ports */
+ for (port = 0; port < E6000SW_NUM_PORTS; port++) {
+ ret = e6000sw_readreg(sc, REG_PORT(port), PORT_CONTROL);
+ e6000sw_writereg(sc, REG_PORT(port), PORT_CONTROL,
+ (ret & ~PORT_CONTROL_ENABLE));
+ }
+
+ /* Set port priority */
+ for (port = 0; port < E6000SW_NUM_PORTS; port++) {
+ ret = e6000sw_readreg(sc, REG_PORT(port), PORT_VID);
+ ret &= ~PORT_VID_PRIORITY_MASK;
+ e6000sw_writereg(sc, REG_PORT(port), PORT_VID, ret);
+ }
+
+ vg.es_vlangroup = 0;
+ vg.es_vid = 0;
+ vg.es_member_ports = vg.es_untagged_ports = E6000SW_DEF_VLANGROUP0;
+ e6000sw_setvgroup(dev, &vg);
+ vg.es_vlangroup = 1;
+ vg.es_vid = 1;
+ vg.es_member_ports = vg.es_untagged_ports = E6000SW_DEF_VLANGROUP1;
+ e6000sw_setvgroup(dev, &vg);
+
+ device_printf(dev, "Default vlangroups set.\n");
+ /* Set VID map */
+ for (port = 0; port < E6000SW_NUM_PORTS; port++) {
+ ret = e6000sw_readreg(sc, REG_PORT(port), PORT_VID);
+ ret &= ~PORT_VID_DEF_VID_MASK;
+ ret |= (port + 1);
+ e6000sw_writereg(sc, REG_PORT(port), PORT_VID, ret);
+ }
+
+ /* Enable all ports */
+ for (port = 0; port < E6000SW_NUM_PORTS; port++) {
+ ret = e6000sw_readreg(sc, REG_PORT(port), PORT_CONTROL);
+ e6000sw_writereg(sc, REG_PORT(port), PORT_CONTROL, (ret |
+ PORT_CONTROL_ENABLE));
+ }
+}
+
+static void
+e6000sw_set_atustat(device_t dev, e6000sw_softc_t *sc, int bin, int flag)
+{
+ uint16_t ret;
+
+ ret = e6000sw_readreg(sc, REG_GLOBAL2, ATU_STATS);
+ e6000sw_writereg(sc, REG_GLOBAL2, ATU_STATS, (bin << ATU_STATS_BIN ) |
+ (flag << ATU_STATS_FLAG));
+}
+
+static int
+e6000sw_atu_mac_table(device_t dev, e6000sw_softc_t *sc, struct atu_opt *atu,
+ int flag)
+{
+ uint16_t ret_opt;
+ uint16_t ret_data;
+ int retries;
+
+ if (flag == NO_OPERATION)
+ return (0);
+ else if ((flag & (LOAD_FROM_FIB | PURGE_FROM_FIB | GET_NEXT_IN_FIB |
+ GET_VIOLATION_DATA | CLEAR_VIOLATION_DATA)) == 0) {
+ device_printf(dev, "Wrong Opcode for ATU operation\n");
+ return (EINVAL);
+ }
+
+ ret_opt = e6000sw_readreg(sc, REG_GLOBAL, ATU_OPERATION);
+
+ if (ret_opt & ATU_UNIT_BUSY) {
+ device_printf(dev, "ATU unit is busy, cannot access"
+ "register\n");
+ return (EBUSY);
+ } else {
+ if(flag & LOAD_FROM_FIB) {
+ ret_data = e6000sw_readreg(sc, REG_GLOBAL, ATU_DATA);
+ e6000sw_writereg(sc, REG_GLOBAL2, ATU_DATA, (ret_data &
+ ~ENTRY_STATE));
+ }
+ e6000sw_writereg(sc, REG_GLOBAL, ATU_MAC_ADDR01, atu->mac_01);
+ e6000sw_writereg(sc, REG_GLOBAL, ATU_MAC_ADDR23, atu->mac_23);
+ e6000sw_writereg(sc, REG_GLOBAL, ATU_MAC_ADDR45, atu->mac_45);
+ e6000sw_writereg(sc, REG_GLOBAL, ATU_FID, atu->fid);
+
+ e6000sw_writereg(sc, REG_GLOBAL, ATU_OPERATION, (ret_opt |
+ ATU_UNIT_BUSY | flag));
+
+ retries = E6000SW_RETRIES;
+ while (--retries & (e6000sw_readreg(sc, REG_GLOBAL,
+ ATU_OPERATION) & ATU_UNIT_BUSY))
+ DELAY(1);
+
+ if (retries == 0)
+ device_printf(dev, "Timeout while flushing\n");
+ else if (flag & GET_NEXT_IN_FIB) {
+ atu->mac_01 = e6000sw_readreg(sc, REG_GLOBAL,
+ ATU_MAC_ADDR01);
+ atu->mac_23 = e6000sw_readreg(sc, REG_GLOBAL,
+ ATU_MAC_ADDR23);
+ atu->mac_45 = e6000sw_readreg(sc, REG_GLOBAL,
+ ATU_MAC_ADDR45);
+ }
+ }
+
+ return (0);
+}
+
+static int
+e6000sw_atu_flush(device_t dev, e6000sw_softc_t *sc, int flag)
+{
+ uint16_t ret;
+ int retries;
+
+ if (flag == NO_OPERATION)
+ return (0);
+
+ ret = e6000sw_readreg(sc, REG_GLOBAL, ATU_OPERATION);
+ if (ret & ATU_UNIT_BUSY) {
+ device_printf(dev, "Atu unit is busy, cannot flush\n");
+ return (EBUSY);
+ } else {
+ e6000sw_writereg(sc, REG_GLOBAL, ATU_OPERATION, (ret |
+ ATU_UNIT_BUSY | flag));
+ retries = E6000SW_RETRIES;
+ while (--retries & (e6000sw_readreg(sc, REG_GLOBAL,
+ ATU_OPERATION) & ATU_UNIT_BUSY))
+ DELAY(1);
+
+ if (retries == 0)
+ device_printf(dev, "Timeout while flushing\n");
+ }
+
+ return (0);
+}
Index: sys/dev/etherswitch/e6000sw/e6000swreg.h
===================================================================
--- /dev/null
+++ sys/dev/etherswitch/e6000sw/e6000swreg.h
@@ -0,0 +1,182 @@
+/*-
+ * Copyright (c) 2015 Semihalf.
+ * Copyright (c) 2015 Stormshield.
+ * 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.
+ */
+
+#ifndef _E6000SWREG_H_
+#define _E6000SWREG_H_
+
+struct atu_opt {
+ uint16_t mac_01;
+ uint16_t mac_23;
+ uint16_t mac_45;
+ uint16_t fid;
+};
+
+/*
+ * Definitions for the Marvell 88E6000 series Ethernet Switch.
+ */
+
+#define CPU_PORT 0x5
+
+/*
+ * Switch Registers
+ */
+#define REG_GLOBAL 0x1b
+#define REG_GLOBAL2 0x1c
+#define REG_PORT(p) (0x10 + (p))
+
+#define REG_NUM_MAX 31
+
+/*
+ * Per-Port Switch Registers
+ */
+#define PORT_STATUS 0x0
+#define PSC_CONTROL 0x1
+#define SWITCH_ID 0x3
+#define PORT_CONTROL 0x4
+#define PORT_CONTROL_1 0x5
+#define PORT_VLAN_MAP 0x6
+#define PORT_VID 0x7
+#define PORT_ASSOCIATION_VECTOR 0xb
+#define PORT_ATU_CTRL 0xc
+#define RX_COUNTER 0x12
+#define TX_COUNTER 0x13
+
+#define PORT_VID_DEF_VID 0
+#define PORT_VID_DEF_VID_MASK 0xfff
+#define PORT_VID_PRIORITY_MASK 0xc00
+
+#define PORT_CONTROL_ENABLE 0x3
+
+/* PORT_VLAN fields */
+#define PORT_VLAN_MAP_TABLE_MASK 0x7f
+#define PORT_VLAN_MAP_FID 12
+#define PORT_VLAN_MAP_FID_MASK 0xf000
+/*
+ * Switch Global Register 1 accessed via REG_GLOBAL_ADDR
+ */
+#define SWITCH_GLOBAL_STATUS 0
+#define SWITCH_GLOBAL_CONTROL 4
+#define SWITCH_GLOBAL_CONTROL2 28
+
+#define MONITOR_CONTROL 26
+
+/* ATU operation */
+#define ATU_FID 1
+#define ATU_CONTROL 10
+#define ATU_OPERATION 11
+#define ATU_DATA 12
+#define ATU_MAC_ADDR01 13
+#define ATU_MAC_ADDR23 14
+#define ATU_MAC_ADDR45 15
+
+#define ATU_UNIT_BUSY (1 << 15)
+#define ENTRY_STATE 0xf
+
+/* ATU_CONTROL fields */
+#define ATU_CONTROL_AGETIME 4
+#define ATU_CONTROL_AGETIME_MASK 0xff0
+#define ATU_CONTROL_LEARN2ALL 3
+
+/* ATU opcode */
+#define NO_OPERATION (0 << 0)
+#define FLUSH_ALL (1 << 0)
+#define FLUSH_NON_STATIC (1 << 1)
+#define LOAD_FROM_FIB (3 << 0)
+#define PURGE_FROM_FIB (3 << 0)
+#define GET_NEXT_IN_FIB (1 << 2)
+#define FLUSH_ALL_IN_FIB (5 << 0)
+#define FLUSH_NON_STATIC_IN_FIB (3 << 1)
+#define GET_VIOLATION_DATA (7 << 0)
+#define CLEAR_VIOLATION_DATA (7 << 0)
+
+/* ATU Stats */
+#define COUNT_ALL (0 << 0)
+
+/*
+ * Switch Global Register 2 accessed via REG_GLOBAL2_ADDR
+ */
+#define MGMT_EN_2x 2
+#define MGMT_EN_0x 3
+#define SWITCH_MGMT 5
+#define ATU_STATS 14
+
+#define MGMT_EN_ALL 0xffff
+
+/* SWITCH_MGMT fields */
+
+#define SWITCH_MGMT_PRI 0
+#define SWITCH_MGMT_PRI_MASK 7
+#define SWITCH_MGMT_RSVD2CPU 3
+#define SWITCH_MGMT_FC_PRI 4
+#define SWITCH_MGMT_FC_PRI_MASK (7 << 4)
+#define SWITCH_MGMT_FORCEFLOW 7
+
+/* ATU_STATS fields */
+
+#define ATU_STATS_BIN 14
+#define ATU_STATS_FLAG 12
+
+/*
+ * PHY registers accessed via 'Switch Global Registers' (REG_GLOBAL2).
+ */
+#define SMI_PHY_CMD_REG 0x18
+#define SMI_PHY_DATA_REG 0x19
+
+#define PHY_CMD 0x18
+#define PHY_DATA 0x19
+#define PHY_DATA_MASK 0xffff
+
+#define PHY_CMD_SMI_BUSY 15
+#define PHY_CMD_MODE 12
+#define PHY_CMD_MODE_MDIO 1
+#define PHY_CMD_MODE_XMDIO 0
+#define PHY_CMD_OPCODE 10
+#define PHY_CMD_OPCODE_WRITE 1
+#define PHY_CMD_OPCODE_READ 2
+#define PHY_CMD_DEV_ADDR 5
+#define PHY_CMD_DEV_ADDR_MASK 0x3e0
+#define PHY_CMD_REG_ADDR 0
+#define PHY_CMD_REG_ADDR_MASK 0x1f
+
+#define PHY_PAGE_REG 22
+
+#define E6000SW_NUM_PHYS 5
+#define E6000SW_NUM_PHY_REGS 29
+#define E6000SW_CPUPORTS_MASK ((1 << 5) | (1 << 6))
+#define E6000SW_NUM_VGROUPS 8
+#define E6000SW_NUM_PORTS 7
+#define E6000SW_PORT_NO_VGROUP -1
+#define E6000SW_DEFAULT_AGETIME 20
+#define E6000SW_RETRIES 100
+
+
+/* Default vlangroups */
+#define E6000SW_DEF_VLANGROUP0 (1 | (1 << 1) | (1 << 2) | (1 << 3) | \
+ (1 << 6))
+#define E6000SW_DEF_VLANGROUP1 ((1 << 4) | (1 << 5))
+
+#endif /* _E6000SWREG_H_ */
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Mon, Oct 13, 11:39 PM (10 h, 24 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
23676129
Default Alt Text
D3902.id9412.diff (30 KB)
Attached To
Mode
D3902: Introduce e6000sw etherswitch support
Attached
Detach File
Event Timeline
Log In to Comment