diff options
author | 2025-06-17 17:52:32 -0700 | |
---|---|---|
committer | 2025-06-17 17:52:32 -0700 | |
commit | 67f2efc47850e1e8e3a74203459dc218a27d21f9 (patch) | |
tree | ce2b23e927aa7a23e951bf13041e3f69ab34a5ae | |
parent | Merge branch 'nte-stmmac-visconti-cleanups' (diff) | |
parent | net: dsa: b53: ensure BCM5325 PHYs are enabled (diff) | |
download | wireguard-linux-67f2efc47850e1e8e3a74203459dc218a27d21f9.tar.xz wireguard-linux-67f2efc47850e1e8e3a74203459dc218a27d21f9.zip |
Merge branch 'net-dsa-b53-fix-bcm5325-support'
Álvaro Fernández Rojas says:
====================
net: dsa: b53: fix BCM5325 support
These patches get the BCM5325 switch working with b53.
The existing brcm legacy tag only works with BCM63xx switches.
We need to add a new legacy tag for BCM5325 and BCM5365 switches, which
require including the FCS and length.
I'm not really sure that everything here is correct since I don't work for
Broadcom and all this is based on the public datasheet available for the
BCM5325 and my own experiments with a Huawei HG556a (BCM6358).
Both sets of patches have been merged due to the change requested by Jonas
about BRCM_HDR register access depending on legacy tags.
====================
Link: https://patch.msgid.link/20250614080000.1884236-1-noltari@gmail.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
-rw-r--r-- | drivers/net/dsa/b53/Kconfig | 1 | ||||
-rw-r--r-- | drivers/net/dsa/b53/b53_common.c | 284 | ||||
-rw-r--r-- | drivers/net/dsa/b53/b53_priv.h | 48 | ||||
-rw-r--r-- | drivers/net/dsa/b53/b53_regs.h | 27 | ||||
-rw-r--r-- | include/net/dsa.h | 2 | ||||
-rw-r--r-- | net/dsa/Kconfig | 16 | ||||
-rw-r--r-- | net/dsa/tag_brcm.c | 119 |
7 files changed, 408 insertions, 89 deletions
diff --git a/drivers/net/dsa/b53/Kconfig b/drivers/net/dsa/b53/Kconfig index ebaa4a80d544..915008e8eff5 100644 --- a/drivers/net/dsa/b53/Kconfig +++ b/drivers/net/dsa/b53/Kconfig @@ -5,6 +5,7 @@ menuconfig B53 select NET_DSA_TAG_NONE select NET_DSA_TAG_BRCM select NET_DSA_TAG_BRCM_LEGACY + select NET_DSA_TAG_BRCM_LEGACY_FCS select NET_DSA_TAG_BRCM_PREPEND help This driver adds support for Broadcom managed switch chips. It supports diff --git a/drivers/net/dsa/b53/b53_common.c b/drivers/net/dsa/b53/b53_common.c index dc2f4adac9bc..46978757c972 100644 --- a/drivers/net/dsa/b53/b53_common.c +++ b/drivers/net/dsa/b53/b53_common.c @@ -361,18 +361,23 @@ static void b53_set_forwarding(struct b53_device *dev, int enable) b53_write8(dev, B53_CTRL_PAGE, B53_SWITCH_MODE, mgmt); - /* Include IMP port in dumb forwarding mode - */ - 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); + if (!is5325(dev)) { + /* Include IMP port in dumb forwarding mode */ + 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); + } else { + b53_read8(dev, B53_CTRL_PAGE, B53_IP_MULTICAST_CTRL, &mgmt); + mgmt |= B53_IP_MCAST_25; + b53_write8(dev, B53_CTRL_PAGE, B53_IP_MULTICAST_CTRL, mgmt); + } } static void b53_enable_vlan(struct b53_device *dev, int port, bool enable, @@ -487,6 +492,9 @@ static int b53_flush_arl(struct b53_device *dev, u8 mask) { unsigned int i; + if (is5325(dev)) + return 0; + b53_write8(dev, B53_CTRL_PAGE, B53_FAST_AGE_CTRL, FAST_AGE_DONE | FAST_AGE_DYNAMIC | mask); @@ -511,6 +519,9 @@ out: static int b53_fast_age_port(struct b53_device *dev, int port) { + if (is5325(dev)) + return 0; + b53_write8(dev, B53_CTRL_PAGE, B53_FAST_AGE_PORT_CTRL, port); return b53_flush_arl(dev, FAST_AGE_PORT); @@ -518,6 +529,9 @@ static int b53_fast_age_port(struct b53_device *dev, int port) static int b53_fast_age_vlan(struct b53_device *dev, u16 vid) { + if (is5325(dev)) + return 0; + b53_write16(dev, B53_CTRL_PAGE, B53_FAST_AGE_VID_CTRL, vid); return b53_flush_arl(dev, FAST_AGE_VLAN); @@ -529,6 +543,10 @@ void b53_imp_vlan_setup(struct dsa_switch *ds, int cpu_port) unsigned int i; u16 pvlan; + /* BCM5325 CPU port is at 8 */ + if ((is5325(dev) || is5365(dev)) && cpu_port == B53_CPU_PORT_25) + cpu_port = B53_CPU_PORT; + /* Enable the IMP port to be in the same VLAN as the other ports * on a per-port basis such that we only have Port i and IMP in * the same VLAN. @@ -546,12 +564,24 @@ static void b53_port_set_ucast_flood(struct b53_device *dev, int port, { u16 uc; - 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); + if (is5325(dev)) { + if (port == B53_CPU_PORT_25) + port = B53_CPU_PORT; + + b53_read16(dev, B53_IEEE_PAGE, B53_IEEE_UCAST_DLF, &uc); + if (unicast) + uc |= BIT(port) | B53_IEEE_UCAST_DROP_EN; + else + uc &= ~BIT(port); + b53_write16(dev, B53_IEEE_PAGE, B53_IEEE_UCAST_DLF, uc); + } else { + 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); + } } static void b53_port_set_mcast_flood(struct b53_device *dev, int port, @@ -559,19 +589,31 @@ static void b53_port_set_mcast_flood(struct b53_device *dev, int port, { u16 mc; - 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); + if (is5325(dev)) { + if (port == B53_CPU_PORT_25) + port = B53_CPU_PORT; - 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); + b53_read16(dev, B53_IEEE_PAGE, B53_IEEE_MCAST_DLF, &mc); + if (multicast) + mc |= BIT(port) | B53_IEEE_MCAST_DROP_EN; + else + mc &= ~BIT(port); + b53_write16(dev, B53_IEEE_PAGE, B53_IEEE_MCAST_DLF, mc); + } else { + 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); + } } static void b53_port_set_learning(struct b53_device *dev, int port, @@ -579,6 +621,9 @@ static void b53_port_set_learning(struct b53_device *dev, int port, { u16 reg; + if (is5325(dev)) + return; + b53_read16(dev, B53_CTRL_PAGE, B53_DIS_LEARNING, ®); if (learning) reg &= ~BIT(port); @@ -615,6 +660,19 @@ int b53_setup_port(struct dsa_switch *ds, int port) if (dsa_is_user_port(ds, port)) b53_set_eap_mode(dev, port, EAP_MODE_SIMPLIFIED); + if (is5325(dev) && + in_range(port, 1, 4)) { + u8 reg; + + b53_read8(dev, B53_CTRL_PAGE, B53_PD_MODE_CTRL_25, ®); + reg &= ~PD_MODE_POWER_DOWN_PORT(0); + if (dsa_is_unused_port(ds, port)) + reg |= PD_MODE_POWER_DOWN_PORT(port); + else + reg &= ~PD_MODE_POWER_DOWN_PORT(port); + b53_write8(dev, B53_CTRL_PAGE, B53_PD_MODE_CTRL_25, reg); + } + return 0; } EXPORT_SYMBOL(b53_setup_port); @@ -713,6 +771,11 @@ void b53_brcm_hdr_setup(struct dsa_switch *ds, int port) hdr_ctl |= GC_FRM_MGMT_PORT_M; b53_write8(dev, B53_MGMT_PAGE, B53_GLOBAL_CONFIG, hdr_ctl); + /* B53_BRCM_HDR not present on devices with legacy tags */ + if (dev->tag_protocol == DSA_TAG_PROTO_BRCM_LEGACY || + dev->tag_protocol == DSA_TAG_PROTO_BRCM_LEGACY_FCS) + return; + /* Enable Broadcom tags for IMP port */ b53_read8(dev, B53_MGMT_PAGE, B53_BRCM_HDR, &hdr_ctl); if (tag_en) @@ -1257,6 +1320,8 @@ static void b53_force_link(struct b53_device *dev, int port, int link) if (port == dev->imp_port) { off = B53_PORT_OVERRIDE_CTRL; val = PORT_OVERRIDE_EN; + } else if (is5325(dev)) { + return; } else { off = B53_GMII_PORT_OVERRIDE_CTRL(port); val = GMII_PO_EN; @@ -1281,6 +1346,8 @@ static void b53_force_port_config(struct b53_device *dev, int port, if (port == dev->imp_port) { off = B53_PORT_OVERRIDE_CTRL; val = PORT_OVERRIDE_EN; + } else if (is5325(dev)) { + return; } else { off = B53_GMII_PORT_OVERRIDE_CTRL(port); val = GMII_PO_EN; @@ -1311,10 +1378,19 @@ static void b53_force_port_config(struct b53_device *dev, int port, return; } - if (rx_pause) - reg |= PORT_OVERRIDE_RX_FLOW; - if (tx_pause) - reg |= PORT_OVERRIDE_TX_FLOW; + if (rx_pause) { + if (is5325(dev)) + reg |= PORT_OVERRIDE_LP_FLOW_25; + else + reg |= PORT_OVERRIDE_RX_FLOW; + } + + if (tx_pause) { + if (is5325(dev)) + reg |= PORT_OVERRIDE_LP_FLOW_25; + else + reg |= PORT_OVERRIDE_TX_FLOW; + } b53_write8(dev, B53_CTRL_PAGE, off, reg); } @@ -1764,6 +1840,45 @@ static int b53_arl_read(struct b53_device *dev, u64 mac, return *idx >= dev->num_arl_bins ? -ENOSPC : -ENOENT; } +static int b53_arl_read_25(struct b53_device *dev, u64 mac, + u16 vid, struct b53_arl_entry *ent, u8 *idx) +{ + DECLARE_BITMAP(free_bins, B53_ARLTBL_MAX_BIN_ENTRIES); + unsigned int i; + int ret; + + ret = b53_arl_op_wait(dev); + if (ret) + return ret; + + bitmap_zero(free_bins, dev->num_arl_bins); + + /* Read the bins */ + for (i = 0; i < dev->num_arl_bins; i++) { + u64 mac_vid; + + b53_read64(dev, B53_ARLIO_PAGE, + B53_ARLTBL_MAC_VID_ENTRY(i), &mac_vid); + + b53_arl_to_entry_25(ent, mac_vid); + + if (!(mac_vid & ARLTBL_VALID_25)) { + set_bit(i, free_bins); + continue; + } + if ((mac_vid & ARLTBL_MAC_MASK) != mac) + continue; + if (dev->vlan_enabled && + ((mac_vid >> ARLTBL_VID_S_65) & ARLTBL_VID_MASK_25) != vid) + continue; + *idx = i; + return 0; + } + + *idx = find_first_bit(free_bins, dev->num_arl_bins); + return *idx >= dev->num_arl_bins ? -ENOSPC : -ENOENT; +} + static int b53_arl_op(struct b53_device *dev, int op, int port, const unsigned char *addr, u16 vid, bool is_valid) { @@ -1778,14 +1893,18 @@ static int b53_arl_op(struct b53_device *dev, int op, int port, /* Perform a read for the given MAC and VID */ b53_write48(dev, B53_ARLIO_PAGE, B53_MAC_ADDR_IDX, mac); - b53_write16(dev, B53_ARLIO_PAGE, B53_VLAN_ID_IDX, vid); + if (!is5325m(dev)) + b53_write16(dev, B53_ARLIO_PAGE, B53_VLAN_ID_IDX, vid); /* Issue a read operation for this MAC */ ret = b53_arl_rw_op(dev, 1); if (ret) return ret; - ret = b53_arl_read(dev, mac, vid, &ent, &idx); + if (is5325(dev) || is5365(dev)) + ret = b53_arl_read_25(dev, mac, vid, &ent, &idx); + else + ret = b53_arl_read(dev, mac, vid, &ent, &idx); /* If this is a read, just finish now */ if (op) @@ -1829,12 +1948,17 @@ static int b53_arl_op(struct b53_device *dev, int op, int port, ent.is_static = true; ent.is_age = false; memcpy(ent.mac, addr, ETH_ALEN); - b53_arl_from_entry(&mac_vid, &fwd_entry, &ent); + if (is5325(dev) || is5365(dev)) + b53_arl_from_entry_25(&mac_vid, &ent); + else + b53_arl_from_entry(&mac_vid, &fwd_entry, &ent); b53_write64(dev, B53_ARLIO_PAGE, B53_ARLTBL_MAC_VID_ENTRY(idx), mac_vid); - b53_write32(dev, B53_ARLIO_PAGE, - B53_ARLTBL_DATA_ENTRY(idx), fwd_entry); + + if (!is5325(dev) && !is5365(dev)) + b53_write32(dev, B53_ARLIO_PAGE, + B53_ARLTBL_DATA_ENTRY(idx), fwd_entry); return b53_arl_rw_op(dev, 0); } @@ -1846,12 +1970,6 @@ int b53_fdb_add(struct dsa_switch *ds, int port, struct b53_device *priv = ds->priv; int ret; - /* 5325 and 5365 require some more massaging, but could - * be supported eventually - */ - if (is5325(priv) || is5365(priv)) - return -EOPNOTSUPP; - mutex_lock(&priv->arl_mutex); ret = b53_arl_op(priv, 0, port, addr, vid, true); mutex_unlock(&priv->arl_mutex); @@ -1878,10 +1996,15 @@ EXPORT_SYMBOL(b53_fdb_del); static int b53_arl_search_wait(struct b53_device *dev) { unsigned int timeout = 1000; - u8 reg; + u8 reg, offset; + + if (is5325(dev) || is5365(dev)) + offset = B53_ARL_SRCH_CTL_25; + else + offset = B53_ARL_SRCH_CTL; do { - b53_read8(dev, B53_ARLIO_PAGE, B53_ARL_SRCH_CTL, ®); + b53_read8(dev, B53_ARLIO_PAGE, offset, ®); if (!(reg & ARL_SRCH_STDN)) return 0; @@ -1898,13 +2021,24 @@ static void b53_arl_search_rd(struct b53_device *dev, u8 idx, struct b53_arl_entry *ent) { u64 mac_vid; - u32 fwd_entry; - b53_read64(dev, B53_ARLIO_PAGE, - B53_ARL_SRCH_RSTL_MACVID(idx), &mac_vid); - b53_read32(dev, B53_ARLIO_PAGE, - B53_ARL_SRCH_RSTL(idx), &fwd_entry); - b53_arl_to_entry(ent, mac_vid, fwd_entry); + if (is5325(dev)) { + b53_read64(dev, B53_ARLIO_PAGE, B53_ARL_SRCH_RSTL_0_MACVID_25, + &mac_vid); + b53_arl_to_entry_25(ent, mac_vid); + } else if (is5365(dev)) { + b53_read64(dev, B53_ARLIO_PAGE, B53_ARL_SRCH_RSTL_0_MACVID_65, + &mac_vid); + b53_arl_to_entry_25(ent, mac_vid); + } else { + u32 fwd_entry; + + b53_read64(dev, B53_ARLIO_PAGE, B53_ARL_SRCH_RSTL_MACVID(idx), + &mac_vid); + b53_read32(dev, B53_ARLIO_PAGE, B53_ARL_SRCH_RSTL(idx), + &fwd_entry); + b53_arl_to_entry(ent, mac_vid, fwd_entry); + } } static int b53_fdb_copy(int port, const struct b53_arl_entry *ent, @@ -1925,14 +2059,20 @@ int b53_fdb_dump(struct dsa_switch *ds, int port, struct b53_device *priv = ds->priv; struct b53_arl_entry results[2]; unsigned int count = 0; + u8 offset; int ret; u8 reg; mutex_lock(&priv->arl_mutex); + if (is5325(priv) || is5365(priv)) + offset = B53_ARL_SRCH_CTL_25; + else + offset = B53_ARL_SRCH_CTL; + /* Start search operation */ reg = ARL_SRCH_STDN; - b53_write8(priv, B53_ARLIO_PAGE, B53_ARL_SRCH_CTL, reg); + b53_write8(priv, offset, B53_ARL_SRCH_CTL, reg); do { ret = b53_arl_search_wait(priv); @@ -2165,7 +2305,13 @@ int b53_br_flags_pre(struct dsa_switch *ds, int port, struct switchdev_brport_flags flags, struct netlink_ext_ack *extack) { - if (flags.mask & ~(BR_FLOOD | BR_MCAST_FLOOD | BR_LEARNING)) + struct b53_device *dev = ds->priv; + unsigned long mask = (BR_FLOOD | BR_MCAST_FLOOD); + + if (!is5325(dev)) + mask |= BR_LEARNING; + + if (flags.mask & ~mask) return -EINVAL; return 0; @@ -2241,8 +2387,11 @@ enum dsa_tag_protocol b53_get_tag_protocol(struct dsa_switch *ds, int port, goto out; } - /* Older models require a different 6 byte tag */ - if (is5325(dev) || is5365(dev) || is63xx(dev)) { + /* Older models require different 6 byte tags */ + if (is5325(dev) || is5365(dev)) { + dev->tag_protocol = DSA_TAG_PROTO_BRCM_LEGACY_FCS; + goto out; + } else if (is63xx(dev)) { dev->tag_protocol = DSA_TAG_PROTO_BRCM_LEGACY; goto out; } @@ -2830,6 +2979,9 @@ static int b53_switch_init(struct b53_device *dev) } } + if (is5325e(dev)) + dev->num_arl_buckets = 512; + dev->num_ports = fls(dev->enabled_ports); dev->ds->num_ports = min_t(unsigned int, dev->num_ports, DSA_MAX_PORTS); @@ -2931,10 +3083,24 @@ int b53_switch_detect(struct b53_device *dev) b53_write16(dev, B53_VLAN_PAGE, B53_VLAN_TABLE_ACCESS_25, 0xf); b53_read16(dev, B53_VLAN_PAGE, B53_VLAN_TABLE_ACCESS_25, &tmp); - if (tmp == 0xf) + if (tmp == 0xf) { + u32 phy_id; + int val; + dev->chip_id = BCM5325_DEVICE_ID; - else + + val = b53_phy_read16(dev->ds, 0, MII_PHYSID1); + phy_id = (val & 0xffff) << 16; + val = b53_phy_read16(dev->ds, 0, MII_PHYSID2); + phy_id |= (val & 0xfff0); + + if (phy_id == 0x00406330) + dev->variant_id = B53_VARIANT_5325M; + else if (phy_id == 0x0143bc30) + dev->variant_id = B53_VARIANT_5325E; + } else { dev->chip_id = BCM5365_DEVICE_ID; + } break; case BCM5389_DEVICE_ID: case BCM5395_DEVICE_ID: diff --git a/drivers/net/dsa/b53/b53_priv.h b/drivers/net/dsa/b53/b53_priv.h index a5ef7071ba07..b1b9e8882ba4 100644 --- a/drivers/net/dsa/b53/b53_priv.h +++ b/drivers/net/dsa/b53/b53_priv.h @@ -84,6 +84,12 @@ enum { BCM53134_DEVICE_ID = 0x5075, }; +enum b53_variant_id { + B53_VARIANT_NONE = 0, + B53_VARIANT_5325E, + B53_VARIANT_5325M, +}; + struct b53_pcs { struct phylink_pcs pcs; struct b53_device *dev; @@ -118,6 +124,7 @@ struct b53_device { /* chip specific data */ u32 chip_id; + enum b53_variant_id variant_id; u8 core_rev; u8 vta_regs[3]; u8 duplex_reg; @@ -165,6 +172,18 @@ static inline int is5325(struct b53_device *dev) return dev->chip_id == BCM5325_DEVICE_ID; } +static inline int is5325e(struct b53_device *dev) +{ + return is5325(dev) && + dev->variant_id == B53_VARIANT_5325E; +} + +static inline int is5325m(struct b53_device *dev) +{ + return is5325(dev) && + dev->variant_id == B53_VARIANT_5325M; +} + static inline int is5365(struct b53_device *dev) { #ifdef CONFIG_BCM47XX @@ -298,6 +317,19 @@ static inline void b53_arl_to_entry(struct b53_arl_entry *ent, ent->vid = mac_vid >> ARLTBL_VID_S; } +static inline void b53_arl_to_entry_25(struct b53_arl_entry *ent, + u64 mac_vid) +{ + memset(ent, 0, sizeof(*ent)); + ent->port = (mac_vid >> ARLTBL_DATA_PORT_ID_S_25) & + ARLTBL_DATA_PORT_ID_MASK_25; + ent->is_valid = !!(mac_vid & ARLTBL_VALID_25); + ent->is_age = !!(mac_vid & ARLTBL_AGE_25); + ent->is_static = !!(mac_vid & ARLTBL_STATIC_25); + u64_to_ether_addr(mac_vid, ent->mac); + ent->vid = mac_vid >> ARLTBL_VID_S_65; +} + static inline void b53_arl_from_entry(u64 *mac_vid, u32 *fwd_entry, const struct b53_arl_entry *ent) { @@ -312,6 +344,22 @@ static inline void b53_arl_from_entry(u64 *mac_vid, u32 *fwd_entry, *fwd_entry |= ARLTBL_AGE; } +static inline void b53_arl_from_entry_25(u64 *mac_vid, + const struct b53_arl_entry *ent) +{ + *mac_vid = ether_addr_to_u64(ent->mac); + *mac_vid |= (u64)(ent->port & ARLTBL_DATA_PORT_ID_MASK_25) << + ARLTBL_DATA_PORT_ID_S_25; + *mac_vid |= (u64)(ent->vid & ARLTBL_VID_MASK_25) << + ARLTBL_VID_S_65; + if (ent->is_valid) + *mac_vid |= ARLTBL_VALID_25; + if (ent->is_static) + *mac_vid |= ARLTBL_STATIC_25; + if (ent->is_age) + *mac_vid |= ARLTBL_AGE_25; +} + #ifdef CONFIG_BCM47XX #include <linux/bcm47xx_nvram.h> diff --git a/drivers/net/dsa/b53/b53_regs.h b/drivers/net/dsa/b53/b53_regs.h index 1fbc5a204bc7..309fe0e46dad 100644 --- a/drivers/net/dsa/b53/b53_regs.h +++ b/drivers/net/dsa/b53/b53_regs.h @@ -29,6 +29,7 @@ #define B53_ARLIO_PAGE 0x05 /* ARL Access */ #define B53_FRAMEBUF_PAGE 0x06 /* Management frame access */ #define B53_MEM_ACCESS_PAGE 0x08 /* Memory access */ +#define B53_IEEE_PAGE 0x0a /* IEEE 802.1X */ /* PHY Registers */ #define B53_PORT_MII_PAGE(i) (0x10 + (i)) /* Port i MII Registers */ @@ -95,17 +96,22 @@ #define PORT_OVERRIDE_SPEED_10M (0 << PORT_OVERRIDE_SPEED_S) #define PORT_OVERRIDE_SPEED_100M (1 << PORT_OVERRIDE_SPEED_S) #define PORT_OVERRIDE_SPEED_1000M (2 << PORT_OVERRIDE_SPEED_S) +#define PORT_OVERRIDE_LP_FLOW_25 BIT(3) /* BCM5325 only */ #define PORT_OVERRIDE_RV_MII_25 BIT(4) /* BCM5325 only */ #define PORT_OVERRIDE_RX_FLOW BIT(4) #define PORT_OVERRIDE_TX_FLOW BIT(5) #define PORT_OVERRIDE_SPEED_2000M BIT(6) /* BCM5301X only, requires setting 1000M */ #define PORT_OVERRIDE_EN BIT(7) /* Use the register contents */ -/* Power-down mode control */ +/* Power-down mode control (8 bit) */ #define B53_PD_MODE_CTRL_25 0x0f +#define PD_MODE_PORT_MASK 0x1f +/* Bit 0 also powers down the switch. */ +#define PD_MODE_POWER_DOWN_PORT(i) BIT(i) /* IP Multicast control (8 bit) */ #define B53_IP_MULTICAST_CTRL 0x21 +#define B53_IP_MCAST_25 BIT(0) #define B53_IPMC_FWD_EN BIT(1) #define B53_UC_FWD_EN BIT(6) #define B53_MC_FWD_EN BIT(7) @@ -324,9 +330,10 @@ #define ARLTBL_VID_MASK 0xfff #define ARLTBL_DATA_PORT_ID_S_25 48 #define ARLTBL_DATA_PORT_ID_MASK_25 0xf -#define ARLTBL_AGE_25 BIT(61) -#define ARLTBL_STATIC_25 BIT(62) -#define ARLTBL_VALID_25 BIT(63) +#define ARLTBL_VID_S_65 53 +#define ARLTBL_AGE_25 BIT_ULL(61) +#define ARLTBL_STATIC_25 BIT_ULL(62) +#define ARLTBL_VALID_25 BIT_ULL(63) /* ARL Table Data Entry N Registers (32 bit) */ #define B53_ARLTBL_DATA_ENTRY(n) ((0x10 * (n)) + 0x18) @@ -366,6 +373,18 @@ #define B53_ARL_SRCH_RSTL(x) (B53_ARL_SRCH_RSTL_0 + ((x) * 0x10)) /************************************************************************* + * IEEE 802.1X Registers + *************************************************************************/ + +/* Multicast DLF Drop Control register (16 bit) */ +#define B53_IEEE_MCAST_DLF 0x94 +#define B53_IEEE_MCAST_DROP_EN BIT(11) + +/* Unicast DLF Drop Control register (16 bit) */ +#define B53_IEEE_UCAST_DLF 0x96 +#define B53_IEEE_UCAST_DROP_EN BIT(11) + +/************************************************************************* * Port VLAN Registers *************************************************************************/ diff --git a/include/net/dsa.h b/include/net/dsa.h index 55e2d97f247e..d73ea0880066 100644 --- a/include/net/dsa.h +++ b/include/net/dsa.h @@ -54,11 +54,13 @@ struct tc_action; #define DSA_TAG_PROTO_RZN1_A5PSW_VALUE 26 #define DSA_TAG_PROTO_LAN937X_VALUE 27 #define DSA_TAG_PROTO_VSC73XX_8021Q_VALUE 28 +#define DSA_TAG_PROTO_BRCM_LEGACY_FCS_VALUE 29 enum dsa_tag_protocol { DSA_TAG_PROTO_NONE = DSA_TAG_PROTO_NONE_VALUE, DSA_TAG_PROTO_BRCM = DSA_TAG_PROTO_BRCM_VALUE, DSA_TAG_PROTO_BRCM_LEGACY = DSA_TAG_PROTO_BRCM_LEGACY_VALUE, + DSA_TAG_PROTO_BRCM_LEGACY_FCS = DSA_TAG_PROTO_BRCM_LEGACY_FCS_VALUE, DSA_TAG_PROTO_BRCM_PREPEND = DSA_TAG_PROTO_BRCM_PREPEND_VALUE, DSA_TAG_PROTO_DSA = DSA_TAG_PROTO_DSA_VALUE, DSA_TAG_PROTO_EDSA = DSA_TAG_PROTO_EDSA_VALUE, diff --git a/net/dsa/Kconfig b/net/dsa/Kconfig index 2dfe9063613f..869cbe57162f 100644 --- a/net/dsa/Kconfig +++ b/net/dsa/Kconfig @@ -42,12 +42,24 @@ config NET_DSA_TAG_BRCM Broadcom switches which place the tag after the MAC source address. config NET_DSA_TAG_BRCM_LEGACY - tristate "Tag driver for Broadcom legacy switches using in-frame headers" + tristate "Tag driver for BCM63xx legacy switches using in-frame headers" select NET_DSA_TAG_BRCM_COMMON help Say Y if you want to enable support for tagging frames for the - Broadcom legacy switches which place the tag after the MAC source + BCM63xx legacy switches which place the tag after the MAC source address. + This tag is used in BCM63xx legacy switches which work without the + original FCS and length before the tag insertion. + +config NET_DSA_TAG_BRCM_LEGACY_FCS + tristate "Tag driver for BCM53xx legacy switches using in-frame headers" + select NET_DSA_TAG_BRCM_COMMON + help + Say Y if you want to enable support for tagging frames for the + BCM53xx legacy switches which place the tag after the MAC source + address. + This tag is used in BCM53xx legacy switches which expect original + FCS and length before the tag insertion to be present. config NET_DSA_TAG_BRCM_PREPEND tristate "Tag driver for Broadcom switches using prepended headers" diff --git a/net/dsa/tag_brcm.c b/net/dsa/tag_brcm.c index fe75821623a4..26bb657ceac3 100644 --- a/net/dsa/tag_brcm.c +++ b/net/dsa/tag_brcm.c @@ -15,6 +15,7 @@ #define BRCM_NAME "brcm" #define BRCM_LEGACY_NAME "brcm-legacy" +#define BRCM_LEGACY_FCS_NAME "brcm-legacy-fcs" #define BRCM_PREPEND_NAME "brcm-prepend" /* Legacy Broadcom tag (6 bytes) */ @@ -32,6 +33,10 @@ #define BRCM_LEG_MULTICAST (1 << 5) #define BRCM_LEG_EGRESS (2 << 5) #define BRCM_LEG_INGRESS (3 << 5) +#define BRCM_LEG_LEN_HI(x) (((x) >> 8) & 0x7) + +/* 4th byte in the tag */ +#define BRCM_LEG_LEN_LO(x) ((x) & 0xff) /* 6th byte in the tag */ #define BRCM_LEG_PORT_ID (0xf) @@ -212,6 +217,41 @@ DSA_TAG_DRIVER(brcm_netdev_ops); MODULE_ALIAS_DSA_TAG_DRIVER(DSA_TAG_PROTO_BRCM, BRCM_NAME); #endif +#if IS_ENABLED(CONFIG_NET_DSA_TAG_BRCM_LEGACY) || \ + IS_ENABLED(CONFIG_NET_DSA_TAG_BRCM_LEGACY_FCS) +static struct sk_buff *brcm_leg_tag_rcv(struct sk_buff *skb, + struct net_device *dev) +{ + int len = BRCM_LEG_TAG_LEN; + int source_port; + u8 *brcm_tag; + + if (unlikely(!pskb_may_pull(skb, BRCM_LEG_TAG_LEN + VLAN_HLEN))) + return NULL; + + brcm_tag = dsa_etype_header_pos_rx(skb); + + source_port = brcm_tag[5] & BRCM_LEG_PORT_ID; + + skb->dev = dsa_conduit_find_user(dev, 0, source_port); + if (!skb->dev) + return NULL; + + /* VLAN tag is added by BCM63xx internal switch */ + if (netdev_uses_dsa(skb->dev)) + len += VLAN_HLEN; + + /* Remove Broadcom tag and update checksum */ + skb_pull_rcsum(skb, len); + + dsa_default_offload_fwd_mark(skb); + + dsa_strip_etype_header(skb, len); + + return skb; +} +#endif /* CONFIG_NET_DSA_TAG_BRCM_LEGACY || CONFIG_NET_DSA_TAG_BRCM_LEGACY_FCS */ + #if IS_ENABLED(CONFIG_NET_DSA_TAG_BRCM_LEGACY) static struct sk_buff *brcm_leg_tag_xmit(struct sk_buff *skb, struct net_device *dev) @@ -250,49 +290,77 @@ static struct sk_buff *brcm_leg_tag_xmit(struct sk_buff *skb, return skb; } -static struct sk_buff *brcm_leg_tag_rcv(struct sk_buff *skb, - struct net_device *dev) +static const struct dsa_device_ops brcm_legacy_netdev_ops = { + .name = BRCM_LEGACY_NAME, + .proto = DSA_TAG_PROTO_BRCM_LEGACY, + .xmit = brcm_leg_tag_xmit, + .rcv = brcm_leg_tag_rcv, + .needed_headroom = BRCM_LEG_TAG_LEN, +}; + +DSA_TAG_DRIVER(brcm_legacy_netdev_ops); +MODULE_ALIAS_DSA_TAG_DRIVER(DSA_TAG_PROTO_BRCM_LEGACY, BRCM_LEGACY_NAME); +#endif /* CONFIG_NET_DSA_TAG_BRCM_LEGACY */ + +#if IS_ENABLED(CONFIG_NET_DSA_TAG_BRCM_LEGACY_FCS) +static struct sk_buff *brcm_leg_fcs_tag_xmit(struct sk_buff *skb, + struct net_device *dev) { - int len = BRCM_LEG_TAG_LEN; - int source_port; + struct dsa_port *dp = dsa_user_to_port(dev); + unsigned int fcs_len; + __le32 fcs_val; u8 *brcm_tag; - if (unlikely(!pskb_may_pull(skb, BRCM_LEG_TAG_LEN + VLAN_HLEN))) + /* The Ethernet switch we are interfaced with needs packets to be at + * least 64 bytes (including FCS) otherwise they will be discarded when + * they enter the switch port logic. When Broadcom tags are enabled, we + * need to make sure that packets are at least 70 bytes (including FCS + * and tag) because the length verification is done after the Broadcom + * tag is stripped off the ingress packet. + * + * Let dsa_user_xmit() free the SKB. + */ + if (__skb_put_padto(skb, ETH_ZLEN + BRCM_LEG_TAG_LEN, false)) return NULL; - brcm_tag = dsa_etype_header_pos_rx(skb); + fcs_len = skb->len; + fcs_val = cpu_to_le32(crc32_le(~0, skb->data, fcs_len) ^ ~0); - source_port = brcm_tag[5] & BRCM_LEG_PORT_ID; + skb_push(skb, BRCM_LEG_TAG_LEN); - skb->dev = dsa_conduit_find_user(dev, 0, source_port); - if (!skb->dev) - return NULL; + dsa_alloc_etype_header(skb, BRCM_LEG_TAG_LEN); - /* VLAN tag is added by BCM63xx internal switch */ - if (netdev_uses_dsa(skb->dev)) - len += VLAN_HLEN; + brcm_tag = skb->data + 2 * ETH_ALEN; - /* Remove Broadcom tag and update checksum */ - skb_pull_rcsum(skb, len); + /* Broadcom tag type */ + brcm_tag[0] = BRCM_LEG_TYPE_HI; + brcm_tag[1] = BRCM_LEG_TYPE_LO; - dsa_default_offload_fwd_mark(skb); + /* Broadcom tag value */ + brcm_tag[2] = BRCM_LEG_EGRESS | BRCM_LEG_LEN_HI(fcs_len); + brcm_tag[3] = BRCM_LEG_LEN_LO(fcs_len); + brcm_tag[4] = 0; + brcm_tag[5] = dp->index & BRCM_LEG_PORT_ID; - dsa_strip_etype_header(skb, len); + /* Original FCS value */ + if (__skb_pad(skb, ETH_FCS_LEN, false)) + return NULL; + skb_put_data(skb, &fcs_val, ETH_FCS_LEN); return skb; } -static const struct dsa_device_ops brcm_legacy_netdev_ops = { - .name = BRCM_LEGACY_NAME, - .proto = DSA_TAG_PROTO_BRCM_LEGACY, - .xmit = brcm_leg_tag_xmit, +static const struct dsa_device_ops brcm_legacy_fcs_netdev_ops = { + .name = BRCM_LEGACY_FCS_NAME, + .proto = DSA_TAG_PROTO_BRCM_LEGACY_FCS, + .xmit = brcm_leg_fcs_tag_xmit, .rcv = brcm_leg_tag_rcv, .needed_headroom = BRCM_LEG_TAG_LEN, }; -DSA_TAG_DRIVER(brcm_legacy_netdev_ops); -MODULE_ALIAS_DSA_TAG_DRIVER(DSA_TAG_PROTO_BRCM_LEGACY, BRCM_LEGACY_NAME); -#endif /* CONFIG_NET_DSA_TAG_BRCM_LEGACY */ +DSA_TAG_DRIVER(brcm_legacy_fcs_netdev_ops); +MODULE_ALIAS_DSA_TAG_DRIVER(DSA_TAG_PROTO_BRCM_LEGACY_FCS, BRCM_LEGACY_FCS_NAME); +#endif /* CONFIG_NET_DSA_TAG_BRCM_LEGACY_FCS */ #if IS_ENABLED(CONFIG_NET_DSA_TAG_BRCM_PREPEND) static struct sk_buff *brcm_tag_xmit_prepend(struct sk_buff *skb, @@ -328,6 +396,9 @@ static struct dsa_tag_driver *dsa_tag_driver_array[] = { #if IS_ENABLED(CONFIG_NET_DSA_TAG_BRCM_LEGACY) &DSA_TAG_DRIVER_NAME(brcm_legacy_netdev_ops), #endif +#if IS_ENABLED(CONFIG_NET_DSA_TAG_BRCM_LEGACY_FCS) + &DSA_TAG_DRIVER_NAME(brcm_legacy_fcs_netdev_ops), +#endif #if IS_ENABLED(CONFIG_NET_DSA_TAG_BRCM_PREPEND) &DSA_TAG_DRIVER_NAME(brcm_prepend_netdev_ops), #endif |