diff options
Diffstat (limited to 'drivers/net/dsa/mv88e6xxx/chip.c')
-rw-r--r-- | drivers/net/dsa/mv88e6xxx/chip.c | 1251 |
1 files changed, 951 insertions, 300 deletions
diff --git a/drivers/net/dsa/mv88e6xxx/chip.c b/drivers/net/dsa/mv88e6xxx/chip.c index 14f87f6ac479..2479be3a1e35 100644 --- a/drivers/net/dsa/mv88e6xxx/chip.c +++ b/drivers/net/dsa/mv88e6xxx/chip.c @@ -86,12 +86,16 @@ int mv88e6xxx_write(struct mv88e6xxx_chip *chip, int addr, int reg, u16 val) int mv88e6xxx_wait_mask(struct mv88e6xxx_chip *chip, int addr, int reg, u16 mask, u16 val) { + const unsigned long timeout = jiffies + msecs_to_jiffies(50); u16 data; int err; int i; - /* There's no bus specific operation to wait for a mask */ - for (i = 0; i < 16; i++) { + /* There's no bus specific operation to wait for a mask. Even + * if the initial poll takes longer than 50ms, always do at + * least one more attempt. + */ + for (i = 0; time_before(jiffies, timeout) || (i < 2); i++) { err = mv88e6xxx_read(chip, addr, reg, &data); if (err) return err; @@ -99,7 +103,10 @@ int mv88e6xxx_wait_mask(struct mv88e6xxx_chip *chip, int addr, int reg, if ((data & mask) == val) return 0; - usleep_range(1000, 2000); + if (i < 2) + cpu_relax(); + else + usleep_range(1000, 2000); } dev_err(chip->dev, "Timeout while waiting for switch\n"); @@ -442,9 +449,6 @@ static int mv88e6xxx_port_setup_mac(struct mv88e6xxx_chip *chip, int port, goto restore_link; } - if (speed == SPEED_MAX && chip->info->ops->port_max_speed_mode) - mode = chip->info->ops->port_max_speed_mode(port); - if (chip->info->ops->port_set_pause) { err = chip->info->ops->port_set_pause(chip, port, pause); if (err) @@ -563,133 +567,276 @@ static int mv88e6xxx_serdes_pcs_link_up(struct mv88e6xxx_chip *chip, int port, return 0; } -static void mv88e6065_phylink_validate(struct mv88e6xxx_chip *chip, int port, - unsigned long *mask, - struct phylink_link_state *state) +static const u8 mv88e6185_phy_interface_modes[] = { + [MV88E6185_PORT_STS_CMODE_GMII_FD] = PHY_INTERFACE_MODE_GMII, + [MV88E6185_PORT_STS_CMODE_MII_100_FD_PS] = PHY_INTERFACE_MODE_MII, + [MV88E6185_PORT_STS_CMODE_MII_100] = PHY_INTERFACE_MODE_MII, + [MV88E6185_PORT_STS_CMODE_MII_10] = PHY_INTERFACE_MODE_MII, + [MV88E6185_PORT_STS_CMODE_SERDES] = PHY_INTERFACE_MODE_1000BASEX, + [MV88E6185_PORT_STS_CMODE_1000BASE_X] = PHY_INTERFACE_MODE_1000BASEX, + [MV88E6185_PORT_STS_CMODE_PHY] = PHY_INTERFACE_MODE_SGMII, +}; + +static void mv88e6095_phylink_get_caps(struct mv88e6xxx_chip *chip, int port, + struct phylink_config *config) { - if (!phy_interface_mode_is_8023z(state->interface)) { - /* 10M and 100M are only supported in non-802.3z mode */ - phylink_set(mask, 10baseT_Half); - phylink_set(mask, 10baseT_Full); - phylink_set(mask, 100baseT_Half); - phylink_set(mask, 100baseT_Full); + u8 cmode = chip->ports[port].cmode; + + config->mac_capabilities = MAC_SYM_PAUSE | MAC_10 | MAC_100; + + if (mv88e6xxx_phy_is_internal(chip->ds, port)) { + __set_bit(PHY_INTERFACE_MODE_MII, config->supported_interfaces); + } else { + if (cmode < ARRAY_SIZE(mv88e6185_phy_interface_modes) && + mv88e6185_phy_interface_modes[cmode]) + __set_bit(mv88e6185_phy_interface_modes[cmode], + config->supported_interfaces); + + config->mac_capabilities |= MAC_1000FD; } } -static void mv88e6185_phylink_validate(struct mv88e6xxx_chip *chip, int port, - unsigned long *mask, - struct phylink_link_state *state) +static void mv88e6185_phylink_get_caps(struct mv88e6xxx_chip *chip, int port, + struct phylink_config *config) { - /* FIXME: if the port is in 1000Base-X mode, then it only supports - * 1000M FD speeds. In this case, CMODE will indicate 5. + u8 cmode = chip->ports[port].cmode; + + if (cmode < ARRAY_SIZE(mv88e6185_phy_interface_modes) && + mv88e6185_phy_interface_modes[cmode]) + __set_bit(mv88e6185_phy_interface_modes[cmode], + config->supported_interfaces); + + config->mac_capabilities = MAC_SYM_PAUSE | MAC_10 | MAC_100 | + MAC_1000FD; +} + +static const u8 mv88e6xxx_phy_interface_modes[] = { + [MV88E6XXX_PORT_STS_CMODE_MII_PHY] = PHY_INTERFACE_MODE_MII, + [MV88E6XXX_PORT_STS_CMODE_MII] = PHY_INTERFACE_MODE_MII, + [MV88E6XXX_PORT_STS_CMODE_GMII] = PHY_INTERFACE_MODE_GMII, + [MV88E6XXX_PORT_STS_CMODE_RMII_PHY] = PHY_INTERFACE_MODE_RMII, + [MV88E6XXX_PORT_STS_CMODE_RMII] = PHY_INTERFACE_MODE_RMII, + [MV88E6XXX_PORT_STS_CMODE_100BASEX] = PHY_INTERFACE_MODE_100BASEX, + [MV88E6XXX_PORT_STS_CMODE_1000BASEX] = PHY_INTERFACE_MODE_1000BASEX, + [MV88E6XXX_PORT_STS_CMODE_SGMII] = PHY_INTERFACE_MODE_SGMII, + /* higher interface modes are not needed here, since ports supporting + * them are writable, and so the supported interfaces are filled in the + * corresponding .phylink_set_interfaces() implementation below */ - phylink_set(mask, 1000baseT_Full); - phylink_set(mask, 1000baseX_Full); +}; - mv88e6065_phylink_validate(chip, port, mask, state); +static void mv88e6xxx_translate_cmode(u8 cmode, unsigned long *supported) +{ + if (cmode < ARRAY_SIZE(mv88e6xxx_phy_interface_modes) && + mv88e6xxx_phy_interface_modes[cmode]) + __set_bit(mv88e6xxx_phy_interface_modes[cmode], supported); + else if (cmode == MV88E6XXX_PORT_STS_CMODE_RGMII) + phy_interface_set_rgmii(supported); } -static void mv88e6341_phylink_validate(struct mv88e6xxx_chip *chip, int port, - unsigned long *mask, - struct phylink_link_state *state) +static void mv88e6250_phylink_get_caps(struct mv88e6xxx_chip *chip, int port, + struct phylink_config *config) { - if (port >= 5) - phylink_set(mask, 2500baseX_Full); + unsigned long *supported = config->supported_interfaces; - /* No ethtool bits for 200Mbps */ - phylink_set(mask, 1000baseT_Full); - phylink_set(mask, 1000baseX_Full); + /* Translate the default cmode */ + mv88e6xxx_translate_cmode(chip->ports[port].cmode, supported); - mv88e6065_phylink_validate(chip, port, mask, state); + config->mac_capabilities = MAC_SYM_PAUSE | MAC_10 | MAC_100; } -static void mv88e6352_phylink_validate(struct mv88e6xxx_chip *chip, int port, - unsigned long *mask, - struct phylink_link_state *state) +static int mv88e6352_get_port4_serdes_cmode(struct mv88e6xxx_chip *chip) { - /* No ethtool bits for 200Mbps */ - phylink_set(mask, 1000baseT_Full); - phylink_set(mask, 1000baseX_Full); + u16 reg, val; + int err; + + err = mv88e6xxx_port_read(chip, 4, MV88E6XXX_PORT_STS, ®); + if (err) + return err; + + /* If PHY_DETECT is zero, then we are not in auto-media mode */ + if (!(reg & MV88E6XXX_PORT_STS_PHY_DETECT)) + return 0xf; + + val = reg & ~MV88E6XXX_PORT_STS_PHY_DETECT; + err = mv88e6xxx_port_write(chip, 4, MV88E6XXX_PORT_STS, val); + if (err) + return err; + + err = mv88e6xxx_port_read(chip, 4, MV88E6XXX_PORT_STS, &val); + if (err) + return err; + + /* Restore PHY_DETECT value */ + err = mv88e6xxx_port_write(chip, 4, MV88E6XXX_PORT_STS, reg); + if (err) + return err; - mv88e6065_phylink_validate(chip, port, mask, state); + return val & MV88E6XXX_PORT_STS_CMODE_MASK; } -static void mv88e6390_phylink_validate(struct mv88e6xxx_chip *chip, int port, - unsigned long *mask, - struct phylink_link_state *state) +static void mv88e6352_phylink_get_caps(struct mv88e6xxx_chip *chip, int port, + struct phylink_config *config) { - if (port >= 9) { - phylink_set(mask, 2500baseX_Full); - phylink_set(mask, 2500baseT_Full); + unsigned long *supported = config->supported_interfaces; + int err, cmode; + + /* Translate the default cmode */ + mv88e6xxx_translate_cmode(chip->ports[port].cmode, supported); + + config->mac_capabilities = MAC_SYM_PAUSE | MAC_10 | MAC_100 | + MAC_1000FD; + + /* Port 4 supports automedia if the serdes is associated with it. */ + if (port == 4) { + mv88e6xxx_reg_lock(chip); + err = mv88e6352_g2_scratch_port_has_serdes(chip, port); + if (err < 0) + dev_err(chip->dev, "p%d: failed to read scratch\n", + port); + if (err <= 0) + goto unlock; + + cmode = mv88e6352_get_port4_serdes_cmode(chip); + if (cmode < 0) + dev_err(chip->dev, "p%d: failed to read serdes cmode\n", + port); + else + mv88e6xxx_translate_cmode(cmode, supported); +unlock: + mv88e6xxx_reg_unlock(chip); } +} + +static void mv88e6341_phylink_get_caps(struct mv88e6xxx_chip *chip, int port, + struct phylink_config *config) +{ + unsigned long *supported = config->supported_interfaces; + + /* Translate the default cmode */ + mv88e6xxx_translate_cmode(chip->ports[port].cmode, supported); /* No ethtool bits for 200Mbps */ - phylink_set(mask, 1000baseT_Full); - phylink_set(mask, 1000baseX_Full); + config->mac_capabilities = MAC_SYM_PAUSE | MAC_10 | MAC_100 | + MAC_1000FD; + + /* The C_Mode field is programmable on port 5 */ + if (port == 5) { + __set_bit(PHY_INTERFACE_MODE_SGMII, supported); + __set_bit(PHY_INTERFACE_MODE_1000BASEX, supported); + __set_bit(PHY_INTERFACE_MODE_2500BASEX, supported); - mv88e6065_phylink_validate(chip, port, mask, state); + config->mac_capabilities |= MAC_2500FD; + } } -static void mv88e6390x_phylink_validate(struct mv88e6xxx_chip *chip, int port, - unsigned long *mask, - struct phylink_link_state *state) +static void mv88e6390_phylink_get_caps(struct mv88e6xxx_chip *chip, int port, + struct phylink_config *config) { - if (port >= 9) { - phylink_set(mask, 10000baseT_Full); - phylink_set(mask, 10000baseKR_Full); + unsigned long *supported = config->supported_interfaces; + + /* Translate the default cmode */ + mv88e6xxx_translate_cmode(chip->ports[port].cmode, supported); + + /* No ethtool bits for 200Mbps */ + config->mac_capabilities = MAC_SYM_PAUSE | MAC_10 | MAC_100 | + MAC_1000FD; + + /* The C_Mode field is programmable on ports 9 and 10 */ + if (port == 9 || port == 10) { + __set_bit(PHY_INTERFACE_MODE_SGMII, supported); + __set_bit(PHY_INTERFACE_MODE_1000BASEX, supported); + __set_bit(PHY_INTERFACE_MODE_2500BASEX, supported); + + config->mac_capabilities |= MAC_2500FD; } +} - mv88e6390_phylink_validate(chip, port, mask, state); +static void mv88e6390x_phylink_get_caps(struct mv88e6xxx_chip *chip, int port, + struct phylink_config *config) +{ + unsigned long *supported = config->supported_interfaces; + + mv88e6390_phylink_get_caps(chip, port, config); + + /* For the 6x90X, ports 2-7 can be in automedia mode. + * (Note that 6x90 doesn't support RXAUI nor XAUI). + * + * Port 2 can also support 1000BASE-X in automedia mode if port 9 is + * configured for 1000BASE-X, SGMII or 2500BASE-X. + * Port 3-4 can also support 1000BASE-X in automedia mode if port 9 is + * configured for RXAUI, 1000BASE-X, SGMII or 2500BASE-X. + * + * Port 5 can also support 1000BASE-X in automedia mode if port 10 is + * configured for 1000BASE-X, SGMII or 2500BASE-X. + * Port 6-7 can also support 1000BASE-X in automedia mode if port 10 is + * configured for RXAUI, 1000BASE-X, SGMII or 2500BASE-X. + * + * For now, be permissive (as the old code was) and allow 1000BASE-X + * on ports 2..7. + */ + if (port >= 2 && port <= 7) + __set_bit(PHY_INTERFACE_MODE_1000BASEX, supported); + + /* The C_Mode field can also be programmed for 10G speeds */ + if (port == 9 || port == 10) { + __set_bit(PHY_INTERFACE_MODE_XAUI, supported); + __set_bit(PHY_INTERFACE_MODE_RXAUI, supported); + + config->mac_capabilities |= MAC_10000FD; + } } -static void mv88e6393x_phylink_validate(struct mv88e6xxx_chip *chip, int port, - unsigned long *mask, - struct phylink_link_state *state) +static void mv88e6393x_phylink_get_caps(struct mv88e6xxx_chip *chip, int port, + struct phylink_config *config) { + unsigned long *supported = config->supported_interfaces; bool is_6191x = chip->info->prod_num == MV88E6XXX_PORT_SWITCH_ID_PROD_6191X; - if (((port == 0 || port == 9) && !is_6191x) || port == 10) { - phylink_set(mask, 10000baseT_Full); - phylink_set(mask, 10000baseKR_Full); - phylink_set(mask, 10000baseCR_Full); - phylink_set(mask, 10000baseSR_Full); - phylink_set(mask, 10000baseLR_Full); - phylink_set(mask, 10000baseLRM_Full); - phylink_set(mask, 10000baseER_Full); - phylink_set(mask, 5000baseT_Full); - phylink_set(mask, 2500baseX_Full); - phylink_set(mask, 2500baseT_Full); - } + mv88e6xxx_translate_cmode(chip->ports[port].cmode, supported); + + config->mac_capabilities = MAC_SYM_PAUSE | MAC_10 | MAC_100 | + MAC_1000FD; + + /* The C_Mode field can be programmed for ports 0, 9 and 10 */ + if (port == 0 || port == 9 || port == 10) { + __set_bit(PHY_INTERFACE_MODE_SGMII, supported); + __set_bit(PHY_INTERFACE_MODE_1000BASEX, supported); - phylink_set(mask, 1000baseT_Full); - phylink_set(mask, 1000baseX_Full); + /* 6191X supports >1G modes only on port 10 */ + if (!is_6191x || port == 10) { + __set_bit(PHY_INTERFACE_MODE_2500BASEX, supported); + __set_bit(PHY_INTERFACE_MODE_5GBASER, supported); + __set_bit(PHY_INTERFACE_MODE_10GBASER, supported); + /* FIXME: USXGMII is not supported yet */ + /* __set_bit(PHY_INTERFACE_MODE_USXGMII, supported); */ + + config->mac_capabilities |= MAC_2500FD | MAC_5000FD | + MAC_10000FD; + } + } - mv88e6065_phylink_validate(chip, port, mask, state); + if (port == 0) { + __set_bit(PHY_INTERFACE_MODE_RMII, supported); + __set_bit(PHY_INTERFACE_MODE_RGMII, supported); + __set_bit(PHY_INTERFACE_MODE_RGMII_ID, supported); + __set_bit(PHY_INTERFACE_MODE_RGMII_RXID, supported); + __set_bit(PHY_INTERFACE_MODE_RGMII_TXID, supported); + } } -static void mv88e6xxx_validate(struct dsa_switch *ds, int port, - unsigned long *supported, - struct phylink_link_state *state) +static void mv88e6xxx_get_caps(struct dsa_switch *ds, int port, + struct phylink_config *config) { - __ETHTOOL_DECLARE_LINK_MODE_MASK(mask) = { 0, }; struct mv88e6xxx_chip *chip = ds->priv; - /* Allow all the expected bits */ - phylink_set(mask, Autoneg); - phylink_set(mask, Pause); - phylink_set_port_modes(mask); - - if (chip->info->ops->phylink_validate) - chip->info->ops->phylink_validate(chip, port, mask, state); - - linkmode_and(supported, supported, mask); - linkmode_and(state->advertising, state->advertising, mask); + chip->info->ops->phylink_get_caps(chip, port, config); - /* We can only operate at 2500BaseX or 1000BaseX. If requested - * to advertise both, only report advertising at 2500BaseX. - */ - phylink_helper_basex_speed(state); + /* Internal ports need GMII for PHYLIB */ + if (mv88e6xxx_phy_is_internal(ds, port)) + __set_bit(PHY_INTERFACE_MODE_GMII, + config->supported_interfaces); } static void mv88e6xxx_mac_config(struct dsa_switch *ds, int port, @@ -768,6 +915,10 @@ static void mv88e6xxx_mac_link_down(struct dsa_switch *ds, int port, if ((!mv88e6xxx_port_ppu_updates(chip, port) || mode == MLO_AN_FIXED) && ops->port_sync_link) err = ops->port_sync_link(chip, port, mode, false); + + if (!err && ops->port_set_speed_duplex) + err = ops->port_set_speed_duplex(chip, port, SPEED_UNFORCED, + DUPLEX_UNFORCED); mv88e6xxx_reg_unlock(chip); if (err) @@ -985,7 +1136,7 @@ static void mv88e6xxx_atu_vtu_get_strings(uint8_t *data) unsigned int i; for (i = 0; i < ARRAY_SIZE(mv88e6xxx_atu_vtu_stats_strings); i++) - strlcpy(data + i * ETH_GSTRING_LEN, + strscpy(data + i * ETH_GSTRING_LEN, mv88e6xxx_atu_vtu_stats_strings[i], ETH_GSTRING_LEN); } @@ -1237,8 +1388,7 @@ static u16 mv88e6xxx_port_vlan(struct mv88e6xxx_chip *chip, int dev, int port) { struct dsa_switch *ds = chip->ds; struct dsa_switch_tree *dst = ds->dst; - struct net_device *br; - struct dsa_port *dp; + struct dsa_port *dp, *other_dp; bool found = false; u16 pvlan; @@ -1247,11 +1397,9 @@ static u16 mv88e6xxx_port_vlan(struct mv88e6xxx_chip *chip, int dev, int port) list_for_each_entry(dp, &dst->ports, list) { if (dp->ds->index == dev && dp->index == port) { /* dp might be a DSA link or a user port, so it - * might or might not have a bridge_dev - * pointer. Use the "found" variable for both - * cases. + * might or might not have a bridge. + * Use the "found" variable for both cases. */ - br = dp->bridge_dev; found = true; break; } @@ -1259,13 +1407,14 @@ static u16 mv88e6xxx_port_vlan(struct mv88e6xxx_chip *chip, int dev, int port) /* dev is a virtual bridge */ } else { list_for_each_entry(dp, &dst->ports, list) { - if (dp->bridge_num < 0) + unsigned int bridge_num = dsa_port_bridge_num_get(dp); + + if (!bridge_num) continue; - if (dp->bridge_num + 1 + dst->last_switch != dev) + if (bridge_num + dst->last_switch != dev) continue; - br = dp->bridge_dev; found = true; break; } @@ -1281,15 +1430,21 @@ static u16 mv88e6xxx_port_vlan(struct mv88e6xxx_chip *chip, int dev, int port) pvlan = 0; - /* Frames from user ports can egress any local DSA links and CPU ports, - * as well as any local member of their bridge group. + /* Frames from standalone user ports can only egress on the + * upstream port. + */ + if (!dsa_port_bridge_dev_get(dp)) + return BIT(dsa_switch_upstream_port(ds)); + + /* Frames from bridged user ports can egress any local DSA + * links and CPU ports, as well as any local member of their + * bridge group. */ - list_for_each_entry(dp, &dst->ports, list) - if (dp->ds == ds && - (dp->type == DSA_PORT_TYPE_CPU || - dp->type == DSA_PORT_TYPE_DSA || - (br && dp->bridge_dev == br))) - pvlan |= BIT(dp->index); + dsa_switch_for_each_port(other_dp, ds) + if (other_dp->type == DSA_PORT_TYPE_CPU || + other_dp->type == DSA_PORT_TYPE_DSA || + dsa_port_bridge_same(dp, other_dp)) + pvlan |= BIT(other_dp->index); return pvlan; } @@ -1475,15 +1630,16 @@ static int mv88e6xxx_pvt_map(struct mv88e6xxx_chip *chip, int dev, int port) ds = dsa_switch_find(dst->index, dev); dp = ds ? dsa_to_port(ds, port) : NULL; - if (dp && dp->lag_dev) { + if (dp && dp->lag) { /* As the PVT is used to limit flooding of * FORWARD frames, which use the LAG ID as the * source port, we must translate dev/port to * the special "LAG device" in the PVT, using - * the LAG ID as the port number. + * the LAG ID (one-based) as the port number + * (zero-based). */ dev = MV88E6XXX_G2_PVT_ADDR_DEV_TRUNK; - port = dsa_lag_id(dst, dp->lag_dev); + port = dsa_port_lag_id_get(dp) - 1; } } @@ -1516,24 +1672,31 @@ static int mv88e6xxx_pvt_setup(struct mv88e6xxx_chip *chip) return 0; } -static void mv88e6xxx_port_fast_age(struct dsa_switch *ds, int port) +static int mv88e6xxx_port_fast_age_fid(struct mv88e6xxx_chip *chip, int port, + u16 fid) { - struct mv88e6xxx_chip *chip = ds->priv; - int err; - - if (dsa_to_port(ds, port)->lag_dev) + if (dsa_to_port(chip->ds, port)->lag) /* Hardware is incapable of fast-aging a LAG through a * regular ATU move operation. Until we have something * more fancy in place this is a no-op. */ - return; + return -EOPNOTSUPP; + + return mv88e6xxx_g1_atu_remove(chip, fid, port, false); +} + +static void mv88e6xxx_port_fast_age(struct dsa_switch *ds, int port) +{ + struct mv88e6xxx_chip *chip = ds->priv; + int err; mv88e6xxx_reg_lock(chip); - err = mv88e6xxx_g1_atu_remove(chip, 0, port, false); + err = mv88e6xxx_port_fast_age_fid(chip, port, 0); mv88e6xxx_reg_unlock(chip); if (err) - dev_err(ds->dev, "p%d: failed to flush ATU\n", port); + dev_err(chip->ds->dev, "p%d: failed to flush ATU: %d\n", + port, err); } static int mv88e6xxx_vtu_setup(struct mv88e6xxx_chip *chip) @@ -1615,21 +1778,11 @@ static int mv88e6xxx_fid_map_vlan(struct mv88e6xxx_chip *chip, int mv88e6xxx_fid_map(struct mv88e6xxx_chip *chip, unsigned long *fid_bitmap) { - int i, err; - u16 fid; - bitmap_zero(fid_bitmap, MV88E6XXX_N_FID); - /* Set every FID bit used by the (un)bridged ports */ - for (i = 0; i < mv88e6xxx_num_ports(chip); ++i) { - err = mv88e6xxx_port_get_fid(chip, i, &fid); - if (err) - return err; - - set_bit(fid, fid_bitmap); - } - - /* Set every FID bit used by the VLAN entries */ + /* Every FID has an associated VID, so walking the VTU + * will discover the full set of FIDs in use. + */ return mv88e6xxx_vtu_walk(chip, mv88e6xxx_fid_map_vlan, fid_bitmap); } @@ -1642,10 +1795,7 @@ static int mv88e6xxx_atu_new(struct mv88e6xxx_chip *chip, u16 *fid) if (err) return err; - /* The reset value 0x000 is used to indicate that multiple address - * databases are not needed. Return the next positive available. - */ - *fid = find_next_zero_bit(fid_bitmap, MV88E6XXX_N_FID, 1); + *fid = find_first_zero_bit(fid_bitmap, MV88E6XXX_N_FID); if (unlikely(*fid >= mv88e6xxx_num_databases(chip))) return -ENOSPC; @@ -1653,15 +1803,197 @@ static int mv88e6xxx_atu_new(struct mv88e6xxx_chip *chip, u16 *fid) return mv88e6xxx_g1_atu_flush(chip, *fid, true); } +static int mv88e6xxx_stu_loadpurge(struct mv88e6xxx_chip *chip, + struct mv88e6xxx_stu_entry *entry) +{ + if (!chip->info->ops->stu_loadpurge) + return -EOPNOTSUPP; + + return chip->info->ops->stu_loadpurge(chip, entry); +} + +static int mv88e6xxx_stu_setup(struct mv88e6xxx_chip *chip) +{ + struct mv88e6xxx_stu_entry stu = { + .valid = true, + .sid = 0 + }; + + if (!mv88e6xxx_has_stu(chip)) + return 0; + + /* Make sure that SID 0 is always valid. This is used by VTU + * entries that do not make use of the STU, e.g. when creating + * a VLAN upper on a port that is also part of a VLAN + * filtering bridge. + */ + return mv88e6xxx_stu_loadpurge(chip, &stu); +} + +static int mv88e6xxx_sid_get(struct mv88e6xxx_chip *chip, u8 *sid) +{ + DECLARE_BITMAP(busy, MV88E6XXX_N_SID) = { 0 }; + struct mv88e6xxx_mst *mst; + + __set_bit(0, busy); + + list_for_each_entry(mst, &chip->msts, node) + __set_bit(mst->stu.sid, busy); + + *sid = find_first_zero_bit(busy, MV88E6XXX_N_SID); + + return (*sid >= mv88e6xxx_max_sid(chip)) ? -ENOSPC : 0; +} + +static int mv88e6xxx_mst_put(struct mv88e6xxx_chip *chip, u8 sid) +{ + struct mv88e6xxx_mst *mst, *tmp; + int err; + + if (!sid) + return 0; + + list_for_each_entry_safe(mst, tmp, &chip->msts, node) { + if (mst->stu.sid != sid) + continue; + + if (!refcount_dec_and_test(&mst->refcnt)) + return 0; + + mst->stu.valid = false; + err = mv88e6xxx_stu_loadpurge(chip, &mst->stu); + if (err) { + refcount_set(&mst->refcnt, 1); + return err; + } + + list_del(&mst->node); + kfree(mst); + return 0; + } + + return -ENOENT; +} + +static int mv88e6xxx_mst_get(struct mv88e6xxx_chip *chip, struct net_device *br, + u16 msti, u8 *sid) +{ + struct mv88e6xxx_mst *mst; + int err, i; + + if (!mv88e6xxx_has_stu(chip)) { + err = -EOPNOTSUPP; + goto err; + } + + if (!msti) { + *sid = 0; + return 0; + } + + list_for_each_entry(mst, &chip->msts, node) { + if (mst->br == br && mst->msti == msti) { + refcount_inc(&mst->refcnt); + *sid = mst->stu.sid; + return 0; + } + } + + err = mv88e6xxx_sid_get(chip, sid); + if (err) + goto err; + + mst = kzalloc(sizeof(*mst), GFP_KERNEL); + if (!mst) { + err = -ENOMEM; + goto err; + } + + INIT_LIST_HEAD(&mst->node); + refcount_set(&mst->refcnt, 1); + mst->br = br; + mst->msti = msti; + mst->stu.valid = true; + mst->stu.sid = *sid; + + /* The bridge starts out all ports in the disabled state. But + * a STU state of disabled means to go by the port-global + * state. So we set all user port's initial state to blocking, + * to match the bridge's behavior. + */ + for (i = 0; i < mv88e6xxx_num_ports(chip); i++) + mst->stu.state[i] = dsa_is_user_port(chip->ds, i) ? + MV88E6XXX_PORT_CTL0_STATE_BLOCKING : + MV88E6XXX_PORT_CTL0_STATE_DISABLED; + + err = mv88e6xxx_stu_loadpurge(chip, &mst->stu); + if (err) + goto err_free; + + list_add_tail(&mst->node, &chip->msts); + return 0; + +err_free: + kfree(mst); +err: + return err; +} + +static int mv88e6xxx_port_mst_state_set(struct dsa_switch *ds, int port, + const struct switchdev_mst_state *st) +{ + struct dsa_port *dp = dsa_to_port(ds, port); + struct mv88e6xxx_chip *chip = ds->priv; + struct mv88e6xxx_mst *mst; + u8 state; + int err; + + if (!mv88e6xxx_has_stu(chip)) + return -EOPNOTSUPP; + + switch (st->state) { + case BR_STATE_DISABLED: + case BR_STATE_BLOCKING: + case BR_STATE_LISTENING: + state = MV88E6XXX_PORT_CTL0_STATE_BLOCKING; + break; + case BR_STATE_LEARNING: + state = MV88E6XXX_PORT_CTL0_STATE_LEARNING; + break; + case BR_STATE_FORWARDING: + state = MV88E6XXX_PORT_CTL0_STATE_FORWARDING; + break; + default: + return -EINVAL; + } + + list_for_each_entry(mst, &chip->msts, node) { + if (mst->br == dsa_port_bridge_dev_get(dp) && + mst->msti == st->msti) { + if (mst->stu.state[port] == state) + return 0; + + mst->stu.state[port] = state; + mv88e6xxx_reg_lock(chip); + err = mv88e6xxx_stu_loadpurge(chip, &mst->stu); + mv88e6xxx_reg_unlock(chip); + return err; + } + } + + return -ENOENT; +} + static int mv88e6xxx_port_check_hw_vlan(struct dsa_switch *ds, int port, u16 vid) { + struct dsa_port *dp = dsa_to_port(ds, port), *other_dp; struct mv88e6xxx_chip *chip = ds->priv; struct mv88e6xxx_vtu_entry vlan; - int i, err; + int err; /* DSA and CPU ports have to be members of multiple vlans */ - if (dsa_is_dsa_port(ds, port) || dsa_is_cpu_port(ds, port)) + if (dsa_port_is_dsa(dp) || dsa_port_is_cpu(dp)) return 0; err = mv88e6xxx_vtu_get(chip, vid, &vlan); @@ -1671,27 +2003,22 @@ static int mv88e6xxx_port_check_hw_vlan(struct dsa_switch *ds, int port, if (!vlan.valid) return 0; - for (i = 0; i < mv88e6xxx_num_ports(chip); ++i) { - if (dsa_is_dsa_port(ds, i) || dsa_is_cpu_port(ds, i)) - continue; - - if (!dsa_to_port(ds, i)->slave) - continue; + dsa_switch_for_each_user_port(other_dp, ds) { + struct net_device *other_br; - if (vlan.member[i] == + if (vlan.member[other_dp->index] == MV88E6XXX_G1_VTU_DATA_MEMBER_TAG_NON_MEMBER) continue; - if (dsa_to_port(ds, i)->bridge_dev == - dsa_to_port(ds, port)->bridge_dev) + if (dsa_port_bridge_same(dp, other_dp)) break; /* same bridge, check next VLAN */ - if (!dsa_to_port(ds, i)->bridge_dev) + other_br = dsa_port_bridge_dev_get(other_dp); + if (!other_br) continue; dev_err(ds->dev, "p%d: hw VLAN %d already used by port %d in %s\n", - port, vlan.vid, i, - netdev_name(dsa_to_port(ds, i)->bridge_dev)); + port, vlan.vid, other_dp->index, netdev_name(other_br)); return -EOPNOTSUPP; } @@ -1701,13 +2028,14 @@ static int mv88e6xxx_port_check_hw_vlan(struct dsa_switch *ds, int port, static int mv88e6xxx_port_commit_pvid(struct mv88e6xxx_chip *chip, int port) { struct dsa_port *dp = dsa_to_port(chip->ds, port); + struct net_device *br = dsa_port_bridge_dev_get(dp); struct mv88e6xxx_port *p = &chip->ports[port]; u16 pvid = MV88E6XXX_VID_STANDALONE; bool drop_untagged = false; int err; - if (dp->bridge_dev) { - if (br_vlan_enabled(dp->bridge_dev)) { + if (br) { + if (br_vlan_enabled(br)) { pvid = p->bridge_pvid.vid; drop_untagged = !p->bridge_pvid.valid; } else { @@ -2140,6 +2468,9 @@ static int mv88e6xxx_port_vlan_join(struct mv88e6xxx_chip *chip, int port, if (!vlan.valid) { memset(&vlan, 0, sizeof(vlan)); + if (vid == MV88E6XXX_VID_STANDALONE) + vlan.policy = true; + err = mv88e6xxx_atu_new(chip, &vlan.fid); if (err) return err; @@ -2272,6 +2603,12 @@ static int mv88e6xxx_port_vlan_leave(struct mv88e6xxx_chip *chip, if (err) return err; + if (!vlan.valid) { + err = mv88e6xxx_mst_put(chip, vlan.sid); + if (err) + return err; + } + return mv88e6xxx_g1_atu_remove(chip, vlan.fid, port, false); } @@ -2286,6 +2623,13 @@ static int mv88e6xxx_port_vlan_del(struct dsa_switch *ds, int port, if (!mv88e6xxx_max_vid(chip)) return -EOPNOTSUPP; + /* The ATU removal procedure needs the FID to be mapped in the VTU, + * but FDB deletion runs concurrently with VLAN deletion. Flush the DSA + * switchdev workqueue to ensure that all FDB entries are deleted + * before we remove the VLAN. + */ + dsa_flush_workqueue(); + mv88e6xxx_reg_lock(chip); err = mv88e6xxx_port_get_pvid(chip, port, &pvid); @@ -2310,8 +2654,75 @@ unlock: return err; } +static int mv88e6xxx_port_vlan_fast_age(struct dsa_switch *ds, int port, u16 vid) +{ + struct mv88e6xxx_chip *chip = ds->priv; + struct mv88e6xxx_vtu_entry vlan; + int err; + + mv88e6xxx_reg_lock(chip); + + err = mv88e6xxx_vtu_get(chip, vid, &vlan); + if (err) + goto unlock; + + err = mv88e6xxx_port_fast_age_fid(chip, port, vlan.fid); + +unlock: + mv88e6xxx_reg_unlock(chip); + + return err; +} + +static int mv88e6xxx_vlan_msti_set(struct dsa_switch *ds, + struct dsa_bridge bridge, + const struct switchdev_vlan_msti *msti) +{ + struct mv88e6xxx_chip *chip = ds->priv; + struct mv88e6xxx_vtu_entry vlan; + u8 old_sid, new_sid; + int err; + + if (!mv88e6xxx_has_stu(chip)) + return -EOPNOTSUPP; + + mv88e6xxx_reg_lock(chip); + + err = mv88e6xxx_vtu_get(chip, msti->vid, &vlan); + if (err) + goto unlock; + + if (!vlan.valid) { + err = -EINVAL; + goto unlock; + } + + old_sid = vlan.sid; + + err = mv88e6xxx_mst_get(chip, bridge.dev, msti->msti, &new_sid); + if (err) + goto unlock; + + if (new_sid != old_sid) { + vlan.sid = new_sid; + + err = mv88e6xxx_vtu_loadpurge(chip, &vlan); + if (err) { + mv88e6xxx_mst_put(chip, new_sid); + goto unlock; + } + } + + err = mv88e6xxx_mst_put(chip, old_sid); + +unlock: + mv88e6xxx_reg_unlock(chip); + return err; +} + static int mv88e6xxx_port_fdb_add(struct dsa_switch *ds, int port, - const unsigned char *addr, u16 vid) + const unsigned char *addr, u16 vid, + struct dsa_db db) { struct mv88e6xxx_chip *chip = ds->priv; int err; @@ -2325,7 +2736,8 @@ static int mv88e6xxx_port_fdb_add(struct dsa_switch *ds, int port, } static int mv88e6xxx_port_fdb_del(struct dsa_switch *ds, int port, - const unsigned char *addr, u16 vid) + const unsigned char *addr, u16 vid, + struct dsa_db db) { struct mv88e6xxx_chip *chip = ds->priv; int err; @@ -2425,7 +2837,7 @@ static int mv88e6xxx_port_fdb_dump(struct dsa_switch *ds, int port, } static int mv88e6xxx_bridge_map(struct mv88e6xxx_chip *chip, - struct net_device *br) + struct dsa_bridge bridge) { struct dsa_switch *ds = chip->ds; struct dsa_switch_tree *dst = ds->dst; @@ -2433,7 +2845,7 @@ static int mv88e6xxx_bridge_map(struct mv88e6xxx_chip *chip, int err; list_for_each_entry(dp, &dst->ports, list) { - if (dp->bridge_dev == br) { + if (dsa_port_offloads_bridge(dp, &bridge)) { if (dp->ds == ds) { /* This is a local bridge group member, * remap its Port VLAN Map. @@ -2456,15 +2868,34 @@ static int mv88e6xxx_bridge_map(struct mv88e6xxx_chip *chip, return 0; } +/* Treat the software bridge as a virtual single-port switch behind the + * CPU and map in the PVT. First dst->last_switch elements are taken by + * physical switches, so start from beyond that range. + */ +static int mv88e6xxx_map_virtual_bridge_to_pvt(struct dsa_switch *ds, + unsigned int bridge_num) +{ + u8 dev = bridge_num + ds->dst->last_switch; + struct mv88e6xxx_chip *chip = ds->priv; + + return mv88e6xxx_pvt_map(chip, dev, 0); +} + static int mv88e6xxx_port_bridge_join(struct dsa_switch *ds, int port, - struct net_device *br) + struct dsa_bridge bridge, + bool *tx_fwd_offload, + struct netlink_ext_ack *extack) { struct mv88e6xxx_chip *chip = ds->priv; int err; mv88e6xxx_reg_lock(chip); - err = mv88e6xxx_bridge_map(chip, br); + err = mv88e6xxx_bridge_map(chip, bridge); + if (err) + goto unlock; + + err = mv88e6xxx_port_set_map_da(chip, port, true); if (err) goto unlock; @@ -2472,6 +2903,14 @@ static int mv88e6xxx_port_bridge_join(struct dsa_switch *ds, int port, if (err) goto unlock; + if (mv88e6xxx_has_pvt(chip)) { + err = mv88e6xxx_map_virtual_bridge_to_pvt(ds, bridge.num); + if (err) + goto unlock; + + *tx_fwd_offload = true; + } + unlock: mv88e6xxx_reg_unlock(chip); @@ -2479,17 +2918,27 @@ unlock: } static void mv88e6xxx_port_bridge_leave(struct dsa_switch *ds, int port, - struct net_device *br) + struct dsa_bridge bridge) { struct mv88e6xxx_chip *chip = ds->priv; int err; mv88e6xxx_reg_lock(chip); - if (mv88e6xxx_bridge_map(chip, br) || + if (bridge.tx_fwd_offload && + mv88e6xxx_map_virtual_bridge_to_pvt(ds, bridge.num)) + dev_err(ds->dev, "failed to remap cross-chip Port VLAN\n"); + + if (mv88e6xxx_bridge_map(chip, bridge) || mv88e6xxx_port_vlan_map(chip, port)) dev_err(ds->dev, "failed to remap in-chip Port VLAN\n"); + err = mv88e6xxx_port_set_map_da(chip, port, false); + if (err) + dev_err(ds->dev, + "port %d failed to restore map-DA: %pe\n", + port, ERR_PTR(err)); + err = mv88e6xxx_port_commit_pvid(chip, port); if (err) dev_err(ds->dev, @@ -2501,7 +2950,8 @@ static void mv88e6xxx_port_bridge_leave(struct dsa_switch *ds, int port, static int mv88e6xxx_crosschip_bridge_join(struct dsa_switch *ds, int tree_index, int sw_index, - int port, struct net_device *br) + int port, struct dsa_bridge bridge, + struct netlink_ext_ack *extack) { struct mv88e6xxx_chip *chip = ds->priv; int err; @@ -2511,6 +2961,7 @@ static int mv88e6xxx_crosschip_bridge_join(struct dsa_switch *ds, mv88e6xxx_reg_lock(chip); err = mv88e6xxx_pvt_map(chip, sw_index, port); + err = err ? : mv88e6xxx_map_virtual_bridge_to_pvt(ds, bridge.num); mv88e6xxx_reg_unlock(chip); return err; @@ -2518,7 +2969,7 @@ static int mv88e6xxx_crosschip_bridge_join(struct dsa_switch *ds, static void mv88e6xxx_crosschip_bridge_leave(struct dsa_switch *ds, int tree_index, int sw_index, - int port, struct net_device *br) + int port, struct dsa_bridge bridge) { struct mv88e6xxx_chip *chip = ds->priv; @@ -2526,49 +2977,12 @@ static void mv88e6xxx_crosschip_bridge_leave(struct dsa_switch *ds, return; mv88e6xxx_reg_lock(chip); - if (mv88e6xxx_pvt_map(chip, sw_index, port)) + if (mv88e6xxx_pvt_map(chip, sw_index, port) || + mv88e6xxx_map_virtual_bridge_to_pvt(ds, bridge.num)) dev_err(ds->dev, "failed to remap cross-chip Port VLAN\n"); mv88e6xxx_reg_unlock(chip); } -/* Treat the software bridge as a virtual single-port switch behind the - * CPU and map in the PVT. First dst->last_switch elements are taken by - * physical switches, so start from beyond that range. - */ -static int mv88e6xxx_map_virtual_bridge_to_pvt(struct dsa_switch *ds, - int bridge_num) -{ - u8 dev = bridge_num + ds->dst->last_switch + 1; - struct mv88e6xxx_chip *chip = ds->priv; - int err; - - mv88e6xxx_reg_lock(chip); - err = mv88e6xxx_pvt_map(chip, dev, 0); - mv88e6xxx_reg_unlock(chip); - - return err; -} - -static int mv88e6xxx_bridge_tx_fwd_offload(struct dsa_switch *ds, int port, - struct net_device *br, - int bridge_num) -{ - return mv88e6xxx_map_virtual_bridge_to_pvt(ds, bridge_num); -} - -static void mv88e6xxx_bridge_tx_fwd_unoffload(struct dsa_switch *ds, int port, - struct net_device *br, - int bridge_num) -{ - int err; - - err = mv88e6xxx_map_virtual_bridge_to_pvt(ds, bridge_num); - if (err) { - dev_err(ds->dev, "failed to remap cross-chip Port VLAN: %pe\n", - ERR_PTR(err)); - } -} - static int mv88e6xxx_software_reset(struct mv88e6xxx_chip *chip) { if (chip->info->ops->reset) @@ -2869,27 +3283,58 @@ static int mv88e6xxx_setup_upstream_port(struct mv88e6xxx_chip *chip, int port) static int mv88e6xxx_setup_port(struct mv88e6xxx_chip *chip, int port) { + struct device_node *phy_handle = NULL; struct dsa_switch *ds = chip->ds; + phy_interface_t mode; + struct dsa_port *dp; + int tx_amp, speed; int err; u16 reg; chip->ports[port].chip = chip; chip->ports[port].port = port; + dp = dsa_to_port(ds, port); + /* MAC Forcing register: don't force link, speed, duplex or flow control * state to any particular values on physical ports, but force the CPU * port and all DSA ports to their maximum bandwidth and full duplex. */ - if (dsa_is_cpu_port(ds, port) || dsa_is_dsa_port(ds, port)) + if (dsa_is_cpu_port(ds, port) || dsa_is_dsa_port(ds, port)) { + struct phylink_config pl_config = {}; + unsigned long caps; + + mv88e6xxx_get_caps(ds, port, &pl_config); + + caps = pl_config.mac_capabilities; + + if (chip->info->ops->port_max_speed_mode) + mode = chip->info->ops->port_max_speed_mode(port); + else + mode = PHY_INTERFACE_MODE_NA; + + if (caps & MAC_10000FD) + speed = SPEED_10000; + else if (caps & MAC_5000FD) + speed = SPEED_5000; + else if (caps & MAC_2500FD) + speed = SPEED_2500; + else if (caps & MAC_1000) + speed = SPEED_1000; + else if (caps & MAC_100) + speed = SPEED_100; + else + speed = SPEED_10; + err = mv88e6xxx_port_setup_mac(chip, port, LINK_FORCED_UP, - SPEED_MAX, DUPLEX_FULL, - PAUSE_OFF, - PHY_INTERFACE_MODE_NA); - else + speed, DUPLEX_FULL, + PAUSE_OFF, mode); + } else { err = mv88e6xxx_port_setup_mac(chip, port, LINK_UNFORCED, SPEED_UNFORCED, DUPLEX_UNFORCED, PAUSE_ON, PHY_INTERFACE_MODE_NA); + } if (err) return err; @@ -2923,12 +3368,13 @@ static int mv88e6xxx_setup_port(struct mv88e6xxx_chip *chip, int port) return err; /* Port Control 2: don't force a good FCS, set the MTU size to - * 10222 bytes, disable 802.1q tags checking, don't discard tagged or - * untagged frames on this port, do a destination address lookup on all - * received packets as usual, disable ARP mirroring and don't send a - * copy of all transmitted/received frames on this port to the CPU. + * 10222 bytes, disable 802.1q tags checking, don't discard + * tagged or untagged frames on this port, skip destination + * address lookup on user ports, disable ARP mirroring and don't + * send a copy of all transmitted/received frames on this port + * to the CPU. */ - err = mv88e6xxx_port_set_map_da(chip, port); + err = mv88e6xxx_port_set_map_da(chip, port, !dsa_is_user_port(ds, port)); if (err) return err; @@ -2936,8 +3382,44 @@ static int mv88e6xxx_setup_port(struct mv88e6xxx_chip *chip, int port) if (err) return err; + /* On chips that support it, set all downstream DSA ports' + * VLAN policy to TRAP. In combination with loading + * MV88E6XXX_VID_STANDALONE as a policy entry in the VTU, this + * provides a better isolation barrier between standalone + * ports, as the ATU is bypassed on any intermediate switches + * between the incoming port and the CPU. + */ + if (dsa_is_downstream_port(ds, port) && + chip->info->ops->port_set_policy) { + err = chip->info->ops->port_set_policy(chip, port, + MV88E6XXX_POLICY_MAPPING_VTU, + MV88E6XXX_POLICY_ACTION_TRAP); + if (err) + return err; + } + + /* User ports start out in standalone mode and 802.1Q is + * therefore disabled. On DSA ports, all valid VIDs are always + * loaded in the VTU - therefore, enable 802.1Q in order to take + * advantage of VLAN policy on chips that supports it. + */ err = mv88e6xxx_port_set_8021q_mode(chip, port, - MV88E6XXX_PORT_CTL2_8021Q_MODE_DISABLED); + dsa_is_user_port(ds, port) ? + MV88E6XXX_PORT_CTL2_8021Q_MODE_DISABLED : + MV88E6XXX_PORT_CTL2_8021Q_MODE_SECURE); + if (err) + return err; + + /* Bind MV88E6XXX_VID_STANDALONE to MV88E6XXX_FID_STANDALONE by + * virtue of the fact that mv88e6xxx_atu_new() will pick it as + * the first free FID. This will be used as the private PVID for + * unbridged ports. Shared (DSA and CPU) ports must also be + * members of this VID, in order to trap all frames assigned to + * it to the CPU. + */ + err = mv88e6xxx_port_vlan_join(chip, port, MV88E6XXX_VID_STANDALONE, + MV88E6XXX_G1_VTU_DATA_MEMBER_TAG_UNMODIFIED, + false); if (err) return err; @@ -2950,7 +3432,7 @@ static int mv88e6xxx_setup_port(struct mv88e6xxx_chip *chip, int port) * relying on their port default FID. */ err = mv88e6xxx_port_vlan_join(chip, port, MV88E6XXX_VID_BRIDGED, - MV88E6XXX_G1_VTU_DATA_MEMBER_TAG_UNTAGGED, + MV88E6XXX_G1_VTU_DATA_MEMBER_TAG_UNMODIFIED, false); if (err) return err; @@ -3023,6 +3505,22 @@ static int mv88e6xxx_setup_port(struct mv88e6xxx_chip *chip, int port) return err; } + if (chip->info->ops->serdes_set_tx_amplitude) { + if (dp) + phy_handle = of_parse_phandle(dp->dn, "phy-handle", 0); + + if (phy_handle && !of_property_read_u32(phy_handle, + "tx-p2p-microvolt", + &tx_amp)) + err = chip->info->ops->serdes_set_tx_amplitude(chip, + port, tx_amp); + if (phy_handle) { + of_node_put(phy_handle); + if (err) + return err; + } + } + /* Port based VLAN map: give each port the same default address * database, and allow bidirectional communication between the * CPU and DSA port(s), and the other ports. @@ -3195,8 +3693,8 @@ static int mv88e6xxx_setup(struct dsa_switch *ds) * time. */ if (mv88e6xxx_has_pvt(chip)) - ds->num_fwd_offloading_bridges = MV88E6XXX_MAX_PVT_SWITCHES - - ds->dst->last_switch - 1; + ds->max_num_bridges = MV88E6XXX_MAX_PVT_SWITCHES - + ds->dst->last_switch - 1; mv88e6xxx_reg_lock(chip); @@ -3221,6 +3719,13 @@ static int mv88e6xxx_setup(struct dsa_switch *ds) if (err) goto unlock; + /* Must be called after mv88e6xxx_vtu_setup (which flushes the + * VTU, thereby also flushing the STU). + */ + err = mv88e6xxx_stu_setup(chip); + if (err) + goto unlock; + /* Setup Switch Port Registers */ for (i = 0; i < mv88e6xxx_num_ports(chip); i++) { if (dsa_is_unused_port(ds, i)) @@ -3411,7 +3916,7 @@ static int mv88e6xxx_mdio_register(struct mv88e6xxx_chip *chip, return err; } - bus = devm_mdiobus_alloc_size(chip->dev, sizeof(*mdio_bus)); + bus = mdiobus_alloc_size(sizeof(*mdio_bus)); if (!bus) return -ENOMEM; @@ -3436,14 +3941,14 @@ static int mv88e6xxx_mdio_register(struct mv88e6xxx_chip *chip, if (!external) { err = mv88e6xxx_g2_irq_mdio_setup(chip, bus); if (err) - return err; + goto out; } err = of_mdiobus_register(bus, np); if (err) { dev_err(chip->dev, "Cannot register MDIO bus (%d)\n", err); mv88e6xxx_g2_irq_mdio_free(chip, bus); - return err; + goto out; } if (external) @@ -3452,21 +3957,26 @@ static int mv88e6xxx_mdio_register(struct mv88e6xxx_chip *chip, list_add(&mdio_bus->list, &chip->mdios); return 0; + +out: + mdiobus_free(bus); + return err; } static void mv88e6xxx_mdios_unregister(struct mv88e6xxx_chip *chip) { - struct mv88e6xxx_mdio_bus *mdio_bus; + struct mv88e6xxx_mdio_bus *mdio_bus, *p; struct mii_bus *bus; - list_for_each_entry(mdio_bus, &chip->mdios, list) { + list_for_each_entry_safe(mdio_bus, p, &chip->mdios, list) { bus = mdio_bus->bus; if (!mdio_bus->external) mv88e6xxx_g2_irq_mdio_free(chip, bus); mdiobus_unregister(bus); + mdiobus_free(bus); } } @@ -3482,6 +3992,7 @@ static int mv88e6xxx_mdios_register(struct mv88e6xxx_chip *chip, */ child = of_get_child_by_name(np, "mdio"); err = mv88e6xxx_mdio_register(chip, child, false); + of_node_put(child); if (err) return err; @@ -3589,7 +4100,9 @@ static const struct mv88e6xxx_ops mv88e6085_ops = { .rmu_disable = mv88e6085_g1_rmu_disable, .vtu_getnext = mv88e6352_g1_vtu_getnext, .vtu_loadpurge = mv88e6352_g1_vtu_loadpurge, - .phylink_validate = mv88e6185_phylink_validate, + .stu_getnext = mv88e6352_g1_stu_getnext, + .stu_loadpurge = mv88e6352_g1_stu_loadpurge, + .phylink_get_caps = mv88e6185_phylink_get_caps, .set_max_frame_size = mv88e6185_g1_set_max_frame_size, }; @@ -3623,7 +4136,7 @@ static const struct mv88e6xxx_ops mv88e6095_ops = { .reset = mv88e6185_g1_reset, .vtu_getnext = mv88e6185_g1_vtu_getnext, .vtu_loadpurge = mv88e6185_g1_vtu_loadpurge, - .phylink_validate = mv88e6185_phylink_validate, + .phylink_get_caps = mv88e6095_phylink_get_caps, .set_max_frame_size = mv88e6185_g1_set_max_frame_size, }; @@ -3639,6 +4152,7 @@ static const struct mv88e6xxx_ops mv88e6097_ops = { .port_sync_link = mv88e6185_port_sync_link, .port_set_speed_duplex = mv88e6185_port_set_speed_duplex, .port_tag_remap = mv88e6095_port_tag_remap, + .port_set_policy = mv88e6352_port_set_policy, .port_set_frame_mode = mv88e6351_port_set_frame_mode, .port_set_ucast_flood = mv88e6352_port_set_ucast_flood, .port_set_mcast_flood = mv88e6352_port_set_mcast_flood, @@ -3669,7 +4183,9 @@ static const struct mv88e6xxx_ops mv88e6097_ops = { .rmu_disable = mv88e6085_g1_rmu_disable, .vtu_getnext = mv88e6352_g1_vtu_getnext, .vtu_loadpurge = mv88e6352_g1_vtu_loadpurge, - .phylink_validate = mv88e6185_phylink_validate, + .phylink_get_caps = mv88e6095_phylink_get_caps, + .stu_getnext = mv88e6352_g1_stu_getnext, + .stu_loadpurge = mv88e6352_g1_stu_loadpurge, .set_max_frame_size = mv88e6185_g1_set_max_frame_size, }; @@ -3706,7 +4222,9 @@ static const struct mv88e6xxx_ops mv88e6123_ops = { .atu_set_hash = mv88e6165_g1_atu_set_hash, .vtu_getnext = mv88e6352_g1_vtu_getnext, .vtu_loadpurge = mv88e6352_g1_vtu_loadpurge, - .phylink_validate = mv88e6185_phylink_validate, + .stu_getnext = mv88e6352_g1_stu_getnext, + .stu_loadpurge = mv88e6352_g1_stu_loadpurge, + .phylink_get_caps = mv88e6185_phylink_get_caps, .set_max_frame_size = mv88e6185_g1_set_max_frame_size, }; @@ -3747,7 +4265,7 @@ static const struct mv88e6xxx_ops mv88e6131_ops = { .reset = mv88e6185_g1_reset, .vtu_getnext = mv88e6185_g1_vtu_getnext, .vtu_loadpurge = mv88e6185_g1_vtu_loadpurge, - .phylink_validate = mv88e6185_phylink_validate, + .phylink_get_caps = mv88e6185_phylink_get_caps, }; static const struct mv88e6xxx_ops mv88e6141_ops = { @@ -3795,6 +4313,8 @@ static const struct mv88e6xxx_ops mv88e6141_ops = { .atu_set_hash = mv88e6165_g1_atu_set_hash, .vtu_getnext = mv88e6352_g1_vtu_getnext, .vtu_loadpurge = mv88e6352_g1_vtu_loadpurge, + .stu_getnext = mv88e6352_g1_stu_getnext, + .stu_loadpurge = mv88e6352_g1_stu_loadpurge, .serdes_power = mv88e6390_serdes_power, .serdes_get_lane = mv88e6341_serdes_get_lane, /* Check status register pause & lpa register */ @@ -3811,7 +4331,7 @@ static const struct mv88e6xxx_ops mv88e6141_ops = { .serdes_get_stats = mv88e6390_serdes_get_stats, .serdes_get_regs_len = mv88e6390_serdes_get_regs_len, .serdes_get_regs = mv88e6390_serdes_get_regs, - .phylink_validate = mv88e6341_phylink_validate, + .phylink_get_caps = mv88e6341_phylink_get_caps, }; static const struct mv88e6xxx_ops mv88e6161_ops = { @@ -3851,9 +4371,11 @@ static const struct mv88e6xxx_ops mv88e6161_ops = { .atu_set_hash = mv88e6165_g1_atu_set_hash, .vtu_getnext = mv88e6352_g1_vtu_getnext, .vtu_loadpurge = mv88e6352_g1_vtu_loadpurge, + .stu_getnext = mv88e6352_g1_stu_getnext, + .stu_loadpurge = mv88e6352_g1_stu_loadpurge, .avb_ops = &mv88e6165_avb_ops, .ptp_ops = &mv88e6165_ptp_ops, - .phylink_validate = mv88e6185_phylink_validate, + .phylink_get_caps = mv88e6185_phylink_get_caps, .set_max_frame_size = mv88e6185_g1_set_max_frame_size, }; @@ -3887,9 +4409,11 @@ static const struct mv88e6xxx_ops mv88e6165_ops = { .atu_set_hash = mv88e6165_g1_atu_set_hash, .vtu_getnext = mv88e6352_g1_vtu_getnext, .vtu_loadpurge = mv88e6352_g1_vtu_loadpurge, + .stu_getnext = mv88e6352_g1_stu_getnext, + .stu_loadpurge = mv88e6352_g1_stu_loadpurge, .avb_ops = &mv88e6165_avb_ops, .ptp_ops = &mv88e6165_ptp_ops, - .phylink_validate = mv88e6185_phylink_validate, + .phylink_get_caps = mv88e6185_phylink_get_caps, }; static const struct mv88e6xxx_ops mv88e6171_ops = { @@ -3931,7 +4455,9 @@ static const struct mv88e6xxx_ops mv88e6171_ops = { .atu_set_hash = mv88e6165_g1_atu_set_hash, .vtu_getnext = mv88e6352_g1_vtu_getnext, .vtu_loadpurge = mv88e6352_g1_vtu_loadpurge, - .phylink_validate = mv88e6185_phylink_validate, + .stu_getnext = mv88e6352_g1_stu_getnext, + .stu_loadpurge = mv88e6352_g1_stu_loadpurge, + .phylink_get_caps = mv88e6185_phylink_get_caps, }; static const struct mv88e6xxx_ops mv88e6172_ops = { @@ -3977,6 +4503,8 @@ static const struct mv88e6xxx_ops mv88e6172_ops = { .atu_set_hash = mv88e6165_g1_atu_set_hash, .vtu_getnext = mv88e6352_g1_vtu_getnext, .vtu_loadpurge = mv88e6352_g1_vtu_loadpurge, + .stu_getnext = mv88e6352_g1_stu_getnext, + .stu_loadpurge = mv88e6352_g1_stu_loadpurge, .serdes_get_lane = mv88e6352_serdes_get_lane, .serdes_pcs_get_state = mv88e6352_serdes_pcs_get_state, .serdes_pcs_config = mv88e6352_serdes_pcs_config, @@ -3986,7 +4514,7 @@ static const struct mv88e6xxx_ops mv88e6172_ops = { .serdes_get_regs_len = mv88e6352_serdes_get_regs_len, .serdes_get_regs = mv88e6352_serdes_get_regs, .gpio_ops = &mv88e6352_gpio_ops, - .phylink_validate = mv88e6352_phylink_validate, + .phylink_get_caps = mv88e6352_phylink_get_caps, }; static const struct mv88e6xxx_ops mv88e6175_ops = { @@ -4028,7 +4556,9 @@ static const struct mv88e6xxx_ops mv88e6175_ops = { .atu_set_hash = mv88e6165_g1_atu_set_hash, .vtu_getnext = mv88e6352_g1_vtu_getnext, .vtu_loadpurge = mv88e6352_g1_vtu_loadpurge, - .phylink_validate = mv88e6185_phylink_validate, + .stu_getnext = mv88e6352_g1_stu_getnext, + .stu_loadpurge = mv88e6352_g1_stu_loadpurge, + .phylink_get_caps = mv88e6185_phylink_get_caps, }; static const struct mv88e6xxx_ops mv88e6176_ops = { @@ -4074,6 +4604,8 @@ static const struct mv88e6xxx_ops mv88e6176_ops = { .atu_set_hash = mv88e6165_g1_atu_set_hash, .vtu_getnext = mv88e6352_g1_vtu_getnext, .vtu_loadpurge = mv88e6352_g1_vtu_loadpurge, + .stu_getnext = mv88e6352_g1_stu_getnext, + .stu_loadpurge = mv88e6352_g1_stu_loadpurge, .serdes_get_lane = mv88e6352_serdes_get_lane, .serdes_pcs_get_state = mv88e6352_serdes_pcs_get_state, .serdes_pcs_config = mv88e6352_serdes_pcs_config, @@ -4085,8 +4617,9 @@ static const struct mv88e6xxx_ops mv88e6176_ops = { .serdes_irq_status = mv88e6352_serdes_irq_status, .serdes_get_regs_len = mv88e6352_serdes_get_regs_len, .serdes_get_regs = mv88e6352_serdes_get_regs, + .serdes_set_tx_amplitude = mv88e6352_serdes_set_tx_amplitude, .gpio_ops = &mv88e6352_gpio_ops, - .phylink_validate = mv88e6352_phylink_validate, + .phylink_get_caps = mv88e6352_phylink_get_caps, }; static const struct mv88e6xxx_ops mv88e6185_ops = { @@ -4125,7 +4658,7 @@ static const struct mv88e6xxx_ops mv88e6185_ops = { .reset = mv88e6185_g1_reset, .vtu_getnext = mv88e6185_g1_vtu_getnext, .vtu_loadpurge = mv88e6185_g1_vtu_loadpurge, - .phylink_validate = mv88e6185_phylink_validate, + .phylink_get_caps = mv88e6185_phylink_get_caps, .set_max_frame_size = mv88e6185_g1_set_max_frame_size, }; @@ -4172,6 +4705,8 @@ static const struct mv88e6xxx_ops mv88e6190_ops = { .atu_set_hash = mv88e6165_g1_atu_set_hash, .vtu_getnext = mv88e6390_g1_vtu_getnext, .vtu_loadpurge = mv88e6390_g1_vtu_loadpurge, + .stu_getnext = mv88e6390_g1_stu_getnext, + .stu_loadpurge = mv88e6390_g1_stu_loadpurge, .serdes_power = mv88e6390_serdes_power, .serdes_get_lane = mv88e6390_serdes_get_lane, /* Check status register pause & lpa register */ @@ -4187,7 +4722,7 @@ static const struct mv88e6xxx_ops mv88e6190_ops = { .serdes_get_regs_len = mv88e6390_serdes_get_regs_len, .serdes_get_regs = mv88e6390_serdes_get_regs, .gpio_ops = &mv88e6352_gpio_ops, - .phylink_validate = mv88e6390_phylink_validate, + .phylink_get_caps = mv88e6390_phylink_get_caps, }; static const struct mv88e6xxx_ops mv88e6190x_ops = { @@ -4233,6 +4768,8 @@ static const struct mv88e6xxx_ops mv88e6190x_ops = { .atu_set_hash = mv88e6165_g1_atu_set_hash, .vtu_getnext = mv88e6390_g1_vtu_getnext, .vtu_loadpurge = mv88e6390_g1_vtu_loadpurge, + .stu_getnext = mv88e6390_g1_stu_getnext, + .stu_loadpurge = mv88e6390_g1_stu_loadpurge, .serdes_power = mv88e6390_serdes_power, .serdes_get_lane = mv88e6390x_serdes_get_lane, /* Check status register pause & lpa register */ @@ -4248,7 +4785,7 @@ static const struct mv88e6xxx_ops mv88e6190x_ops = { .serdes_get_regs_len = mv88e6390_serdes_get_regs_len, .serdes_get_regs = mv88e6390_serdes_get_regs, .gpio_ops = &mv88e6352_gpio_ops, - .phylink_validate = mv88e6390x_phylink_validate, + .phylink_get_caps = mv88e6390x_phylink_get_caps, }; static const struct mv88e6xxx_ops mv88e6191_ops = { @@ -4292,6 +4829,8 @@ static const struct mv88e6xxx_ops mv88e6191_ops = { .atu_set_hash = mv88e6165_g1_atu_set_hash, .vtu_getnext = mv88e6390_g1_vtu_getnext, .vtu_loadpurge = mv88e6390_g1_vtu_loadpurge, + .stu_getnext = mv88e6390_g1_stu_getnext, + .stu_loadpurge = mv88e6390_g1_stu_loadpurge, .serdes_power = mv88e6390_serdes_power, .serdes_get_lane = mv88e6390_serdes_get_lane, /* Check status register pause & lpa register */ @@ -4308,7 +4847,7 @@ static const struct mv88e6xxx_ops mv88e6191_ops = { .serdes_get_regs = mv88e6390_serdes_get_regs, .avb_ops = &mv88e6390_avb_ops, .ptp_ops = &mv88e6352_ptp_ops, - .phylink_validate = mv88e6390_phylink_validate, + .phylink_get_caps = mv88e6390_phylink_get_caps, }; static const struct mv88e6xxx_ops mv88e6240_ops = { @@ -4354,6 +4893,8 @@ static const struct mv88e6xxx_ops mv88e6240_ops = { .atu_set_hash = mv88e6165_g1_atu_set_hash, .vtu_getnext = mv88e6352_g1_vtu_getnext, .vtu_loadpurge = mv88e6352_g1_vtu_loadpurge, + .stu_getnext = mv88e6352_g1_stu_getnext, + .stu_loadpurge = mv88e6352_g1_stu_loadpurge, .serdes_get_lane = mv88e6352_serdes_get_lane, .serdes_pcs_get_state = mv88e6352_serdes_pcs_get_state, .serdes_pcs_config = mv88e6352_serdes_pcs_config, @@ -4365,10 +4906,11 @@ static const struct mv88e6xxx_ops mv88e6240_ops = { .serdes_irq_status = mv88e6352_serdes_irq_status, .serdes_get_regs_len = mv88e6352_serdes_get_regs_len, .serdes_get_regs = mv88e6352_serdes_get_regs, + .serdes_set_tx_amplitude = mv88e6352_serdes_set_tx_amplitude, .gpio_ops = &mv88e6352_gpio_ops, .avb_ops = &mv88e6352_avb_ops, .ptp_ops = &mv88e6352_ptp_ops, - .phylink_validate = mv88e6352_phylink_validate, + .phylink_get_caps = mv88e6352_phylink_get_caps, }; static const struct mv88e6xxx_ops mv88e6250_ops = { @@ -4408,7 +4950,7 @@ static const struct mv88e6xxx_ops mv88e6250_ops = { .vtu_loadpurge = mv88e6185_g1_vtu_loadpurge, .avb_ops = &mv88e6352_avb_ops, .ptp_ops = &mv88e6250_ptp_ops, - .phylink_validate = mv88e6065_phylink_validate, + .phylink_get_caps = mv88e6250_phylink_get_caps, }; static const struct mv88e6xxx_ops mv88e6290_ops = { @@ -4453,6 +4995,8 @@ static const struct mv88e6xxx_ops mv88e6290_ops = { .atu_set_hash = mv88e6165_g1_atu_set_hash, .vtu_getnext = mv88e6390_g1_vtu_getnext, .vtu_loadpurge = mv88e6390_g1_vtu_loadpurge, + .stu_getnext = mv88e6390_g1_stu_getnext, + .stu_loadpurge = mv88e6390_g1_stu_loadpurge, .serdes_power = mv88e6390_serdes_power, .serdes_get_lane = mv88e6390_serdes_get_lane, /* Check status register pause & lpa register */ @@ -4470,7 +5014,7 @@ static const struct mv88e6xxx_ops mv88e6290_ops = { .gpio_ops = &mv88e6352_gpio_ops, .avb_ops = &mv88e6390_avb_ops, .ptp_ops = &mv88e6352_ptp_ops, - .phylink_validate = mv88e6390_phylink_validate, + .phylink_get_caps = mv88e6390_phylink_get_caps, }; static const struct mv88e6xxx_ops mv88e6320_ops = { @@ -4514,7 +5058,7 @@ static const struct mv88e6xxx_ops mv88e6320_ops = { .gpio_ops = &mv88e6352_gpio_ops, .avb_ops = &mv88e6352_avb_ops, .ptp_ops = &mv88e6352_ptp_ops, - .phylink_validate = mv88e6185_phylink_validate, + .phylink_get_caps = mv88e6185_phylink_get_caps, }; static const struct mv88e6xxx_ops mv88e6321_ops = { @@ -4556,7 +5100,7 @@ static const struct mv88e6xxx_ops mv88e6321_ops = { .gpio_ops = &mv88e6352_gpio_ops, .avb_ops = &mv88e6352_avb_ops, .ptp_ops = &mv88e6352_ptp_ops, - .phylink_validate = mv88e6185_phylink_validate, + .phylink_get_caps = mv88e6185_phylink_get_caps, }; static const struct mv88e6xxx_ops mv88e6341_ops = { @@ -4604,6 +5148,8 @@ static const struct mv88e6xxx_ops mv88e6341_ops = { .atu_set_hash = mv88e6165_g1_atu_set_hash, .vtu_getnext = mv88e6352_g1_vtu_getnext, .vtu_loadpurge = mv88e6352_g1_vtu_loadpurge, + .stu_getnext = mv88e6352_g1_stu_getnext, + .stu_loadpurge = mv88e6352_g1_stu_loadpurge, .serdes_power = mv88e6390_serdes_power, .serdes_get_lane = mv88e6341_serdes_get_lane, /* Check status register pause & lpa register */ @@ -4622,7 +5168,7 @@ static const struct mv88e6xxx_ops mv88e6341_ops = { .serdes_get_stats = mv88e6390_serdes_get_stats, .serdes_get_regs_len = mv88e6390_serdes_get_regs_len, .serdes_get_regs = mv88e6390_serdes_get_regs, - .phylink_validate = mv88e6341_phylink_validate, + .phylink_get_caps = mv88e6341_phylink_get_caps, }; static const struct mv88e6xxx_ops mv88e6350_ops = { @@ -4664,7 +5210,9 @@ static const struct mv88e6xxx_ops mv88e6350_ops = { .atu_set_hash = mv88e6165_g1_atu_set_hash, .vtu_getnext = mv88e6352_g1_vtu_getnext, .vtu_loadpurge = mv88e6352_g1_vtu_loadpurge, - .phylink_validate = mv88e6185_phylink_validate, + .stu_getnext = mv88e6352_g1_stu_getnext, + .stu_loadpurge = mv88e6352_g1_stu_loadpurge, + .phylink_get_caps = mv88e6185_phylink_get_caps, }; static const struct mv88e6xxx_ops mv88e6351_ops = { @@ -4706,9 +5254,11 @@ static const struct mv88e6xxx_ops mv88e6351_ops = { .atu_set_hash = mv88e6165_g1_atu_set_hash, .vtu_getnext = mv88e6352_g1_vtu_getnext, .vtu_loadpurge = mv88e6352_g1_vtu_loadpurge, + .stu_getnext = mv88e6352_g1_stu_getnext, + .stu_loadpurge = mv88e6352_g1_stu_loadpurge, .avb_ops = &mv88e6352_avb_ops, .ptp_ops = &mv88e6352_ptp_ops, - .phylink_validate = mv88e6185_phylink_validate, + .phylink_get_caps = mv88e6185_phylink_get_caps, }; static const struct mv88e6xxx_ops mv88e6352_ops = { @@ -4754,6 +5304,8 @@ static const struct mv88e6xxx_ops mv88e6352_ops = { .atu_set_hash = mv88e6165_g1_atu_set_hash, .vtu_getnext = mv88e6352_g1_vtu_getnext, .vtu_loadpurge = mv88e6352_g1_vtu_loadpurge, + .stu_getnext = mv88e6352_g1_stu_getnext, + .stu_loadpurge = mv88e6352_g1_stu_loadpurge, .serdes_get_lane = mv88e6352_serdes_get_lane, .serdes_pcs_get_state = mv88e6352_serdes_pcs_get_state, .serdes_pcs_config = mv88e6352_serdes_pcs_config, @@ -4771,7 +5323,8 @@ static const struct mv88e6xxx_ops mv88e6352_ops = { .serdes_get_stats = mv88e6352_serdes_get_stats, .serdes_get_regs_len = mv88e6352_serdes_get_regs_len, .serdes_get_regs = mv88e6352_serdes_get_regs, - .phylink_validate = mv88e6352_phylink_validate, + .serdes_set_tx_amplitude = mv88e6352_serdes_set_tx_amplitude, + .phylink_get_caps = mv88e6352_phylink_get_caps, }; static const struct mv88e6xxx_ops mv88e6390_ops = { @@ -4818,6 +5371,8 @@ static const struct mv88e6xxx_ops mv88e6390_ops = { .atu_set_hash = mv88e6165_g1_atu_set_hash, .vtu_getnext = mv88e6390_g1_vtu_getnext, .vtu_loadpurge = mv88e6390_g1_vtu_loadpurge, + .stu_getnext = mv88e6390_g1_stu_getnext, + .stu_loadpurge = mv88e6390_g1_stu_loadpurge, .serdes_power = mv88e6390_serdes_power, .serdes_get_lane = mv88e6390_serdes_get_lane, /* Check status register pause & lpa register */ @@ -4836,7 +5391,7 @@ static const struct mv88e6xxx_ops mv88e6390_ops = { .serdes_get_stats = mv88e6390_serdes_get_stats, .serdes_get_regs_len = mv88e6390_serdes_get_regs_len, .serdes_get_regs = mv88e6390_serdes_get_regs, - .phylink_validate = mv88e6390_phylink_validate, + .phylink_get_caps = mv88e6390_phylink_get_caps, }; static const struct mv88e6xxx_ops mv88e6390x_ops = { @@ -4883,6 +5438,8 @@ static const struct mv88e6xxx_ops mv88e6390x_ops = { .atu_set_hash = mv88e6165_g1_atu_set_hash, .vtu_getnext = mv88e6390_g1_vtu_getnext, .vtu_loadpurge = mv88e6390_g1_vtu_loadpurge, + .stu_getnext = mv88e6390_g1_stu_getnext, + .stu_loadpurge = mv88e6390_g1_stu_loadpurge, .serdes_power = mv88e6390_serdes_power, .serdes_get_lane = mv88e6390x_serdes_get_lane, .serdes_pcs_get_state = mv88e6390_serdes_pcs_get_state, @@ -4900,7 +5457,7 @@ static const struct mv88e6xxx_ops mv88e6390x_ops = { .gpio_ops = &mv88e6352_gpio_ops, .avb_ops = &mv88e6390_avb_ops, .ptp_ops = &mv88e6352_ptp_ops, - .phylink_validate = mv88e6390x_phylink_validate, + .phylink_get_caps = mv88e6390x_phylink_get_caps, }; static const struct mv88e6xxx_ops mv88e6393x_ops = { @@ -4951,6 +5508,8 @@ static const struct mv88e6xxx_ops mv88e6393x_ops = { .atu_set_hash = mv88e6165_g1_atu_set_hash, .vtu_getnext = mv88e6390_g1_vtu_getnext, .vtu_loadpurge = mv88e6390_g1_vtu_loadpurge, + .stu_getnext = mv88e6390_g1_stu_getnext, + .stu_loadpurge = mv88e6390_g1_stu_loadpurge, .serdes_power = mv88e6393x_serdes_power, .serdes_get_lane = mv88e6393x_serdes_get_lane, .serdes_pcs_get_state = mv88e6393x_serdes_pcs_get_state, @@ -4964,7 +5523,7 @@ static const struct mv88e6xxx_ops mv88e6393x_ops = { .gpio_ops = &mv88e6352_gpio_ops, .avb_ops = &mv88e6390_avb_ops, .ptp_ops = &mv88e6352_ptp_ops, - .phylink_validate = mv88e6393x_phylink_validate, + .phylink_get_caps = mv88e6393x_phylink_get_caps, }; static const struct mv88e6xxx_info mv88e6xxx_table[] = { @@ -4977,6 +5536,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { .num_ports = 10, .num_internal_phys = 5, .max_vid = 4095, + .max_sid = 63, .port_base_addr = 0x10, .phy_base_addr = 0x0, .global1_addr = 0x1b, @@ -5019,6 +5579,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { .num_ports = 11, .num_internal_phys = 8, .max_vid = 4095, + .max_sid = 63, .port_base_addr = 0x10, .phy_base_addr = 0x0, .global1_addr = 0x1b, @@ -5042,6 +5603,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { .num_ports = 3, .num_internal_phys = 5, .max_vid = 4095, + .max_sid = 63, .port_base_addr = 0x10, .phy_base_addr = 0x0, .global1_addr = 0x1b, @@ -5086,6 +5648,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { .num_internal_phys = 5, .num_gpio = 11, .max_vid = 4095, + .max_sid = 63, .port_base_addr = 0x10, .phy_base_addr = 0x10, .global1_addr = 0x1b, @@ -5109,6 +5672,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { .num_ports = 6, .num_internal_phys = 5, .max_vid = 4095, + .max_sid = 63, .port_base_addr = 0x10, .phy_base_addr = 0x0, .global1_addr = 0x1b, @@ -5133,6 +5697,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { .num_ports = 6, .num_internal_phys = 0, .max_vid = 4095, + .max_sid = 63, .port_base_addr = 0x10, .phy_base_addr = 0x0, .global1_addr = 0x1b, @@ -5156,6 +5721,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { .num_ports = 7, .num_internal_phys = 5, .max_vid = 4095, + .max_sid = 63, .port_base_addr = 0x10, .phy_base_addr = 0x0, .global1_addr = 0x1b, @@ -5180,6 +5746,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { .num_internal_phys = 5, .num_gpio = 15, .max_vid = 4095, + .max_sid = 63, .port_base_addr = 0x10, .phy_base_addr = 0x0, .global1_addr = 0x1b, @@ -5203,6 +5770,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { .num_ports = 7, .num_internal_phys = 5, .max_vid = 4095, + .max_sid = 63, .port_base_addr = 0x10, .phy_base_addr = 0x0, .global1_addr = 0x1b, @@ -5227,6 +5795,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { .num_internal_phys = 5, .num_gpio = 15, .max_vid = 4095, + .max_sid = 63, .port_base_addr = 0x10, .phy_base_addr = 0x0, .global1_addr = 0x1b, @@ -5272,6 +5841,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { .num_internal_phys = 9, .num_gpio = 16, .max_vid = 8191, + .max_sid = 63, .port_base_addr = 0x0, .phy_base_addr = 0x0, .global1_addr = 0x1b, @@ -5295,6 +5865,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { .num_internal_phys = 9, .num_gpio = 16, .max_vid = 8191, + .max_sid = 63, .port_base_addr = 0x0, .phy_base_addr = 0x0, .global1_addr = 0x1b, @@ -5317,6 +5888,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { .num_ports = 11, /* 10 + Z80 */ .num_internal_phys = 9, .max_vid = 8191, + .max_sid = 63, .port_base_addr = 0x0, .phy_base_addr = 0x0, .global1_addr = 0x1b, @@ -5339,6 +5911,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { .num_ports = 11, /* 10 + Z80 */ .num_internal_phys = 9, .max_vid = 8191, + .max_sid = 63, .port_base_addr = 0x0, .phy_base_addr = 0x0, .global1_addr = 0x1b, @@ -5361,6 +5934,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { .num_ports = 11, /* 10 + Z80 */ .num_internal_phys = 9, .max_vid = 8191, + .max_sid = 63, .port_base_addr = 0x0, .phy_base_addr = 0x0, .global1_addr = 0x1b, @@ -5411,6 +5985,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { .num_internal_phys = 5, .num_gpio = 15, .max_vid = 4095, + .max_sid = 63, .port_base_addr = 0x10, .phy_base_addr = 0x0, .global1_addr = 0x1b, @@ -5456,6 +6031,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { .num_internal_phys = 9, .num_gpio = 16, .max_vid = 8191, + .max_sid = 63, .port_base_addr = 0x0, .phy_base_addr = 0x0, .global1_addr = 0x1b, @@ -5529,6 +6105,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { .num_ports = 6, .num_gpio = 11, .max_vid = 4095, + .max_sid = 63, .port_base_addr = 0x10, .phy_base_addr = 0x10, .global1_addr = 0x1b, @@ -5553,6 +6130,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { .num_ports = 7, .num_internal_phys = 5, .max_vid = 4095, + .max_sid = 63, .port_base_addr = 0x10, .phy_base_addr = 0x0, .global1_addr = 0x1b, @@ -5576,6 +6154,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { .num_ports = 7, .num_internal_phys = 5, .max_vid = 4095, + .max_sid = 63, .port_base_addr = 0x10, .phy_base_addr = 0x0, .global1_addr = 0x1b, @@ -5600,6 +6179,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { .num_internal_phys = 5, .num_gpio = 15, .max_vid = 4095, + .max_sid = 63, .port_base_addr = 0x10, .phy_base_addr = 0x0, .global1_addr = 0x1b, @@ -5624,6 +6204,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { .num_internal_phys = 9, .num_gpio = 16, .max_vid = 8191, + .max_sid = 63, .port_base_addr = 0x0, .phy_base_addr = 0x0, .global1_addr = 0x1b, @@ -5648,6 +6229,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { .num_internal_phys = 9, .num_gpio = 16, .max_vid = 8191, + .max_sid = 63, .port_base_addr = 0x0, .phy_base_addr = 0x0, .global1_addr = 0x1b, @@ -5671,6 +6253,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { .num_ports = 11, /* 10 + Z80 */ .num_internal_phys = 9, .max_vid = 8191, + .max_sid = 63, .port_base_addr = 0x0, .phy_base_addr = 0x0, .global1_addr = 0x1b, @@ -5726,6 +6309,32 @@ static int mv88e6xxx_detect(struct mv88e6xxx_chip *chip) return 0; } +static int mv88e6xxx_single_chip_detect(struct mv88e6xxx_chip *chip, + struct mdio_device *mdiodev) +{ + int err; + + /* dual_chip takes precedence over single/multi-chip modes */ + if (chip->info->dual_chip) + return -EINVAL; + + /* If the mdio addr is 16 indicating the first port address of a switch + * (e.g. mv88e6*41) in single chip addressing mode the device may be + * configured in single chip addressing mode. Setup the smi access as + * single chip addressing mode and attempt to detect the model of the + * switch, if this fails the device is not configured in single chip + * addressing mode. + */ + if (mdiodev->addr != 16) + return -EINVAL; + + err = mv88e6xxx_smi_init(chip, mdiodev->bus, 0); + if (err) + return err; + + return mv88e6xxx_detect(chip); +} + static struct mv88e6xxx_chip *mv88e6xxx_alloc_chip(struct device *dev) { struct mv88e6xxx_chip *chip; @@ -5739,6 +6348,7 @@ static struct mv88e6xxx_chip *mv88e6xxx_alloc_chip(struct device *dev) mutex_init(&chip->reg_lock); INIT_LIST_HEAD(&chip->mdios); idr_init(&chip->policies); + INIT_LIST_HEAD(&chip->msts); return chip; } @@ -5752,11 +6362,12 @@ static enum dsa_tag_protocol mv88e6xxx_get_tag_protocol(struct dsa_switch *ds, return chip->tag_protocol; } -static int mv88e6xxx_change_tag_protocol(struct dsa_switch *ds, int port, +static int mv88e6xxx_change_tag_protocol(struct dsa_switch *ds, enum dsa_tag_protocol proto) { struct mv88e6xxx_chip *chip = ds->priv; enum dsa_tag_protocol old_protocol; + struct dsa_port *cpu_dp; int err; switch (proto) { @@ -5781,17 +6392,31 @@ static int mv88e6xxx_change_tag_protocol(struct dsa_switch *ds, int port, chip->tag_protocol = proto; mv88e6xxx_reg_lock(chip); - err = mv88e6xxx_setup_port_mode(chip, port); + dsa_switch_for_each_cpu_port(cpu_dp, ds) { + err = mv88e6xxx_setup_port_mode(chip, cpu_dp->index); + if (err) { + mv88e6xxx_reg_unlock(chip); + goto unwind; + } + } mv88e6xxx_reg_unlock(chip); - if (err) - chip->tag_protocol = old_protocol; + return 0; + +unwind: + chip->tag_protocol = old_protocol; + + mv88e6xxx_reg_lock(chip); + dsa_switch_for_each_cpu_port_continue_reverse(cpu_dp, ds) + mv88e6xxx_setup_port_mode(chip, cpu_dp->index); + mv88e6xxx_reg_unlock(chip); return err; } static int mv88e6xxx_port_mdb_add(struct dsa_switch *ds, int port, - const struct switchdev_obj_port_mdb *mdb) + const struct switchdev_obj_port_mdb *mdb, + struct dsa_db db) { struct mv88e6xxx_chip *chip = ds->priv; int err; @@ -5805,7 +6430,8 @@ static int mv88e6xxx_port_mdb_add(struct dsa_switch *ds, int port, } static int mv88e6xxx_port_mdb_del(struct dsa_switch *ds, int port, - const struct switchdev_obj_port_mdb *mdb) + const struct switchdev_obj_port_mdb *mdb, + struct dsa_db db) { struct mv88e6xxx_chip *chip = ds->priv; int err; @@ -5819,7 +6445,8 @@ static int mv88e6xxx_port_mdb_del(struct dsa_switch *ds, int port, static int mv88e6xxx_port_mirror_add(struct dsa_switch *ds, int port, struct dsa_mall_mirror_tc_entry *mirror, - bool ingress) + bool ingress, + struct netlink_ext_ack *extack) { enum mv88e6xxx_egress_direction direction = ingress ? MV88E6XXX_EGRESS_DIR_INGRESS : @@ -5893,7 +6520,7 @@ static int mv88e6xxx_port_pre_bridge_flags(struct dsa_switch *ds, int port, const struct mv88e6xxx_ops *ops; if (flags.mask & ~(BR_LEARNING | BR_FLOOD | BR_MCAST_FLOOD | - BR_BCAST_FLOOD)) + BR_BCAST_FLOOD | BR_PORT_LOCKED)) return -EINVAL; ops = chip->info->ops; @@ -5951,6 +6578,13 @@ static int mv88e6xxx_port_bridge_flags(struct dsa_switch *ds, int port, goto out; } + if (flags.mask & BR_PORT_LOCKED) { + bool locked = !!(flags.val & BR_PORT_LOCKED); + + err = mv88e6xxx_port_set_lock(chip, port, locked); + if (err) + goto out; + } out: mv88e6xxx_reg_unlock(chip); @@ -5958,32 +6592,40 @@ out: } static bool mv88e6xxx_lag_can_offload(struct dsa_switch *ds, - struct net_device *lag, - struct netdev_lag_upper_info *info) + struct dsa_lag lag, + struct netdev_lag_upper_info *info, + struct netlink_ext_ack *extack) { struct mv88e6xxx_chip *chip = ds->priv; struct dsa_port *dp; - int id, members = 0; + int members = 0; - if (!mv88e6xxx_has_lag(chip)) + if (!mv88e6xxx_has_lag(chip)) { + NL_SET_ERR_MSG_MOD(extack, "Chip does not support LAG offload"); return false; + } - id = dsa_lag_id(ds->dst, lag); - if (id < 0 || id >= ds->num_lag_ids) + if (!lag.id) return false; - dsa_lag_foreach_port(dp, ds->dst, lag) + dsa_lag_foreach_port(dp, ds->dst, &lag) /* Includes the port joining the LAG */ members++; - if (members > 8) + if (members > 8) { + NL_SET_ERR_MSG_MOD(extack, + "Cannot offload more than 8 LAG ports"); return false; + } /* We could potentially relax this to include active * backup in the future. */ - if (info->tx_type != NETDEV_LAG_TX_TYPE_HASH) + if (info->tx_type != NETDEV_LAG_TX_TYPE_HASH) { + NL_SET_ERR_MSG_MOD(extack, + "Can only offload LAG using hash TX type"); return false; + } /* Ideally we would also validate that the hash type matches * the hardware. Alas, this is always set to unknown on team @@ -5992,20 +6634,21 @@ static bool mv88e6xxx_lag_can_offload(struct dsa_switch *ds, return true; } -static int mv88e6xxx_lag_sync_map(struct dsa_switch *ds, struct net_device *lag) +static int mv88e6xxx_lag_sync_map(struct dsa_switch *ds, struct dsa_lag lag) { struct mv88e6xxx_chip *chip = ds->priv; struct dsa_port *dp; u16 map = 0; int id; - id = dsa_lag_id(ds->dst, lag); + /* DSA LAG IDs are one-based, hardware is zero-based */ + id = lag.id - 1; /* Build the map of all ports to distribute flows destined for * this LAG. This can be either a local user port, or a DSA * port if the LAG port is on a remote chip. */ - dsa_lag_foreach_port(dp, ds->dst, lag) + dsa_lag_foreach_port(dp, ds->dst, &lag) map |= BIT(dsa_towards_port(ds, dp->ds->index, dp->index)); return mv88e6xxx_g2_trunk_mapping_write(chip, id, map); @@ -6050,8 +6693,8 @@ static int mv88e6xxx_lag_sync_masks(struct dsa_switch *ds) { struct mv88e6xxx_chip *chip = ds->priv; unsigned int id, num_tx; - struct net_device *lag; struct dsa_port *dp; + struct dsa_lag *lag; int i, err, nth; u16 mask[8]; u16 ivec; @@ -6060,8 +6703,8 @@ static int mv88e6xxx_lag_sync_masks(struct dsa_switch *ds) ivec = BIT(mv88e6xxx_num_ports(chip)) - 1; /* Disable all masks for ports that _are_ members of a LAG. */ - list_for_each_entry(dp, &ds->dst->ports, list) { - if (!dp->lag_dev || dp->ds != ds) + dsa_switch_for_each_port(dp, ds) { + if (!dp->lag) continue; ivec &= ~BIT(dp->index); @@ -6074,7 +6717,7 @@ static int mv88e6xxx_lag_sync_masks(struct dsa_switch *ds) * are in the Tx set. */ dsa_lags_foreach_id(id, ds->dst) { - lag = dsa_lag_dev(ds->dst, id); + lag = dsa_lag_by_id(ds->dst, id); if (!lag) continue; @@ -6110,7 +6753,7 @@ static int mv88e6xxx_lag_sync_masks(struct dsa_switch *ds) } static int mv88e6xxx_lag_sync_masks_map(struct dsa_switch *ds, - struct net_device *lag) + struct dsa_lag lag) { int err; @@ -6134,16 +6777,18 @@ static int mv88e6xxx_port_lag_change(struct dsa_switch *ds, int port) } static int mv88e6xxx_port_lag_join(struct dsa_switch *ds, int port, - struct net_device *lag, - struct netdev_lag_upper_info *info) + struct dsa_lag lag, + struct netdev_lag_upper_info *info, + struct netlink_ext_ack *extack) { struct mv88e6xxx_chip *chip = ds->priv; int err, id; - if (!mv88e6xxx_lag_can_offload(ds, lag, info)) + if (!mv88e6xxx_lag_can_offload(ds, lag, info, extack)) return -EOPNOTSUPP; - id = dsa_lag_id(ds->dst, lag); + /* DSA LAG IDs are one-based */ + id = lag.id - 1; mv88e6xxx_reg_lock(chip); @@ -6166,7 +6811,7 @@ err_unlock: } static int mv88e6xxx_port_lag_leave(struct dsa_switch *ds, int port, - struct net_device *lag) + struct dsa_lag lag) { struct mv88e6xxx_chip *chip = ds->priv; int err_sync, err_trunk; @@ -6191,13 +6836,14 @@ static int mv88e6xxx_crosschip_lag_change(struct dsa_switch *ds, int sw_index, } static int mv88e6xxx_crosschip_lag_join(struct dsa_switch *ds, int sw_index, - int port, struct net_device *lag, - struct netdev_lag_upper_info *info) + int port, struct dsa_lag lag, + struct netdev_lag_upper_info *info, + struct netlink_ext_ack *extack) { struct mv88e6xxx_chip *chip = ds->priv; int err; - if (!mv88e6xxx_lag_can_offload(ds, lag, info)) + if (!mv88e6xxx_lag_can_offload(ds, lag, info, extack)) return -EOPNOTSUPP; mv88e6xxx_reg_lock(chip); @@ -6214,7 +6860,7 @@ unlock: } static int mv88e6xxx_crosschip_lag_leave(struct dsa_switch *ds, int sw_index, - int port, struct net_device *lag) + int port, struct dsa_lag lag) { struct mv88e6xxx_chip *chip = ds->priv; int err_sync, err_pvt; @@ -6233,7 +6879,7 @@ static const struct dsa_switch_ops mv88e6xxx_switch_ops = { .teardown = mv88e6xxx_teardown, .port_setup = mv88e6xxx_port_setup, .port_teardown = mv88e6xxx_port_teardown, - .phylink_validate = mv88e6xxx_validate, + .phylink_get_caps = mv88e6xxx_get_caps, .phylink_mac_link_state = mv88e6xxx_serdes_pcs_get_state, .phylink_mac_config = mv88e6xxx_mac_config, .phylink_mac_an_restart = mv88e6xxx_serdes_pcs_an_restart, @@ -6261,15 +6907,18 @@ static const struct dsa_switch_ops mv88e6xxx_switch_ops = { .port_pre_bridge_flags = mv88e6xxx_port_pre_bridge_flags, .port_bridge_flags = mv88e6xxx_port_bridge_flags, .port_stp_state_set = mv88e6xxx_port_stp_state_set, + .port_mst_state_set = mv88e6xxx_port_mst_state_set, .port_fast_age = mv88e6xxx_port_fast_age, + .port_vlan_fast_age = mv88e6xxx_port_vlan_fast_age, .port_vlan_filtering = mv88e6xxx_port_vlan_filtering, .port_vlan_add = mv88e6xxx_port_vlan_add, .port_vlan_del = mv88e6xxx_port_vlan_del, - .port_fdb_add = mv88e6xxx_port_fdb_add, - .port_fdb_del = mv88e6xxx_port_fdb_del, - .port_fdb_dump = mv88e6xxx_port_fdb_dump, - .port_mdb_add = mv88e6xxx_port_mdb_add, - .port_mdb_del = mv88e6xxx_port_mdb_del, + .vlan_msti_set = mv88e6xxx_vlan_msti_set, + .port_fdb_add = mv88e6xxx_port_fdb_add, + .port_fdb_del = mv88e6xxx_port_fdb_del, + .port_fdb_dump = mv88e6xxx_port_fdb_dump, + .port_mdb_add = mv88e6xxx_port_mdb_add, + .port_mdb_del = mv88e6xxx_port_mdb_del, .port_mirror_add = mv88e6xxx_port_mirror_add, .port_mirror_del = mv88e6xxx_port_mirror_del, .crosschip_bridge_join = mv88e6xxx_crosschip_bridge_join, @@ -6288,8 +6937,6 @@ static const struct dsa_switch_ops mv88e6xxx_switch_ops = { .crosschip_lag_change = mv88e6xxx_crosschip_lag_change, .crosschip_lag_join = mv88e6xxx_crosschip_lag_join, .crosschip_lag_leave = mv88e6xxx_crosschip_lag_leave, - .port_bridge_tx_fwd_offload = mv88e6xxx_bridge_tx_fwd_offload, - .port_bridge_tx_fwd_unoffload = mv88e6xxx_bridge_tx_fwd_unoffload, }; static int mv88e6xxx_register_switch(struct mv88e6xxx_chip *chip) @@ -6396,10 +7043,6 @@ static int mv88e6xxx_probe(struct mdio_device *mdiodev) chip->info = compat_info; - err = mv88e6xxx_smi_init(chip, mdiodev->bus, mdiodev->addr); - if (err) - goto out; - chip->reset = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_LOW); if (IS_ERR(chip->reset)) { err = PTR_ERR(chip->reset); @@ -6408,9 +7051,19 @@ static int mv88e6xxx_probe(struct mdio_device *mdiodev) if (chip->reset) usleep_range(1000, 2000); - err = mv88e6xxx_detect(chip); - if (err) - goto out; + /* Detect if the device is configured in single chip addressing mode, + * otherwise continue with address specific smi init/detection. + */ + err = mv88e6xxx_single_chip_detect(chip, mdiodev); + if (err) { + err = mv88e6xxx_smi_init(chip, mdiodev->bus, mdiodev->addr); + if (err) + goto out; + + err = mv88e6xxx_detect(chip); + if (err) + goto out; + } if (chip->info->edsa_support == MV88E6XXX_EDSA_SUPPORTED) chip->tag_protocol = DSA_TAG_PROTO_EDSA; @@ -6532,8 +7185,6 @@ static void mv88e6xxx_remove(struct mdio_device *mdiodev) mv88e6xxx_g1_irq_free(chip); else mv88e6xxx_irq_poll_free(chip); - - dev_set_drvdata(&mdiodev->dev, NULL); } static void mv88e6xxx_shutdown(struct mdio_device *mdiodev) |