diff options
author | Luiz Souza <luiz@netgate.com> | 2017-06-22 10:51:07 -0500 |
---|---|---|
committer | Luiz Souza <luiz@netgate.com> | 2017-06-22 10:51:07 -0500 |
commit | b9f3af6b8a65f84f59910eac8f0f25655843059d (patch) | |
tree | cb0bb84868b16d0430f10e6fbe7082331fe03eb4 | |
parent | c3d78dab701406f5bc46cfc1267df5f24ed4ad7a (diff) | |
download | FreeBSD-src-b9f3af6b8a65f84f59910eac8f0f25655843059d.zip FreeBSD-src-b9f3af6b8a65f84f59910eac8f0f25655843059d.tar.gz |
Fix the port based vlans for the Marvell e6000 series switches.
-rw-r--r-- | sys/dev/etherswitch/e6000sw/e6000sw.c | 173 | ||||
-rw-r--r-- | sys/dev/etherswitch/e6000sw/e6000swreg.h | 6 |
2 files changed, 87 insertions, 92 deletions
diff --git a/sys/dev/etherswitch/e6000sw/e6000sw.c b/sys/dev/etherswitch/e6000sw/e6000sw.c index cc79ba2..15701a7 100644 --- a/sys/dev/etherswitch/e6000sw/e6000sw.c +++ b/sys/dev/etherswitch/e6000sw/e6000sw.c @@ -72,6 +72,7 @@ typedef struct e6000sw_softc { struct proc *kproc; uint32_t swid; + uint32_t vlan_mode; uint32_t cpuports_mask; uint32_t fixed_mask; uint32_t fixed25_mask; @@ -80,16 +81,13 @@ typedef struct e6000sw_softc { int sw_addr; int num_ports; boolean_t multi_chip; - - int vid[E6000SW_NUM_VGROUPS]; - int members[E6000SW_NUM_VGROUPS]; - int vgroup[E6000SW_MAX_PORTS]; } e6000sw_softc_t; static etherswitch_info_t etherswitch_info = { .es_nports = 0, - .es_nvlangroups = E6000SW_NUM_VGROUPS, + .es_nvlangroups = 0, .es_vlan_caps = ETHERSWITCH_VLAN_PORT, + .es_switch_caps = ETHERSWITCH_CAPS_PORTS_MASK, .es_name = "Marvell 6000 series switch" }; @@ -263,15 +261,14 @@ e6000sw_probe(device_t dev) } static int -e6000sw_parse_child_fdt(e6000sw_softc_t *sc, phandle_t child, int *pport, - int *pvlangroup) +e6000sw_parse_child_fdt(e6000sw_softc_t *sc, phandle_t child, int *pport) { char *name, *portlabel; int speed; phandle_t fixed_link; - uint32_t port, vlangroup; + uint32_t port; - if (pport == NULL || pvlangroup == NULL) + if (pport == NULL) return (ENXIO); if (OF_getencprop(child, "reg", (void *)&port, sizeof(port)) < 0) @@ -280,15 +277,6 @@ e6000sw_parse_child_fdt(e6000sw_softc_t *sc, phandle_t child, int *pport, return (ENXIO); *pport = port; - if (OF_getencprop(child, "vlangroup", (void *)&vlangroup, - sizeof(vlangroup)) > 0) { - if (vlangroup >= E6000SW_NUM_VGROUPS) - return (ENXIO); - *pvlangroup = vlangroup; - } else { - *pvlangroup = -1; - } - if (OF_getprop_alloc(child, "label", 1, (void **)&portlabel) > 0) { if (strncmp(portlabel, "cpu", 3) == 0) { device_printf(sc->dev, "CPU port at %d\n", port); @@ -364,11 +352,9 @@ e6000sw_attach_miibus(e6000sw_softc_t *sc, int port) static int e6000sw_attach(device_t dev) { - etherswitch_vlangroup_t vg; e6000sw_softc_t *sc; phandle_t child; - int err, port, vlangroup; - int member_ports[E6000SW_NUM_VGROUPS]; + int err, port; uint32_t reg; err = 0; @@ -383,18 +369,14 @@ e6000sw_attach(device_t dev) E6000SW_LOCK(sc); e6000sw_setup(dev, sc); - bzero(member_ports, sizeof(member_ports)); for (child = OF_child(sc->node); child != 0; child = OF_peer(child)) { - err = e6000sw_parse_child_fdt(sc, child, &port, &vlangroup); + err = e6000sw_parse_child_fdt(sc, child, &port); if (err != 0) { device_printf(sc->dev, "failed to parse DTS\n"); goto out_fail; } - if (vlangroup != -1) - member_ports[vlangroup] |= (1 << port); - /* Port is in use. */ sc->ports_mask |= (1 << port); @@ -440,21 +422,10 @@ e6000sw_attach(device_t dev) } etherswitch_info.es_nports = sc->num_ports; - for (port = 0; port < sc->num_ports; port++) - sc->vgroup[port] = E6000SW_PORT_NO_VGROUP; + etherswitch_info.es_ports_mask[0] = sc->ports_mask; - /* Set VLAN configuration */ + /* Default to port vlan. */ e6000sw_port_vlan_conf(sc); - - /* Set vlangroups */ - for (vlangroup = 0; vlangroup < E6000SW_NUM_VGROUPS; vlangroup++) - if (member_ports[vlangroup] != 0) { - vg.es_vlangroup = vg.es_vid = vlangroup; - vg.es_member_ports = vg.es_untagged_ports = - member_ports[vlangroup]; - e6000sw_setvgroup(dev, &vg); - } - E6000SW_UNLOCK(sc); bus_generic_probe(dev); @@ -597,12 +568,14 @@ e6000sw_getinfo(device_t dev) } static int -e6000sw_getconf(device_t dev __unused, etherswitch_conf_t *conf) +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 = ETHERSWITCH_VLAN_PORT; + conf->vlan_mode = sc->vlan_mode; return (0); } @@ -808,66 +781,78 @@ e6000sw_getvgroup_wrapper(device_t dev, etherswitch_vlangroup_t *vg) } static __inline void -e6000sw_flush_port(e6000sw_softc_t *sc, int port) +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); - 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; - } + 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 __inline void -e6000sw_port_assign_vgroup(e6000sw_softc_t *sc, int port, int fid, int vgroup, - int members) +static int +e6000sw_set_port_vlan(e6000sw_softc_t *sc, etherswitch_vlangroup_t *vg) { - uint32_t reg; + uint32_t port; - 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; + 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); + vg->es_vid = port | ETHERSWITCH_VID_VALID; + + return (0); } 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"); + if (sc->vlan_mode == ETHERSWITCH_VLAN_PORT) + return (e6000sw_set_port_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); - } - vg->es_untagged_ports &= PORT_VLAN_MAP_TABLE_MASK; - fid = vg->es_vlangroup + 1; - for (port = 0; port < sc->num_ports; port++) { - if ((sc->members[sc->vgroup[port]] & (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); + if (!e6000sw_is_portenabled(sc, port)) { + vg->es_vid = port; + return (0); } - sc->vid[vg->es_vlangroup] = vg->es_vid; - sc->members[vg->es_vlangroup] = vg->es_untagged_ports; + + 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_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; return (0); } @@ -880,14 +865,10 @@ e6000sw_getvgroup(device_t dev, etherswitch_vlangroup_t *vg) 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]; - if (vg->es_untagged_ports != 0) - vg->es_vid = ETHERSWITCH_VID_VALID; + if (sc->vlan_mode == ETHERSWITCH_VLAN_PORT) + return (e6000sw_get_port_vlan(sc, vg)); - return (0); + return (EINVAL); } static __inline struct mii_data* @@ -1172,10 +1153,9 @@ e6000sw_setup(device_t dev, e6000sw_softc_t *sc) static void e6000sw_port_vlan_conf(e6000sw_softc_t *sc) { - int port, ret; - device_t dev; + int i, port, ret; + uint32_t members; - dev = sc->dev; /* Disable all ports */ for (port = 0; port < sc->num_ports; port++) { ret = e6000sw_readreg(sc, REG_PORT(port), PORT_CONTROL); @@ -1207,8 +1187,23 @@ e6000sw_port_vlan_conf(e6000sw_softc_t *sc) 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)); + 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); } } diff --git a/sys/dev/etherswitch/e6000sw/e6000swreg.h b/sys/dev/etherswitch/e6000sw/e6000swreg.h index 6a92377..f491ab3 100644 --- a/sys/dev/etherswitch/e6000sw/e6000swreg.h +++ b/sys/dev/etherswitch/e6000sw/e6000swreg.h @@ -87,6 +87,7 @@ struct atu_opt { #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 @@ -104,6 +105,7 @@ struct atu_opt { #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 */ @@ -201,9 +203,7 @@ struct atu_opt { #define SCR_AND_MISC_DATA_CFG_MASK 0xf0 #define E6000SW_NUM_PHY_REGS 29 -#define E6000SW_NUM_VGROUPS 8 -#define E6000SW_MAX_PORTS 10 -#define E6000SW_PORT_NO_VGROUP -1 +#define E6000SW_MAX_PORTS 8 #define E6000SW_DEFAULT_AGETIME 20 #define E6000SW_RETRIES 100 #define E6000SW_SMI_TIMEOUT 16 |