* [PATCH net-next v5 1/7] net: dsa: motorcomm: Fix VLAN SVL and learn limit definitions
2026-11-08 21:44 [PATCH net-next v5 0/7] net: dsa: motorcomm: Add BR_FLOOD and BR_MCAST_FLOOD support David Yang
@ 2026-11-08 21:44 ` David Yang
2026-11-08 21:44 ` [PATCH net-next v5 2/7] net: dsa: motorcomm: Drop VID 0 VLAN table programming David Yang
` (5 subsequent siblings)
6 siblings, 0 replies; 8+ messages in thread
From: David Yang @ 2026-11-08 21:44 UTC (permalink / raw)
To: netdev
Cc: David Yang, Andrew Lunn, Vladimir Oltean, David S. Miller,
Eric Dumazet, Jakub Kicinski, Paolo Abeni, linux-kernel
Bit 3 of the VLAN table control enables shared VLAN learning (IVL/SVL),
it has nothing to do with S-VLAN tagging, so name it SVL_EN rather than
SVLAN_EN.
Widen PORT_LEARN_LIMIT_M to the 13 bits up to LEARN_DROP_ON_EXCEEDED at
bit 2. Nothing uses it, so this is a no-op.
Signed-off-by: David Yang <mmyangfl@gmail.com>
---
drivers/net/dsa/motorcomm/chip.c | 4 +---
drivers/net/dsa/motorcomm/chip.h | 4 ++--
2 files changed, 3 insertions(+), 5 deletions(-)
diff --git a/drivers/net/dsa/motorcomm/chip.c b/drivers/net/dsa/motorcomm/chip.c
index 33e5fc127244..693564aeaec1 100644
--- a/drivers/net/dsa/motorcomm/chip.c
+++ b/drivers/net/dsa/motorcomm/chip.c
@@ -922,9 +922,7 @@ yt921x_vlan_aware_set(struct yt921x_priv *priv, int port, bool vlan_aware)
{
u32 ctrl;
- /* Abuse SVLAN for PCP parsing without polluting the FDB - it just works
- * despite YT921X_VLAN_CTRL_SVLAN_EN never being set
- */
+ /* Abuse SVLAN for PCP parsing without polluting the FDB */
if (!vlan_aware)
ctrl = YT921X_PORT_IGR_TPIDn_STAG(0);
else
diff --git a/drivers/net/dsa/motorcomm/chip.h b/drivers/net/dsa/motorcomm/chip.h
index fc315f1d8f87..d9425afc7bce 100644
--- a/drivers/net/dsa/motorcomm/chip.h
+++ b/drivers/net/dsa/motorcomm/chip.h
@@ -240,7 +240,7 @@ enum yt921x_app_selector {
#define YT921X_PORT_LEARN_SUSPEND_DROP_EN BIT(18)
#define YT921X_PORT_LEARN_DIS BIT(17)
#define YT921X_PORT_LEARN_LIMIT_EN BIT(16)
-#define YT921X_PORT_LEARN_LIMIT_M GENMASK(15, 8)
+#define YT921X_PORT_LEARN_LIMIT_M GENMASK(15, 3)
#define YT921X_PORT_LEARN_LIMIT(x) FIELD_PREP(YT921X_PORT_LEARN_LIMIT_M, (x))
#define YT921X_PORT_LEARN_DROP_ON_EXCEEDED BIT(2)
#define YT921X_PORT_LEARN_MODE_M GENMASK(1, 0)
@@ -357,7 +357,7 @@ enum yt921x_app_selector {
#define YT921X_VLAN_CTRLb_UNTAG_PORTn(port) BIT((port) + 8)
#define YT921X_VLAN_CTRLb_STP_ID_M GENMASK(7, 4)
#define YT921X_VLAN_CTRLb_STP_ID(x) FIELD_PREP(YT921X_VLAN_CTRLb_STP_ID_M, (x))
-#define YT921X_VLAN_CTRLb_SVLAN_EN BIT(3)
+#define YT921X_VLAN_CTRLb_SVL_EN BIT(3)
#define YT921X_VLAN_CTRLab_FID_M GENMASK_ULL(34, 23)
#define YT921X_VLAN_CTRLab_FID(x) FIELD_PREP(YT921X_VLAN_CTRLab_FID_M, (x))
#define YT921X_VLAN_CTRLa_LEARN_DIS BIT(22)
--
2.53.0
^ permalink raw reply [flat|nested] 8+ messages in thread* [PATCH net-next v5 2/7] net: dsa: motorcomm: Drop VID 0 VLAN table programming
2026-11-08 21:44 [PATCH net-next v5 0/7] net: dsa: motorcomm: Add BR_FLOOD and BR_MCAST_FLOOD support David Yang
2026-11-08 21:44 ` [PATCH net-next v5 1/7] net: dsa: motorcomm: Fix VLAN SVL and learn limit definitions David Yang
@ 2026-11-08 21:44 ` David Yang
2026-11-08 21:44 ` [PATCH net-next v5 3/7] net: dsa: motorcomm: Remove local cpu_ports_mask David Yang
` (4 subsequent siblings)
6 siblings, 0 replies; 8+ messages in thread
From: David Yang @ 2026-11-08 21:44 UTC (permalink / raw)
To: netdev
Cc: David Yang, Andrew Lunn, Vladimir Oltean, David S. Miller,
Eric Dumazet, Jakub Kicinski, Paolo Abeni, linux-kernel
The VID 0 VLAN table entry is unreachable for priority-tagged frames:
the hardware classifies them to the port's PVID before the VLAN table
lookup, just like untagged frames (measured on YT9215S), so the VID 0
entry is never looked up. Drop the dead programming.
Signed-off-by: David Yang <mmyangfl@gmail.com>
---
drivers/net/dsa/motorcomm/chip.c | 10 ----------
1 file changed, 10 deletions(-)
diff --git a/drivers/net/dsa/motorcomm/chip.c b/drivers/net/dsa/motorcomm/chip.c
index 693564aeaec1..47f06945b8c4 100644
--- a/drivers/net/dsa/motorcomm/chip.c
+++ b/drivers/net/dsa/motorcomm/chip.c
@@ -2263,7 +2263,6 @@ 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;
@@ -2321,15 +2320,6 @@ static int yt921x_chip_setup_dsa(struct yt921x_priv *priv)
if (res)
return res;
- /* Tagged VID 0 should be treated as untagged, which confuses the
- * hardware a lot
- */
- ctrls[0] = YT921X_VLAN_CTRLa_LEARN_DIS | YT921X_VLAN_CTRLa_PORTS_M;
- ctrls[1] = 0;
- res = yt921x_reg64_write(priv, YT921X_VLANn_CTRL(0), ctrls);
- if (res)
- return res;
-
return 0;
}
--
2.53.0
^ permalink raw reply [flat|nested] 8+ messages in thread* [PATCH net-next v5 3/7] net: dsa: motorcomm: Remove local cpu_ports_mask
2026-11-08 21:44 [PATCH net-next v5 0/7] net: dsa: motorcomm: Add BR_FLOOD and BR_MCAST_FLOOD support David Yang
2026-11-08 21:44 ` [PATCH net-next v5 1/7] net: dsa: motorcomm: Fix VLAN SVL and learn limit definitions David Yang
2026-11-08 21:44 ` [PATCH net-next v5 2/7] net: dsa: motorcomm: Drop VID 0 VLAN table programming David Yang
@ 2026-11-08 21:44 ` David Yang
2026-11-08 21:44 ` [PATCH net-next v5 4/7] net: dsa: motorcomm: Isolate all ports during chip setup David Yang
` (3 subsequent siblings)
6 siblings, 0 replies; 8+ messages in thread
From: David Yang @ 2026-11-08 21:44 UTC (permalink / raw)
To: netdev
Cc: David Yang, Andrew Lunn, Vladimir Oltean, David S. Miller,
Eric Dumazet, Jakub Kicinski, Paolo Abeni, linux-kernel
Use dsa_cpu_ports() or dsa_is_cpu_port() instead for a clearer view of
code.
Signed-off-by: David Yang <mmyangfl@gmail.com>
---
drivers/net/dsa/motorcomm/chip.c | 18 +++++++++---------
drivers/net/dsa/motorcomm/chip.h | 2 --
2 files changed, 9 insertions(+), 11 deletions(-)
diff --git a/drivers/net/dsa/motorcomm/chip.c b/drivers/net/dsa/motorcomm/chip.c
index 47f06945b8c4..7686995b803f 100644
--- a/drivers/net/dsa/motorcomm/chip.c
+++ b/drivers/net/dsa/motorcomm/chip.c
@@ -1003,7 +1003,7 @@ yt921x_vlan_add(struct yt921x_priv *priv, int port, u16 vid, bool untagged)
u32 ctrls[2];
masks[0] = YT921X_VLAN_CTRLa_PORTn(port) |
- YT921X_VLAN_CTRLa_PORTS(priv->cpu_ports_mask);
+ YT921X_VLAN_CTRLa_PORTS(dsa_cpu_ports(&priv->ds));
ctrls[0] = masks[0];
masks[1] = YT921X_VLAN_CTRLb_UNTAG_PORTn(port);
@@ -1158,11 +1158,12 @@ yt921x_dsa_port_vlan_add(struct dsa_switch *ds, int port,
static int yt921x_userport_standalone(struct yt921x_priv *priv, int port)
{
+ struct dsa_switch *ds = &priv->ds;
u32 mask;
u32 ctrl;
int res;
- ctrl = ~priv->cpu_ports_mask;
+ ctrl = ~dsa_cpu_ports(ds);
res = yt921x_reg_write(priv, YT921X_PORTn_ISOLATION(port), ctrl);
if (res)
return res;
@@ -1208,7 +1209,7 @@ static int yt921x_isolate(struct yt921x_priv *priv, int port)
mask = BIT(port);
for (int i = 0; i < YT921X_PORT_NUM; i++) {
- if ((BIT(i) & priv->cpu_ports_mask) || i == port)
+ if (dsa_is_cpu_port(&priv->ds, i) || i == port)
continue;
res = yt921x_reg_set_bits(priv, YT921X_PORTn_ISOLATION(i),
@@ -1225,7 +1226,7 @@ static int yt921x_isolate(struct yt921x_priv *priv, int port)
*/
static int yt921x_bridge(struct yt921x_priv *priv, u16 ports_mask)
{
- unsigned long targets_mask = ports_mask & ~priv->cpu_ports_mask;
+ unsigned long targets_mask = ports_mask & ~dsa_cpu_ports(&priv->ds);
u32 isolated_mask;
u32 ctrl;
int port;
@@ -1338,7 +1339,7 @@ yt921x_bridge_flags(struct yt921x_priv *priv, int port,
u32 ports_mask;
ports_mask = dsa_bridge_ports(ds, bdev);
- ports_mask |= priv->cpu_ports_mask;
+ ports_mask |= dsa_cpu_ports(ds);
res = yt921x_bridge(priv, ports_mask);
if (res)
return res;
@@ -1410,7 +1411,7 @@ yt921x_dsa_port_bridge_join(struct dsa_switch *ds, int port,
return 0;
ports_mask = dsa_bridge_ports(ds, bridge.dev);
- ports_mask |= priv->cpu_ports_mask;
+ ports_mask |= dsa_cpu_ports(ds);
mutex_lock(&priv->reg_lock);
res = yt921x_bridge_join(priv, port, ports_mask);
@@ -2268,10 +2269,10 @@ static int yt921x_chip_setup_dsa(struct yt921x_priv *priv)
int res;
/* Enable DSA */
- priv->cpu_ports_mask = dsa_cpu_ports(ds);
+ cpu_ports_mask = dsa_cpu_ports(ds);
ctrl = YT921X_EXT_CPU_PORT_TAG_EN | YT921X_EXT_CPU_PORT_PORT_EN |
- YT921X_EXT_CPU_PORT_PORT(__ffs(priv->cpu_ports_mask));
+ YT921X_EXT_CPU_PORT_PORT(__ffs(cpu_ports_mask));
res = yt921x_reg_write(priv, YT921X_EXT_CPU_PORT, ctrl);
if (res)
return res;
@@ -2308,7 +2309,6 @@ static int yt921x_chip_setup_dsa(struct yt921x_priv *priv)
/* 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);
diff --git a/drivers/net/dsa/motorcomm/chip.h b/drivers/net/dsa/motorcomm/chip.h
index d9425afc7bce..ab6f28d057cb 100644
--- a/drivers/net/dsa/motorcomm/chip.h
+++ b/drivers/net/dsa/motorcomm/chip.h
@@ -509,8 +509,6 @@ struct yt921x_priv {
const struct yt921x_info *info;
unsigned int meter_slot_ns;
unsigned int port_shape_slot_ns;
- /* cache of dsa_cpu_ports(ds) */
- u16 cpu_ports_mask;
unsigned char cycle_ns;
/* protect the access to the switch registers */
--
2.53.0
^ permalink raw reply [flat|nested] 8+ messages in thread* [PATCH net-next v5 4/7] net: dsa: motorcomm: Isolate all ports during chip setup
2026-11-08 21:44 [PATCH net-next v5 0/7] net: dsa: motorcomm: Add BR_FLOOD and BR_MCAST_FLOOD support David Yang
` (2 preceding siblings ...)
2026-11-08 21:44 ` [PATCH net-next v5 3/7] net: dsa: motorcomm: Remove local cpu_ports_mask David Yang
@ 2026-11-08 21:44 ` David Yang
2026-11-08 21:44 ` [PATCH net-next v5 5/7] net: dsa: motorcomm: Translate FDB VID for VLAN-unaware bridges David Yang
` (2 subsequent siblings)
6 siblings, 0 replies; 8+ messages in thread
From: David Yang @ 2026-11-08 21:44 UTC (permalink / raw)
To: netdev
Cc: David Yang, Andrew Lunn, Vladimir Oltean, David S. Miller,
Eric Dumazet, Jakub Kicinski, Paolo Abeni, 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 7686995b803f..fbf3a0cba49b 100644
--- a/drivers/net/dsa/motorcomm/chip.c
+++ b/drivers/net/dsa/motorcomm/chip.c
@@ -2220,6 +2220,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;
@@ -2257,6 +2258,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] 8+ messages in thread* [PATCH net-next v5 5/7] net: dsa: motorcomm: Translate FDB VID for VLAN-unaware bridges
2026-11-08 21:44 [PATCH net-next v5 0/7] net: dsa: motorcomm: Add BR_FLOOD and BR_MCAST_FLOOD support David Yang
` (3 preceding siblings ...)
2026-11-08 21:44 ` [PATCH net-next v5 4/7] net: dsa: motorcomm: Isolate all ports during chip setup David Yang
@ 2026-11-08 21:44 ` David Yang
2026-11-08 21:44 ` [PATCH net-next v5 6/7] net: dsa: motorcomm: Report inconsistency on bridge join failure David Yang
2026-11-08 21:44 ` [PATCH net-next v5 7/7] net: dsa: motorcomm: Support BR_FLOOD and BR_MCAST_FLOOD offloading David Yang
6 siblings, 0 replies; 8+ messages in thread
From: David Yang @ 2026-11-08 21:44 UTC (permalink / raw)
To: netdev
Cc: David Yang, Andrew Lunn, Vladimir Oltean, David S. Miller,
Eric Dumazet, Jakub Kicinski, Paolo Abeni, 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 fbf3a0cba49b..46534127d070 100644
--- a/drivers/net/dsa/motorcomm/chip.c
+++ b/drivers/net/dsa/motorcomm/chip.c
@@ -590,7 +590,10 @@ 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 ((pmask & ports_mask) && !is_multicast_ether_addr(addr)) {
+ if (vid == YT921X_VID_UNWARE)
+ vid = 0;
res = cb(addr, vid,
status == YT921X_FDB_ENTRY_STATUS_STATIC, data);
if (res)
@@ -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] 8+ messages in thread* [PATCH net-next v5 6/7] net: dsa: motorcomm: Report inconsistency on bridge join failure
2026-11-08 21:44 [PATCH net-next v5 0/7] net: dsa: motorcomm: Add BR_FLOOD and BR_MCAST_FLOOD support David Yang
` (4 preceding siblings ...)
2026-11-08 21:44 ` [PATCH net-next v5 5/7] net: dsa: motorcomm: Translate FDB VID for VLAN-unaware bridges David Yang
@ 2026-11-08 21:44 ` David Yang
2026-11-08 21:44 ` [PATCH net-next v5 7/7] net: dsa: motorcomm: Support BR_FLOOD and BR_MCAST_FLOOD offloading David Yang
6 siblings, 0 replies; 8+ messages in thread
From: David Yang @ 2026-11-08 21:44 UTC (permalink / raw)
To: netdev
Cc: David Yang, Andrew Lunn, Vladimir Oltean, David S. Miller,
Eric Dumazet, Jakub Kicinski, Paolo Abeni, linux-kernel
DSA does not send bridge_leave when port_bridge_join fails, so a join
that fails midway leaves the port half configured. Rolling back from the
driver would just issue more register accesses that can fail the same
way. Set an extack message on failure instead.
Signed-off-by: David Yang <mmyangfl@gmail.com>
---
drivers/net/dsa/motorcomm/chip.c | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/drivers/net/dsa/motorcomm/chip.c b/drivers/net/dsa/motorcomm/chip.c
index 46534127d070..3d4aa79ac026 100644
--- a/drivers/net/dsa/motorcomm/chip.c
+++ b/drivers/net/dsa/motorcomm/chip.c
@@ -1433,6 +1433,11 @@ yt921x_dsa_port_bridge_join(struct dsa_switch *ds, int port,
res = yt921x_bridge_join(priv, port, ports_mask);
mutex_unlock(&priv->reg_lock);
+ /* DSA does not send bridge_leave when join fails */
+ if (res)
+ NL_SET_ERR_MSG_MOD(extack,
+ "Fatal IO error; hardware no longer consistent");
+
return res;
}
--
2.53.0
^ permalink raw reply [flat|nested] 8+ messages in thread* [PATCH net-next v5 7/7] net: dsa: motorcomm: Support BR_FLOOD and BR_MCAST_FLOOD offloading
2026-11-08 21:44 [PATCH net-next v5 0/7] net: dsa: motorcomm: Add BR_FLOOD and BR_MCAST_FLOOD support David Yang
` (5 preceding siblings ...)
2026-11-08 21:44 ` [PATCH net-next v5 6/7] net: dsa: motorcomm: Report inconsistency on bridge join failure David Yang
@ 2026-11-08 21:44 ` David Yang
6 siblings, 0 replies; 8+ messages in thread
From: David Yang @ 2026-11-08 21:44 UTC (permalink / raw)
To: netdev
Cc: David Yang, Andrew Lunn, Vladimir Oltean, David S. Miller,
Eric Dumazet, Jakub Kicinski, Paolo Abeni, linux-kernel
Let the hardware flood the unknown unicast / multicast for the port if:
- the switch carries only one bridge, otherwise frames hitting FDB entry
from another bridge will be dropped;
- all other ports in the bridge have BR_FLOOD / BR_MCAST_FLOOD enabled;
- the port is not hairpined, as the hardware never floods back to its
ingress port.
This should cover most single-bridge usage. Other data paths remain
unchanged in terms of behavior.
Signed-off-by: David Yang <mmyangfl@gmail.com>
---
drivers/net/dsa/motorcomm/chip.c | 181 ++++++++++++++++++++++++++-----
drivers/net/dsa/motorcomm/chip.h | 15 +++
2 files changed, 170 insertions(+), 26 deletions(-)
diff --git a/drivers/net/dsa/motorcomm/chip.c b/drivers/net/dsa/motorcomm/chip.c
index 3d4aa79ac026..480203ac7c8e 100644
--- a/drivers/net/dsa/motorcomm/chip.c
+++ b/drivers/net/dsa/motorcomm/chip.c
@@ -1195,13 +1195,17 @@ static int yt921x_userport_standalone(struct yt921x_priv *priv, int port)
if (res)
return res;
- /* Unrelated since learning is off and all packets are trapped;
- * set it anyway
+ /* Unrelated since learning is off and there is only one possible
+ * destination; set it anyway
*/
res = yt921x_port_set_pvid(priv, port, YT921X_VID_UNWARE);
if (res)
return res;
+ /* Also unrelated for FILTER_UNK_UCAST / FILTER_UNK_MCAST since all
+ * packets are handled by software
+ */
+
return 0;
}
@@ -1218,6 +1222,97 @@ static int yt921x_userport_bridge(struct yt921x_priv *priv, int port)
return 0;
}
+/* ports_mask is optional */
+static int yt921x_bridge_flood_set(struct yt921x_priv *priv, u16 ports_mask)
+{
+ struct dsa_bridge *bridges[YT921X_PORT_NUM];
+ struct dsa_switch *ds = &priv->ds;
+ struct dsa_port *dp;
+ unsigned int count;
+ u32 ctrl;
+ int res;
+ int ret;
+
+ /* Offloading is only enabled if there is at most 1 bridge - see the
+ * cross-bridge FDB mismatch below. (0 does nothing, of course.)
+ */
+ count = 0;
+ dsa_switch_for_each_user_port(dp, ds) {
+ unsigned int i;
+
+ if (!dp->bridge)
+ continue;
+
+ for (i = 0; i < count; i++)
+ if (bridges[i] == dp->bridge)
+ break;
+ if (i == count) {
+ bridges[count] = dp->bridge;
+ count++;
+ }
+ }
+
+ ret = 0;
+ if (count != 1) {
+ ctrl = YT921X_FILTER_PORTS_M;
+ res = yt921x_reg_write(priv, YT921X_FILTER_UNK_UCAST, ctrl);
+ if (res)
+ ret = res;
+ res = yt921x_reg_write(priv, YT921X_FILTER_UNK_MCAST, ctrl);
+ if (res)
+ ret = res;
+ return ret;
+ }
+
+ if (ports_mask)
+ ports_mask &= ~dsa_cpu_ports(ds);
+ if (!ports_mask)
+ ports_mask = dsa_bridge_ports(ds, bridges[0]->dev);
+
+ for (int type = 0; type < 2; type++) {
+ unsigned long targets_mask = ports_mask;
+ u16 flood_mask;
+ u32 addr;
+ int port;
+
+ addr = !type ? YT921X_FILTER_UNK_UCAST :
+ YT921X_FILTER_UNK_MCAST;
+
+ flood_mask = 0;
+ 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);
+ }
+
+ ctrl = 0;
+ for_each_set_bit(port, &targets_mask, YT921X_PORT_NUM) {
+ struct yt921x_port *pp = &priv->ports[port];
+
+ /* Hardware never floods back to its own ingress port,
+ * keep hairpin port trapped: the software bridge does
+ * deliver a flood there.
+ */
+ if (!pp->hairpin && !((flood_mask ^ ports_mask) &
+ ~BIT(port)))
+ continue;
+
+ ctrl |= BIT(port);
+ }
+
+ /* ACT_UNK is fixed to TRAP for every port, see
+ * yt921x_chip_setup_dsa().
+ */
+
+ res = yt921x_reg_update_bits(priv, addr, ports_mask, ctrl);
+ if (res)
+ ret = res;
+ }
+
+ return ret;
+}
+
static int yt921x_isolate(struct yt921x_priv *priv, int port)
{
u32 mask;
@@ -1292,6 +1387,10 @@ static int yt921x_bridge_leave(struct yt921x_priv *priv, int port)
if (res)
return res;
+ res = yt921x_bridge_flood_set(priv, 0);
+ if (res)
+ return res;
+
return 0;
}
@@ -1308,6 +1407,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;
}
@@ -1316,7 +1419,12 @@ yt921x_bridge_flags(struct yt921x_priv *priv, int port,
struct switchdev_brport_flags flags)
{
struct yt921x_port *pp = &priv->ports[port];
- bool do_flush;
+ struct dsa_switch *ds = &priv->ds;
+ bool change_isolated = false;
+ bool change_flood = false;
+ struct net_device *bdev;
+ struct dsa_port *dp;
+ u32 ports_mask;
u32 mask;
int res;
@@ -1330,36 +1438,48 @@ 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;
if (flags.mask & BR_HAIRPIN_MODE) {
pp->hairpin = flags.val & BR_HAIRPIN_MODE;
- do_flush = true;
+ change_isolated = true;
+ /* Hairpin also affects flood flags */
+ change_flood = 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;
+ 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 |= dsa_cpu_ports(ds);
- res = yt921x_bridge(priv, ports_mask);
- if (res)
- return res;
- }
+ if (!change_isolated && !change_flood)
+ return 0;
+
+ dp = dsa_to_port(ds, port);
+ bdev = dsa_port_bridge_dev_get(dp);
+ if (!bdev)
+ return 0;
+
+ ports_mask = dsa_bridge_ports(ds, bdev);
+ ports_mask |= dsa_cpu_ports(ds);
+
+ 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;
@@ -2332,9 +2452,8 @@ 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.
+ * The flood action is offloaded to the hardware 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++)
@@ -2353,6 +2472,16 @@ static int yt921x_chip_setup_dsa(struct yt921x_priv *priv)
if (res)
return res;
+ /* Capture RMA when offloading mcast flood to hardware */
+ for (int i = 0; i < 0x10; i++) {
+ /* Reuse unknown mcast code */
+ ctrl = YT921X_RMA_CTRL_CPU_CODE(0x1a) |
+ YT921X_RMA_CTRL_COPY_TO_CPU | YT921X_RMA_CTRL_DROP;
+ res = yt921x_reg_write(priv, YT921X_RMA_CTRLn(i), ctrl);
+ if (res)
+ return res;
+ }
+
return 0;
}
diff --git a/drivers/net/dsa/motorcomm/chip.h b/drivers/net/dsa/motorcomm/chip.h
index ab6f28d057cb..12a7d1bf9f1d 100644
--- a/drivers/net/dsa/motorcomm/chip.h
+++ b/drivers/net/dsa/motorcomm/chip.h
@@ -329,6 +329,17 @@ enum yt921x_app_selector {
#define YT921X_LAG_MEMBERnm(n, m) (0x1805b0 + 4 * (4 * (n) + (m)))
#define YT921X_LAG_MEMBER_PORT_M GENMASK(3, 0)
#define YT921X_LAG_MEMBER_PORT(x) FIELD_PREP(YT921X_LAG_MEMBER_PORT_M, (x))
+#define YT921X_RMA_CTRLn(n) (0x1805d0 + 4 * (n))
+#define YT921X_RMA_CTRL_BYPASS_STORM BIT(12)
+#define YT921X_RMA_CTRL_BYPASS_FLOW_METER BIT(11)
+#define YT921X_RMA_CTRL_BYPASS_PORT_METER BIT(10)
+#define YT921X_RMA_CTRL_DROP BIT(9)
+#define YT921X_RMA_CTRL_COPY_TO_CPU BIT(8)
+#define YT921X_RMA_CTRL_IGNORE_VLAN BIT(7)
+#define YT921X_RMA_CTRL_IGNORE_PORT_ISOLATE BIT(6)
+#define YT921X_RMA_CTRL_CPU_CODE_M GENMASK(5, 0)
+#define YT921X_RMA_CTRL_CPU_CODE(x) FIELD_PREP(YT921X_RMA_CTRL_CPU_CODE_M, (x))
+#define YT921X_RMA_CTRL_CPU_CODE_DEFAULT 0x3f
#define YT921X_CPU_COPY 0x180690
#define YT921X_CPU_COPY_FORCE_INT_PORT BIT(2)
#define YT921X_CPU_COPY_TO_INT_CPU BIT(1)
@@ -461,6 +472,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] 8+ messages in thread