Fix the port vlan support in e6000 based switches.

Reduce the use of local copies of switch register data.

The switch now works with the upstream dsa node (i.e. the upstream DTS).

Tested on:	ClearFog Pro (88E6176), SG-3100 (88E6141)
Sponsored by:	Rubicon Communications, LLC (Netgate)
This commit is contained in:
Luiz Otavio O Souza 2017-07-27 02:38:53 +00:00
parent 7fcf47802a
commit 0e779c2f7b
2 changed files with 88 additions and 95 deletions

View File

@ -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,15 +81,11 @@ 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_name = "Marvell 6000 series switch"
};
@ -263,15 +260,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 +276,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 +351,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 +368,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 +421,9 @@ 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;
/* 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 +566,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 +779,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;
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 |= 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);
sc->vgroup[port] = 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 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);
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);
if (!e6000sw_is_portenabled(sc, port)) {
vg->es_vid = port;
return (0);
}
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);
}
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 +863,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 +1151,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 +1185,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);
}
}

View File

@ -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