Index: head/sys/dev/etherswitch/e6000sw/e6000sw.c =================================================================== --- head/sys/dev/etherswitch/e6000sw/e6000sw.c (revision 349520) +++ head/sys/dev/etherswitch/e6000sw/e6000sw.c (revision 349521) @@ -1,1347 +1,1540 @@ /*- * Copyright (c) 2015 Semihalf * Copyright (c) 2015 Stormshield + * Copyright (c) 2018-2019, Rubicon Communications, LLC (Netgate) * 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 __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #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) +#define E6000SW_WAITREADY(_sc, _reg, _bit) \ + e6000sw_waitready((_sc), REG_GLOBAL, (_reg), (_bit)) +#define E6000SW_WAITREADY2(_sc, _reg, _bit) \ + e6000sw_waitready((_sc), REG_GLOBAL2, (_reg), (_bit)) +#define MDIO_READ(dev, addr, reg) \ + MDIO_READREG(device_get_parent(dev), (addr), (reg)) +#define MDIO_WRITE(dev, addr, reg, val) \ + MDIO_WRITEREG(device_get_parent(dev), (addr), (reg), (val)) + typedef struct e6000sw_softc { device_t dev; phandle_t node; struct sx sx; struct ifnet *ifp[E6000SW_MAX_PORTS]; char *ifname[E6000SW_MAX_PORTS]; device_t miibus[E6000SW_MAX_PORTS]; struct proc *kproc; + int vlans[E6000SW_NUM_VLANS]; uint32_t swid; uint32_t vlan_mode; uint32_t cpuports_mask; uint32_t fixed_mask; uint32_t fixed25_mask; uint32_t ports_mask; int phy_base; int sw_addr; int num_ports; - boolean_t multi_chip; } e6000sw_softc_t; static etherswitch_info_t etherswitch_info = { .es_nports = 0, .es_nvlangroups = 0, - .es_vlan_caps = ETHERSWITCH_VLAN_PORT, + .es_vlan_caps = ETHERSWITCH_VLAN_PORT | ETHERSWITCH_VLAN_DOT1Q, .es_name = "Marvell 6000 series switch" }; static void e6000sw_identify(driver_t *, device_t); static int e6000sw_probe(device_t); static int e6000sw_parse_fixed_link(e6000sw_softc_t *, phandle_t, uint32_t); static int e6000sw_parse_ethernet(e6000sw_softc_t *, phandle_t, uint32_t); static int e6000sw_attach(device_t); static int e6000sw_detach(device_t); static int e6000sw_readphy(device_t, int, int); static int e6000sw_writephy(device_t, int, int, int); static etherswitch_info_t* e6000sw_getinfo(device_t); static int e6000sw_getconf(device_t, etherswitch_conf_t *); +static int e6000sw_setconf(device_t, etherswitch_conf_t *); static void e6000sw_lock(device_t); static void e6000sw_unlock(device_t); static int e6000sw_getport(device_t, etherswitch_port_t *); static int e6000sw_setport(device_t, etherswitch_port_t *); +static int e6000sw_set_vlan_mode(e6000sw_softc_t *, uint32_t); static int e6000sw_readreg_wrapper(device_t, int); static int e6000sw_writereg_wrapper(device_t, int, int); static int e6000sw_readphy_wrapper(device_t, int, int); static int e6000sw_writephy_wrapper(device_t, int, int, int); static int e6000sw_getvgroup_wrapper(device_t, etherswitch_vlangroup_t *); static int e6000sw_setvgroup_wrapper(device_t, etherswitch_vlangroup_t *); static int e6000sw_setvgroup(device_t, etherswitch_vlangroup_t *); static int e6000sw_getvgroup(device_t, etherswitch_vlangroup_t *); static void e6000sw_setup(device_t, e6000sw_softc_t *); -static void e6000sw_port_vlan_conf(e6000sw_softc_t *); static void e6000sw_tick(void *); static void e6000sw_set_atustat(device_t, e6000sw_softc_t *, int, int); static int e6000sw_atu_flush(device_t, e6000sw_softc_t *, int); +static int e6000sw_vtu_flush(e6000sw_softc_t *); +static int e6000sw_vtu_update(e6000sw_softc_t *, int, int, int, int, int); static __inline void e6000sw_writereg(e6000sw_softc_t *, int, int, int); static __inline uint32_t e6000sw_readreg(e6000sw_softc_t *, int, int); static int e6000sw_ifmedia_upd(struct ifnet *); static void e6000sw_ifmedia_sts(struct ifnet *, struct ifmediareq *); static int e6000sw_atu_mac_table(device_t, e6000sw_softc_t *, struct atu_opt *, int); static int e6000sw_get_pvid(e6000sw_softc_t *, int, int *); -static int e6000sw_set_pvid(e6000sw_softc_t *, int, int); +static void e6000sw_set_pvid(e6000sw_softc_t *, int, int); static __inline bool e6000sw_is_cpuport(e6000sw_softc_t *, int); static __inline bool e6000sw_is_fixedport(e6000sw_softc_t *, int); static __inline bool e6000sw_is_fixed25port(e6000sw_softc_t *, int); static __inline bool e6000sw_is_phyport(e6000sw_softc_t *, int); static __inline bool e6000sw_is_portenabled(e6000sw_softc_t *, int); static __inline struct mii_data *e6000sw_miiforphy(e6000sw_softc_t *, unsigned int); 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_getconf, e6000sw_getconf), + DEVMETHOD(etherswitch_setconf, e6000sw_setconf), 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); -#define SMI_CMD 0 -#define SMI_CMD_BUSY (1 << 15) -#define SMI_CMD_OP_READ ((2 << 10) | SMI_CMD_BUSY | (1 << 12)) -#define SMI_CMD_OP_WRITE ((1 << 10) | SMI_CMD_BUSY | (1 << 12)) -#define SMI_DATA 1 -#define MDIO_READ(dev, addr, reg) \ - MDIO_READREG(device_get_parent(dev), (addr), (reg)) -#define MDIO_WRITE(dev, addr, reg, val) \ - MDIO_WRITEREG(device_get_parent(dev), (addr), (reg), (val)) - 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; phandle_t switch_node; switch_node = ofw_bus_find_compatible(OF_finddevice("/"), "marvell,mv88e6085"); if (switch_node == 0) return (ENXIO); if (bootverbose) device_printf(dev, "Found switch_node: 0x%x\n", switch_node); sc = device_get_softc(dev); sc->dev = dev; sc->node = switch_node; if (OF_getencprop(sc->node, "reg", &sc->sw_addr, sizeof(sc->sw_addr)) < 0) return (ENXIO); + if (sc->sw_addr < 0 || sc->sw_addr > 32) + return (ENXIO); /* - * According to the Linux source code, all of the Switch IDs we support - * are multi_chip capable, and should go into multi-chip mode if the - * sw_addr != 0. - */ - if (!OF_hasprop(sc->node, "single-chip-addressing") && sc->sw_addr != 0) - sc->multi_chip = true; - - /* * Create temporary lock, just to satisfy assertions, * when obtaining the switch ID. Destroy immediately afterwards. */ sx_init(&sc->sx, "e6000sw_tmp"); E6000SW_LOCK(sc); sc->swid = e6000sw_readreg(sc, REG_PORT(0), SWITCH_ID) & 0xfff0; E6000SW_UNLOCK(sc); sx_destroy(&sc->sx); switch (sc->swid) { case MV88E6141: description = "Marvell 88E6141"; sc->phy_base = 0x10; sc->num_ports = 6; break; case MV88E6341: description = "Marvell 88E6341"; sc->phy_base = 0x10; sc->num_ports = 6; break; case MV88E6352: description = "Marvell 88E6352"; sc->num_ports = 7; break; case MV88E6172: description = "Marvell 88E6172"; sc->num_ports = 7; break; case MV88E6176: description = "Marvell 88E6176"; sc->num_ports = 7; break; default: device_printf(dev, "Unrecognized device, id 0x%x.\n", sc->swid); return (ENXIO); } device_set_desc(dev, description); return (BUS_PROBE_DEFAULT); } static int e6000sw_parse_fixed_link(e6000sw_softc_t *sc, phandle_t node, uint32_t port) { int speed; phandle_t fixed_link; fixed_link = ofw_bus_find_child(node, "fixed-link"); if (fixed_link != 0) { sc->fixed_mask |= (1 << port); if (OF_getencprop(fixed_link, "speed", &speed, sizeof(speed))> 0) { if (speed == 2500 && (MVSWITCH(sc, MV88E6141) || MVSWITCH(sc, MV88E6341))) sc->fixed25_mask |= (1 << port); } else { device_printf(sc->dev, "Port %d has a fixed-link node without a speed " "property\n", port); return (ENXIO); } } return (0); } static int e6000sw_parse_ethernet(e6000sw_softc_t *sc, phandle_t port_handle, uint32_t port) { phandle_t switch_eth, switch_eth_handle; if (OF_getencprop(port_handle, "ethernet", (void*)&switch_eth_handle, sizeof(switch_eth_handle)) > 0) { if (switch_eth_handle > 0) { switch_eth = OF_node_from_xref(switch_eth_handle); device_printf(sc->dev, "CPU port at %d\n", port); sc->cpuports_mask |= (1 << port); return (e6000sw_parse_fixed_link(sc, switch_eth, port)); } else device_printf(sc->dev, "Port %d has ethernet property but it points " "to an invalid location\n", port); } return (0); } static int e6000sw_parse_child_fdt(e6000sw_softc_t *sc, phandle_t child, int *pport) { uint32_t port; if (pport == NULL) return (ENXIO); if (OF_getencprop(child, "reg", (void *)&port, sizeof(port)) < 0) return (ENXIO); if (port >= sc->num_ports) return (ENXIO); *pport = port; if (e6000sw_parse_fixed_link(sc, child, port) != 0) return (ENXIO); if (e6000sw_parse_ethernet(sc, child, port) != 0) return (ENXIO); if ((sc->fixed_mask & (1 << port)) != 0) device_printf(sc->dev, "fixed port at %d\n", port); else device_printf(sc->dev, "PHY at port %d\n", port); return (0); } static int e6000sw_init_interface(e6000sw_softc_t *sc, int port) { char name[IFNAMSIZ]; snprintf(name, IFNAMSIZ, "%sport", device_get_nameunit(sc->dev)); sc->ifp[port] = if_alloc(IFT_ETHER); if (sc->ifp[port] == NULL) return (ENOMEM); sc->ifp[port]->if_softc = sc; sc->ifp[port]->if_flags |= IFF_UP | IFF_BROADCAST | IFF_DRV_RUNNING | IFF_SIMPLEX; sc->ifname[port] = malloc(strlen(name) + 1, M_E6000SW, M_NOWAIT); if (sc->ifname[port] == NULL) { if_free(sc->ifp[port]); return (ENOMEM); } memcpy(sc->ifname[port], name, strlen(name) + 1); if_initname(sc->ifp[port], sc->ifname[port], port); return (0); } static int e6000sw_attach_miibus(e6000sw_softc_t *sc, int port) { int err; err = mii_attach(sc->dev, &sc->miibus[port], sc->ifp[port], e6000sw_ifmedia_upd, e6000sw_ifmedia_sts, BMSR_DEFCAPMASK, port + sc->phy_base, MII_OFFSET_ANY, 0); if (err != 0) return (err); return (0); } static int e6000sw_attach(device_t dev) { e6000sw_softc_t *sc; phandle_t child, ports; int err, port; uint32_t reg; err = 0; sc = device_get_softc(dev); - if (sc->multi_chip) - device_printf(dev, "multi-chip addressing mode\n"); + /* + * According to the Linux source code, all of the Switch IDs we support + * are multi_chip capable, and should go into multi-chip mode if the + * sw_addr != 0. + */ + if (MVSWITCH_MULTICHIP(sc)) + device_printf(dev, "multi-chip addressing mode (%#x)\n", + sc->sw_addr); else device_printf(dev, "single-chip addressing mode\n"); sx_init(&sc->sx, "e6000sw"); E6000SW_LOCK(sc); e6000sw_setup(dev, sc); ports = ofw_bus_find_child(sc->node, "ports"); if (ports == 0) { device_printf(dev, "failed to parse DTS: no ports found for " "switch\n"); return (ENXIO); } for (child = OF_child(ports); child != 0; child = OF_peer(child)) { err = e6000sw_parse_child_fdt(sc, child, &port); if (err != 0) { device_printf(sc->dev, "failed to parse DTS\n"); goto out_fail; } /* Port is in use. */ sc->ports_mask |= (1 << port); err = e6000sw_init_interface(sc, port); if (err != 0) { device_printf(sc->dev, "failed to init interface\n"); goto out_fail; } if (e6000sw_is_fixedport(sc, port)) { /* Link must be down to change speed force value. */ reg = e6000sw_readreg(sc, REG_PORT(port), PSC_CONTROL); reg &= ~PSC_CONTROL_LINK_UP; reg |= PSC_CONTROL_FORCED_LINK; e6000sw_writereg(sc, REG_PORT(port), PSC_CONTROL, reg); /* * Force speed, full-duplex, EEE off and flow-control * on. */ reg &= ~(PSC_CONTROL_SPD2500 | PSC_CONTROL_ALT_SPD | PSC_CONTROL_FORCED_EEE); if (e6000sw_is_fixed25port(sc, port)) reg |= PSC_CONTROL_SPD2500; else reg |= PSC_CONTROL_SPD1000; reg |= PSC_CONTROL_FORCED_DPX | PSC_CONTROL_FULLDPX | PSC_CONTROL_FORCED_LINK | PSC_CONTROL_LINK_UP | PSC_CONTROL_FORCED_FC | PSC_CONTROL_FC_ON | PSC_CONTROL_FORCED_SPD; if (MVSWITCH(sc, MV88E6141) || MVSWITCH(sc, MV88E6341)) reg |= PSC_CONTROL_FORCED_EEE; e6000sw_writereg(sc, REG_PORT(port), PSC_CONTROL, reg); } /* Don't attach miibus at CPU/fixed ports */ if (!e6000sw_is_phyport(sc, port)) continue; err = e6000sw_attach_miibus(sc, port); if (err != 0) { device_printf(sc->dev, "failed to attach miibus\n"); goto out_fail; } } etherswitch_info.es_nports = sc->num_ports; /* Default to port vlan. */ - e6000sw_port_vlan_conf(sc); + e6000sw_set_vlan_mode(sc, ETHERSWITCH_VLAN_PORT); + + reg = e6000sw_readreg(sc, REG_GLOBAL, SWITCH_GLOBAL_STATUS); + if (reg & SWITCH_GLOBAL_STATUS_IR) + device_printf(dev, "switch is ready.\n"); E6000SW_UNLOCK(sc); bus_generic_probe(dev); bus_generic_attach(dev); kproc_create(e6000sw_tick, sc, &sc->kproc, 0, 0, "e6000sw tick kproc"); return (0); out_fail: E6000SW_UNLOCK(sc); e6000sw_detach(dev); return (err); } -static __inline int -e6000sw_poll_done(e6000sw_softc_t *sc) +static int +e6000sw_waitready(e6000sw_softc_t *sc, uint32_t phy, uint32_t reg, + uint32_t busybit) { int i; - for (i = 0; i < E6000SW_SMI_TIMEOUT; i++) { - - if ((e6000sw_readreg(sc, REG_GLOBAL2, SMI_PHY_CMD_REG) & - (1 << PHY_CMD_SMI_BUSY)) == 0) + for (i = 0; i < E6000SW_RETRIES; i++) { + if ((e6000sw_readreg(sc, phy, reg) & busybit) == 0) return (0); - - pause("e6000sw PHY poll", hz/1000); + DELAY(1); } - return (ETIMEDOUT); + return (1); } /* * 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; - int err; sc = device_get_softc(dev); if (!e6000sw_is_phyport(sc, phy) || reg >= E6000SW_NUM_PHY_REGS) { device_printf(dev, "Wrong register address.\n"); return (EINVAL); } E6000SW_LOCK_ASSERT(sc, SA_XLOCKED); - - err = e6000sw_poll_done(sc); - if (err != 0) { + if (E6000SW_WAITREADY2(sc, SMI_PHY_CMD_REG, SMI_CMD_BUSY)) { device_printf(dev, "Timeout while waiting for switch\n"); - return (err); + return (ETIMEDOUT); } - 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); - - err = e6000sw_poll_done(sc); - if (err != 0) { + e6000sw_writereg(sc, REG_GLOBAL2, SMI_PHY_CMD_REG, + SMI_CMD_OP_C22_READ | (reg & SMI_CMD_REG_ADDR_MASK) | + ((phy << SMI_CMD_DEV_ADDR) & SMI_CMD_DEV_ADDR_MASK)); + if (E6000SW_WAITREADY2(sc, SMI_PHY_CMD_REG, SMI_CMD_BUSY)) { device_printf(dev, "Timeout while waiting for switch\n"); - return (err); + return (ETIMEDOUT); } val = e6000sw_readreg(sc, REG_GLOBAL2, SMI_PHY_DATA_REG); return (val & PHY_DATA_MASK); } static int e6000sw_writephy(device_t dev, int phy, int reg, int data) { e6000sw_softc_t *sc; - uint32_t val; - int err; sc = device_get_softc(dev); if (!e6000sw_is_phyport(sc, phy) || reg >= E6000SW_NUM_PHY_REGS) { device_printf(dev, "Wrong register address.\n"); return (EINVAL); } E6000SW_LOCK_ASSERT(sc, SA_XLOCKED); - - err = e6000sw_poll_done(sc); - if (err != 0) { + if (E6000SW_WAITREADY2(sc, SMI_PHY_CMD_REG, SMI_CMD_BUSY)) { device_printf(dev, "Timeout while waiting for switch\n"); - return (err); + return (ETIMEDOUT); } - val = 1 << PHY_CMD_SMI_BUSY; - val |= PHY_CMD_MODE_MDIO << PHY_CMD_MODE; - 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_writereg(sc, REG_GLOBAL2, SMI_PHY_CMD_REG, + SMI_CMD_OP_C22_WRITE | (reg & SMI_CMD_REG_ADDR_MASK) | + ((phy << SMI_CMD_DEV_ADDR) & SMI_CMD_DEV_ADDR_MASK)); - err = e6000sw_poll_done(sc); - if (err != 0) - device_printf(dev, "Timeout while waiting for switch\n"); - - return (err); + 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 < sc->num_ports; 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 int e6000sw_getconf(device_t dev, etherswitch_conf_t *conf) { struct e6000sw_softc *sc; /* Return the VLAN mode. */ sc = device_get_softc(dev); conf->cmd = ETHERSWITCH_CONF_VLAN_MODE; conf->vlan_mode = sc->vlan_mode; return (0); } +static int +e6000sw_setconf(device_t dev, etherswitch_conf_t *conf) +{ + struct e6000sw_softc *sc; + + /* Set the VLAN mode. */ + sc = device_get_softc(dev); + if (conf->cmd & ETHERSWITCH_CONF_VLAN_MODE) { + E6000SW_LOCK(sc); + e6000sw_set_vlan_mode(sc, conf->vlan_mode); + E6000SW_UNLOCK(sc); + } + + return (0); +} + 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; + uint32_t reg; e6000sw_softc_t *sc = device_get_softc(dev); E6000SW_LOCK_ASSERT(sc, SA_UNLOCKED); if (p->es_port >= sc->num_ports || p->es_port < 0) return (EINVAL); if (!e6000sw_is_portenabled(sc, p->es_port)) return (0); - err = 0; E6000SW_LOCK(sc); e6000sw_get_pvid(sc, p->es_port, &p->es_pvid); + /* Port flags. */ + reg = e6000sw_readreg(sc, REG_PORT(p->es_port), PORT_CONTROL2); + if (reg & PORT_CONTROL2_DISC_TAGGED) + p->es_flags |= ETHERSWITCH_PORT_DROPTAGGED; + if (reg & PORT_CONTROL2_DISC_UNTAGGED) + p->es_flags |= ETHERSWITCH_PORT_DROPUNTAGGED; + + err = 0; if (e6000sw_is_fixedport(sc, p->es_port)) { if (e6000sw_is_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; if (e6000sw_is_fixed25port(sc, p->es_port)) ifmr->ifm_active = IFM_2500_T; else ifmr->ifm_active = IFM_1000_T; ifmr->ifm_active |= IFM_ETHER | IFM_FDX; ifmr->ifm_current = ifmr->ifm_active; 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); } 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; + uint32_t reg; sc = device_get_softc(dev); E6000SW_LOCK_ASSERT(sc, SA_UNLOCKED); if (p->es_port >= sc->num_ports || p->es_port < 0) return (EINVAL); if (!e6000sw_is_portenabled(sc, p->es_port)) return (0); + /* Port flags. */ + reg = e6000sw_readreg(sc, REG_PORT(p->es_port), PORT_CONTROL2); + if (p->es_flags & ETHERSWITCH_PORT_DROPTAGGED) + reg |= PORT_CONTROL2_DISC_TAGGED; + else + reg &= ~PORT_CONTROL2_DISC_TAGGED; + if (p->es_flags & ETHERSWITCH_PORT_DROPUNTAGGED) + reg |= PORT_CONTROL2_DISC_UNTAGGED; + else + reg &= ~PORT_CONTROL2_DISC_UNTAGGED; + e6000sw_writereg(sc, REG_PORT(p->es_port), PORT_CONTROL2, reg); + err = 0; E6000SW_LOCK(sc); if (p->es_pvid != 0) e6000sw_set_pvid(sc, p->es_port, p->es_pvid); if (e6000sw_is_phyport(sc, p->es_port)) { mii = e6000sw_miiforphy(sc, p->es_port); err = ifmedia_ioctl(mii->mii_ifp, &p->es_ifr, &mii->mii_media, SIOCSIFMEDIA); } E6000SW_UNLOCK(sc); return (err); } +static __inline void +e6000sw_port_vlan_assign(e6000sw_softc_t *sc, int port, uint32_t fid, + uint32_t members) +{ + uint32_t reg; + + reg = e6000sw_readreg(sc, REG_PORT(port), PORT_VLAN_MAP); + reg &= ~(PORT_MASK(sc) | PORT_VLAN_MAP_FID_MASK); + reg |= members & PORT_MASK(sc) & ~(1 << port); + reg |= (fid << PORT_VLAN_MAP_FID) & PORT_VLAN_MAP_FID_MASK; + e6000sw_writereg(sc, REG_PORT(port), PORT_VLAN_MAP, reg); + reg = e6000sw_readreg(sc, REG_PORT(port), PORT_CONTROL1); + reg &= ~PORT_CONTROL1_FID_MASK; + reg |= (fid >> 4) & PORT_CONTROL1_FID_MASK; + e6000sw_writereg(sc, REG_PORT(port), PORT_CONTROL1, reg); +} + +static int +e6000sw_init_vlan(struct e6000sw_softc *sc) +{ + int i, port, ret; + uint32_t members; + + /* Disable all ports */ + for (port = 0; port < sc->num_ports; port++) { + ret = e6000sw_readreg(sc, REG_PORT(port), PORT_CONTROL); + e6000sw_writereg(sc, REG_PORT(port), PORT_CONTROL, + (ret & ~PORT_CONTROL_ENABLE)); + } + + /* Flush VTU. */ + e6000sw_vtu_flush(sc); + + for (port = 0; port < sc->num_ports; port++) { + /* Reset the egress and frame mode. */ + ret = e6000sw_readreg(sc, REG_PORT(port), PORT_CONTROL); + ret &= ~(PORT_CONTROL_EGRESS | PORT_CONTROL_FRAME); + e6000sw_writereg(sc, REG_PORT(port), PORT_CONTROL, ret); + + /* Set the the 802.1q mode. */ + ret = e6000sw_readreg(sc, REG_PORT(port), PORT_CONTROL2); + ret &= ~PORT_CONTROL2_DOT1Q; + if (sc->vlan_mode == ETHERSWITCH_VLAN_DOT1Q) + ret |= PORT_CONTROL2_DOT1Q; + e6000sw_writereg(sc, REG_PORT(port), PORT_CONTROL2, ret); + } + + for (port = 0; port < sc->num_ports; port++) { + if (!e6000sw_is_portenabled(sc, port)) + continue; + + ret = e6000sw_readreg(sc, REG_PORT(port), PORT_VID); + + /* Set port priority */ + ret &= ~PORT_VID_PRIORITY_MASK; + + /* Set VID map */ + ret &= ~PORT_VID_DEF_VID_MASK; + if (sc->vlan_mode == ETHERSWITCH_VLAN_DOT1Q) + ret |= 1; + else + ret |= (port + 1); + e6000sw_writereg(sc, REG_PORT(port), PORT_VID, ret); + } + + /* Assign the member ports to each origin port. */ + for (port = 0; port < sc->num_ports; port++) { + members = 0; + if (e6000sw_is_portenabled(sc, port)) { + for (i = 0; i < sc->num_ports; i++) { + if (i == port || !e6000sw_is_portenabled(sc, i)) + continue; + members |= (1 << i); + } + } + /* Default to FID 0. */ + e6000sw_port_vlan_assign(sc, port, 0, members); + } + + /* Reset internal VLAN table. */ + for (i = 0; i < nitems(sc->vlans); i++) + sc->vlans[i] = 0; + + /* Create default VLAN (1). */ + if (sc->vlan_mode == ETHERSWITCH_VLAN_DOT1Q) { + sc->vlans[0] = 1; + e6000sw_vtu_update(sc, 0, sc->vlans[0], 1, 0, sc->ports_mask); + } + + /* Enable all ports */ + for (port = 0; port < sc->num_ports; port++) { + if (!e6000sw_is_portenabled(sc, port)) + continue; + ret = e6000sw_readreg(sc, REG_PORT(port), PORT_CONTROL); + e6000sw_writereg(sc, REG_PORT(port), PORT_CONTROL, + (ret | PORT_CONTROL_ENABLE)); + } + + return (0); +} + +static int +e6000sw_set_vlan_mode(struct e6000sw_softc *sc, uint32_t mode) +{ + + E6000SW_LOCK_ASSERT(sc, SA_XLOCKED); + switch (mode) { + case ETHERSWITCH_VLAN_PORT: + sc->vlan_mode = ETHERSWITCH_VLAN_PORT; + etherswitch_info.es_nvlangroups = sc->num_ports; + return (e6000sw_init_vlan(sc)); + break; + case ETHERSWITCH_VLAN_DOT1Q: + sc->vlan_mode = ETHERSWITCH_VLAN_DOT1Q; + etherswitch_info.es_nvlangroups = E6000SW_NUM_VLANS; + return (e6000sw_init_vlan(sc)); + break; + default: + return (EINVAL); + } +} + /* * 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_port_vlan_assign(e6000sw_softc_t *sc, int port, uint32_t fid, - uint32_t 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 & PORT_VLAN_MAP_TABLE_MASK & ~(1 << port); - reg |= (fid << PORT_VLAN_MAP_FID) & PORT_VLAN_MAP_FID_MASK; - e6000sw_writereg(sc, REG_PORT(port), PORT_VLAN_MAP, reg); - reg = e6000sw_readreg(sc, REG_PORT(port), PORT_CONTROL_1); - reg &= ~PORT_CONTROL_1_FID_MASK; - reg |= (fid >> 4) & PORT_CONTROL_1_FID_MASK; - e6000sw_writereg(sc, REG_PORT(port), PORT_CONTROL_1, reg); -} - static int e6000sw_set_port_vlan(e6000sw_softc_t *sc, etherswitch_vlangroup_t *vg) { uint32_t port; port = vg->es_vlangroup; if (port > sc->num_ports) return (EINVAL); if (vg->es_member_ports != vg->es_untagged_ports) { device_printf(sc->dev, "Tagged ports not supported.\n"); return (EINVAL); } - e6000sw_port_vlan_assign(sc, port, port + 1, vg->es_untagged_ports); + e6000sw_port_vlan_assign(sc, port, 0, vg->es_untagged_ports); vg->es_vid = port | ETHERSWITCH_VID_VALID; return (0); } static int +e6000sw_set_dot1q_vlan(e6000sw_softc_t *sc, etherswitch_vlangroup_t *vg) +{ + int i, vlan; + + vlan = vg->es_vid & ETHERSWITCH_VID_MASK; + + /* Set VLAN to '0' removes it from table. */ + if (vlan == 0) { + e6000sw_vtu_update(sc, VTU_PURGE, + sc->vlans[vg->es_vlangroup], 0, 0, 0); + sc->vlans[vg->es_vlangroup] = 0; + return (0); + } + + /* Is this VLAN already in table ? */ + for (i = 0; i < etherswitch_info.es_nvlangroups; i++) + if (i != vg->es_vlangroup && vlan == sc->vlans[i]) + return (EINVAL); + + sc->vlans[vg->es_vlangroup] = vlan; + e6000sw_vtu_update(sc, 0, vlan, vg->es_vlangroup + 1, + vg->es_member_ports & sc->ports_mask, + vg->es_untagged_ports & sc->ports_mask); + + return (0); +} + +static int e6000sw_setvgroup(device_t dev, etherswitch_vlangroup_t *vg) { e6000sw_softc_t *sc; sc = device_get_softc(dev); E6000SW_LOCK_ASSERT(sc, SA_XLOCKED); if (sc->vlan_mode == ETHERSWITCH_VLAN_PORT) return (e6000sw_set_port_vlan(sc, vg)); + else if (sc->vlan_mode == ETHERSWITCH_VLAN_DOT1Q) + return (e6000sw_set_dot1q_vlan(sc, vg)); return (EINVAL); } static int e6000sw_get_port_vlan(e6000sw_softc_t *sc, etherswitch_vlangroup_t *vg) { uint32_t port, reg; port = vg->es_vlangroup; if (port > sc->num_ports) return (EINVAL); if (!e6000sw_is_portenabled(sc, port)) { vg->es_vid = port; return (0); } reg = e6000sw_readreg(sc, REG_PORT(port), PORT_VLAN_MAP); - vg->es_untagged_ports = vg->es_member_ports = - reg & PORT_VLAN_MAP_TABLE_MASK; + vg->es_untagged_ports = vg->es_member_ports = reg & PORT_MASK(sc); vg->es_vid = port | ETHERSWITCH_VID_VALID; vg->es_fid = (reg & PORT_VLAN_MAP_FID_MASK) >> PORT_VLAN_MAP_FID; - reg = e6000sw_readreg(sc, REG_PORT(port), PORT_CONTROL_1); - vg->es_fid |= (reg & PORT_CONTROL_1_FID_MASK) << 4; + reg = e6000sw_readreg(sc, REG_PORT(port), PORT_CONTROL1); + vg->es_fid |= (reg & PORT_CONTROL1_FID_MASK) << 4; return (0); } static int +e6000sw_get_dot1q_vlan(e6000sw_softc_t *sc, etherswitch_vlangroup_t *vg) +{ + int i, port; + uint32_t reg; + + vg->es_fid = 0; + vg->es_vid = sc->vlans[vg->es_vlangroup]; + vg->es_untagged_ports = vg->es_member_ports = 0; + if (vg->es_vid == 0) + return (0); + + if (E6000SW_WAITREADY(sc, VTU_OPERATION, VTU_BUSY)) { + device_printf(sc->dev, "VTU unit is busy, cannot access\n"); + return (EBUSY); + } + + e6000sw_writereg(sc, REG_GLOBAL, VTU_VID, vg->es_vid - 1); + + reg = e6000sw_readreg(sc, REG_GLOBAL, VTU_OPERATION); + reg &= ~VTU_OP_MASK; + reg |= VTU_GET_NEXT | VTU_BUSY; + e6000sw_writereg(sc, REG_GLOBAL, VTU_OPERATION, reg); + if (E6000SW_WAITREADY(sc, VTU_OPERATION, VTU_BUSY)) { + device_printf(sc->dev, "Timeout while reading\n"); + return (EBUSY); + } + + reg = e6000sw_readreg(sc, REG_GLOBAL, VTU_VID); + if (reg == VTU_VID_MASK || (reg & VTU_VID_VALID) == 0) + return (EINVAL); + if ((reg & VTU_VID_MASK) != vg->es_vid) + return (EINVAL); + + vg->es_vid |= ETHERSWITCH_VID_VALID; + reg = e6000sw_readreg(sc, REG_GLOBAL, VTU_DATA); + for (i = 0; i < sc->num_ports; i++) { + if (i == VTU_PPREG(sc)) + reg = e6000sw_readreg(sc, REG_GLOBAL, VTU_DATA2); + port = (reg >> VTU_PORT(sc, i)) & VTU_PORT_MASK; + if (port == VTU_PORT_UNTAGGED) { + vg->es_untagged_ports |= (1 << i); + vg->es_member_ports |= (1 << i); + } else if (port == VTU_PORT_TAGGED) + vg->es_member_ports |= (1 << i); + } + + 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 (sc->vlan_mode == ETHERSWITCH_VLAN_PORT) return (e6000sw_get_port_vlan(sc, vg)); + else if (sc->vlan_mode == ETHERSWITCH_VLAN_DOT1Q) + return (e6000sw_get_dot1q_vlan(sc, vg)); return (EINVAL); } static __inline struct mii_data* e6000sw_miiforphy(e6000sw_softc_t *sc, unsigned int phy) { if (!e6000sw_is_phyport(sc, phy)) 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 int e6000sw_smi_waitready(e6000sw_softc_t *sc, int phy) { int i; for (i = 0; i < E6000SW_SMI_TIMEOUT; i++) { if ((MDIO_READ(sc->dev, phy, SMI_CMD) & SMI_CMD_BUSY) == 0) return (0); DELAY(1); } return (1); } static __inline uint32_t e6000sw_readreg(e6000sw_softc_t *sc, int addr, int reg) { E6000SW_LOCK_ASSERT(sc, SA_XLOCKED); - if (!sc->multi_chip) + if (!MVSWITCH_MULTICHIP(sc)) return (MDIO_READ(sc->dev, addr, reg) & 0xffff); if (e6000sw_smi_waitready(sc, sc->sw_addr)) { printf("e6000sw: readreg timeout\n"); return (0xffff); } MDIO_WRITE(sc->dev, sc->sw_addr, SMI_CMD, - SMI_CMD_OP_READ | (addr << 5) | reg); + SMI_CMD_OP_C22_READ | (reg & SMI_CMD_REG_ADDR_MASK) | + ((addr << SMI_CMD_DEV_ADDR) & SMI_CMD_DEV_ADDR_MASK)); if (e6000sw_smi_waitready(sc, sc->sw_addr)) { printf("e6000sw: readreg timeout\n"); return (0xffff); } return (MDIO_READ(sc->dev, sc->sw_addr, SMI_DATA) & 0xffff); } static __inline void e6000sw_writereg(e6000sw_softc_t *sc, int addr, int reg, int val) { E6000SW_LOCK_ASSERT(sc, SA_XLOCKED); - if (!sc->multi_chip) { + if (!MVSWITCH_MULTICHIP(sc)) { MDIO_WRITE(sc->dev, addr, reg, val); return; } if (e6000sw_smi_waitready(sc, sc->sw_addr)) { printf("e6000sw: readreg timeout\n"); return; } MDIO_WRITE(sc->dev, sc->sw_addr, SMI_DATA, val); MDIO_WRITE(sc->dev, sc->sw_addr, SMI_CMD, - SMI_CMD_OP_WRITE | (addr << 5) | reg); - if (e6000sw_smi_waitready(sc, sc->sw_addr)) { - printf("e6000sw: readreg timeout\n"); - return; - } + SMI_CMD_OP_C22_WRITE | (reg & SMI_CMD_REG_ADDR_MASK) | + ((addr << SMI_CMD_DEV_ADDR) & SMI_CMD_DEV_ADDR_MASK)); } static __inline bool e6000sw_is_cpuport(e6000sw_softc_t *sc, int port) { return ((sc->cpuports_mask & (1 << port)) ? true : false); } static __inline bool e6000sw_is_fixedport(e6000sw_softc_t *sc, int port) { return ((sc->fixed_mask & (1 << port)) ? true : false); } static __inline bool e6000sw_is_fixed25port(e6000sw_softc_t *sc, int port) { return ((sc->fixed25_mask & (1 << port)) ? true : false); } static __inline bool e6000sw_is_phyport(e6000sw_softc_t *sc, int port) { uint32_t phy_mask; phy_mask = ~(sc->fixed_mask | sc->cpuports_mask); return ((phy_mask & (1 << port)) ? true : false); } static __inline bool e6000sw_is_portenabled(e6000sw_softc_t *sc, int port) { return ((sc->ports_mask & (1 << port)) ? true : false); } -static __inline int +static __inline void e6000sw_set_pvid(e6000sw_softc_t *sc, int port, int pvid) { + uint32_t reg; - e6000sw_writereg(sc, REG_PORT(port), PORT_VID, pvid & - PORT_VID_DEF_VID_MASK); - - return (0); + reg = e6000sw_readreg(sc, REG_PORT(port), PORT_VID); + reg &= ~PORT_VID_DEF_VID_MASK; + reg |= (pvid & PORT_VID_DEF_VID_MASK); + e6000sw_writereg(sc, REG_PORT(port), PORT_VID, reg); } 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); } /* * Convert port status to ifmedia. */ static void e6000sw_update_ifmedia(uint16_t portstatus, u_int *media_status, u_int *media_active) { *media_active = IFM_ETHER; *media_status = IFM_AVALID; if ((portstatus & PORT_STATUS_LINK_MASK) != 0) *media_status |= IFM_ACTIVE; else { *media_active |= IFM_NONE; return; } switch (portstatus & PORT_STATUS_SPEED_MASK) { case PORT_STATUS_SPEED_10: *media_active |= IFM_10_T; break; case PORT_STATUS_SPEED_100: *media_active |= IFM_100_TX; break; case PORT_STATUS_SPEED_1000: *media_active |= IFM_1000_T; break; } if ((portstatus & PORT_STATUS_DUPLEX_MASK) == 0) *media_active |= IFM_FDX; else *media_active |= IFM_HDX; } static void -e6000sw_tick (void *arg) +e6000sw_tick(void *arg) { e6000sw_softc_t *sc; struct mii_data *mii; struct mii_softc *miisc; uint16_t portstatus; int port; sc = arg; E6000SW_LOCK_ASSERT(sc, SA_UNLOCKED); for (;;) { E6000SW_LOCK(sc); for (port = 0; port < sc->num_ports; port++) { /* Tick only on PHY ports */ if (!e6000sw_is_portenabled(sc, port) || !e6000sw_is_phyport(sc, port)) continue; mii = e6000sw_miiforphy(sc, port); if (mii == NULL) continue; portstatus = e6000sw_readreg(sc, REG_PORT(port), PORT_STATUS); e6000sw_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); } } E6000SW_UNLOCK(sc); pause("e6000sw tick", 1000); } } static void e6000sw_setup(device_t dev, e6000sw_softc_t *sc) { - uint16_t atu_ctrl, atu_age; + uint32_t atu_ctrl; - /* Set aging time */ - e6000sw_writereg(sc, REG_GLOBAL, ATU_CONTROL, - (E6000SW_DEFAULT_AGETIME << ATU_CONTROL_AGETIME) | - (1 << ATU_CONTROL_LEARN2ALL)); + /* Set aging time. */ + atu_ctrl = e6000sw_readreg(sc, REG_GLOBAL, ATU_CONTROL); + atu_ctrl &= ~ATU_CONTROL_AGETIME_MASK; + atu_ctrl |= E6000SW_DEFAULT_AGETIME << ATU_CONTROL_AGETIME; + e6000sw_writereg(sc, REG_GLOBAL, ATU_CONTROL, atu_ctrl); /* 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 Management */ 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)); 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 i, port, ret; - uint32_t members; - - /* Disable all ports */ - for (port = 0; port < sc->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 < sc->num_ports; port++) { - if (!e6000sw_is_portenabled(sc, port)) - continue; - ret = e6000sw_readreg(sc, REG_PORT(port), PORT_VID); - ret &= ~PORT_VID_PRIORITY_MASK; - e6000sw_writereg(sc, REG_PORT(port), PORT_VID, ret); - } - - /* Set VID map */ - for (port = 0; port < sc->num_ports; port++) { - if (!e6000sw_is_portenabled(sc, port)) - continue; - 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 < sc->num_ports; port++) { - if (!e6000sw_is_portenabled(sc, port)) - continue; - ret = e6000sw_readreg(sc, REG_PORT(port), PORT_CONTROL); - e6000sw_writereg(sc, REG_PORT(port), PORT_CONTROL, - (ret | PORT_CONTROL_ENABLE)); - } - - /* Set VLAN mode. */ - sc->vlan_mode = ETHERSWITCH_VLAN_PORT; - etherswitch_info.es_nvlangroups = sc->num_ports; - for (port = 0; port < sc->num_ports; port++) { - members = 0; - if (e6000sw_is_portenabled(sc, port)) { - for (i = 0; i < sc->num_ports; i++) { - if (i == port || !e6000sw_is_portenabled(sc, i)) - continue; - members |= (1 << i); - } - } - e6000sw_port_vlan_assign(sc, port, port + 1, members); - } -} - -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"); + if (E6000SW_WAITREADY(sc, ATU_OPERATION, ATU_UNIT_BUSY)) { + device_printf(dev, "ATU unit is busy, cannot access\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)); + ret_opt = e6000sw_readreg(sc, REG_GLOBAL, ATU_OPERATION); + 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); - retries = E6000SW_RETRIES; - while (--retries & (e6000sw_readreg(sc, REG_GLOBAL, - ATU_OPERATION) & ATU_UNIT_BUSY)) - DELAY(1); + e6000sw_writereg(sc, REG_GLOBAL, ATU_OPERATION, + (ret_opt | ATU_UNIT_BUSY | flag)); - 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); - } + if (E6000SW_WAITREADY(sc, ATU_OPERATION, ATU_UNIT_BUSY)) + device_printf(dev, "Timeout while waiting ATU\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; + uint32_t reg; 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"); + if (E6000SW_WAITREADY(sc, ATU_OPERATION, ATU_UNIT_BUSY)) { + device_printf(dev, "ATU unit is busy, cannot access\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); + } + reg = e6000sw_readreg(sc, REG_GLOBAL, ATU_OPERATION); + e6000sw_writereg(sc, REG_GLOBAL, ATU_OPERATION, + (reg | ATU_UNIT_BUSY | flag)); + if (E6000SW_WAITREADY(sc, ATU_OPERATION, ATU_UNIT_BUSY)) + device_printf(dev, "Timeout while flushing ATU\n"); - if (retries == 0) - device_printf(dev, "Timeout while flushing\n"); + return (0); +} + +static int +e6000sw_vtu_flush(e6000sw_softc_t *sc) +{ + + if (E6000SW_WAITREADY(sc, VTU_OPERATION, VTU_BUSY)) { + device_printf(sc->dev, "VTU unit is busy, cannot access\n"); + return (EBUSY); + } + + e6000sw_writereg(sc, REG_GLOBAL, VTU_OPERATION, VTU_FLUSH | VTU_BUSY); + if (E6000SW_WAITREADY(sc, VTU_OPERATION, VTU_BUSY)) { + device_printf(sc->dev, "Timeout while flushing VTU\n"); + return (ETIMEDOUT); + } + + return (0); +} + +static int +e6000sw_vtu_update(e6000sw_softc_t *sc, int purge, int vid, int fid, + int members, int untagged) +{ + int i, op; + uint32_t data[2]; + + if (E6000SW_WAITREADY(sc, VTU_OPERATION, VTU_BUSY)) { + device_printf(sc->dev, "VTU unit is busy, cannot access\n"); + return (EBUSY); + } + + *data = (vid & VTU_VID_MASK); + if (purge == 0) + *data |= VTU_VID_VALID; + e6000sw_writereg(sc, REG_GLOBAL, VTU_VID, *data); + + if (purge == 0) { + data[0] = 0; + data[1] = 0; + for (i = 0; i < sc->num_ports; i++) { + if ((untagged & (1 << i)) != 0) + data[i / VTU_PPREG(sc)] |= + VTU_PORT_UNTAGGED << VTU_PORT(sc, i); + else if ((members & (1 << i)) != 0) + data[i / VTU_PPREG(sc)] |= + VTU_PORT_TAGGED << VTU_PORT(sc, i); + else + data[i / VTU_PPREG(sc)] |= + VTU_PORT_DISCARD << VTU_PORT(sc, i); + } + e6000sw_writereg(sc, REG_GLOBAL, VTU_DATA, data[0]); + e6000sw_writereg(sc, REG_GLOBAL, VTU_DATA2, data[1]); + e6000sw_writereg(sc, REG_GLOBAL, VTU_FID, + fid & VTU_FID_MASK(sc)); + op = VTU_LOAD; + } else + op = VTU_PURGE; + + e6000sw_writereg(sc, REG_GLOBAL, VTU_OPERATION, op | VTU_BUSY); + if (E6000SW_WAITREADY(sc, VTU_OPERATION, VTU_BUSY)) { + device_printf(sc->dev, "Timeout while flushing VTU\n"); + return (ETIMEDOUT); } return (0); } Index: head/sys/dev/etherswitch/e6000sw/e6000swreg.h =================================================================== --- head/sys/dev/etherswitch/e6000sw/e6000swreg.h (revision 349520) +++ head/sys/dev/etherswitch/e6000sw/e6000swreg.h (revision 349521) @@ -1,212 +1,304 @@ /*- * 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. * * $FreeBSD$ * */ #ifndef _E6000SWREG_H_ -#define _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. */ /* Switch IDs. */ #define MV88E6141 0x3400 #define MV88E6341 0x3410 #define MV88E6352 0x3520 #define MV88E6172 0x1720 #define MV88E6176 0x1760 #define MVSWITCH(_sc, id) ((_sc)->swid == (id)) +#define MVSWITCH_MULTICHIP(_sc) ((_sc)->sw_addr != 0) /* * Switch Registers */ -#define REG_GLOBAL 0x1b -#define REG_GLOBAL2 0x1c -#define REG_PORT(p) (0x10 + (p)) +#define REG_GLOBAL 0x1b +#define REG_GLOBAL2 0x1c +#define REG_PORT(p) (0x10 + (p)) -#define REG_NUM_MAX 31 +#define REG_NUM_MAX 31 /* * Per-Port Switch Registers */ -#define PORT_STATUS 0x0 +#define PORT_STATUS 0x0 #define PORT_STATUS_SPEED_MASK 0x300 #define PORT_STATUS_SPEED_10 0 #define PORT_STATUS_SPEED_100 1 #define PORT_STATUS_SPEED_1000 2 #define PORT_STATUS_DUPLEX_MASK (1 << 10) #define PORT_STATUS_LINK_MASK (1 << 11) #define PORT_STATUS_PHY_DETECT_MASK (1 << 12) -#define PSC_CONTROL 0x1 +#define PSC_CONTROL 0x1 #define PSC_CONTROL_FORCED_SPD (1 << 13) #define PSC_CONTROL_ALT_SPD (1 << 12) #define PSC_CONTROL_EEE_ON (1 << 9) #define PSC_CONTROL_FORCED_EEE (1 << 8) #define PSC_CONTROL_FC_ON (1 << 7) #define PSC_CONTROL_FORCED_FC (1 << 6) #define PSC_CONTROL_LINK_UP (1 << 5) #define PSC_CONTROL_FORCED_LINK (1 << 4) #define PSC_CONTROL_FULLDPX (1 << 3) #define PSC_CONTROL_FORCED_DPX (1 << 2) -#define PSC_CONTROL_SPD2500 0x3 +#define PSC_CONTROL_SPD10G 0x3 +#define PSC_CONTROL_SPD2500 PSC_CONTROL_SPD10G #define PSC_CONTROL_SPD1000 0x2 -#define SWITCH_ID 0x3 -#define PORT_CONTROL 0x4 -#define PORT_CONTROL_1 0x5 -#define PORT_CONTROL_1_FID_MASK 0xf -#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 SWITCH_ID 0x3 +#define PORT_CONTROL 0x4 +#define PORT_CONTROL1 0x5 +#define PORT_CONTROL1_LAG_PORT (1 << 14) +#define PORT_CONTROL1_LAG_ID_MASK 0xf +#define PORT_CONTROL1_LAG_ID_SHIFT 8 +#define PORT_CONTROL1_FID_MASK 0xf +#define PORT_VLAN_MAP 0x6 +#define PORT_VID 0x7 +#define PORT_CONTROL2 0x8 +#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_MASK(_sc) 0x7f +#define PORT_VID_DEF_VID 0 +#define PORT_VID_DEF_VID_MASK 0xfff +#define PORT_VID_PRIORITY_MASK 0xc00 -#define PORT_CONTROL_ENABLE 0x3 +#define PORT_CONTROL_DISABLED 0 +#define PORT_CONTROL_BLOCKING 1 +#define PORT_CONTROL_LEARNING 2 +#define PORT_CONTROL_FORWARDING 3 +#define PORT_CONTROL_ENABLE 3 +#define PORT_CONTROL_FRAME 0x0300 +#define PORT_CONTROL_EGRESS 0x3000 +#define PORT_CONTROL2_DOT1Q 0x0c00 +#define PORT_CONTROL2_DISC_TAGGED (1 << 9) +#define PORT_CONTROL2_DISC_UNTAGGED (1 << 8) /* PORT_VLAN fields */ -#define PORT_VLAN_MAP_TABLE_MASK 0x7f -#define PORT_VLAN_MAP_FID 12 -#define PORT_VLAN_MAP_FID_MASK 0xf000 +#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 SWITCH_GLOBAL_STATUS 0 +#define SWITCH_GLOBAL_STATUS_IR (1 << 11) +#define SWITCH_GLOBAL_CONTROL 4 +#define SWITCH_GLOBAL_CONTROL2 28 -#define MONITOR_CONTROL 26 +#define MONITOR_CONTROL 26 +/* VTU operation */ +#define VTU_FID 2 +#define VTU_OPERATION 5 +#define VTU_VID 6 +#define VTU_DATA 7 +#define VTU_DATA2 8 + +#define VTU_FID_MASK(_sc) 0xff +#define VTU_FID_POLICY (1 << 12) +#define VTU_PORT_UNMODIFIED 0 +#define VTU_PORT_UNTAGGED 1 +#define VTU_PORT_TAGGED 2 +#define VTU_PORT_DISCARD 3 +#define VTU_PPREG(_sc) 4 +#define VTU_PORT(_sc, p) (((p) % VTU_PPREG(_sc)) * (16 / VTU_PPREG(_sc))) +#define VTU_PORT_MASK 3 +#define VTU_BUSY (1 << 15) +#define VTU_VID_VALID (1 << 12) +#define VTU_VID_MASK 0xfff + +/* VTU opcodes */ +#define VTU_OP_MASK (7 << 12) +#define VTU_NOP (0 << 12) +#define VTU_FLUSH (1 << 12) +#define VTU_LOAD (3 << 12) +#define VTU_PURGE (3 << 12) +#define VTU_GET_NEXT (4 << 12) +#define STU_LOAD (5 << 12) +#define STU_PURGE (5 << 12) +#define STU_GET_NEXT (6 << 12) +#define VTU_GET_VIOLATION_DATA (7 << 12) +#define VTU_CLEAR_VIOLATION_DATA (7 << 12) + /* 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_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 +#define ATU_DATA_LAG (1 << 15) +#define ATU_PORT_MASK(_sc) 0xff0 +#define ATU_PORT_SHIFT 4 +#define ATU_LAG_MASK 0xf0 +#define ATU_LAG_SHIFT 4 +#define ATU_STATE_MASK 0xf +#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 +#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) +#define ATU_OP_MASK (7 << 12) +#define NO_OPERATION (0 << 12) +#define FLUSH_ALL (1 << 12) +#define FLUSH_NON_STATIC (2 << 12) +#define LOAD_FROM_FIB (3 << 12) +#define PURGE_FROM_FIB (3 << 12) +#define GET_NEXT_IN_FIB (4 << 12) +#define FLUSH_ALL_IN_FIB (5 << 12) +#define FLUSH_NON_STATIC_IN_FIB (6 << 12) +#define GET_VIOLATION_DATA (7 << 12) +#define CLEAR_VIOLATION_DATA (7 << 12) /* ATU Stats */ -#define COUNT_ALL (0 << 0) +#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_2x 2 +#define MGMT_EN_0x 3 +#define SWITCH_MGMT 5 +#define LAG_MASK 7 +#define LAG_MAPPING 8 +#define ATU_STATS 14 -#define MGMT_EN_ALL 0xffff +#define MGMT_EN_ALL 0xffff +#define LAG_UPDATE (1 << 15) +#define LAG_MASKNUM_SHIFT 12 +#define LAGID_SHIFT 11 /* SWITCH_MGMT fields */ -#define SWITCH_MGMT_PRI 0 -#define SWITCH_MGMT_PRI_MASK 7 +#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 +#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 +#define ATU_STATS_BIN 14 +#define ATU_STATS_FLAG 12 +/* Offset of SMI registers in multi-chip setup. */ +#define SMI_CMD 0 +#define SMI_DATA 1 + /* - * PHY registers accessed via 'Switch Global Registers' (REG_GLOBAL2). + * 'Switch Global Registers 2' (REG_GLOBAL2). */ -#define SMI_PHY_CMD_REG 0x18 -#define SMI_PHY_DATA_REG 0x19 -#define PHY_DATA_MASK 0xffff +/* EEPROM registers */ +#define EEPROM_CMD 0x14 +#define EEPROM_BUSY (1 << 15) +#define EEPROM_READ_CMD (4 << 12) +#define EEPROM_WRITE_CMD (3 << 12) +#define EEPROM_WRITE_EN (1 << 10) +#define EEPROM_DATA_MASK 0xff +#define EEPROM_ADDR 0x15 -#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 +/* PHY registers */ +#define SMI_PHY_CMD_REG 0x18 +#define SMI_CMD_BUSY (1 << 15) +#define SMI_CMD_MODE_C22 (1 << 12) +#define SMI_CMD_C22_WRITE (1 << 10) +#define SMI_CMD_C22_READ (2 << 10) +#define SMI_CMD_OP_C22_WRITE \ + (SMI_CMD_C22_WRITE | SMI_CMD_BUSY | SMI_CMD_MODE_C22) +#define SMI_CMD_OP_C22_READ \ + (SMI_CMD_C22_READ | SMI_CMD_BUSY | SMI_CMD_MODE_C22) +#define SMI_CMD_C45 (0 << 12) +#define SMI_CMD_C45_ADDR (0 << 10) +#define SMI_CMD_C45_WRITE (1 << 10) +#define SMI_CMD_C45_READ (3 << 10) +#define SMI_CMD_OP_C45_ADDR \ + (SMI_CMD_C45_ADDR | SMI_CMD_BUSY | SMI_CMD_C45) +#define SMI_CMD_OP_C45_WRITE \ + (SMI_CMD_C45_WRITE | SMI_CMD_BUSY | SMI_CMD_C45) +#define SMI_CMD_OP_C45_READ \ + (SMI_CMD_C45_READ | SMI_CMD_BUSY | SMI_CMD_C45) +#define SMI_CMD_DEV_ADDR 5 +#define SMI_CMD_DEV_ADDR_MASK 0x3e0 +#define SMI_CMD_REG_ADDR_MASK 0x1f +#define SMI_PHY_DATA_REG 0x19 +#define PHY_DATA_MASK 0xffff -#define PHY_PAGE_REG 22 +#define PHY_PAGE_REG 22 /* * Scratch and Misc register accessed via * 'Switch Global Registers' (REG_GLOBAL2) */ -#define SCR_AND_MISC_REG 0x1a +#define SCR_AND_MISC_REG 0x1a -#define SCR_AND_MISC_PTR_CFG 0x7000 -#define SCR_AND_MISC_DATA_CFG_MASK 0xf0 +#define SCR_AND_MISC_PTR_CFG 0x7000 +#define SCR_AND_MISC_DATA_CFG_MASK 0xf0 -#define E6000SW_NUM_PHY_REGS 29 -#define E6000SW_MAX_PORTS 8 -#define E6000SW_DEFAULT_AGETIME 20 -#define E6000SW_RETRIES 100 -#define E6000SW_SMI_TIMEOUT 16 +/* SERDES registers. */ +#define E6000SW_SERDES_DEV 4 +#define E6000SW_SERDES_PCS_CTL1 0x1000 +#define E6000SW_SERDES_SGMII_CTL 0x2000 +#define E6000SW_SERDES_PDOWN (1 << 11) + +#define E6000SW_NUM_VLANS 128 +#define E6000SW_NUM_LAGMASK 8 +#define E6000SW_NUM_PHY_REGS 29 +#define E6000SW_MAX_PORTS 11 +#define E6000SW_DEFAULT_AGETIME 20 +#define E6000SW_RETRIES 100 +#define E6000SW_SMI_TIMEOUT 16 #endif /* _E6000SWREG_H_ */