* [PATCH net-next 0/6] net: dsa: motorcomm: More BR_* flags support
@ 2026-11-02 15:56 David Yang
2026-11-02 15:56 ` [PATCH net-next 1/6] net: dsa: motorcomm: Isolate all ports during chip setup David Yang
` (5 more replies)
0 siblings, 6 replies; 7+ messages in thread
From: David Yang @ 2026-11-02 15:56 UTC (permalink / raw)
To: netdev
Cc: David Yang, Andrew Lunn, Vladimir Oltean, David S. Miller,
Eric Dumazet, Jakub Kicinski, Paolo Abeni, Simon Horman,
linux-kernel
Add BR_FLOOD / BR_MCAST_FLOOD and BR_PORT_LOCKED / BR_PORT_MAB offload
support.
David Yang (6):
net: dsa: motorcomm: Isolate all ports during chip setup
net: dsa: motorcomm: Rely on port isolation for CPU port
net: dsa: motorcomm: Add BR_FLOOD and BR_MCAST_FLOOD support
net: dsa: motorcomm: Translate FDB VID for VLAN-unaware bridges
net: dsa: tag_yt921x: handle FDB suspend copy tag code
net: dsa: motorcomm: Add BR_PORT_LOCKED and BR_PORT_MAB support
drivers/net/dsa/motorcomm/chip.c | 406 ++++++++++++++++++++++++-------
drivers/net/dsa/motorcomm/chip.h | 13 +
net/dsa/tag_yt921x.c | 2 +
3 files changed, 333 insertions(+), 88 deletions(-)
--
2.53.0
^ permalink raw reply [flat|nested] 7+ messages in thread
* [PATCH net-next 1/6] net: dsa: motorcomm: Isolate all ports during chip setup
2026-11-02 15:56 [PATCH net-next 0/6] net: dsa: motorcomm: More BR_* flags support David Yang
@ 2026-11-02 15:56 ` David Yang
2026-11-02 15:56 ` [PATCH net-next 2/6] net: dsa: motorcomm: Rely on port isolation for CPU port David Yang
` (4 subsequent siblings)
5 siblings, 0 replies; 7+ messages in thread
From: David Yang @ 2026-11-02 15:56 UTC (permalink / raw)
To: netdev
Cc: David Yang, Andrew Lunn, Vladimir Oltean, David S. Miller,
Eric Dumazet, Jakub Kicinski, Paolo Abeni, Simon Horman,
linux-kernel
Chip setup only blocks unknown unicast and unknown multicast frames,
leaving known multicast and broadcast open, though known multicast
cannot appear while the FDB is still empty.
Isolate every port right after the hardware reset, in preparation for
the isolation refactoring. Note that this only minimizes the window: a
reset always brings the hardware back to a dumb switch, so traffic can
leak before and during it.
Signed-off-by: David Yang <mmyangfl@gmail.com>
Reviewed-by: Andrew Lunn <andrew@lunn.ch>
---
drivers/net/dsa/motorcomm/chip.c | 12 ++++++++++++
1 file changed, 12 insertions(+)
diff --git a/drivers/net/dsa/motorcomm/chip.c b/drivers/net/dsa/motorcomm/chip.c
index 33e5fc127244..e1fa82c6db95 100644
--- a/drivers/net/dsa/motorcomm/chip.c
+++ b/drivers/net/dsa/motorcomm/chip.c
@@ -2221,6 +2221,7 @@ static int yt921x_chip_reset(struct yt921x_priv *priv)
{
struct device *dev = yt921x_priv_to_device(priv);
u16 eth_p_tag;
+ u32 ctrl;
u32 val;
int res;
@@ -2258,6 +2259,17 @@ static int yt921x_chip_reset(struct yt921x_priv *priv)
return -EINVAL;
}
+ /* Block all traffic as soon as we reset the hardware since it now acts
+ * as a dumb switch.
+ */
+ for (int port = 0; port < YT921X_PORT_NUM; port++) {
+ ctrl = ~(u32)0;
+ res = yt921x_reg_write(priv, YT921X_PORTn_ISOLATION(port),
+ ctrl);
+ if (res)
+ return res;
+ }
+
return 0;
}
--
2.53.0
^ permalink raw reply [flat|nested] 7+ messages in thread
* [PATCH net-next 2/6] net: dsa: motorcomm: Rely on port isolation for CPU port
2026-11-02 15:56 [PATCH net-next 0/6] net: dsa: motorcomm: More BR_* flags support David Yang
2026-11-02 15:56 ` [PATCH net-next 1/6] net: dsa: motorcomm: Isolate all ports during chip setup David Yang
@ 2026-11-02 15:56 ` David Yang
2026-11-02 15:56 ` [PATCH net-next 3/6] net: dsa: motorcomm: Add BR_FLOOD and BR_MCAST_FLOOD support David Yang
` (3 subsequent siblings)
5 siblings, 0 replies; 7+ messages in thread
From: David Yang @ 2026-11-02 15:56 UTC (permalink / raw)
To: netdev
Cc: David Yang, Andrew Lunn, Vladimir Oltean, David S. Miller,
Eric Dumazet, Jakub Kicinski, Paolo Abeni, Simon Horman,
linux-kernel
With every port fully isolated during chip setup, the CPU port already
gets its one-way isolation (drop all packets without tag) there, so the
dedicated handling in port_setup() is redundant. Skip the isolation
write for CPU ports in yt921x_userport_standalone(), which port_setup()
also runs for CPU ports, to keep it intact.
Consequently the CPU ports no longer need to be excluded from the
unknown unicast filtering: frames entering a CPU port without a tag are
now dropped by the port isolation instead of the ACT_UNK action.
Signed-off-by: David Yang <mmyangfl@gmail.com>
---
drivers/net/dsa/motorcomm/chip.c | 46 ++++++--------------------------
1 file changed, 8 insertions(+), 38 deletions(-)
diff --git a/drivers/net/dsa/motorcomm/chip.c b/drivers/net/dsa/motorcomm/chip.c
index e1fa82c6db95..fb993cc9607c 100644
--- a/drivers/net/dsa/motorcomm/chip.c
+++ b/drivers/net/dsa/motorcomm/chip.c
@@ -1164,10 +1164,13 @@ static int yt921x_userport_standalone(struct yt921x_priv *priv, int port)
u32 ctrl;
int res;
- ctrl = ~priv->cpu_ports_mask;
- res = yt921x_reg_write(priv, YT921X_PORTn_ISOLATION(port), ctrl);
- if (res)
- return res;
+ if (!(BIT(port) & priv->cpu_ports_mask)) {
+ ctrl = ~priv->cpu_ports_mask;
+ res = yt921x_reg_write(priv, YT921X_PORTn_ISOLATION(port),
+ ctrl);
+ if (res)
+ return res;
+ }
/* Turn off FDB learning to prevent FDB pollution */
mask = YT921X_PORT_LEARN_DIS;
@@ -1942,8 +1945,6 @@ yt921x_dsa_phylink_get_caps(struct dsa_switch *ds, int port,
static int yt921x_port_setup(struct yt921x_priv *priv, int port)
{
- struct dsa_switch *ds = &priv->ds;
- u32 ctrl;
int res;
res = yt921x_userport_standalone(priv, port);
@@ -1957,27 +1958,6 @@ static int yt921x_port_setup(struct yt921x_priv *priv, int port)
if (res)
return res;
- if (dsa_is_cpu_port(ds, port)) {
- /* Egress of CPU port is supposed to be completely controlled
- * via tagging, so set to oneway isolated (drop all packets
- * without tag).
- */
- ctrl = ~(u32)0;
- res = yt921x_reg_write(priv, YT921X_PORTn_ISOLATION(port),
- ctrl);
- if (res)
- return res;
-
- /* To simplify FDB "isolation" simulation, we also disable
- * learning on the CPU port, and let software identify packets
- * towarding CPU (either trapped or a static FDB entry is
- * matched, no matter which bridge that entry is for), which is
- * already done by yt921x_userport_standalone(). As a result,
- * VLAN-awareness becomes unrelated on the CPU port (set to
- * VLAN-unaware by the way).
- */
- }
-
return 0;
}
@@ -2276,10 +2256,8 @@ static int yt921x_chip_reset(struct yt921x_priv *priv)
static int yt921x_chip_setup_dsa(struct yt921x_priv *priv)
{
struct dsa_switch *ds = &priv->ds;
- unsigned long cpu_ports_mask;
u32 ctrls[2];
u32 ctrl;
- int port;
int res;
/* Enable DSA */
@@ -2297,7 +2275,7 @@ static int yt921x_chip_setup_dsa(struct yt921x_priv *priv)
if (res)
return res;
- ctrl = GENMASK(10, 0);
+ ctrl = GENMASK(10, 0) & ~priv->cpu_ports_mask;
res = yt921x_reg_write(priv, YT921X_FILTER_UNK_UCAST, ctrl);
if (res)
return res;
@@ -2320,14 +2298,6 @@ static int yt921x_chip_setup_dsa(struct yt921x_priv *priv)
ctrl = 0;
for (int i = 0; i < YT921X_PORT_NUM; i++)
ctrl |= YT921X_ACT_UNK_ACTn_TRAP(i);
- /* Except for CPU ports, if any packets are sent via CPU ports without
- * tag, they should be dropped.
- */
- cpu_ports_mask = priv->cpu_ports_mask;
- for_each_set_bit(port, &cpu_ports_mask, YT921X_PORT_NUM) {
- ctrl &= ~YT921X_ACT_UNK_ACTn_M(port);
- ctrl |= YT921X_ACT_UNK_ACTn_DROP(port);
- }
res = yt921x_reg_write(priv, YT921X_ACT_UNK_UCAST, ctrl);
if (res)
return res;
--
2.53.0
^ permalink raw reply [flat|nested] 7+ messages in thread
* [PATCH net-next 3/6] net: dsa: motorcomm: Add BR_FLOOD and BR_MCAST_FLOOD support
2026-11-02 15:56 [PATCH net-next 0/6] net: dsa: motorcomm: More BR_* flags support David Yang
2026-11-02 15:56 ` [PATCH net-next 1/6] net: dsa: motorcomm: Isolate all ports during chip setup David Yang
2026-11-02 15:56 ` [PATCH net-next 2/6] net: dsa: motorcomm: Rely on port isolation for CPU port David Yang
@ 2026-11-02 15:56 ` David Yang
2026-11-02 15:56 ` [PATCH net-next 4/6] net: dsa: motorcomm: Translate FDB VID for VLAN-unaware bridges David Yang
` (2 subsequent siblings)
5 siblings, 0 replies; 7+ messages in thread
From: David Yang @ 2026-11-02 15:56 UTC (permalink / raw)
To: netdev
Cc: David Yang, Andrew Lunn, Vladimir Oltean, David S. Miller,
Eric Dumazet, Jakub Kicinski, Paolo Abeni, Simon Horman,
linux-kernel
Let the hardware flood the unknown unicast / multicast if all other
ports in the bridge have BR_FLOOD / BR_MCAST_FLOOD enabled.
Signed-off-by: David Yang <mmyangfl@gmail.com>
---
drivers/net/dsa/motorcomm/chip.c | 173 ++++++++++++++++++++++++-------
drivers/net/dsa/motorcomm/chip.h | 4 +
2 files changed, 138 insertions(+), 39 deletions(-)
diff --git a/drivers/net/dsa/motorcomm/chip.c b/drivers/net/dsa/motorcomm/chip.c
index fb993cc9607c..907f7a5b0687 100644
--- a/drivers/net/dsa/motorcomm/chip.c
+++ b/drivers/net/dsa/motorcomm/chip.c
@@ -1160,6 +1160,7 @@ yt921x_dsa_port_vlan_add(struct dsa_switch *ds, int port,
static int yt921x_userport_standalone(struct yt921x_priv *priv, int port)
{
+ struct yt921x_port *pp = &priv->ports[port];
u32 mask;
u32 ctrl;
int res;
@@ -1190,6 +1191,17 @@ static int yt921x_userport_standalone(struct yt921x_priv *priv, int port)
if (res)
return res;
+ mask = BIT(port);
+ res = yt921x_reg_clear_bits(priv, YT921X_FILTER_UNK_UCAST, mask);
+ if (res)
+ return res;
+ res = yt921x_reg_clear_bits(priv, YT921X_FILTER_UNK_MCAST, mask);
+ if (res)
+ return res;
+
+ pp->flood = true;
+ pp->mcast_flood = true;
+
return 0;
}
@@ -1225,6 +1237,20 @@ static int yt921x_isolate(struct yt921x_priv *priv, int port)
return 0;
}
+static u16 yt921x_port_dest(struct yt921x_priv *priv, int port)
+{
+ struct dsa_switch *ds = &priv->ds;
+ struct net_device *bdev;
+ struct dsa_port *dp;
+
+ dp = dsa_to_port(ds, port);
+ bdev = dsa_port_bridge_dev_get(dp);
+ if (!bdev)
+ return 0;
+
+ return dsa_bridge_ports(ds, bdev) | priv->cpu_ports_mask;
+}
+
/* Make sure to include the CPU port in ports_mask, or your bridge will
* not have it.
*/
@@ -1268,6 +1294,63 @@ static int yt921x_bridge(struct yt921x_priv *priv, u16 ports_mask)
return 0;
}
+static int
+yt921x_bridge_flood_set_type(struct yt921x_priv *priv, u16 ports_mask, int type)
+{
+ unsigned long targets_mask;
+ u16 flood_ports_xor;
+ u32 filter_ctrl;
+ u16 flood_mask;
+ u32 addr;
+ int port;
+ int res;
+
+ ports_mask &= ~priv->cpu_ports_mask;
+
+ flood_mask = 0;
+ targets_mask = ports_mask;
+ for_each_set_bit(port, &targets_mask, YT921X_PORT_NUM) {
+ struct yt921x_port *pp = &priv->ports[port];
+
+ if (!type ? pp->flood : pp->mcast_flood)
+ flood_mask |= BIT(port);
+ }
+
+ flood_ports_xor = flood_mask ^ ports_mask;
+ filter_ctrl = 0;
+ targets_mask = ports_mask;
+ for_each_set_bit(port, &targets_mask, YT921X_PORT_NUM) {
+ if (!(flood_ports_xor & ~BIT(port)))
+ continue;
+
+ filter_ctrl |= BIT(port);
+ }
+
+ addr = !type ? YT921X_FILTER_UNK_UCAST : YT921X_FILTER_UNK_MCAST;
+ res = yt921x_reg_update_bits(priv, addr, ports_mask, filter_ctrl);
+ if (res)
+ return res;
+
+ /* ACT_UNK is TRAP everywhere but the CPU port, which DROPs; see
+ * yt921x_chip_setup_dsa()
+ */
+
+ return 0;
+}
+
+static int yt921x_bridge_flood_set(struct yt921x_priv *priv, u16 ports_mask)
+{
+ int res;
+
+ for (int type = 0; type < 2; type++) {
+ res = yt921x_bridge_flood_set_type(priv, ports_mask, type);
+ if (res)
+ return res;
+ }
+
+ return 0;
+}
+
static int yt921x_bridge_leave(struct yt921x_priv *priv, int port)
{
int res;
@@ -1280,14 +1363,24 @@ static int yt921x_bridge_leave(struct yt921x_priv *priv, int port)
if (res)
return res;
+ /* By the time port_bridge_leave() is called, the port is already
+ * unbridged, thus we cannot recompute flood flags for the affected
+ * bridge. This leaves some sub-optimality, but the flood will still
+ * be handled (by the CPU).
+ */
+
return 0;
}
-static int
-yt921x_bridge_join(struct yt921x_priv *priv, int port, u16 ports_mask)
+static int yt921x_bridge_join(struct yt921x_priv *priv, int port)
{
+ u16 ports_mask;
int res;
+ ports_mask = yt921x_port_dest(priv, port);
+ if (!ports_mask)
+ return 0;
+
res = yt921x_userport_bridge(priv, port);
if (res)
return res;
@@ -1296,6 +1389,10 @@ yt921x_bridge_join(struct yt921x_priv *priv, int port, u16 ports_mask)
if (res)
return res;
+ res = yt921x_bridge_flood_set(priv, ports_mask);
+ if (res)
+ return res;
+
return 0;
}
@@ -1304,7 +1401,9 @@ yt921x_bridge_flags(struct yt921x_priv *priv, int port,
struct switchdev_brport_flags flags)
{
struct yt921x_port *pp = &priv->ports[port];
- bool do_flush;
+ bool change_isolated;
+ bool change_flood;
+ u32 ports_mask;
u32 mask;
int res;
@@ -1318,36 +1417,44 @@ yt921x_bridge_flags(struct yt921x_priv *priv, int port,
return res;
}
- /* BR_FLOOD, BR_MCAST_FLOOD: see the comment where ACT_UNK_ACTn_TRAP
- * is set
- */
-
/* BR_BCAST_FLOOD: we can filter bcast, but cannot trap them */
- do_flush = false;
+ change_isolated = false;
if (flags.mask & BR_HAIRPIN_MODE) {
pp->hairpin = flags.val & BR_HAIRPIN_MODE;
- do_flush = true;
+ change_isolated = true;
}
if (flags.mask & BR_ISOLATED) {
pp->isolated = flags.val & BR_ISOLATED;
- do_flush = true;
+ change_isolated = true;
}
- if (do_flush) {
- struct dsa_switch *ds = &priv->ds;
- struct dsa_port *dp = dsa_to_port(ds, port);
- struct net_device *bdev;
- bdev = dsa_port_bridge_dev_get(dp);
- if (bdev) {
- u32 ports_mask;
+ change_flood = false;
+ if (flags.mask & BR_FLOOD) {
+ pp->flood = flags.val & BR_FLOOD;
+ change_flood = true;
+ }
+ if (flags.mask & BR_MCAST_FLOOD) {
+ pp->mcast_flood = flags.val & BR_MCAST_FLOOD;
+ change_flood = true;
+ }
- ports_mask = dsa_bridge_ports(ds, bdev);
- ports_mask |= priv->cpu_ports_mask;
- res = yt921x_bridge(priv, ports_mask);
- if (res)
- return res;
- }
+ if (!change_isolated && !change_flood)
+ return 0;
+
+ ports_mask = yt921x_port_dest(priv, port);
+ if (!ports_mask)
+ return 0;
+
+ if (change_isolated) {
+ res = yt921x_bridge(priv, ports_mask);
+ if (res)
+ return res;
+ }
+ if (change_flood) {
+ res = yt921x_bridge_flood_set(priv, ports_mask);
+ if (res)
+ return res;
}
return 0;
@@ -1408,17 +1515,13 @@ yt921x_dsa_port_bridge_join(struct dsa_switch *ds, int port,
struct netlink_ext_ack *extack)
{
struct yt921x_priv *priv = dsa_to_yt921x_priv(ds);
- u16 ports_mask;
int res;
if (dsa_is_cpu_port(ds, port))
return 0;
- ports_mask = dsa_bridge_ports(ds, bridge.dev);
- ports_mask |= priv->cpu_ports_mask;
-
mutex_lock(&priv->reg_lock);
- res = yt921x_bridge_join(priv, port, ports_mask);
+ res = yt921x_bridge_join(priv, port);
mutex_unlock(&priv->reg_lock);
return res;
@@ -2275,14 +2378,6 @@ static int yt921x_chip_setup_dsa(struct yt921x_priv *priv)
if (res)
return res;
- ctrl = GENMASK(10, 0) & ~priv->cpu_ports_mask;
- res = yt921x_reg_write(priv, YT921X_FILTER_UNK_UCAST, ctrl);
- if (res)
- return res;
- res = yt921x_reg_write(priv, YT921X_FILTER_UNK_MCAST, ctrl);
- if (res)
- return res;
-
/* YT921x does not support native DSA port bridging, so we use port
* isolation to emulate it. However, be especially careful that port
* isolation takes _after_ FDB lookups, i.e. if an FDB entry (from
@@ -2291,9 +2386,9 @@ static int yt921x_chip_setup_dsa(struct yt921x_priv *priv)
* the "bridged" ports, thus we need to trap and handle those packets by
* software.
*
- * If there is no more than one bridge, we might be able to drop them
- * directly given some conditions are met, but we trap them in all cases
- * for now.
+ * In some cases, we may offload the flood action to the hardware. This
+ * is done by fixing UNK_ACT to TRAP, and toggling the FILTER_UNK on the
+ * appropriate ports.
*/
ctrl = 0;
for (int i = 0; i < YT921X_PORT_NUM; i++)
diff --git a/drivers/net/dsa/motorcomm/chip.h b/drivers/net/dsa/motorcomm/chip.h
index fc315f1d8f87..842fd8f83903 100644
--- a/drivers/net/dsa/motorcomm/chip.h
+++ b/drivers/net/dsa/motorcomm/chip.h
@@ -461,6 +461,10 @@ struct yt921x_port {
bool hairpin:1;
/* BR_ISOLATED */
bool isolated:1;
+ /* BR_FLOOD */
+ bool flood:1;
+ /* BR_MCAST_FLOOD */
+ bool mcast_flood:1;
unsigned short acl_cnt;
--
2.53.0
^ permalink raw reply [flat|nested] 7+ messages in thread
* [PATCH net-next 4/6] net: dsa: motorcomm: Translate FDB VID for VLAN-unaware bridges
2026-11-02 15:56 [PATCH net-next 0/6] net: dsa: motorcomm: More BR_* flags support David Yang
` (2 preceding siblings ...)
2026-11-02 15:56 ` [PATCH net-next 3/6] net: dsa: motorcomm: Add BR_FLOOD and BR_MCAST_FLOOD support David Yang
@ 2026-11-02 15:56 ` David Yang
2026-11-02 15:56 ` [PATCH net-next 5/6] net: dsa: tag_yt921x: handle FDB suspend copy tag code David Yang
2026-11-02 15:56 ` [PATCH net-next 6/6] net: dsa: motorcomm: Add BR_PORT_LOCKED and BR_PORT_MAB support David Yang
5 siblings, 0 replies; 7+ messages in thread
From: David Yang @ 2026-11-02 15:56 UTC (permalink / raw)
To: netdev
Cc: David Yang, Andrew Lunn, Vladimir Oltean, David S. Miller,
Eric Dumazet, Jakub Kicinski, Paolo Abeni, Simon Horman,
linux-kernel
A VLAN-unaware bridge reports VID 0 for its FDB entries, but lookups run
with the unaware FID (VID 4095). Entries added through switchdev were
installed under FID 0 and could never be hit by the hardware lookups.
Translate the unaware FID and VID 0 back and forth, so the software
mapping stays symmetric with the hardware lookups.
Signed-off-by: David Yang <mmyangfl@gmail.com>
---
drivers/net/dsa/motorcomm/chip.c | 16 ++++++++++++++++
1 file changed, 16 insertions(+)
diff --git a/drivers/net/dsa/motorcomm/chip.c b/drivers/net/dsa/motorcomm/chip.c
index 907f7a5b0687..447736854870 100644
--- a/drivers/net/dsa/motorcomm/chip.c
+++ b/drivers/net/dsa/motorcomm/chip.c
@@ -590,6 +590,9 @@ yt921x_fdb_dump(struct yt921x_priv *priv, u16 ports_mask,
res = yt921x_fdb_read(priv, addr, &vid, &pmask, &index, &status);
if (res)
return res;
+
+ if (vid == YT921X_VID_UNWARE)
+ vid = 0;
if ((pmask & ports_mask) && !is_multicast_ether_addr(addr)) {
res = cb(addr, vid,
status == YT921X_FDB_ENTRY_STATUS_STATIC, data);
@@ -618,6 +621,8 @@ yt921x_fdb_dump(struct yt921x_priv *priv, u16 ports_mask,
if (!pmask)
break;
+ if (vid == YT921X_VID_UNWARE)
+ vid = 0;
if ((pmask & ports_mask) && !is_multicast_ether_addr(addr)) {
res = cb(addr, vid,
status == YT921X_FDB_ENTRY_STATUS_STATIC,
@@ -740,6 +745,10 @@ yt921x_fdb_leave(struct yt921x_priv *priv, const unsigned char *addr,
u32 val;
int res;
+ /* See yt921x_fdb_join() */
+ if (!vid)
+ vid = YT921X_VID_UNWARE;
+
/* Check for presence */
res = yt921x_fdb_has(priv, addr, vid, &index);
if (res)
@@ -782,6 +791,13 @@ yt921x_fdb_join(struct yt921x_priv *priv, const unsigned char *addr, u16 vid,
u32 val2;
int res;
+ /* A vlan-unaware bridge reports vid 0, but untagged lookups on its
+ * ports run with the unaware FID; map so that static entries match the
+ * hardware lookups.
+ */
+ if (!vid)
+ vid = YT921X_VID_UNWARE;
+
/* Check for presence */
res = yt921x_fdb_has(priv, addr, vid, &index);
if (res)
--
2.53.0
^ permalink raw reply [flat|nested] 7+ messages in thread
* [PATCH net-next 5/6] net: dsa: tag_yt921x: handle FDB suspend copy tag code
2026-11-02 15:56 [PATCH net-next 0/6] net: dsa: motorcomm: More BR_* flags support David Yang
` (3 preceding siblings ...)
2026-11-02 15:56 ` [PATCH net-next 4/6] net: dsa: motorcomm: Translate FDB VID for VLAN-unaware bridges David Yang
@ 2026-11-02 15:56 ` David Yang
2026-11-02 15:56 ` [PATCH net-next 6/6] net: dsa: motorcomm: Add BR_PORT_LOCKED and BR_PORT_MAB support David Yang
5 siblings, 0 replies; 7+ messages in thread
From: David Yang @ 2026-11-02 15:56 UTC (permalink / raw)
To: netdev
Cc: David Yang, Andrew Lunn, Vladimir Oltean, David S. Miller,
Eric Dumazet, Jakub Kicinski, Paolo Abeni, Simon Horman,
linux-kernel
Prepare for upcoming MAC Authentication Bypass feature.
Signed-off-by: David Yang <mmyangfl@gmail.com>
---
net/dsa/tag_yt921x.c | 2 ++
1 file changed, 2 insertions(+)
diff --git a/net/dsa/tag_yt921x.c b/net/dsa/tag_yt921x.c
index 294784ab6694..b8a5b6cdb5c5 100644
--- a/net/dsa/tag_yt921x.c
+++ b/net/dsa/tag_yt921x.c
@@ -49,6 +49,7 @@
*/
enum yt921x_tag_code {
YT921X_TAG_CODE_FORWARD = 0,
+ YT921X_TAG_CODE_LEARN_SUSPEND = 0x16,
YT921X_TAG_CODE_ACL = 0x17,
YT921X_TAG_CODE_UNK_UCAST = 0x19,
YT921X_TAG_CODE_UNK_MCAST = 0x1a,
@@ -135,6 +136,7 @@ yt921x_tag_rcv(struct sk_buff *skb, struct net_device *netdev)
/* Already forwarded by hardware */
dsa_default_offload_fwd_mark(skb);
break;
+ case YT921X_TAG_CODE_LEARN_SUSPEND:
case YT921X_TAG_CODE_ACL:
case YT921X_TAG_CODE_UNK_UCAST:
case YT921X_TAG_CODE_UNK_MCAST:
--
2.53.0
^ permalink raw reply [flat|nested] 7+ messages in thread
* [PATCH net-next 6/6] net: dsa: motorcomm: Add BR_PORT_LOCKED and BR_PORT_MAB support
2026-11-02 15:56 [PATCH net-next 0/6] net: dsa: motorcomm: More BR_* flags support David Yang
` (4 preceding siblings ...)
2026-11-02 15:56 ` [PATCH net-next 5/6] net: dsa: tag_yt921x: handle FDB suspend copy tag code David Yang
@ 2026-11-02 15:56 ` David Yang
5 siblings, 0 replies; 7+ messages in thread
From: David Yang @ 2026-11-02 15:56 UTC (permalink / raw)
To: netdev
Cc: David Yang, Andrew Lunn, Vladimir Oltean, David S. Miller,
Eric Dumazet, Jakub Kicinski, Paolo Abeni, Simon Horman,
linux-kernel
Program the per-port learn register to implement BR_PORT_LOCKED and
BR_PORT_MAB offload.
Learn-suspended FDB entries (status pending) created by locked ports
are never aged by the hardware. Add a delayed work to remove all pending
entries if any locked port exists.
Signed-off-by: David Yang <mmyangfl@gmail.com>
---
drivers/net/dsa/motorcomm/chip.c | 161 ++++++++++++++++++++++++++++---
drivers/net/dsa/motorcomm/chip.h | 9 ++
2 files changed, 158 insertions(+), 12 deletions(-)
diff --git a/drivers/net/dsa/motorcomm/chip.c b/drivers/net/dsa/motorcomm/chip.c
index 447736854870..43e001da634e 100644
--- a/drivers/net/dsa/motorcomm/chip.c
+++ b/drivers/net/dsa/motorcomm/chip.c
@@ -822,6 +822,109 @@ yt921x_fdb_join(struct yt921x_priv *priv, const unsigned char *addr, u16 vid,
return yt921x_fdb_add_index_in12(priv, index, ctrl1, ctrl2);
}
+/* Learn-suspended entries are never aged by the hardware, clean them up before
+ * the FDB fills up.
+ */
+static int yt921x_fdb_clean(struct yt921x_priv *priv)
+{
+ u8 status;
+ u16 index;
+ u32 ctrl;
+ u32 val;
+ int res;
+
+ ctrl = YT921X_FDB_OP_INDEX(0) | YT921X_FDB_OP_MODE_INDEX |
+ YT921X_FDB_OP_OP_GET_ONE | YT921X_FDB_OP_START;
+ res = yt921x_reg_write(priv, YT921X_FDB_OP, ctrl);
+ if (res)
+ return res;
+
+ res = yt921x_fdb_wait(priv, &val);
+ if (res)
+ return res;
+
+ status = YT921X_FDB_ENTRY_STATUS_INVALID;
+ if (!(val & YT921X_FDB_RESULT_NOTFOUND)) {
+ res = yt921x_reg_read(priv, YT921X_FDB_OUT1, &val);
+ if (res)
+ return res;
+ status = FIELD_GET(YT921X_FDB_IO1_STATUS_M, val);
+ }
+
+ index = 0;
+ while (1) {
+ if (status == YT921X_FDB_ENTRY_STATUS_PENDING) {
+ ctrl = YT921X_FDB_OP_INDEX(index) |
+ YT921X_FDB_OP_MODE_INDEX | YT921X_FDB_OP_OP_DEL |
+ YT921X_FDB_OP_START;
+ res = yt921x_reg_write(priv, YT921X_FDB_OP, ctrl);
+ if (res)
+ return res;
+
+ res = yt921x_fdb_wait(priv, &val);
+ if (res)
+ return res;
+ }
+
+ /* Never call GET_NEXT with 4095, otherwise it will hang
+ * forever until a reset!
+ */
+ if (index >= YT921X_FDB_NUM - 1)
+ break;
+
+ ctrl = YT921X_FDB_OP_INDEX(index) | YT921X_FDB_OP_MODE_INDEX |
+ YT921X_FDB_OP_NEXT_TYPE_UCAST |
+ YT921X_FDB_OP_OP_GET_NEXT | YT921X_FDB_OP_START;
+ res = yt921x_reg_write(priv, YT921X_FDB_OP, ctrl);
+ if (res)
+ return res;
+
+ res = yt921x_fdb_wait(priv, &val);
+ if (res)
+ return res;
+ if (val & YT921X_FDB_RESULT_NOTFOUND)
+ break;
+ index = FIELD_GET(YT921X_FDB_RESULT_INDEX_M, val);
+
+ res = yt921x_reg_read(priv, YT921X_FDB_OUT1, &val);
+ if (res)
+ return res;
+ status = FIELD_GET(YT921X_FDB_IO1_STATUS_M, val);
+ if (status == YT921X_FDB_ENTRY_STATUS_INVALID)
+ break;
+ }
+
+ return 0;
+}
+
+static void yt921x_fdb_clean_work(struct work_struct *work)
+{
+ struct yt921x_priv *priv = container_of_const(work, struct yt921x_priv,
+ fdb_clean.work);
+ struct device *dev = to_device(priv);
+ bool has_locked = false;
+ int res;
+
+ mutex_lock(&priv->reg_lock);
+
+ res = yt921x_fdb_clean(priv);
+
+ for (int port = 0; port < YT921X_PORT_NUM; port++)
+ if (priv->ports[port].locked) {
+ has_locked = true;
+ break;
+ }
+
+ mutex_unlock(&priv->reg_lock);
+
+ if (res)
+ dev_err(dev, "Failed to clean pending FDB entries: %i\n", res);
+
+ if (has_locked)
+ schedule_delayed_work(&priv->fdb_clean,
+ priv->fdb_clean_interval);
+}
+
static int
yt921x_dsa_port_fdb_dump(struct dsa_switch *ds, int port,
dsa_fdb_dump_cb_t *cb, void *data)
@@ -1412,6 +1515,30 @@ static int yt921x_bridge_join(struct yt921x_priv *priv, int port)
return 0;
}
+static int yt921x_port_learn_cfg(struct yt921x_priv *priv, int port)
+{
+ struct yt921x_port *pp = &priv->ports[port];
+ u32 ctrl;
+ u32 mask;
+
+ mask = YT921X_PORT_LEARN_MODE_M | YT921X_PORT_LEARN_DIS |
+ YT921X_PORT_LEARN_SUSPEND_DROP_EN |
+ YT921X_PORT_LEARN_SUSPEND_COPY_EN;
+ if (pp->locked) {
+ ctrl = YT921X_PORT_LEARN_MODE_CPU_CONTROL;
+ if (pp->mab)
+ ctrl |= YT921X_PORT_LEARN_SUSPEND_COPY_EN;
+ else
+ ctrl |= YT921X_PORT_LEARN_SUSPEND_DROP_EN;
+ } else if (pp->learning) {
+ ctrl = 0;
+ } else {
+ ctrl = YT921X_PORT_LEARN_DIS;
+ }
+ return yt921x_reg_update_bits(priv, YT921X_PORTn_LEARN(port), mask,
+ ctrl);
+}
+
static int
yt921x_bridge_flags(struct yt921x_priv *priv, int port,
struct switchdev_brport_flags flags)
@@ -1420,17 +1547,23 @@ yt921x_bridge_flags(struct yt921x_priv *priv, int port,
bool change_isolated;
bool change_flood;
u32 ports_mask;
- u32 mask;
int res;
- if (flags.mask & BR_LEARNING) {
- bool learning = flags.val & BR_LEARNING;
+ if (flags.mask & (BR_LEARNING | BR_PORT_LOCKED | BR_PORT_MAB)) {
+ if (flags.mask & BR_LEARNING)
+ pp->learning = flags.val & BR_LEARNING;
+ if (flags.mask & BR_PORT_LOCKED)
+ pp->locked = flags.val & BR_PORT_LOCKED;
+ if (flags.mask & BR_PORT_MAB)
+ pp->mab = flags.val & BR_PORT_MAB;
- mask = YT921X_PORT_LEARN_DIS;
- res = yt921x_reg_toggle_bits(priv, YT921X_PORTn_LEARN(port),
- mask, !learning);
+ res = yt921x_port_learn_cfg(priv, port);
if (res)
return res;
+
+ if ((flags.mask & BR_PORT_LOCKED) && pp->locked)
+ schedule_delayed_work(&priv->fdb_clean,
+ priv->fdb_clean_interval);
}
/* BR_BCAST_FLOOD: we can filter bcast, but cannot trap them */
@@ -1482,7 +1615,8 @@ yt921x_dsa_port_pre_bridge_flags(struct dsa_switch *ds, int port,
struct netlink_ext_ack *extack)
{
if (flags.mask & ~(BR_HAIRPIN_MODE | BR_LEARNING | BR_FLOOD |
- BR_MCAST_FLOOD | BR_ISOLATED))
+ BR_MCAST_FLOOD | BR_ISOLATED | BR_PORT_LOCKED |
+ BR_PORT_MAB))
return -EINVAL;
return 0;
}
@@ -1610,6 +1744,7 @@ yt921x_dsa_port_stp_state_set(struct dsa_switch *ds, int port, u8 state)
struct yt921x_priv *priv = dsa_to_yt921x_priv(ds);
struct device *dev = yt921x_priv_to_device(priv);
struct dsa_port *dp = dsa_to_port(ds, port);
+ struct yt921x_port *pp = &priv->ports[port];
bool learning;
u32 mask;
u32 ctrl;
@@ -1644,10 +1779,8 @@ yt921x_dsa_port_stp_state_set(struct dsa_switch *ds, int port, u8 state)
if (res)
break;
- mask = YT921X_PORT_LEARN_DIS;
- ctrl = !learning ? YT921X_PORT_LEARN_DIS : 0;
- res = yt921x_reg_update_bits(priv, YT921X_PORTn_LEARN(port),
- mask, ctrl);
+ pp->learning = learning;
+ res = yt921x_port_learn_cfg(priv, port);
} while (0);
mutex_unlock(&priv->reg_lock);
@@ -2242,9 +2375,11 @@ static int yt921x_edata_read(struct yt921x_priv *priv, u8 addr, u8 *valp)
static void yt921x_dsa_teardown(struct dsa_switch *ds)
{
-#if IS_ENABLED(CONFIG_NET_DSA_YT921X_LEDS)
struct yt921x_priv *priv = dsa_to_yt921x_priv(ds);
+ cancel_delayed_work_sync(&priv->fdb_clean);
+
+#if IS_ENABLED(CONFIG_NET_DSA_YT921X_LEDS)
yt921x_leds_remove(priv);
#endif
}
@@ -2723,6 +2858,8 @@ static int yt921x_mdio_probe(struct mdio_device *mdiodev)
mdio->switchid = 0;
mutex_init(&priv->reg_lock);
+ INIT_DELAYED_WORK(&priv->fdb_clean, yt921x_fdb_clean_work);
+ priv->fdb_clean_interval = 61 * HZ;
priv->reg_ops = &yt921x_reg_ops_mdio;
priv->reg_ctx = mdio;
diff --git a/drivers/net/dsa/motorcomm/chip.h b/drivers/net/dsa/motorcomm/chip.h
index 842fd8f83903..81d2e75fe207 100644
--- a/drivers/net/dsa/motorcomm/chip.h
+++ b/drivers/net/dsa/motorcomm/chip.h
@@ -465,6 +465,12 @@ struct yt921x_port {
bool flood:1;
/* BR_MCAST_FLOOD */
bool mcast_flood:1;
+ /* BR_PORT_LOCKED */
+ bool locked:1;
+ /* BR_PORT_MAB */
+ bool mab:1;
+ /* BR_LEARNING */
+ bool learning:1;
unsigned short acl_cnt;
@@ -526,6 +532,9 @@ struct yt921x_priv {
struct mii_bus *mbus_int;
struct mii_bus *mbus_ext;
+ struct delayed_work fdb_clean;
+ unsigned int fdb_clean_interval;
+
struct yt921x_port ports[YT921X_PORT_NUM];
u16 eee_ports_mask;
--
2.53.0
^ permalink raw reply [flat|nested] 7+ messages in thread
end of thread, other threads:[~2026-11-02 15:58 UTC | newest]
Thread overview: 7+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-11-02 15:56 [PATCH net-next 0/6] net: dsa: motorcomm: More BR_* flags support David Yang
2026-11-02 15:56 ` [PATCH net-next 1/6] net: dsa: motorcomm: Isolate all ports during chip setup David Yang
2026-11-02 15:56 ` [PATCH net-next 2/6] net: dsa: motorcomm: Rely on port isolation for CPU port David Yang
2026-11-02 15:56 ` [PATCH net-next 3/6] net: dsa: motorcomm: Add BR_FLOOD and BR_MCAST_FLOOD support David Yang
2026-11-02 15:56 ` [PATCH net-next 4/6] net: dsa: motorcomm: Translate FDB VID for VLAN-unaware bridges David Yang
2026-11-02 15:56 ` [PATCH net-next 5/6] net: dsa: tag_yt921x: handle FDB suspend copy tag code David Yang
2026-11-02 15:56 ` [PATCH net-next 6/6] net: dsa: motorcomm: Add BR_PORT_LOCKED and BR_PORT_MAB support David Yang
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®