Thread (10 messages) flat view 10 messages, 2 authors, 16d ago
COLD16d

[RFC PATCH net-next v2 4/6] net: dsa: qca8k: use each user port's CPU conduit

From: Brandon Mahdavi <hidden>
Date: 2026-08-02 03:47:26
Also in: lkml
Subsystem: networking drivers, networking [dsa], the rest · Maintainers: Andrew Lunn, "David S. Miller", Eric Dumazet, Jakub Kicinski, Paolo Abeni, Andrew Lunn, Vladimir Oltean, Linus Torvalds

From: Ziyang Huang <redacted>

QCA8K can use ports 0 and 6 as CPU ports, but the setup path
selects only one of them. This leaves the hardware topology out of
sync with the per-user-port CPU assignment already made by DSA.

Remove the separate CPU-port selection and validate that every CPU
port is supported by the hardware. For each user port, program only
its assigned CPU port into lookup membership and add the reciprocal
membership on that CPU port.

Together with flooding unknown traffic to every CPU port, ingress
lookup membership limits delivery to the correct conduit and avoids
duplicate frames.

Link: https://github.com/openwrt/openwrt/pull/21496
Signed-off-by: Ziyang Huang <redacted>
Signed-off-by: Brandon Mahdavi <redacted>
---
 drivers/net/dsa/qca/qca8k-8xxx.c | 56 ++++++++++++--------------------
 1 file changed, 20 insertions(+), 36 deletions(-)
diff --git a/drivers/net/dsa/qca/qca8k-8xxx.c b/drivers/net/dsa/qca/qca8k-8xxx.c
index 4adc5081d05d..9eae921ad716 100644
--- a/drivers/net/dsa/qca/qca8k-8xxx.c
+++ b/drivers/net/dsa/qca/qca8k-8xxx.c
@@ -1087,22 +1087,6 @@ qca8k_setup_mac_pwr_sel(struct qca8k_priv *priv)
 	return ret;
 }
 
-static int qca8k_find_cpu_port(struct dsa_switch *ds)
-{
-	struct qca8k_priv *priv = ds->priv;
-
-	/* Find the connected cpu port. Valid port are 0 or 6 */
-	if (dsa_is_cpu_port(ds, 0))
-		return 0;
-
-	dev_dbg(priv->dev, "port 0 is not the CPU port. Checking port 6");
-
-	if (dsa_is_cpu_port(ds, 6))
-		return 6;
-
-	return -EINVAL;
-}
-
 static int
 qca8k_setup_of_pws_reg(struct qca8k_priv *priv)
 {
@@ -1844,13 +1828,13 @@ qca8k_setup(struct dsa_switch *ds)
 {
 	struct qca8k_priv *priv = ds->priv;
 	struct dsa_port *dp;
-	int cpu_port, ret;
-	u32 mask;
+	u32 cpu_ports, mask;
+	int ret;
 
-	cpu_port = qca8k_find_cpu_port(ds);
-	if (cpu_port < 0) {
-		dev_err(priv->dev, "No cpu port configured in both cpu port0 and port6");
-		return cpu_port;
+	cpu_ports = dsa_cpu_ports(ds);
+	if (!cpu_ports || cpu_ports & ~(BIT(0) | BIT(6))) {
+		dev_err(priv->dev, "CPU ports must be port 0 or 6");
+		return -EINVAL;
 	}
 
 	/* Parse CPU port config to be later used in phy_link mac_config */
@@ -1922,25 +1906,20 @@ qca8k_setup(struct dsa_switch *ds)
 		}
 	}
 
-	/* Forward all unknown frames to CPU port for Linux processing */
+	/* Forward unknown frames to every CPU port. The ingress port lookup
+	 * membership restricts each frame to its assigned CPU port.
+	 */
 	ret = qca8k_write(priv, QCA8K_REG_GLOBAL_FW_CTRL1,
-			  FIELD_PREP(QCA8K_GLOBAL_FW_CTRL1_IGMP_DP_MASK, dsa_cpu_ports(ds)) |
-			  FIELD_PREP(QCA8K_GLOBAL_FW_CTRL1_BC_DP_MASK, dsa_cpu_ports(ds)) |
-			  FIELD_PREP(QCA8K_GLOBAL_FW_CTRL1_MC_DP_MASK, dsa_cpu_ports(ds)) |
-			  FIELD_PREP(QCA8K_GLOBAL_FW_CTRL1_UC_DP_MASK, dsa_cpu_ports(ds)));
-	if (ret)
-		return ret;
-
-	/* CPU port gets connected to all user ports of the switch */
-	ret = qca8k_rmw(priv, QCA8K_PORT_LOOKUP_CTRL(cpu_port),
-			QCA8K_PORT_LOOKUP_MEMBER, dsa_user_ports(ds));
+			  FIELD_PREP(QCA8K_GLOBAL_FW_CTRL1_IGMP_DP_MASK, cpu_ports) |
+			  FIELD_PREP(QCA8K_GLOBAL_FW_CTRL1_BC_DP_MASK, cpu_ports) |
+			  FIELD_PREP(QCA8K_GLOBAL_FW_CTRL1_MC_DP_MASK, cpu_ports) |
+			  FIELD_PREP(QCA8K_GLOBAL_FW_CTRL1_UC_DP_MASK, cpu_ports));
 	if (ret)
 		return ret;
 
-	/* Setup connection between CPU port & user ports
-	 * Individual user ports get connected to CPU port only
-	 */
+	/* Connect each user port bidirectionally to its assigned CPU port. */
 	dsa_switch_for_each_user_port(dp, ds) {
+		u8 cpu_port = dp->cpu_dp->index;
 		u8 port = dp->index;
 
 		ret = qca8k_rmw(priv, QCA8K_PORT_LOOKUP_CTRL(port),
@@ -1954,6 +1933,11 @@ qca8k_setup(struct dsa_switch *ds)
 		if (ret)
 			return ret;
 
+		ret = qca8k_rmw(priv, QCA8K_PORT_LOOKUP_CTRL(cpu_port),
+				BIT(port), BIT(port));
+		if (ret)
+			return ret;
+
 		/* For port based vlans to work we need to set the
 		 * default egress vid
 		 */
-- 
2.43.0
Keyboard shortcuts
hback out one level
jnext message in thread
kprevious message in thread
ldrill in
Escclose help / fold thread tree
?toggle this help