From c6f5eeeab1ecff7eedd9e04fd9a6f4516648a51c Mon Sep 17 00:00:00 2001 From: Luiz Souza Date: Fri, 16 Mar 2018 18:43:19 -0300 Subject: Add the LAGG support for the Marvell E6000 series of switches. (cherry picked from commit db0244864e9cbca5dec13b19fe8ba5ce083df24f) --- sys/dev/etherswitch/e6000sw/e6000sw.c | 201 ++++++++++++++++++++++++++++++- sys/dev/etherswitch/e6000sw/e6000swreg.h | 12 +- 2 files changed, 210 insertions(+), 3 deletions(-) diff --git a/sys/dev/etherswitch/e6000sw/e6000sw.c b/sys/dev/etherswitch/e6000sw/e6000sw.c index 4694667..4020a3c 100644 --- a/sys/dev/etherswitch/e6000sw/e6000sw.c +++ b/sys/dev/etherswitch/e6000sw/e6000sw.c @@ -100,6 +100,7 @@ typedef struct e6000sw_softc { int phy_base; /* SMI base addr of PHY regs */ int port_base; /* SMI base addr of port regs */ int sw_addr; + int num_laggs; int num_ports; ssize_t iosize; @@ -108,10 +109,11 @@ typedef struct e6000sw_softc { static etherswitch_info_t etherswitch_info = { .es_nports = 0, + .es_nlaggroups = 0, .es_nvlangroups = 0, .es_vlan_caps = ETHERSWITCH_VLAN_PORT | ETHERSWITCH_VLAN_DOT1Q, .es_switch_caps = ETHERSWITCH_CAPS_PORTS_MASK | - ETHERSWITCH_CAPS_PSTATE, + ETHERSWITCH_CAPS_PSTATE | ETHERSWITCH_CAPS_LAGG, .es_name = "Marvell 6000 series switch" }; @@ -139,6 +141,11 @@ 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 int e6000sw_resetlagg(e6000sw_softc_t *); +static int e6000sw_getlaggroup_wrapper(device_t, etherswitch_laggroup_t *); +static int e6000sw_setlaggroup_wrapper(device_t, etherswitch_laggroup_t *); +static int e6000sw_setlaggroup(device_t, etherswitch_laggroup_t *); +static int e6000sw_getlaggroup(device_t, etherswitch_laggroup_t *); static ssize_t e6000sw_getiosize(device_t); static ssize_t e6000sw_getioblksize(device_t); static void *e6000sw_getiobuf(device_t); @@ -195,6 +202,8 @@ static device_method_t e6000sw_methods[] = { DEVMETHOD(etherswitch_writephyreg, e6000sw_writephy_wrapper), DEVMETHOD(etherswitch_setvgroup, e6000sw_setvgroup_wrapper), DEVMETHOD(etherswitch_getvgroup, e6000sw_getvgroup_wrapper), + DEVMETHOD(etherswitch_setlaggroup, e6000sw_setlaggroup_wrapper), + DEVMETHOD(etherswitch_getlaggroup, e6000sw_getlaggroup_wrapper), DEVMETHOD(etherswitch_getioblksize, e6000sw_getioblksize), DEVMETHOD(etherswitch_getiosize, e6000sw_getiosize), DEVMETHOD(etherswitch_getiobuf, e6000sw_getiobuf), @@ -421,6 +430,7 @@ e6000sw_probe(device_t dev) E6000SW_UNLOCK(sc); sx_destroy(&sc->sx); + sc->num_laggs = 16; switch (sc->swid) { case MV88E6141: description = "Marvell 88E6141"; @@ -446,6 +456,7 @@ e6000sw_probe(device_t dev) break; case MV88E6190: description = "Marvell 88E6190"; + //sc->num_laggs = 32; /* Only 16 LAGGs for now. */ sc->num_ports = 11; break; default: @@ -741,8 +752,12 @@ e6000sw_attach(device_t dev) } etherswitch_info.es_nports = sc->num_ports; + etherswitch_info.es_nlaggroups = sc->num_laggs; etherswitch_info.es_ports_mask[0] = sc->used_mask; + /* Reset LAGG settings. */ + e6000sw_resetlagg(sc); + /* Default to port vlan. */ e6000sw_set_vlan_mode(sc, ETHERSWITCH_VLAN_PORT); @@ -1367,6 +1382,164 @@ e6000sw_getvgroup_wrapper(device_t dev, etherswitch_vlangroup_t *vg) } static int +e6000sw_setlaggroup_wrapper(device_t dev, etherswitch_laggroup_t *lag) +{ + e6000sw_softc_t *sc; + int ret; + + sc = device_get_softc(dev); + E6000SW_LOCK_ASSERT(sc, SA_UNLOCKED); + + E6000SW_LOCK(sc); + ret = e6000sw_setlaggroup(dev, lag); + E6000SW_UNLOCK(sc); + + return (ret); +} + +static int +e6000sw_getlaggroup_wrapper(device_t dev, etherswitch_laggroup_t *lag) +{ + e6000sw_softc_t *sc; + int ret; + + sc = device_get_softc(dev); + E6000SW_LOCK_ASSERT(sc, SA_UNLOCKED); + + E6000SW_LOCK(sc); + ret = e6000sw_getlaggroup(dev, lag); + E6000SW_UNLOCK(sc); + + return (ret); +} + +static int +e6000sw_resetlagg(e6000sw_softc_t *sc) +{ + int i; + + for (i = 0; i < sc->num_laggs; i++) + e6000sw_writereg(sc, REG_GLOBAL2, LAG_MAPPING, + i << LAGID_SHIFT | LAG_UPDATE); + for (i = 0; i < E6000SW_NUM_LAGMASK; i++) + e6000sw_writereg(sc, REG_GLOBAL2, LAG_MASK, + i << LAG_MASKNUM_SHIFT | LAG_UPDATE | sc->ports_mask); + + return (0); +} + +static int +e6000sw_setlaggmask(e6000sw_softc_t *sc) +{ + int count, cycle, i, m, mask, port; + struct lagg_map { + int cycle; + int lag; + int pcount; + uint32_t ports; + } *map; + uint32_t reg; + + map = malloc(sizeof(*map) * sc->num_laggs, M_E6000SW, M_WAITOK); + for (i = 0; i < sc->num_laggs; i++) { + map[i].lag = 0; + map[i].cycle = 0; + map[i].ports = 0; + map[i].pcount = 0; + } + count = 0; + for (i = 0; i < sc->num_laggs; i++) { + + /* Read the LAGG ports. */ + e6000sw_writereg(sc, REG_GLOBAL2, LAG_MAPPING, + i << LAGID_SHIFT); + reg = e6000sw_readreg(sc, REG_GLOBAL2, LAG_MAPPING); + if ((reg & sc->ports_mask) == 0) + continue; + map[count].lag = i; + map[count].ports = reg & sc->ports_mask; + for (port = 0; port < sc->num_ports; port++) { + if ((map[count].ports & (1 << port)) == 0) + continue; + map[count].pcount++; + } + ++count; + } + + for (mask = 0; mask < E6000SW_NUM_LAGMASK; mask++) { + e6000sw_writereg(sc, REG_GLOBAL2, LAG_MASK, + mask << LAG_MASKNUM_SHIFT); + reg = e6000sw_readreg(sc, REG_GLOBAL2, LAG_MASK); + reg |= sc->ports_mask; + for (port = 0; port < sc->num_ports; port++) { + + for (m = 0; m < count; m++) { + cycle = mask % map[m].pcount; + if ((map[m].ports & (1 << port)) == 0) + continue; + if (map[m].cycle != cycle) + reg &= ~(1 << port); + map[m].cycle = ++map[m].cycle % map[m].pcount; + } + } + e6000sw_writereg(sc, REG_GLOBAL2, LAG_MASK, reg | LAG_UPDATE); + } + + free(map, M_E6000SW); + + return (0); +} + +static int +e6000sw_setlaggroup(device_t dev, etherswitch_laggroup_t *lag) +{ + e6000sw_softc_t *sc; + int i, laggid; + uint32_t laggports, reg; + + sc = device_get_softc(dev); + + E6000SW_LOCK_ASSERT(sc, SA_XLOCKED); + + laggports = 0; + for (i = 0; i < sc->num_ports; i++) { + if (!e6000sw_is_portenabled(sc, i)) + continue; + reg = e6000sw_readreg(sc, REG_PORT(sc, i), PORT_CONTROL1); + laggid = reg >> PORT_CONTROL1_LAG_ID_SHIFT; + laggid &= PORT_CONTROL1_LAG_ID_MASK; + if ((lag->es_untagged_ports & (1 << i)) == 0) { + if ((reg & PORT_CONTROL1_LAG_PORT) != 0 && + laggid == lag->es_laggroup) { + /* Disable LAG on port. */ + reg &= ~PORT_CONTROL1_LAG_PORT; + reg &= ~(PORT_CONTROL1_LAG_ID_MASK << + PORT_CONTROL1_LAG_ID_SHIFT); + e6000sw_writereg(sc, REG_PORT(sc, i), + PORT_CONTROL1, reg); + } + continue; + } + reg |= PORT_CONTROL1_LAG_PORT; + laggid = lag->es_laggroup & PORT_CONTROL1_LAG_ID_MASK; + reg |= laggid << PORT_CONTROL1_LAG_ID_SHIFT; + e6000sw_writereg(sc, REG_PORT(sc, i), PORT_CONTROL1, reg); + + laggports |= (1 << i); + } + + /* Update LAG mapping. */ + reg = (lag->es_laggroup & PORT_CONTROL1_LAG_ID_MASK) << LAGID_SHIFT; + e6000sw_writereg(sc, REG_GLOBAL2, LAG_MAPPING, reg); + reg = e6000sw_readreg(sc, REG_GLOBAL2, LAG_MAPPING); + reg &= ~sc->ports_mask; + reg |= laggports | LAG_UPDATE; + e6000sw_writereg(sc, REG_GLOBAL2, LAG_MAPPING, reg); + + return (e6000sw_setlaggmask(sc)); +} + +static int e6000sw_set_port_vlan(e6000sw_softc_t *sc, etherswitch_vlangroup_t *vg) { uint32_t port; @@ -1431,6 +1604,32 @@ e6000sw_setvgroup(device_t dev, etherswitch_vlangroup_t *vg) } static int +e6000sw_getlaggroup(device_t dev, etherswitch_laggroup_t *lag) +{ + e6000sw_softc_t *sc; + int laggid; + uint32_t reg; + + sc = device_get_softc(dev); + E6000SW_LOCK_ASSERT(sc, SA_XLOCKED); + + lag->es_lag_valid = 0; + lag->es_member_ports = lag->es_untagged_ports = 0; + /* Read the LAGG ports. */ + laggid = lag->es_laggroup & PORT_CONTROL1_LAG_ID_MASK; + e6000sw_writereg(sc, REG_GLOBAL2, LAG_MAPPING, laggid << LAGID_SHIFT); + reg = e6000sw_readreg(sc, REG_GLOBAL2, LAG_MAPPING); + lag->es_member_ports = reg & sc->ports_mask; + lag->es_untagged_ports = reg & sc->ports_mask; + + /* Is this LAG group in use ? */ + if (lag->es_untagged_ports != 0) + lag->es_lag_valid = 1; + + return (0); +} + +static int e6000sw_get_port_vlan(e6000sw_softc_t *sc, etherswitch_vlangroup_t *vg) { uint32_t port, reg; diff --git a/sys/dev/etherswitch/e6000sw/e6000swreg.h b/sys/dev/etherswitch/e6000sw/e6000swreg.h index 4f25547..98f8827 100644 --- a/sys/dev/etherswitch/e6000sw/e6000swreg.h +++ b/sys/dev/etherswitch/e6000sw/e6000swreg.h @@ -90,7 +90,10 @@ struct atu_opt { #define SWITCH_ID 0x3 #define PORT_CONTROL 0x4 #define PORT_CONTROL1 0x5 -#define PORT_CONTROL1_FID_MASK 0xf +#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 @@ -206,9 +209,14 @@ struct atu_opt { #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 LAG_UPDATE (1 << 15) +#define LAG_MASKNUM_SHIFT 12 +#define LAGID_SHIFT 11 /* SWITCH_MGMT fields */ @@ -285,7 +293,7 @@ struct atu_opt { #define E6000SW_SERDES_PDOWN (1 << 11) #define E6000SW_NUM_VLANS 128 -#define E6000SW_NUM_LAGS 16 +#define E6000SW_NUM_LAGMASK 8 #define E6000SW_NUM_PHY_REGS 29 #define E6000SW_MAX_PORTS 11 #define E6000SW_DEFAULT_AGETIME 20 -- cgit v1.1