From 1c055e55a242a33e574e48be530e06770a210dcd Mon Sep 17 00:00:00 2001 From: hc <hc@nodka.com> Date: Mon, 19 Feb 2024 03:26:26 +0000 Subject: [PATCH] add r8169 read mac form eeprom --- kernel/drivers/net/dsa/b53/b53_common.c | 726 +++++++++++++++++++++++++++++++++++++++++++++---------- 1 files changed, 592 insertions(+), 134 deletions(-) diff --git a/kernel/drivers/net/dsa/b53/b53_common.c b/kernel/drivers/net/dsa/b53/b53_common.c index 451121f..d3b37ce 100644 --- a/kernel/drivers/net/dsa/b53/b53_common.c +++ b/kernel/drivers/net/dsa/b53/b53_common.c @@ -17,8 +17,6 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt - #include <linux/delay.h> #include <linux/export.h> #include <linux/gpio.h> @@ -26,6 +24,7 @@ #include <linux/module.h> #include <linux/platform_data/b53.h> #include <linux/phy.h> +#include <linux/phylink.h> #include <linux/etherdevice.h> #include <linux/if_bridge.h> #include <net/dsa.h> @@ -341,6 +340,13 @@ b53_read8(dev, B53_CTRL_PAGE, B53_SWITCH_CTRL, &mgmt); mgmt |= B53_MII_DUMB_FWDG_EN; b53_write8(dev, B53_CTRL_PAGE, B53_SWITCH_CTRL, mgmt); + + /* Look at B53_UC_FWD_EN and B53_MC_FWD_EN to decide whether + * frames should be flooded or not. + */ + b53_read8(dev, B53_CTRL_PAGE, B53_IP_MULTICAST_CTRL, &mgmt); + mgmt |= B53_UC_FWD_EN | B53_MC_FWD_EN | B53_IPMC_FWD_EN; + b53_write8(dev, B53_CTRL_PAGE, B53_IP_MULTICAST_CTRL, mgmt); } static void b53_enable_vlan(struct b53_device *dev, bool enable, @@ -362,8 +368,6 @@ b53_read8(dev, B53_VLAN_PAGE, B53_VLAN_CTRL4, &vc4); b53_read8(dev, B53_VLAN_PAGE, B53_VLAN_CTRL5, &vc5); } - - mgmt &= ~SM_SW_FWD_MODE; if (enable) { vc0 |= VC0_VLAN_EN | VC0_VID_CHK_EN | VC0_VID_HASH_VID; @@ -427,7 +431,6 @@ b53_write8(dev, B53_CTRL_PAGE, B53_SWITCH_MODE, mgmt); dev->vlan_enabled = enable; - dev->vlan_filtering_enabled = enable_filtering; } static int b53_set_jumbo(struct b53_device *dev, bool enable, bool allow_10_100) @@ -523,10 +526,22 @@ int b53_enable_port(struct dsa_switch *ds, int port, struct phy_device *phy) { struct b53_device *dev = ds->priv; - unsigned int cpu_port = ds->ports[port].cpu_dp->index; + unsigned int cpu_port; + int ret = 0; u16 pvlan; + if (!dsa_is_user_port(ds, port)) + return 0; + + cpu_port = dsa_to_port(ds, port)->cpu_dp->index; + + b53_br_egress_floods(ds, port, true, true); b53_port_set_learning(dev, port, false); + + if (dev->ops->irq_enable) + ret = dev->ops->irq_enable(dev, port); + if (ret) + return ret; /* Clear the Rx and Tx disable bits and set to no spanning tree */ b53_write8(dev, B53_CTRL_PAGE, B53_PORT_CTRL(port), 0); @@ -551,7 +566,7 @@ } EXPORT_SYMBOL(b53_enable_port); -void b53_disable_port(struct dsa_switch *ds, int port, struct phy_device *phy) +void b53_disable_port(struct dsa_switch *ds, int port) { struct b53_device *dev = ds->priv; u8 reg; @@ -560,14 +575,16 @@ b53_read8(dev, B53_CTRL_PAGE, B53_PORT_CTRL(port), ®); reg |= PORT_CTRL_RX_DISABLE | PORT_CTRL_TX_DISABLE; b53_write8(dev, B53_CTRL_PAGE, B53_PORT_CTRL(port), reg); + + if (dev->ops->irq_disable) + dev->ops->irq_disable(dev, port); } EXPORT_SYMBOL(b53_disable_port); void b53_brcm_hdr_setup(struct dsa_switch *ds, int port) { - bool tag_en = !(ds->ops->get_tag_protocol(ds, port) == - DSA_TAG_PROTO_NONE); struct b53_device *dev = ds->priv; + bool tag_en = !(dev->tag_protocol == DSA_TAG_PROTO_NONE); u8 hdr_ctl, val; u16 reg; @@ -586,6 +603,22 @@ val = 0; break; } + + /* Enable management mode if tagging is requested */ + b53_read8(dev, B53_CTRL_PAGE, B53_SWITCH_MODE, &hdr_ctl); + if (tag_en) + hdr_ctl |= SM_SW_FWD_MODE; + else + hdr_ctl &= ~SM_SW_FWD_MODE; + b53_write8(dev, B53_CTRL_PAGE, B53_SWITCH_MODE, hdr_ctl); + + /* Configure the appropriate IMP port */ + b53_read8(dev, B53_MGMT_PAGE, B53_GLOBAL_CONFIG, &hdr_ctl); + if (port == 8) + hdr_ctl |= GC_FRM_MGMT_PORT_MII; + else if (port == 5) + hdr_ctl |= GC_FRM_MGMT_PORT_M; + b53_write8(dev, B53_MGMT_PAGE, B53_GLOBAL_CONFIG, hdr_ctl); /* Enable Broadcom tags for IMP port */ b53_read8(dev, B53_MGMT_PAGE, B53_BRCM_HDR, &hdr_ctl); @@ -635,6 +668,8 @@ b53_write8(dev, B53_CTRL_PAGE, B53_PORT_CTRL(port), port_ctrl); b53_brcm_hdr_setup(dev->ds, port); + + b53_br_egress_floods(dev->ds, port, true, true); b53_port_set_learning(dev, port, false); } @@ -659,7 +694,9 @@ { struct b53_device *dev = ds->priv; struct b53_vlan vl = { 0 }; + struct b53_vlan *v; int i, def_vid; + u16 vid; def_vid = b53_default_pvid(dev); @@ -671,14 +708,24 @@ b53_do_vlan_op(dev, VTA_CMD_CLEAR); } - b53_enable_vlan(dev, dev->vlan_enabled, dev->vlan_filtering_enabled); + b53_enable_vlan(dev, dev->vlan_enabled, ds->vlan_filtering); b53_for_each_port(dev, i) b53_write16(dev, B53_VLAN_PAGE, B53_VLAN_PORT_DEF_TAG(i), def_vid); - if (!is5325(dev) && !is5365(dev)) - b53_set_jumbo(dev, dev->enable_jumbo, false); + /* Upon initial call we have not set-up any VLANs, but upon + * system resume, we need to restore all VLAN entries. + */ + for (vid = def_vid; vid < dev->num_vlans; vid++) { + v = &dev->vlans[vid]; + + if (!v->members) + continue; + + b53_set_vlan_entry(dev, vid, v); + b53_fast_age_vlan(dev, vid); + } return 0; } @@ -733,8 +780,11 @@ usleep_range(1000, 2000); } while (timeout-- > 0); - if (timeout == 0) + if (timeout == 0) { + dev_err(dev->dev, + "Timeout waiting for SW_RST to clear!\n"); return -ETIMEDOUT; + } } b53_read8(dev, B53_CTRL_PAGE, B53_SWITCH_MODE, &mgmt); @@ -785,10 +835,10 @@ static int b53_reset_switch(struct b53_device *priv) { /* reset vlans */ - priv->enable_jumbo = false; - memset(priv->vlans, 0, sizeof(*priv->vlans) * priv->num_vlans); memset(priv->ports, 0, sizeof(*priv->ports) * priv->num_ports); + + priv->serdes_lane = B53_INVALID_LANE; return b53_switch_reset(priv); } @@ -942,6 +992,54 @@ } EXPORT_SYMBOL(b53_get_sset_count); +enum b53_devlink_resource_id { + B53_DEVLINK_PARAM_ID_VLAN_TABLE, +}; + +static u64 b53_devlink_vlan_table_get(void *priv) +{ + struct b53_device *dev = priv; + struct b53_vlan *vl; + unsigned int i; + u64 count = 0; + + for (i = 0; i < dev->num_vlans; i++) { + vl = &dev->vlans[i]; + if (vl->members) + count++; + } + + return count; +} + +int b53_setup_devlink_resources(struct dsa_switch *ds) +{ + struct devlink_resource_size_params size_params; + struct b53_device *dev = ds->priv; + int err; + + devlink_resource_size_params_init(&size_params, dev->num_vlans, + dev->num_vlans, + 1, DEVLINK_RESOURCE_UNIT_ENTRY); + + err = dsa_devlink_resource_register(ds, "VLAN", dev->num_vlans, + B53_DEVLINK_PARAM_ID_VLAN_TABLE, + DEVLINK_RESOURCE_ID_PARENT_TOP, + &size_params); + if (err) + goto out; + + dsa_devlink_resource_occ_get_register(ds, + B53_DEVLINK_PARAM_ID_VLAN_TABLE, + b53_devlink_vlan_table_get, dev); + + return 0; +out: + dsa_devlink_resources_unregister(ds); + return err; +} +EXPORT_SYMBOL(b53_setup_devlink_resources); + static int b53_setup(struct dsa_switch *ds) { struct b53_device *dev = ds->priv; @@ -957,52 +1055,77 @@ b53_reset_mib(dev); ret = b53_apply_config(dev); - if (ret) + if (ret) { dev_err(ds->dev, "failed to apply configuration\n"); + return ret; + } - /* Configure IMP/CPU port, disable unused ports. Enabled + /* Configure IMP/CPU port, disable all other ports. Enabled * ports will be configured with .port_enable */ for (port = 0; port < dev->num_ports; port++) { if (dsa_is_cpu_port(ds, port)) b53_enable_cpu_port(dev, port); - else if (dsa_is_unused_port(ds, port)) - b53_disable_port(ds, port, NULL); + else + b53_disable_port(ds, port); } - return ret; + return b53_setup_devlink_resources(ds); } -static void b53_adjust_link(struct dsa_switch *ds, int port, - struct phy_device *phydev) +static void b53_teardown(struct dsa_switch *ds) { - struct b53_device *dev = ds->priv; - struct ethtool_eee *p = &dev->ports[port].eee; - u8 rgmii_ctrl = 0, reg = 0, off; + dsa_devlink_resources_unregister(ds); +} - if (!phy_is_pseudo_fixed_link(phydev)) - return; +static void b53_force_link(struct b53_device *dev, int port, int link) +{ + u8 reg, val, off; /* Override the port settings */ - if (port == dev->cpu_port) { + if (port == dev->imp_port) { off = B53_PORT_OVERRIDE_CTRL; - reg = PORT_OVERRIDE_EN; + val = PORT_OVERRIDE_EN; } else { off = B53_GMII_PORT_OVERRIDE_CTRL(port); - reg = GMII_PO_EN; + val = GMII_PO_EN; } - /* Set the link UP */ - if (phydev->link) + b53_read8(dev, B53_CTRL_PAGE, off, ®); + reg |= val; + if (link) reg |= PORT_OVERRIDE_LINK; + else + reg &= ~PORT_OVERRIDE_LINK; + b53_write8(dev, B53_CTRL_PAGE, off, reg); +} - if (phydev->duplex == DUPLEX_FULL) +static void b53_force_port_config(struct b53_device *dev, int port, + int speed, int duplex, + bool tx_pause, bool rx_pause) +{ + u8 reg, val, off; + + /* Override the port settings */ + if (port == dev->imp_port) { + off = B53_PORT_OVERRIDE_CTRL; + val = PORT_OVERRIDE_EN; + } else { + off = B53_GMII_PORT_OVERRIDE_CTRL(port); + val = GMII_PO_EN; + } + + b53_read8(dev, B53_CTRL_PAGE, off, ®); + reg |= val; + if (duplex == DUPLEX_FULL) reg |= PORT_OVERRIDE_FULL_DUPLEX; + else + reg &= ~PORT_OVERRIDE_FULL_DUPLEX; - switch (phydev->speed) { + switch (speed) { case 2000: reg |= PORT_OVERRIDE_SPEED_2000M; - /* fallthrough */ + fallthrough; case SPEED_1000: reg |= PORT_OVERRIDE_SPEED_1000M; break; @@ -1013,24 +1136,46 @@ reg |= PORT_OVERRIDE_SPEED_10M; break; default: - dev_err(ds->dev, "unknown speed: %d\n", phydev->speed); + dev_err(dev->dev, "unknown speed: %d\n", speed); return; } + if (rx_pause) + reg |= PORT_OVERRIDE_RX_FLOW; + if (tx_pause) + reg |= PORT_OVERRIDE_TX_FLOW; + + b53_write8(dev, B53_CTRL_PAGE, off, reg); +} + +static void b53_adjust_link(struct dsa_switch *ds, int port, + struct phy_device *phydev) +{ + struct b53_device *dev = ds->priv; + struct ethtool_eee *p = &dev->ports[port].eee; + u8 rgmii_ctrl = 0, reg = 0, off; + bool tx_pause = false; + bool rx_pause = false; + + if (!phy_is_pseudo_fixed_link(phydev)) + return; + /* Enable flow control on BCM5301x's CPU port */ if (is5301x(dev) && port == dev->cpu_port) - reg |= PORT_OVERRIDE_RX_FLOW | PORT_OVERRIDE_TX_FLOW; + tx_pause = rx_pause = true; if (phydev->pause) { if (phydev->asym_pause) - reg |= PORT_OVERRIDE_TX_FLOW; - reg |= PORT_OVERRIDE_RX_FLOW; + tx_pause = true; + rx_pause = true; } - b53_write8(dev, B53_CTRL_PAGE, off, reg); + b53_force_port_config(dev, port, phydev->speed, phydev->duplex, + tx_pause, rx_pause); + b53_force_link(dev, port, phydev->link); if (is531x5(dev) && phy_interface_is_rgmii(phydev)) { - if (port == 8) + if (port == dev->imp_port) off = B53_RGMII_CTRL_IMP; else off = B53_RGMII_CTRL_P(port); @@ -1087,16 +1232,9 @@ } } else if (is5301x(dev)) { if (port != dev->cpu_port) { - u8 po_reg = B53_GMII_PORT_OVERRIDE_CTRL(dev->cpu_port); - u8 gmii_po; - - b53_read8(dev, B53_CTRL_PAGE, po_reg, &gmii_po); - gmii_po |= GMII_PO_LINK | - GMII_PO_RX_FLOW | - GMII_PO_TX_FLOW | - GMII_PO_EN | - GMII_PO_SPEED_2000M; - b53_write8(dev, B53_CTRL_PAGE, po_reg, gmii_po); + b53_force_port_config(dev, dev->cpu_port, 2000, + DUPLEX_FULL, true, true); + b53_force_link(dev, dev->cpu_port, 1); } } @@ -1104,45 +1242,153 @@ p->eee_enabled = b53_eee_init(ds, port, phydev); } -int b53_vlan_filtering(struct dsa_switch *ds, int port, bool vlan_filtering) +void b53_port_event(struct dsa_switch *ds, int port) { struct b53_device *dev = ds->priv; - struct net_device *bridge_dev; - unsigned int i; - u16 pvid, new_pvid; + bool link; + u16 sts; - /* Handle the case were multiple bridges span the same switch device - * and one of them has a different setting than what is being requested - * which would be breaking filtering semantics for any of the other - * bridge devices. + b53_read16(dev, B53_STAT_PAGE, B53_LINK_STAT, &sts); + link = !!(sts & BIT(port)); + dsa_port_phylink_mac_change(ds, port, link); +} +EXPORT_SYMBOL(b53_port_event); + +void b53_phylink_validate(struct dsa_switch *ds, int port, + unsigned long *supported, + struct phylink_link_state *state) +{ + struct b53_device *dev = ds->priv; + __ETHTOOL_DECLARE_LINK_MODE_MASK(mask) = { 0, }; + + if (dev->ops->serdes_phylink_validate) + dev->ops->serdes_phylink_validate(dev, port, mask, state); + + /* Allow all the expected bits */ + phylink_set(mask, Autoneg); + phylink_set_port_modes(mask); + phylink_set(mask, Pause); + phylink_set(mask, Asym_Pause); + + /* With the exclusion of 5325/5365, MII, Reverse MII and 802.3z, we + * support Gigabit, including Half duplex. */ - b53_for_each_port(dev, i) { - bridge_dev = dsa_to_port(ds, i)->bridge_dev; - if (bridge_dev && - bridge_dev != dsa_to_port(ds, port)->bridge_dev && - br_vlan_enabled(bridge_dev) != vlan_filtering) { - netdev_err(bridge_dev, - "VLAN filtering is global to the switch!\n"); - return -EINVAL; - } + if (state->interface != PHY_INTERFACE_MODE_MII && + state->interface != PHY_INTERFACE_MODE_REVMII && + !phy_interface_mode_is_8023z(state->interface) && + !(is5325(dev) || is5365(dev))) { + phylink_set(mask, 1000baseT_Full); + phylink_set(mask, 1000baseT_Half); } - b53_read16(dev, B53_VLAN_PAGE, B53_VLAN_PORT_DEF_TAG(port), &pvid); - new_pvid = pvid; - if (dev->vlan_filtering_enabled && !vlan_filtering) { - /* Filtering is currently enabled, use the default PVID since - * the bridge does not expect tagging anymore - */ - dev->ports[port].pvid = pvid; - new_pvid = b53_default_pvid(dev); - } else if (!dev->vlan_filtering_enabled && vlan_filtering) { - /* Filtering is currently disabled, restore the previous PVID */ - new_pvid = dev->ports[port].pvid; + if (!phy_interface_mode_is_8023z(state->interface)) { + phylink_set(mask, 10baseT_Half); + phylink_set(mask, 10baseT_Full); + phylink_set(mask, 100baseT_Half); + phylink_set(mask, 100baseT_Full); } - if (pvid != new_pvid) - b53_write16(dev, B53_VLAN_PAGE, B53_VLAN_PORT_DEF_TAG(port), - new_pvid); + bitmap_and(supported, supported, mask, + __ETHTOOL_LINK_MODE_MASK_NBITS); + bitmap_and(state->advertising, state->advertising, mask, + __ETHTOOL_LINK_MODE_MASK_NBITS); + + phylink_helper_basex_speed(state); +} +EXPORT_SYMBOL(b53_phylink_validate); + +int b53_phylink_mac_link_state(struct dsa_switch *ds, int port, + struct phylink_link_state *state) +{ + struct b53_device *dev = ds->priv; + int ret = -EOPNOTSUPP; + + if ((phy_interface_mode_is_8023z(state->interface) || + state->interface == PHY_INTERFACE_MODE_SGMII) && + dev->ops->serdes_link_state) + ret = dev->ops->serdes_link_state(dev, port, state); + + return ret; +} +EXPORT_SYMBOL(b53_phylink_mac_link_state); + +void b53_phylink_mac_config(struct dsa_switch *ds, int port, + unsigned int mode, + const struct phylink_link_state *state) +{ + struct b53_device *dev = ds->priv; + + if (mode == MLO_AN_PHY || mode == MLO_AN_FIXED) + return; + + if ((phy_interface_mode_is_8023z(state->interface) || + state->interface == PHY_INTERFACE_MODE_SGMII) && + dev->ops->serdes_config) + dev->ops->serdes_config(dev, port, mode, state); +} +EXPORT_SYMBOL(b53_phylink_mac_config); + +void b53_phylink_mac_an_restart(struct dsa_switch *ds, int port) +{ + struct b53_device *dev = ds->priv; + + if (dev->ops->serdes_an_restart) + dev->ops->serdes_an_restart(dev, port); +} +EXPORT_SYMBOL(b53_phylink_mac_an_restart); + +void b53_phylink_mac_link_down(struct dsa_switch *ds, int port, + unsigned int mode, + phy_interface_t interface) +{ + struct b53_device *dev = ds->priv; + + if (mode == MLO_AN_PHY) + return; + + if (mode == MLO_AN_FIXED) { + b53_force_link(dev, port, false); + return; + } + + if (phy_interface_mode_is_8023z(interface) && + dev->ops->serdes_link_set) + dev->ops->serdes_link_set(dev, port, mode, interface, false); +} +EXPORT_SYMBOL(b53_phylink_mac_link_down); + +void b53_phylink_mac_link_up(struct dsa_switch *ds, int port, + unsigned int mode, + phy_interface_t interface, + struct phy_device *phydev, + int speed, int duplex, + bool tx_pause, bool rx_pause) +{ + struct b53_device *dev = ds->priv; + + if (mode == MLO_AN_PHY) + return; + + if (mode == MLO_AN_FIXED) { + b53_force_port_config(dev, port, speed, duplex, + tx_pause, rx_pause); + b53_force_link(dev, port, true); + return; + } + + if (phy_interface_mode_is_8023z(interface) && + dev->ops->serdes_link_set) + dev->ops->serdes_link_set(dev, port, mode, interface, true); +} +EXPORT_SYMBOL(b53_phylink_mac_link_up); + +int b53_vlan_filtering(struct dsa_switch *ds, int port, bool vlan_filtering, + struct switchdev_trans *trans) +{ + struct b53_device *dev = ds->priv; + + if (switchdev_trans_ph_prepare(trans)) + return 0; b53_enable_vlan(dev, dev->vlan_enabled, vlan_filtering); @@ -1158,10 +1404,18 @@ if ((is5325(dev) || is5365(dev)) && vlan->vid_begin == 0) return -EOPNOTSUPP; + /* Port 7 on 7278 connects to the ASP's UniMAC which is not capable of + * receiving VLAN tagged frames at all, we can still allow the port to + * be configured for egress untagged. + */ + if (dev->chip_id == BCM7278_DEVICE_ID && port == 7 && + !(vlan->flags & BRIDGE_VLAN_INFO_UNTAGGED)) + return -EINVAL; + if (vlan->vid_end >= dev->num_vlans) return -ERANGE; - b53_enable_vlan(dev, true, dev->vlan_filtering_enabled); + b53_enable_vlan(dev, true, ds->vlan_filtering); return 0; } @@ -1279,8 +1533,7 @@ } static int b53_arl_read(struct b53_device *dev, u64 mac, - u16 vid, struct b53_arl_entry *ent, u8 *idx, - bool is_valid) + u16 vid, struct b53_arl_entry *ent, u8 *idx) { DECLARE_BITMAP(free_bins, B53_ARLTBL_MAX_BIN_ENTRIES); unsigned int i; @@ -1290,10 +1543,10 @@ if (ret) return ret; - bitmap_zero(free_bins, dev->num_arl_entries); + bitmap_zero(free_bins, dev->num_arl_bins); /* Read the bins */ - for (i = 0; i < dev->num_arl_entries; i++) { + for (i = 0; i < dev->num_arl_bins; i++) { u64 mac_vid; u32 fwd_entry; @@ -1316,10 +1569,10 @@ return 0; } - if (bitmap_weight(free_bins, dev->num_arl_entries) == 0) + if (bitmap_weight(free_bins, dev->num_arl_bins) == 0) return -ENOSPC; - *idx = find_first_bit(free_bins, dev->num_arl_entries); + *idx = find_first_bit(free_bins, dev->num_arl_bins); return -ENOENT; } @@ -1345,7 +1598,8 @@ if (ret) return ret; - ret = b53_arl_read(dev, mac, vid, &ent, &idx, is_valid); + ret = b53_arl_read(dev, mac, vid, &ent, &idx); + /* If this is a read, just finish now */ if (op) return ret; @@ -1369,11 +1623,24 @@ break; } - memset(&ent, 0, sizeof(ent)); - ent.port = port; - ent.is_valid = is_valid; + /* For multicast address, the port is a bitmask and the validity + * is determined by having at least one port being still active + */ + if (!is_multicast_ether_addr(addr)) { + ent.port = port; + ent.is_valid = is_valid; + } else { + if (is_valid) + ent.port |= BIT(port); + else + ent.port &= ~BIT(port); + + ent.is_valid = !!(ent.port); + } + ent.vid = vid; ent.is_static = true; + ent.is_age = false; memcpy(ent.mac, addr, ETH_ALEN); b53_arl_from_entry(&mac_vid, &fwd_entry, &ent); @@ -1476,7 +1743,7 @@ if (ret) return ret; - if (priv->num_arl_entries > 2) { + if (priv->num_arl_bins > 2) { b53_arl_search_rd(priv, 1, &results[1]); ret = b53_fdb_copy(port, &results[1], cb, data); if (ret) @@ -1486,18 +1753,65 @@ break; } - } while (count++ < 1024); + } while (count++ < b53_max_arl_entries(priv) / 2); return 0; } EXPORT_SYMBOL(b53_fdb_dump); +int b53_mdb_prepare(struct dsa_switch *ds, int port, + const struct switchdev_obj_port_mdb *mdb) +{ + struct b53_device *priv = ds->priv; + + /* 5325 and 5365 require some more massaging, but could + * be supported eventually + */ + if (is5325(priv) || is5365(priv)) + return -EOPNOTSUPP; + + return 0; +} +EXPORT_SYMBOL(b53_mdb_prepare); + +void b53_mdb_add(struct dsa_switch *ds, int port, + const struct switchdev_obj_port_mdb *mdb) +{ + struct b53_device *priv = ds->priv; + int ret; + + ret = b53_arl_op(priv, 0, port, mdb->addr, mdb->vid, true); + if (ret) + dev_err(ds->dev, "failed to add MDB entry\n"); +} +EXPORT_SYMBOL(b53_mdb_add); + +int b53_mdb_del(struct dsa_switch *ds, int port, + const struct switchdev_obj_port_mdb *mdb) +{ + struct b53_device *priv = ds->priv; + int ret; + + ret = b53_arl_op(priv, 0, port, mdb->addr, mdb->vid, false); + if (ret) + dev_err(ds->dev, "failed to delete MDB entry\n"); + + return ret; +} +EXPORT_SYMBOL(b53_mdb_del); + int b53_br_join(struct dsa_switch *ds, int port, struct net_device *br) { struct b53_device *dev = ds->priv; - s8 cpu_port = ds->ports[port].cpu_dp->index; + s8 cpu_port = dsa_to_port(ds, port)->cpu_dp->index; u16 pvlan, reg; unsigned int i; + + /* On 7278, port 7 which connects to the ASP should only receive + * traffic from matching CFP rules. + */ + if (dev->chip_id == BCM7278_DEVICE_ID && port == 7) + return -EINVAL; /* Make this port leave the all VLANs join since we will have proper * VLAN entries from now on @@ -1543,7 +1857,7 @@ { struct b53_device *dev = ds->priv; struct b53_vlan *vl = &dev->vlans[0]; - s8 cpu_port = ds->ports[port].cpu_dp->index; + s8 cpu_port = dsa_to_port(ds, port)->cpu_dp->index; unsigned int i; u16 pvlan, reg, pvid; @@ -1629,6 +1943,38 @@ } EXPORT_SYMBOL(b53_br_fast_age); +int b53_br_egress_floods(struct dsa_switch *ds, int port, + bool unicast, bool multicast) +{ + struct b53_device *dev = ds->priv; + u16 uc, mc; + + b53_read16(dev, B53_CTRL_PAGE, B53_UC_FLOOD_MASK, &uc); + if (unicast) + uc |= BIT(port); + else + uc &= ~BIT(port); + b53_write16(dev, B53_CTRL_PAGE, B53_UC_FLOOD_MASK, uc); + + b53_read16(dev, B53_CTRL_PAGE, B53_MC_FLOOD_MASK, &mc); + if (multicast) + mc |= BIT(port); + else + mc &= ~BIT(port); + b53_write16(dev, B53_CTRL_PAGE, B53_MC_FLOOD_MASK, mc); + + b53_read16(dev, B53_CTRL_PAGE, B53_IPMC_FLOOD_MASK, &mc); + if (multicast) + mc |= BIT(port); + else + mc &= ~BIT(port); + b53_write16(dev, B53_CTRL_PAGE, B53_IPMC_FLOOD_MASK, mc); + + return 0; + +} +EXPORT_SYMBOL(b53_br_egress_floods); + static bool b53_possible_cpu_port(struct dsa_switch *ds, int port) { /* Broadcom switches will accept enabling Broadcom tags on the @@ -1644,36 +1990,57 @@ return false; } -static bool b53_can_enable_brcm_tags(struct dsa_switch *ds, int port) +static bool b53_can_enable_brcm_tags(struct dsa_switch *ds, int port, + enum dsa_tag_protocol tag_protocol) { bool ret = b53_possible_cpu_port(ds, port); - if (!ret) + if (!ret) { dev_warn(ds->dev, "Port %d is not Broadcom tag capable\n", port); + return ret; + } + + switch (tag_protocol) { + case DSA_TAG_PROTO_BRCM: + case DSA_TAG_PROTO_BRCM_PREPEND: + dev_warn(ds->dev, + "Port %d is stacked to Broadcom tag switch\n", port); + ret = false; + break; + default: + ret = true; + break; + } + return ret; } -enum dsa_tag_protocol b53_get_tag_protocol(struct dsa_switch *ds, int port) +enum dsa_tag_protocol b53_get_tag_protocol(struct dsa_switch *ds, int port, + enum dsa_tag_protocol mprot) { struct b53_device *dev = ds->priv; /* Older models (5325, 5365) support a different tag format that we do - * not support in net/dsa/tag_brcm.c yet. 539x and 531x5 require managed - * mode to be turned on which means we need to specifically manage ARL - * misses on multicast addresses (TBD). + * not support in net/dsa/tag_brcm.c yet. */ - if (is5325(dev) || is5365(dev) || is539x(dev) || is531x5(dev) || - !b53_can_enable_brcm_tags(ds, port)) - return DSA_TAG_PROTO_NONE; + if (is5325(dev) || is5365(dev) || + !b53_can_enable_brcm_tags(ds, port, mprot)) { + dev->tag_protocol = DSA_TAG_PROTO_NONE; + goto out; + } /* Broadcom BCM58xx chips have a flow accelerator on Port 8 * which requires us to use the prepended Broadcom tag type */ - if (dev->chip_id == BCM58XX_DEVICE_ID && port == B53_CPU_PORT) - return DSA_TAG_PROTO_BRCM_PREPEND; + if (dev->chip_id == BCM58XX_DEVICE_ID && port == B53_CPU_PORT) { + dev->tag_protocol = DSA_TAG_PROTO_BRCM_PREPEND; + goto out; + } - return DSA_TAG_PROTO_BRCM; + dev->tag_protocol = DSA_TAG_PROTO_BRCM; +out: + return dev->tag_protocol; } EXPORT_SYMBOL(b53_get_tag_protocol); @@ -1804,9 +2171,30 @@ } EXPORT_SYMBOL(b53_set_mac_eee); +static int b53_change_mtu(struct dsa_switch *ds, int port, int mtu) +{ + struct b53_device *dev = ds->priv; + bool enable_jumbo; + bool allow_10_100; + + if (is5325(dev) || is5365(dev)) + return -EOPNOTSUPP; + + enable_jumbo = (mtu >= JMS_MIN_SIZE); + allow_10_100 = (dev->chip_id == BCM583XX_DEVICE_ID); + + return b53_set_jumbo(dev, enable_jumbo, allow_10_100); +} + +static int b53_get_max_mtu(struct dsa_switch *ds, int port) +{ + return JMS_MAX_SIZE; +} + static const struct dsa_switch_ops b53_switch_ops = { .get_tag_protocol = b53_get_tag_protocol, .setup = b53_setup, + .teardown = b53_teardown, .get_strings = b53_get_strings, .get_ethtool_stats = b53_get_ethtool_stats, .get_sset_count = b53_get_sset_count, @@ -1814,6 +2202,12 @@ .phy_read = b53_phy_read16, .phy_write = b53_phy_write16, .adjust_link = b53_adjust_link, + .phylink_validate = b53_phylink_validate, + .phylink_mac_link_state = b53_phylink_mac_link_state, + .phylink_mac_config = b53_phylink_mac_config, + .phylink_mac_an_restart = b53_phylink_mac_an_restart, + .phylink_mac_link_down = b53_phylink_mac_link_down, + .phylink_mac_link_up = b53_phylink_mac_link_up, .port_enable = b53_enable_port, .port_disable = b53_disable_port, .get_mac_eee = b53_get_mac_eee, @@ -1822,6 +2216,7 @@ .port_bridge_leave = b53_br_leave, .port_stp_state_set = b53_br_set_stp_state, .port_fast_age = b53_br_fast_age, + .port_egress_floods = b53_br_egress_floods, .port_vlan_filtering = b53_vlan_filtering, .port_vlan_prepare = b53_vlan_prepare, .port_vlan_add = b53_vlan_add, @@ -1831,6 +2226,11 @@ .port_fdb_del = b53_fdb_del, .port_mirror_add = b53_mirror_add, .port_mirror_del = b53_mirror_del, + .port_mdb_prepare = b53_mdb_prepare, + .port_mdb_add = b53_mdb_add, + .port_mdb_del = b53_mdb_del, + .port_max_mtu = b53_get_max_mtu, + .port_change_mtu = b53_change_mtu, }; struct b53_chip_data { @@ -1838,9 +2238,11 @@ const char *dev_name; u16 vlans; u16 enabled_ports; + u8 imp_port; u8 cpu_port; u8 vta_regs[3]; - u8 arl_entries; + u8 arl_bins; + u16 arl_buckets; u8 duplex_reg; u8 jumbo_pm_reg; u8 jumbo_size_reg; @@ -1859,7 +2261,9 @@ .dev_name = "BCM5325", .vlans = 16, .enabled_ports = 0x1f, - .arl_entries = 2, + .arl_bins = 2, + .arl_buckets = 1024, + .imp_port = 5, .cpu_port = B53_CPU_PORT_25, .duplex_reg = B53_DUPLEX_STAT_FE, }, @@ -1868,7 +2272,9 @@ .dev_name = "BCM5365", .vlans = 256, .enabled_ports = 0x1f, - .arl_entries = 2, + .arl_bins = 2, + .arl_buckets = 1024, + .imp_port = 5, .cpu_port = B53_CPU_PORT_25, .duplex_reg = B53_DUPLEX_STAT_FE, }, @@ -1877,7 +2283,9 @@ .dev_name = "BCM5389", .vlans = 4096, .enabled_ports = 0x1f, - .arl_entries = 4, + .arl_bins = 4, + .arl_buckets = 1024, + .imp_port = 8, .cpu_port = B53_CPU_PORT, .vta_regs = B53_VTA_REGS, .duplex_reg = B53_DUPLEX_STAT_GE, @@ -1889,7 +2297,9 @@ .dev_name = "BCM5395", .vlans = 4096, .enabled_ports = 0x1f, - .arl_entries = 4, + .arl_bins = 4, + .arl_buckets = 1024, + .imp_port = 8, .cpu_port = B53_CPU_PORT, .vta_regs = B53_VTA_REGS, .duplex_reg = B53_DUPLEX_STAT_GE, @@ -1901,7 +2311,9 @@ .dev_name = "BCM5397", .vlans = 4096, .enabled_ports = 0x1f, - .arl_entries = 4, + .arl_bins = 4, + .arl_buckets = 1024, + .imp_port = 8, .cpu_port = B53_CPU_PORT, .vta_regs = B53_VTA_REGS_9798, .duplex_reg = B53_DUPLEX_STAT_GE, @@ -1913,7 +2325,9 @@ .dev_name = "BCM5398", .vlans = 4096, .enabled_ports = 0x7f, - .arl_entries = 4, + .arl_bins = 4, + .arl_buckets = 1024, + .imp_port = 8, .cpu_port = B53_CPU_PORT, .vta_regs = B53_VTA_REGS_9798, .duplex_reg = B53_DUPLEX_STAT_GE, @@ -1925,8 +2339,10 @@ .dev_name = "BCM53115", .vlans = 4096, .enabled_ports = 0x1f, - .arl_entries = 4, + .arl_bins = 4, + .arl_buckets = 1024, .vta_regs = B53_VTA_REGS, + .imp_port = 8, .cpu_port = B53_CPU_PORT, .duplex_reg = B53_DUPLEX_STAT_GE, .jumbo_pm_reg = B53_JUMBO_PORT_MASK, @@ -1937,7 +2353,9 @@ .dev_name = "BCM53125", .vlans = 4096, .enabled_ports = 0xff, - .arl_entries = 4, + .arl_bins = 4, + .arl_buckets = 1024, + .imp_port = 8, .cpu_port = B53_CPU_PORT, .vta_regs = B53_VTA_REGS, .duplex_reg = B53_DUPLEX_STAT_GE, @@ -1949,7 +2367,9 @@ .dev_name = "BCM53128", .vlans = 4096, .enabled_ports = 0x1ff, - .arl_entries = 4, + .arl_bins = 4, + .arl_buckets = 1024, + .imp_port = 8, .cpu_port = B53_CPU_PORT, .vta_regs = B53_VTA_REGS, .duplex_reg = B53_DUPLEX_STAT_GE, @@ -1961,7 +2381,9 @@ .dev_name = "BCM63xx", .vlans = 4096, .enabled_ports = 0, /* pdata must provide them */ - .arl_entries = 4, + .arl_bins = 4, + .arl_buckets = 1024, + .imp_port = 8, .cpu_port = B53_CPU_PORT, .vta_regs = B53_VTA_REGS_63XX, .duplex_reg = B53_DUPLEX_STAT_63XX, @@ -1973,7 +2395,9 @@ .dev_name = "BCM53010", .vlans = 4096, .enabled_ports = 0x1f, - .arl_entries = 4, + .arl_bins = 4, + .arl_buckets = 1024, + .imp_port = 8, .cpu_port = B53_CPU_PORT_25, /* TODO: auto detect */ .vta_regs = B53_VTA_REGS, .duplex_reg = B53_DUPLEX_STAT_GE, @@ -1985,7 +2409,9 @@ .dev_name = "BCM53011", .vlans = 4096, .enabled_ports = 0x1bf, - .arl_entries = 4, + .arl_bins = 4, + .arl_buckets = 1024, + .imp_port = 8, .cpu_port = B53_CPU_PORT_25, /* TODO: auto detect */ .vta_regs = B53_VTA_REGS, .duplex_reg = B53_DUPLEX_STAT_GE, @@ -1997,7 +2423,9 @@ .dev_name = "BCM53012", .vlans = 4096, .enabled_ports = 0x1bf, - .arl_entries = 4, + .arl_bins = 4, + .arl_buckets = 1024, + .imp_port = 8, .cpu_port = B53_CPU_PORT_25, /* TODO: auto detect */ .vta_regs = B53_VTA_REGS, .duplex_reg = B53_DUPLEX_STAT_GE, @@ -2009,7 +2437,9 @@ .dev_name = "BCM53018", .vlans = 4096, .enabled_ports = 0x1f, - .arl_entries = 4, + .arl_bins = 4, + .arl_buckets = 1024, + .imp_port = 8, .cpu_port = B53_CPU_PORT_25, /* TODO: auto detect */ .vta_regs = B53_VTA_REGS, .duplex_reg = B53_DUPLEX_STAT_GE, @@ -2021,7 +2451,9 @@ .dev_name = "BCM53019", .vlans = 4096, .enabled_ports = 0x1f, - .arl_entries = 4, + .arl_bins = 4, + .arl_buckets = 1024, + .imp_port = 8, .cpu_port = B53_CPU_PORT_25, /* TODO: auto detect */ .vta_regs = B53_VTA_REGS, .duplex_reg = B53_DUPLEX_STAT_GE, @@ -2033,7 +2465,9 @@ .dev_name = "BCM585xx/586xx/88312", .vlans = 4096, .enabled_ports = 0x1ff, - .arl_entries = 4, + .arl_bins = 4, + .arl_buckets = 1024, + .imp_port = 8, .cpu_port = B53_CPU_PORT, .vta_regs = B53_VTA_REGS, .duplex_reg = B53_DUPLEX_STAT_GE, @@ -2045,7 +2479,9 @@ .dev_name = "BCM583xx/11360", .vlans = 4096, .enabled_ports = 0x103, - .arl_entries = 4, + .arl_bins = 4, + .arl_buckets = 1024, + .imp_port = 8, .cpu_port = B53_CPU_PORT, .vta_regs = B53_VTA_REGS, .duplex_reg = B53_DUPLEX_STAT_GE, @@ -2057,7 +2493,9 @@ .dev_name = "BCM7445", .vlans = 4096, .enabled_ports = 0x1ff, - .arl_entries = 4, + .arl_bins = 4, + .arl_buckets = 1024, + .imp_port = 8, .cpu_port = B53_CPU_PORT, .vta_regs = B53_VTA_REGS, .duplex_reg = B53_DUPLEX_STAT_GE, @@ -2069,7 +2507,9 @@ .dev_name = "BCM7278", .vlans = 4096, .enabled_ports = 0x1ff, - .arl_entries= 4, + .arl_bins = 4, + .arl_buckets = 256, + .imp_port = 8, .cpu_port = B53_CPU_PORT, .vta_regs = B53_VTA_REGS, .duplex_reg = B53_DUPLEX_STAT_GE, @@ -2095,9 +2535,11 @@ dev->vta_regs[1] = chip->vta_regs[1]; dev->vta_regs[2] = chip->vta_regs[2]; dev->jumbo_pm_reg = chip->jumbo_pm_reg; + dev->imp_port = chip->imp_port; dev->cpu_port = chip->cpu_port; dev->num_vlans = chip->vlans; - dev->num_arl_entries = chip->arl_entries; + dev->num_arl_bins = chip->arl_bins; + dev->num_arl_buckets = chip->arl_buckets; break; } } @@ -2138,6 +2580,8 @@ dev->enabled_ports |= BIT(dev->cpu_port); dev->num_ports = fls(dev->enabled_ports); + dev->ds->num_ports = min_t(unsigned int, dev->num_ports, DSA_MAX_PORTS); + /* Include non standard CPU port built-in PHYs to be probed */ if (is539x(dev) || is531x5(dev)) { for (i = 0; i < dev->num_ports; i++) { @@ -2177,9 +2621,11 @@ struct dsa_switch *ds; struct b53_device *dev; - ds = dsa_switch_alloc(base, DSA_MAX_PORTS); + ds = devm_kzalloc(base, sizeof(*ds), GFP_KERNEL); if (!ds) return NULL; + + ds->dev = base; dev = devm_kzalloc(base, sizeof(*dev), GFP_KERNEL); if (!dev) @@ -2192,6 +2638,16 @@ dev->priv = priv; dev->ops = ops; ds->ops = &b53_switch_ops; + ds->configure_vlan_while_not_filtering = true; + ds->untag_bridge_pvid = true; + dev->vlan_enabled = ds->configure_vlan_while_not_filtering; + /* Let DSA handle the case were multiple bridges span the same switch + * device and different VLAN awareness settings are requested, which + * would be breaking filtering semantics for any of the other bridge + * devices. (not hardware supported) + */ + ds->vlan_filtering_is_global = true; + mutex_init(&dev->reg_mutex); mutex_init(&dev->stats_mutex); @@ -2250,8 +2706,9 @@ dev->chip_id = id32; break; default: - pr_err("unsupported switch detected (BCM53%02x/BCM%x)\n", - id8, id32); + dev_err(dev->dev, + "unsupported switch detected (BCM53%02x/BCM%x)\n", + id8, id32); return -ENODEV; } } @@ -2281,7 +2738,8 @@ if (ret) return ret; - pr_info("found switch: %s, rev %i\n", dev->name, dev->core_rev); + dev_info(dev->dev, "found switch: %s, rev %i\n", + dev->name, dev->core_rev); return dsa_register_switch(dev->ds); } -- Gitblit v1.6.2