Add the initial support for the Marvell 88E6141 and 88E6341 switches.

Right now the driver only supports port VLANs, so make sure
etherswitch_getinfo() return the proper switch capabilities.

Handle the cases where not all ports are in use (that will also require
etherswitch cooperation).

Sponsored by:	Rubicon Communications, LLC (Netgate)
This commit is contained in:
Luiz Otavio O Souza 2017-06-13 00:42:23 +00:00
parent 56c940ba49
commit ff2748ec10

View file

@ -73,6 +73,8 @@ typedef struct e6000sw_softc {
uint32_t cpuports_mask;
uint32_t fixed_mask;
uint32_t ports_mask;
int phy_base;
int sw_addr;
int num_ports;
boolean_t multi_chip;
@ -85,6 +87,7 @@ typedef struct e6000sw_softc {
static etherswitch_info_t etherswitch_info = {
.es_nports = 0,
.es_nvlangroups = E6000SW_NUM_VGROUPS,
.es_vlan_caps = ETHERSWITCH_VLAN_PORT,
.es_name = "Marvell 6000 series switch"
};
@ -95,6 +98,7 @@ 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 void e6000sw_lock(device_t);
static void e6000sw_unlock(device_t);
static int e6000sw_getport(device_t, etherswitch_port_t *);
@ -123,6 +127,7 @@ static int 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_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);
@ -142,6 +147,7 @@ static device_method_t e6000sw_methods[] = {
/* etherswitch interface */
DEVMETHOD(etherswitch_getinfo, e6000sw_getinfo),
DEVMETHOD(etherswitch_getconf, e6000sw_getconf),
DEVMETHOD(etherswitch_lock, e6000sw_lock),
DEVMETHOD(etherswitch_unlock, e6000sw_unlock),
DEVMETHOD(etherswitch_getport, e6000sw_getport),
@ -202,7 +208,6 @@ e6000sw_probe(device_t dev)
return (ENXIO);
sc = device_get_softc(dev);
bzero(sc, sizeof(e6000sw_softc_t));
sc->dev = dev;
sc->node = switch_node;
@ -219,14 +224,27 @@ e6000sw_probe(device_t dev)
E6000SW_UNLOCK(sc);
switch (id & 0xfff0) {
case 0x3400:
description = "Marvell 88E6141";
sc->phy_base = 0x10;
sc->num_ports = 6;
break;
case 0x3410:
description = "Marvell 88E6341";
sc->phy_base = 0x10;
sc->num_ports = 6;
break;
case 0x3520:
description = "Marvell 88E6352";
sc->num_ports = 7;
break;
case 0x1720:
description = "Marvell 88E6172";
sc->num_ports = 7;
break;
case 0x1760:
description = "Marvell 88E6176";
sc->num_ports = 7;
break;
default:
sx_destroy(&sc->sx);
@ -240,17 +258,17 @@ e6000sw_probe(device_t dev)
}
static int
e6000sw_parse_child_fdt(device_t dev, phandle_t child, uint32_t *fixed_mask,
uint32_t *cpu_mask, int *pport, int *pvlangroup)
e6000sw_parse_child_fdt(e6000sw_softc_t *sc, phandle_t child,
uint32_t *fixed_mask, uint32_t *cpu_mask, int *pport, int *pvlangroup)
{
boolean_t fixed_link;
char portlabel[100];
uint32_t port, vlangroup;
boolean_t fixed_link;
if (fixed_mask == NULL || cpu_mask == NULL || pport == NULL)
return (ENXIO);
OF_getprop(child, "label", (void *)portlabel, 100);
OF_getprop(child, "label", (void *)portlabel, sizeof(portlabel));
OF_getencprop(child, "reg", (void *)&port, sizeof(port));
if (OF_getencprop(child, "vlangroup", (void *)&vlangroup,
@ -262,22 +280,21 @@ e6000sw_parse_child_fdt(device_t dev, phandle_t child, uint32_t *fixed_mask,
*pvlangroup = -1;
}
if (port >= E6000SW_MAX_PORTS)
if (port >= sc->num_ports)
return (ENXIO);
*pport = port;
if (strncmp(portlabel, "cpu", 3) == 0) {
device_printf(dev, "CPU port at %d\n", port);
device_printf(sc->dev, "CPU port at %d\n", port);
*cpu_mask |= (1 << port);
return (0);
}
fixed_link = OF_child(child);
if (fixed_link) {
*fixed_mask |= (1 << port);
device_printf(dev, "fixed port at %d\n", port);
device_printf(sc->dev, "fixed port at %d\n", port);
} else {
device_printf(dev, "PHY at port %d\n", port);
device_printf(sc->dev, "PHY at port %d\n", port);
}
return (0);
@ -314,7 +331,7 @@ e6000sw_attach_miibus(e6000sw_softc_t *sc, int port)
err = mii_attach(sc->dev, &sc->miibus[port], sc->ifp[port],
e6000sw_ifmedia_upd, e6000sw_ifmedia_sts, BMSR_DEFCAPMASK,
port, MII_OFFSET_ANY, 0);
port + sc->phy_base, MII_OFFSET_ANY, 0);
if (err != 0)
return (err);
@ -343,7 +360,7 @@ e6000sw_attach(device_t dev)
bzero(member_ports, sizeof(member_ports));
for (child = OF_child(sc->node); child != 0; child = OF_peer(child)) {
err = e6000sw_parse_child_fdt(dev, child, &sc->fixed_mask,
err = e6000sw_parse_child_fdt(sc, child, &sc->fixed_mask,
&sc->cpuports_mask, &port, &vlangroup);
if (err != 0) {
device_printf(sc->dev, "failed to parse DTS\n");
@ -353,7 +370,8 @@ e6000sw_attach(device_t dev)
if (vlangroup != -1)
member_ports[vlangroup] |= (1 << port);
sc->num_ports++;
/* Port is in use. */
sc->ports_mask |= (1 << port);
err = e6000sw_init_interface(sc, port);
if (err != 0) {
@ -529,6 +547,17 @@ e6000sw_getinfo(device_t dev)
return (&etherswitch_info);
}
static int
e6000sw_getconf(device_t dev __unused, etherswitch_conf_t *conf)
{
/* Return the VLAN mode. */
conf->cmd = ETHERSWITCH_CONF_VLAN_MODE;
conf->vlan_mode = ETHERSWITCH_VLAN_PORT;
return (0);
}
static void
e6000sw_lock(device_t dev)
{
@ -563,6 +592,8 @@ e6000sw_getport(device_t dev, etherswitch_port_t *p)
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);
@ -605,12 +636,15 @@ e6000sw_setport(device_t dev, etherswitch_port_t *p)
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);
if (p->es_pvid != 0)
e6000sw_set_pvid(sc, p->es_port, p->es_pvid);
if (!e6000sw_is_cpuport(sc, p->es_port)) {
if (!e6000sw_is_cpuport(sc, p->es_port) &&
!e6000sw_is_fixedport(sc, p->es_port)) {
mii = e6000sw_miiforphy(sc, p->es_port);
err = ifmedia_ioctl(mii->mii_ifp, &p->es_ifr, &mii->mii_media,
SIOCSIFMEDIA);
@ -780,8 +814,7 @@ e6000sw_setvgroup(device_t dev, etherswitch_vlangroup_t *vg)
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[vg->es_vlangroup] & (1 << port)) ||
(vg->es_untagged_ports & (1 << 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,
@ -805,7 +838,8 @@ e6000sw_getvgroup(device_t dev, etherswitch_vlangroup_t *vg)
return (EINVAL);
vg->es_untagged_ports = vg->es_member_ports =
sc->members[vg->es_vlangroup];
vg->es_vid = ETHERSWITCH_VID_VALID;
if (vg->es_untagged_ports != 0)
vg->es_vid = ETHERSWITCH_VID_VALID;
return (0);
}
@ -936,6 +970,13 @@ e6000sw_is_phyport(e6000sw_softc_t *sc, int port)
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
e6000sw_set_pvid(e6000sw_softc_t *sc, int port, int pvid)
{
@ -1010,7 +1051,8 @@ e6000sw_tick (void *arg)
E6000SW_LOCK(sc);
for (port = 0; port < sc->num_ports; port++) {
/* Tick only on PHY ports */
if (!e6000sw_is_phyport(sc, port))
if (!e6000sw_is_portenabled(sc, port) ||
!e6000sw_is_phyport(sc, port))
continue;
mii = e6000sw_miiforphy(sc, port);
@ -1090,6 +1132,8 @@ e6000sw_port_vlan_conf(e6000sw_softc_t *sc)
/* 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);
@ -1097,6 +1141,8 @@ e6000sw_port_vlan_conf(e6000sw_softc_t *sc)
/* 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);
@ -1105,6 +1151,8 @@ e6000sw_port_vlan_conf(e6000sw_softc_t *sc)
/* 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));