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>
This commit is contained in:
Jakub Kicinski 2025-06-17 17:52:32 -07:00
commit 67f2efc478
7 changed files with 415 additions and 96 deletions

View File

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

View File

@ -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);
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);
/* 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 @@ static int b53_flush_arl(struct b53_device *dev, u8 mask)
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, &reg);
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);
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, &reg);
b53_read8(dev, B53_ARLIO_PAGE, offset, &reg);
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:

View File

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

View File

@ -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)
@ -365,6 +372,18 @@
#define B53_ARL_SRCH_RSTL_MACVID(x) (B53_ARL_SRCH_RSTL_0_MACVID + ((x) * 0x10))
#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
*************************************************************************/

View File

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

View File

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

View File

@ -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,38 +290,6 @@ 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)
{
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;
}
static const struct dsa_device_ops brcm_legacy_netdev_ops = {
.name = BRCM_LEGACY_NAME,
.proto = DSA_TAG_PROTO_BRCM_LEGACY,
@ -294,6 +302,66 @@ 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)
{
struct dsa_port *dp = dsa_user_to_port(dev);
unsigned int fcs_len;
__le32 fcs_val;
u8 *brcm_tag;
/* 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;
fcs_len = skb->len;
fcs_val = cpu_to_le32(crc32_le(~0, skb->data, fcs_len) ^ ~0);
skb_push(skb, BRCM_LEG_TAG_LEN);
dsa_alloc_etype_header(skb, BRCM_LEG_TAG_LEN);
brcm_tag = skb->data + 2 * ETH_ALEN;
/* Broadcom tag type */
brcm_tag[0] = BRCM_LEG_TYPE_HI;
brcm_tag[1] = BRCM_LEG_TYPE_LO;
/* 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;
/* 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_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_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,
struct net_device *dev)
@ -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