--- /dev/null
+From e0a77b1ed9001bdd0c73b99c67abb90f4bd7d7f5 Mon Sep 17 00:00:00 2001
+From: George Moussalem <george.moussalem@outlook.com>
+Date: Fri, 5 Jun 2026 12:11:29 +0400
+Subject: [PATCH] net: dsa: qca8k: Add support for force mode for fixed link
+ topology
+
+A fixed link topology is commonly used to connect this switch (on port
+0 or 6) to a SoC's MAC over SGMII. When inband negotiation is not used,
+the switch needs to be configured to operate in force mode. As such,
+enable support for force mode.
+
+Reviewed-by: Andrew Lunn <andrew@lunn.ch>
+Signed-off-by: George Moussalem <george.moussalem@outlook.com>
+Link: https://patch.msgid.link/20260605-qca8337-force-mode-v2-1-d9a6b6545bfa@outlook.com
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+---
+ drivers/net/dsa/qca/qca8k-8xxx.c | 22 ++++++++++++++++------
+ drivers/net/dsa/qca/qca8k.h | 1 +
+ 2 files changed, 17 insertions(+), 6 deletions(-)
+
+--- a/drivers/net/dsa/qca/qca8k-8xxx.c
++++ b/drivers/net/dsa/qca/qca8k-8xxx.c
+@@ -1538,7 +1538,7 @@ static int qca8k_pcs_config(struct phyli
+ {
+ struct qca8k_priv *priv = pcs_to_qca8k_pcs(pcs)->priv;
+ int cpu_port_index, ret, port;
+- u32 reg, val;
++ u32 mask, reg, val;
+
+ port = pcs_to_qca8k_pcs(pcs)->port;
+ switch (port) {
+@@ -1611,11 +1611,21 @@ static int qca8k_pcs_config(struct phyli
+ if (priv->ports_config.sgmii_tx_clk_falling_edge)
+ val |= QCA8K_PORT0_PAD_SGMII_TXCLK_FALLING_EDGE;
+
+- if (val)
+- ret = qca8k_rmw(priv, reg,
+- QCA8K_PORT0_PAD_SGMII_RXCLK_FALLING_EDGE |
+- QCA8K_PORT0_PAD_SGMII_TXCLK_FALLING_EDGE,
+- val);
++ mask = (val) ? (QCA8K_PORT0_PAD_SGMII_RXCLK_FALLING_EDGE |
++ QCA8K_PORT0_PAD_SGMII_TXCLK_FALLING_EDGE) : 0;
++
++ /*
++ * (Un)set force mode on QCA8337 only, don't include it in the mask for
++ * others. It is written to the PORT0 PAD register for both port 0 and 6.
++ */
++ if (priv->switch_id == QCA8K_ID_QCA8337) {
++ if (neg_mode == PHYLINK_PCS_NEG_OUTBAND)
++ val |= QCA8K_PORT_PAD_SGMII_FORCE_MODE;
++ mask |= QCA8K_PORT_PAD_SGMII_FORCE_MODE;
++ }
++
++ if (mask)
++ ret = qca8k_rmw(priv, reg, mask, val);
+
+ return 0;
+ }
+--- a/drivers/net/dsa/qca/qca8k.h
++++ b/drivers/net/dsa/qca/qca8k.h
+@@ -58,6 +58,7 @@
+ #define QCA8K_PORT_PAD_RGMII_TX_DELAY_EN BIT(25)
+ #define QCA8K_PORT_PAD_RGMII_RX_DELAY_EN BIT(24)
+ #define QCA8K_PORT_PAD_SGMII_EN BIT(7)
++#define QCA8K_PORT_PAD_SGMII_FORCE_MODE BIT(3)
+ #define QCA8K_REG_PWS 0x010
+ #define QCA8K_PWS_POWER_ON_SEL BIT(31)
+ /* This reg is only valid for QCA832x and toggle the package
-From ec0031908d3abb24e791af2d5be9f3b0e265c945 Mon Sep 17 00:00:00 2001
+From 0b7b378ce6cafbb948786cb6f17f406d94016c8c Mon Sep 17 00:00:00 2001
From: George Moussalem <george.moussalem@outlook.com>
-Date: Fri, 25 Apr 2025 10:51:28 +0400
-Subject: [PATCH] net: dsa: qca8k: fix led devicename when using external mdio bus
+Date: Mon, 8 Jun 2026 11:22:08 +0400
+Subject: [PATCH] net: dsa: qca8k: fix led devicename when using external mdio
+ bus
The qca8k dsa switch can use either an external or internal mdio bus.
This depends on whether the mdio node is defined under the switch node
-itself and, as such, the internal_mdio_mask is populated with its
-internal phys. Upon registering the internal mdio bus, the slave_mii_bus
+itself. Upon registering the internal mdio bus, the internal_mdio_bus
of the dsa switch is assigned to this bus. When an external mdio bus is
-used, it is left unassigned, though its id is used to create the device
-names of the leds.
-This leads to the leds being named '(efault):00:green:lan' and so on as
-the slave_mii_bus is null. So let's fix this by adding a null check and
+used, the driver still uses the internal_mdio_bus id which is used to
+create the device names of the leds.
+This leads to the leds being prefixed with '(efault)' as the
+internal_mii_bus is null. So let's fix this by adding a null check and
use the devicename of the external bus instead when an external bus is
configured.
+Fixes: 1e264f9d2918 ("net: dsa: qca8k: add LEDs basic support")
Signed-off-by: George Moussalem <george.moussalem@outlook.com>
+Reviewed-by: Andrew Lunn <andrew@lunn.ch>
+Link: https://patch.msgid.link/20260608-qca8k-leds-fix-v3-1-a915bb2f37ae@outlook.com
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
---
drivers/net/dsa/qca/qca8k-leds.c | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
--- /dev/null
+From e0a77b1ed9001bdd0c73b99c67abb90f4bd7d7f5 Mon Sep 17 00:00:00 2001
+From: George Moussalem <george.moussalem@outlook.com>
+Date: Fri, 5 Jun 2026 12:11:29 +0400
+Subject: [PATCH] net: dsa: qca8k: Add support for force mode for fixed link
+ topology
+
+A fixed link topology is commonly used to connect this switch (on port
+0 or 6) to a SoC's MAC over SGMII. When inband negotiation is not used,
+the switch needs to be configured to operate in force mode. As such,
+enable support for force mode.
+
+Reviewed-by: Andrew Lunn <andrew@lunn.ch>
+Signed-off-by: George Moussalem <george.moussalem@outlook.com>
+Link: https://patch.msgid.link/20260605-qca8337-force-mode-v2-1-d9a6b6545bfa@outlook.com
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+---
+ drivers/net/dsa/qca/qca8k-8xxx.c | 22 ++++++++++++++++------
+ drivers/net/dsa/qca/qca8k.h | 1 +
+ 2 files changed, 17 insertions(+), 6 deletions(-)
+
+--- a/drivers/net/dsa/qca/qca8k-8xxx.c
++++ b/drivers/net/dsa/qca/qca8k-8xxx.c
+@@ -1538,7 +1538,7 @@ static int qca8k_pcs_config(struct phyli
+ {
+ struct qca8k_priv *priv = pcs_to_qca8k_pcs(pcs)->priv;
+ int cpu_port_index, ret, port;
+- u32 reg, val;
++ u32 mask, reg, val;
+
+ port = pcs_to_qca8k_pcs(pcs)->port;
+ switch (port) {
+@@ -1611,11 +1611,21 @@ static int qca8k_pcs_config(struct phyli
+ if (priv->ports_config.sgmii_tx_clk_falling_edge)
+ val |= QCA8K_PORT0_PAD_SGMII_TXCLK_FALLING_EDGE;
+
+- if (val)
+- ret = qca8k_rmw(priv, reg,
+- QCA8K_PORT0_PAD_SGMII_RXCLK_FALLING_EDGE |
+- QCA8K_PORT0_PAD_SGMII_TXCLK_FALLING_EDGE,
+- val);
++ mask = (val) ? (QCA8K_PORT0_PAD_SGMII_RXCLK_FALLING_EDGE |
++ QCA8K_PORT0_PAD_SGMII_TXCLK_FALLING_EDGE) : 0;
++
++ /*
++ * (Un)set force mode on QCA8337 only, don't include it in the mask for
++ * others. It is written to the PORT0 PAD register for both port 0 and 6.
++ */
++ if (priv->switch_id == QCA8K_ID_QCA8337) {
++ if (neg_mode == PHYLINK_PCS_NEG_OUTBAND)
++ val |= QCA8K_PORT_PAD_SGMII_FORCE_MODE;
++ mask |= QCA8K_PORT_PAD_SGMII_FORCE_MODE;
++ }
++
++ if (mask)
++ ret = qca8k_rmw(priv, reg, mask, val);
+
+ return 0;
+ }
+--- a/drivers/net/dsa/qca/qca8k.h
++++ b/drivers/net/dsa/qca/qca8k.h
+@@ -58,6 +58,7 @@
+ #define QCA8K_PORT_PAD_RGMII_TX_DELAY_EN BIT(25)
+ #define QCA8K_PORT_PAD_RGMII_RX_DELAY_EN BIT(24)
+ #define QCA8K_PORT_PAD_SGMII_EN BIT(7)
++#define QCA8K_PORT_PAD_SGMII_FORCE_MODE BIT(3)
+ #define QCA8K_REG_PWS 0x010
+ #define QCA8K_PWS_POWER_ON_SEL BIT(31)
+ /* This reg is only valid for QCA832x and toggle the package
--- /dev/null
+From 0b7b378ce6cafbb948786cb6f17f406d94016c8c Mon Sep 17 00:00:00 2001
+From: George Moussalem <george.moussalem@outlook.com>
+Date: Mon, 8 Jun 2026 11:22:08 +0400
+Subject: [PATCH] net: dsa: qca8k: fix led devicename when using external mdio
+ bus
+
+The qca8k dsa switch can use either an external or internal mdio bus.
+This depends on whether the mdio node is defined under the switch node
+itself. Upon registering the internal mdio bus, the internal_mdio_bus
+of the dsa switch is assigned to this bus. When an external mdio bus is
+used, the driver still uses the internal_mdio_bus id which is used to
+create the device names of the leds.
+This leads to the leds being prefixed with '(efault)' as the
+internal_mii_bus is null. So let's fix this by adding a null check and
+use the devicename of the external bus instead when an external bus is
+configured.
+
+Fixes: 1e264f9d2918 ("net: dsa: qca8k: add LEDs basic support")
+Signed-off-by: George Moussalem <george.moussalem@outlook.com>
+Reviewed-by: Andrew Lunn <andrew@lunn.ch>
+Link: https://patch.msgid.link/20260608-qca8k-leds-fix-v3-1-a915bb2f37ae@outlook.com
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+---
+ drivers/net/dsa/qca/qca8k-leds.c | 3 ++-
+ 1 file changed, 2 insertions(+), 1 deletion(-)
+
+--- a/drivers/net/dsa/qca/qca8k-leds.c
++++ b/drivers/net/dsa/qca/qca8k-leds.c
+@@ -429,7 +429,8 @@ qca8k_parse_port_leds(struct qca8k_priv
+ init_data.fwnode = led;
+ init_data.devname_mandatory = true;
+ init_data.devicename = kasprintf(GFP_KERNEL, "%s:0%d",
+- priv->internal_mdio_bus->id,
++ priv->internal_mdio_bus ?
++ priv->internal_mdio_bus->id : priv->bus->id,
+ port_num);
+ if (!init_data.devicename) {
+ fwnode_handle_put(led);
--- a/drivers/net/dsa/qca/qca8k-8xxx.c
+++ b/drivers/net/dsa/qca/qca8k-8xxx.c
-@@ -2031,6 +2031,8 @@ static const struct dsa_switch_ops qca8k
+@@ -2041,6 +2041,8 @@ static const struct dsa_switch_ops qca8k
.port_fdb_add = qca8k_port_fdb_add,
.port_fdb_del = qca8k_port_fdb_del,
.port_fdb_dump = qca8k_port_fdb_dump,
u32 val;
--- a/drivers/net/dsa/qca/qca8k.h
+++ b/drivers/net/dsa/qca/qca8k.h
-@@ -592,5 +592,11 @@ int qca8k_port_lag_join(struct dsa_switc
+@@ -593,5 +593,11 @@ int qca8k_port_lag_join(struct dsa_switc
struct netlink_ext_ack *extack);
int qca8k_port_lag_leave(struct dsa_switch *ds, int port,
struct dsa_lag lag);
--- a/drivers/net/dsa/qca/qca8k-8xxx.c
+++ b/drivers/net/dsa/qca/qca8k-8xxx.c
-@@ -1913,15 +1913,12 @@ qca8k_setup(struct dsa_switch *ds)
+@@ -1923,15 +1923,12 @@ qca8k_setup(struct dsa_switch *ds)
}
}
--- a/drivers/net/dsa/qca/qca8k-8xxx.c
+++ b/drivers/net/dsa/qca/qca8k-8xxx.c
-@@ -1750,6 +1750,117 @@ qca8k_get_tag_protocol(struct dsa_switch
+@@ -1760,6 +1760,117 @@ qca8k_get_tag_protocol(struct dsa_switch
return DSA_TAG_PROTO_QCA;
}
static void
qca8k_conduit_change(struct dsa_switch *ds, const struct net_device *conduit,
bool operational)
-@@ -2039,8 +2150,9 @@ static const struct dsa_switch_ops qca8k
+@@ -2049,8 +2160,9 @@ static const struct dsa_switch_ops qca8k
.port_vlan_del = qca8k_port_vlan_del,
.phylink_get_caps = qca8k_phylink_get_caps,
.get_phy_flags = qca8k_get_phy_flags,
+++ /dev/null
-From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
-From: Kevin Staley <glaciertablet@gmail.com>
-Date: Mon, 29 Dec 2025 00:00:00 +0000
-Subject: [PATCH] net: dsa: qca8k: merge host FDB for multi-CPU setups
-
-In multi-CPU-port setups, DSA may install the same host FDB entry on
-multiple upstream-facing ports when bridged user ports use different
-conduits. qca8k_port_fdb_add currently overwrites any existing entry for
-the same {MAC, VID}, so the last CPU port wins and host reachability can
-break for ports serviced by the other CPU port (e.g. Netgear R7800 when
-wan is bridged into br-lan).
-
-Use the existing search-and-insert/delete helpers for CPU and DSA ports
-so the destination port mask is merged instead of replaced.
-
-Fixes: openwrt/openwrt#17891
-Signed-off-by: Kevin Staley <glaciertablet@gmail.com>
----
- drivers/net/dsa/qca/qca8k-common.c | 10 ++++++++++
- 1 file changed, 10 insertions(+)
-
---- a/drivers/net/dsa/qca/qca8k-common.c
-+++ b/drivers/net/dsa/qca/qca8k-common.c
-@@ -836,6 +836,13 @@ int qca8k_port_fdb_add(struct dsa_switch
- struct qca8k_priv *priv = ds->priv;
- u16 port_mask = BIT(port);
-
-+ if (!vid)
-+ vid = QCA8K_PORT_VID_DEF;
-+
-+ if (dsa_is_cpu_port(ds, port) || dsa_is_dsa_port(ds, port))
-+ return qca8k_fdb_search_and_insert(priv, BIT(port), addr, vid,
-+ QCA8K_ATU_STATUS_STATIC);
-+
- return qca8k_port_fdb_insert(priv, addr, port_mask, vid);
- }
-
-@@ -849,6 +856,9 @@ int qca8k_port_fdb_del(struct dsa_switch
- if (!vid)
- vid = QCA8K_PORT_VID_DEF;
-
-+ if (dsa_is_cpu_port(ds, port) || dsa_is_dsa_port(ds, port))
-+ return qca8k_fdb_search_and_del(priv, BIT(port), addr, vid);
-+
- return qca8k_fdb_del(priv, addr, port_mask, vid);
- }
-
--- /dev/null
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Olaf Marzocchi <lists@marzocchi.net>
+Date: Fri, 26 Jun 2026 00:00:00 +0000
+Subject: [PATCH] net: dsa: qca8k: don't program unicast host FDB entries on
+ CPU ports
+
+DSA installs host FDB entries for the same {MAC, VID} once per CPU port
+in use. qca8k used to overwrite the single ATU entry, so the last CPU
+port won and host reachability broke for user ports served by the other
+CPU port (e.g. Netgear R7800 with wan bridged into br-lan). Merging the
+CPU port bits into the destination mask instead is no better: the ATU
+forwards a unicast hit to every port set in the mask, so a host entry
+spanning both CPU ports makes the switch deliver one copy of each
+host-bound frame per CPU port. This duplicated nearly all host traffic
+and halved throughput on TP-Link TGR1900, Archer C2600 and ASUS
+RT-AX89X, with "failed to delete ... from fdb: -22" log spam from the
+asymmetric delete path.
+
+Host FDB entries are a pure optimization on this switch: the unknown
+unicast forward mask in GLOBAL_FW_CTRL1 already contains all CPU ports
+and is filtered per ingress port by PORT_LOOKUP_CTRL members, so a
+host-bound frame with no ATU entry reaches exactly one CPU port, the
+conduit of its ingress port, and the tag parser delivers it to the
+correct user netdevice from either conduit. The conduits run in
+promiscuous mode (qca8k does not implement FDB isolation), so no RX
+filtering relies on these entries either.
+
+Skip programming unicast host FDB entries on CPU ports entirely. This
+fixes the multi-CPU bridged setups without ever creating a unicast
+destination mask with more than one CPU port bit, regardless of how a
+board assigns its conduits.
+
+Disclaimer: patch generated via Fable 5 and tested by @xback
+
+Fixes: openwrt/openwrt#21317
+Signed-off-by: Olaf Marzocchi <lists@marzocchi.net>
+
+---
+ drivers/net/dsa/qca/qca8k-common.c | 14 ++++++++++++++
+ 1 file changed, 14 insertions(+)
+
+--- a/drivers/net/dsa/qca/qca8k-common.c
++++ b/drivers/net/dsa/qca/qca8k-common.c
+@@ -836,6 +836,17 @@ int qca8k_port_fdb_add(struct dsa_switch
+ struct qca8k_priv *priv = ds->priv;
+ u16 port_mask = BIT(port);
+
++ /* The ATU forwards a unicast hit to every port set in the
++ * destination mask, so a host entry spanning multiple CPU ports
++ * would deliver one copy of each host-bound frame per CPU port.
++ * Skip host entries entirely: unknown unicast is already forwarded
++ * to all CPU ports by GLOBAL_FW_CTRL1 and filtered by the ingress
++ * port LOOKUP_MEMBER, so host-bound frames reach exactly one CPU
++ * port, the conduit of the ingress port.
++ */
++ if (dsa_is_cpu_port(ds, port) || dsa_is_dsa_port(ds, port))
++ return 0;
++
+ return qca8k_port_fdb_insert(priv, addr, port_mask, vid);
+ }
+
+@@ -846,6 +857,9 @@ int qca8k_port_fdb_del(struct dsa_switch
+ struct qca8k_priv *priv = ds->priv;
+ u16 port_mask = BIT(port);
+
++ if (dsa_is_cpu_port(ds, port) || dsa_is_dsa_port(ds, port))
++ return 0;
++
+ if (!vid)
+ vid = QCA8K_PORT_VID_DEF;
+
--- /dev/null
+From a4918445f34b80589a76cc047dd7d907b28b37ce Mon Sep 17 00:00:00 2001
+From: Rosen Penev <rosenp@gmail.com>
+Date: Sat, 27 Jun 2026 14:50:13 -0700
+Subject: [PATCH] net: dsa: qca8k: fall back to ethernet-ports node name for
+ LEDs
+
+The device tree binding allows both "ports" and "ethernet-ports" as
+the container node name. Try "ethernet-ports" when "ports" is absent
+so that newer DTBs with the preferred name work.
+
+This matches the handling already present in qca8k-8xxx.c
+
+Assisted-by: opencode:big-pickle
+Signed-off-by: Rosen Penev <rosenp@gmail.com>
+---
+ drivers/net/dsa/qca/qca8k-leds.c | 3 +++
+ 1 file changed, 3 insertions(+)
+
+--- a/drivers/net/dsa/qca/qca8k-leds.c
++++ b/drivers/net/dsa/qca/qca8k-leds.c
+@@ -457,6 +457,9 @@ qca8k_setup_led_ctrl(struct qca8k_priv *
+ int ret;
+
+ ports = device_get_named_child_node(priv->dev, "ports");
++ if (!ports)
++ ports = device_get_named_child_node(priv->dev, "ethernet-ports");
++
+ if (!ports) {
+ dev_info(priv->dev, "No ports node specified in device tree!");
+ return 0;
/* Parse CPU port config to be later used in phy_link mac_config */
ret = qca8k_parse_port_config(priv);
if (ret)
-@@ -2051,17 +2027,12 @@ qca8k_setup(struct dsa_switch *ds)
+@@ -2045,17 +2021,12 @@ qca8k_setup(struct dsa_switch *ds)
if (ret)
return ret;
ret = qca8k_rmw(priv, QCA8K_PORT_LOOKUP_CTRL(port),
QCA8K_PORT_LOOKUP_MEMBER,
-@@ -2069,6 +2040,11 @@ qca8k_setup(struct dsa_switch *ds)
+@@ -2068,6 +2039,11 @@ qca8k_setup(struct dsa_switch *ds)
if (ret)
return ret;
static int qca8k_port_change_master(struct dsa_switch *ds, int port,
struct net_device *master,
struct netlink_ext_ack *extack)
-@@ -2144,6 +2156,7 @@ static const struct dsa_switch_ops qca8k
+@@ -2140,6 +2152,7 @@ static const struct dsa_switch_ops qca8k
.get_phy_flags = qca8k_get_phy_flags,
.port_lag_join = qca8xxx_port_lag_join,
.port_lag_leave = qca8xxx_port_lag_leave,
}
/* Forward all unknown frames to CPU port for Linux processing */
-@@ -2051,11 +2057,6 @@ qca8k_setup(struct dsa_switch *ds)
+@@ -2046,11 +2052,6 @@ qca8k_setup(struct dsa_switch *ds)
if (ret)
return ret;
- if (ret)
- return ret;
-
- /* For port based vlans to work we need to set the
- * default egress vid
- */
+ ret = qca8k_rmw(priv, QCA8K_PORT_LOOKUP_CTRL(cpu_port),
+ BIT(port), BIT(port));
+ if (ret)
@@ -2107,6 +2108,9 @@ qca8k_setup(struct dsa_switch *ds)
/* Set max number of LAGs supported */
ds->num_lag_ids = QCA8K_NUM_LAGS;
--- a/drivers/net/dsa/qca/qca8k-8xxx.c
+++ b/drivers/net/dsa/qca/qca8k-8xxx.c
-@@ -2030,6 +2030,8 @@ static const struct dsa_switch_ops qca8k
+@@ -2040,6 +2040,8 @@ static const struct dsa_switch_ops qca8k
.port_fdb_add = qca8k_port_fdb_add,
.port_fdb_del = qca8k_port_fdb_del,
.port_fdb_dump = qca8k_port_fdb_dump,
u32 val;
--- a/drivers/net/dsa/qca/qca8k.h
+++ b/drivers/net/dsa/qca/qca8k.h
-@@ -591,5 +591,11 @@ int qca8k_port_lag_join(struct dsa_switc
+@@ -592,5 +592,11 @@ int qca8k_port_lag_join(struct dsa_switc
struct netlink_ext_ack *extack);
int qca8k_port_lag_leave(struct dsa_switch *ds, int port,
struct dsa_lag lag);
--- a/drivers/net/dsa/qca/qca8k-8xxx.c
+++ b/drivers/net/dsa/qca/qca8k-8xxx.c
-@@ -1912,15 +1912,12 @@ qca8k_setup(struct dsa_switch *ds)
+@@ -1922,15 +1922,12 @@ qca8k_setup(struct dsa_switch *ds)
}
}
--- a/drivers/net/dsa/qca/qca8k-8xxx.c
+++ b/drivers/net/dsa/qca/qca8k-8xxx.c
-@@ -1749,6 +1749,117 @@ qca8k_get_tag_protocol(struct dsa_switch
+@@ -1759,6 +1759,117 @@ qca8k_get_tag_protocol(struct dsa_switch
return DSA_TAG_PROTO_QCA;
}
static void
qca8k_conduit_change(struct dsa_switch *ds, const struct net_device *conduit,
bool operational)
-@@ -2038,8 +2149,9 @@ static const struct dsa_switch_ops qca8k
+@@ -2048,8 +2159,9 @@ static const struct dsa_switch_ops qca8k
.port_vlan_del = qca8k_port_vlan_del,
.phylink_get_caps = qca8k_phylink_get_caps,
.get_phy_flags = qca8k_get_phy_flags,
+++ /dev/null
-From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
-From: Kevin Staley <glaciertablet@gmail.com>
-Date: Mon, 29 Dec 2025 00:00:00 +0000
-Subject: [PATCH] net: dsa: qca8k: merge host FDB for multi-CPU setups
-
-In multi-CPU-port setups, DSA may install the same host FDB entry on
-multiple upstream-facing ports when bridged user ports use different
-conduits. qca8k_port_fdb_add currently overwrites any existing entry for
-the same {MAC, VID}, so the last CPU port wins and host reachability can
-break for ports serviced by the other CPU port (e.g. Netgear R7800 when
-wan is bridged into br-lan).
-
-Use the existing search-and-insert/delete helpers for CPU and DSA ports
-so the destination port mask is merged instead of replaced.
-
-Fixes: openwrt/openwrt#17891
-Signed-off-by: Kevin Staley <glaciertablet@gmail.com>
----
- drivers/net/dsa/qca/qca8k-common.c | 10 ++++++++++
- 1 file changed, 10 insertions(+)
-
---- a/drivers/net/dsa/qca/qca8k-common.c
-+++ b/drivers/net/dsa/qca/qca8k-common.c
-@@ -829,6 +829,13 @@ int qca8k_port_fdb_add(struct dsa_switch
- struct qca8k_priv *priv = ds->priv;
- u16 port_mask = BIT(port);
-
-+ if (!vid)
-+ vid = QCA8K_PORT_VID_DEF;
-+
-+ if (dsa_is_cpu_port(ds, port) || dsa_is_dsa_port(ds, port))
-+ return qca8k_fdb_search_and_insert(priv, BIT(port), addr, vid,
-+ QCA8K_ATU_STATUS_STATIC);
-+
- return qca8k_port_fdb_insert(priv, addr, port_mask, vid);
- }
-
-@@ -842,6 +849,9 @@ int qca8k_port_fdb_del(struct dsa_switch
- if (!vid)
- vid = QCA8K_PORT_VID_DEF;
-
-+ if (dsa_is_cpu_port(ds, port) || dsa_is_dsa_port(ds, port))
-+ return qca8k_fdb_search_and_del(priv, BIT(port), addr, vid);
-+
- return qca8k_fdb_del(priv, addr, port_mask, vid);
- }
-
--- /dev/null
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Olaf Marzocchi <lists@marzocchi.net>
+Date: Fri, 26 Jun 2026 00:00:00 +0000
+Subject: [PATCH] net: dsa: qca8k: don't program unicast host FDB entries on
+ CPU ports
+
+DSA installs host FDB entries for the same {MAC, VID} once per CPU port
+in use. qca8k used to overwrite the single ATU entry, so the last CPU
+port won and host reachability broke for user ports served by the other
+CPU port (e.g. Netgear R7800 with wan bridged into br-lan). Merging the
+CPU port bits into the destination mask instead is no better: the ATU
+forwards a unicast hit to every port set in the mask, so a host entry
+spanning both CPU ports makes the switch deliver one copy of each
+host-bound frame per CPU port. This duplicated nearly all host traffic
+and halved throughput on TP-Link TGR1900, Archer C2600 and ASUS
+RT-AX89X, with "failed to delete ... from fdb: -22" log spam from the
+asymmetric delete path.
+
+Host FDB entries are a pure optimization on this switch: the unknown
+unicast forward mask in GLOBAL_FW_CTRL1 already contains all CPU ports
+and is filtered per ingress port by PORT_LOOKUP_CTRL members, so a
+host-bound frame with no ATU entry reaches exactly one CPU port, the
+conduit of its ingress port, and the tag parser delivers it to the
+correct user netdevice from either conduit. The conduits run in
+promiscuous mode (qca8k does not implement FDB isolation), so no RX
+filtering relies on these entries either.
+
+Skip programming unicast host FDB entries on CPU ports entirely. This
+fixes the multi-CPU bridged setups without ever creating a unicast
+destination mask with more than one CPU port bit, regardless of how a
+board assigns its conduits.
+
+Disclaimer: patch generated via Fable 5 and tested by @xback
+
+Fixes: openwrt/openwrt#21317
+Signed-off-by: Olaf Marzocchi <lists@marzocchi.net>
+
+---
+ drivers/net/dsa/qca/qca8k-common.c | 14 ++++++++++++++
+ 1 file changed, 14 insertions(+)
+
+--- a/drivers/net/dsa/qca/qca8k-common.c
++++ b/drivers/net/dsa/qca/qca8k-common.c
+@@ -829,6 +829,17 @@ int qca8k_port_fdb_add(struct dsa_switch
+ struct qca8k_priv *priv = ds->priv;
+ u16 port_mask = BIT(port);
+
++ /* The ATU forwards a unicast hit to every port set in the
++ * destination mask, so a host entry spanning multiple CPU ports
++ * would deliver one copy of each host-bound frame per CPU port.
++ * Skip host entries entirely: unknown unicast is already forwarded
++ * to all CPU ports by GLOBAL_FW_CTRL1 and filtered by the ingress
++ * port LOOKUP_MEMBER, so host-bound frames reach exactly one CPU
++ * port, the conduit of the ingress port.
++ */
++ if (dsa_is_cpu_port(ds, port) || dsa_is_dsa_port(ds, port))
++ return 0;
++
+ return qca8k_port_fdb_insert(priv, addr, port_mask, vid);
+ }
+
+@@ -839,6 +850,9 @@ int qca8k_port_fdb_del(struct dsa_switch
+ struct qca8k_priv *priv = ds->priv;
+ u16 port_mask = BIT(port);
+
++ if (dsa_is_cpu_port(ds, port) || dsa_is_dsa_port(ds, port))
++ return 0;
++
+ if (!vid)
+ vid = QCA8K_PORT_VID_DEF;
+
--- /dev/null
+From a4918445f34b80589a76cc047dd7d907b28b37ce Mon Sep 17 00:00:00 2001
+From: Rosen Penev <rosenp@gmail.com>
+Date: Sat, 27 Jun 2026 14:50:13 -0700
+Subject: [PATCH] net: dsa: qca8k: fall back to ethernet-ports node name for
+ LEDs
+
+The device tree binding allows both "ports" and "ethernet-ports" as
+the container node name. Try "ethernet-ports" when "ports" is absent
+so that newer DTBs with the preferred name work.
+
+This matches the handling already present in qca8k-8xxx.c
+
+Assisted-by: opencode:big-pickle
+Signed-off-by: Rosen Penev <rosenp@gmail.com>
+---
+ drivers/net/dsa/qca/qca8k-leds.c | 3 +++
+ 1 file changed, 3 insertions(+)
+
+--- a/drivers/net/dsa/qca/qca8k-leds.c
++++ b/drivers/net/dsa/qca/qca8k-leds.c
+@@ -457,6 +457,9 @@ qca8k_setup_led_ctrl(struct qca8k_priv *
+ int ret;
+
+ ports = device_get_named_child_node(priv->dev, "ports");
++ if (!ports)
++ ports = device_get_named_child_node(priv->dev, "ethernet-ports");
++
+ if (!ports) {
+ dev_info(priv->dev, "No ports node specified in device tree!");
+ return 0;
--- /dev/null
+From e82cfa658fc4a0429b3d159d0e1bedb6b5cc6d38 Mon Sep 17 00:00:00 2001
+From: Ziyang Huang <hzyitc@outlook.com>
+Date: Sat, 18 Jan 2025 16:18:40 +0800
+Subject: [PATCH 1/3] net: dsa: qca8k: support PHY-to-PHY CPU link
+
+PHY-to-PHY CPU link is a common/demo design in IPQ50xx platform, since it only has a SGMII/SGMII+ link and a MDI link.
+
+For DSA, CPU tag is the only requirement. Fortunately, qca8337 can enable it on any port. So it's ok to trust a PHY-to-PHY link as a CPU link.
+
+Signed-off-by: Ziyang Huang <hzyitc@outlook.com>
+---
+ drivers/net/dsa/qca/qca8k-8xxx.c | 14 ++++++++------
+ 1 file changed, 8 insertions(+), 6 deletions(-)
+
+--- a/drivers/net/dsa/qca/qca8k-8xxx.c
++++ b/drivers/net/dsa/qca/qca8k-8xxx.c
+@@ -1014,7 +1014,7 @@ qca8k_setup_mdio_bus(struct qca8k_priv *
+ return ret;
+ }
+
+- if (!dsa_is_user_port(priv->ds, reg))
++ if (reg == 0 || reg == 6)
+ continue;
+
+ of_get_phy_mode(port, &mode);
+@@ -1089,17 +1089,19 @@ qca8k_setup_mac_pwr_sel(struct qca8k_pri
+
+ static int qca8k_find_cpu_port(struct dsa_switch *ds)
+ {
+- struct qca8k_priv *priv = ds->priv;
++ int i;
+
+- /* 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;
+
++ /* PHY-to-PHY link */
++ for (i = 1; i <= 5; i++)
++ if (dsa_is_cpu_port(ds, i))
++ return i;
++
+ return -EINVAL;
+ }
+
+@@ -1960,7 +1962,7 @@ qca8k_setup(struct dsa_switch *ds)
+
+ 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");
++ dev_err(priv->dev, "No cpu port configured");
+ return cpu_port;
+ }
+
--- /dev/null
+From 08740093ef69e94613f19323f3abc55cb5d6863d Mon Sep 17 00:00:00 2001
+From: Ziyang Huang <hzyitc@outlook.com>
+Date: Wed, 14 Jan 2026 23:38:30 +0800
+Subject: [PATCH 2/3] net: dsa: qca8k: use correct CPU port when having multi
+ CPU ports
+
+DSA framework call dsa_tree_setup_cpu_ports() to find a default CPU port.
+If .preferred_default_local_cpu_port() isn't implemented, it will use the
+first CPU port, which is different from qca8k_find_cpu_port().
+
+Signed-off-by: Ziyang Huang <hzyitc@outlook.com>
+---
+ drivers/net/dsa/qca/qca8k-8xxx.c | 38 ++++++--------------------------
+ 1 file changed, 7 insertions(+), 31 deletions(-)
+
+--- a/drivers/net/dsa/qca/qca8k-8xxx.c
++++ b/drivers/net/dsa/qca/qca8k-8xxx.c
+@@ -1087,24 +1087,6 @@ qca8k_setup_mac_pwr_sel(struct qca8k_pri
+ return ret;
+ }
+
+-static int qca8k_find_cpu_port(struct dsa_switch *ds)
+-{
+- int i;
+-
+- if (dsa_is_cpu_port(ds, 0))
+- return 0;
+-
+- if (dsa_is_cpu_port(ds, 6))
+- return 6;
+-
+- /* PHY-to-PHY link */
+- for (i = 1; i <= 5; i++)
+- if (dsa_is_cpu_port(ds, i))
+- return i;
+-
+- return -EINVAL;
+-}
+-
+ static int
+ qca8k_setup_of_pws_reg(struct qca8k_priv *priv)
+ {
+@@ -1957,15 +1939,9 @@ qca8k_setup(struct dsa_switch *ds)
+ {
+ struct qca8k_priv *priv = ds->priv;
+ struct dsa_port *dp;
+- int cpu_port, ret;
++ int ret;
+ u32 mask;
+
+- cpu_port = qca8k_find_cpu_port(ds);
+- if (cpu_port < 0) {
+- dev_err(priv->dev, "No cpu port configured");
+- return cpu_port;
+- }
+-
+ /* Parse CPU port config to be later used in phy_link mac_config */
+ ret = qca8k_parse_port_config(priv);
+ if (ret)
+@@ -2044,17 +2020,12 @@ qca8k_setup(struct dsa_switch *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));
+- if (ret)
+- return ret;
+-
+ /* Setup connection between CPU port & user ports
+ * Individual user ports get connected to CPU port only
+ */
+ dsa_switch_for_each_user_port(dp, ds) {
+ u8 port = dp->index;
++ u8 cpu_port = dp->cpu_dp->index;
+
+ ret = qca8k_rmw(priv, QCA8K_PORT_LOOKUP_CTRL(port),
+ QCA8K_PORT_LOOKUP_MEMBER,
+@@ -2067,6 +2038,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
+ */
--- /dev/null
+From a572f1020159294fe980606996e7e3af5fa7ba9a Mon Sep 17 00:00:00 2001
+From: Ziyang Huang <hzyitc@outlook.com>
+Date: Wed, 14 Jan 2026 23:38:30 +0800
+Subject: [PATCH 3/3] net: dsa: qca8k: implement
+ ds->ops->preferred_default_local_cpu_port()
+
+Signed-off-by: Ziyang Huang <hzyitc@outlook.com>
+---
+ drivers/net/dsa/qca/qca8k-8xxx.c | 13 +++++++++++++
+ 1 file changed, 13 insertions(+)
+
+--- a/drivers/net/dsa/qca/qca8k-8xxx.c
++++ b/drivers/net/dsa/qca/qca8k-8xxx.c
+@@ -1743,6 +1743,18 @@ qca8k_get_tag_protocol(struct dsa_switch
+ return DSA_TAG_PROTO_QCA;
+ }
+
++static struct dsa_port *
++qca8k_preferred_default_local_cpu_port(struct dsa_switch *ds)
++{
++ if (dsa_is_cpu_port(ds, 0))
++ return dsa_to_port(ds, 0);
++
++ if (dsa_is_cpu_port(ds, 6))
++ return dsa_to_port(ds, 6);
++
++ return NULL;
++}
++
+ static int qca8k_port_change_master(struct dsa_switch *ds, int port,
+ struct net_device *master,
+ struct netlink_ext_ack *extack)
+@@ -2139,6 +2151,7 @@ static const struct dsa_switch_ops qca8k
+ .get_phy_flags = qca8k_get_phy_flags,
+ .port_lag_join = qca8xxx_port_lag_join,
+ .port_lag_leave = qca8xxx_port_lag_leave,
++ .preferred_default_local_cpu_port = qca8k_preferred_default_local_cpu_port,
+ .port_change_conduit = qca8k_port_change_master,
+ .conduit_state_change = qca8k_conduit_change,
+ .connect_tag_protocol = qca8k_connect_tag_protocol,
}
/* Forward all unknown frames to CPU port for Linux processing */
-@@ -2050,11 +2056,6 @@ qca8k_setup(struct dsa_switch *ds)
+@@ -2045,11 +2051,6 @@ qca8k_setup(struct dsa_switch *ds)
if (ret)
return ret;
- if (ret)
- return ret;
-
- /* For port based vlans to work we need to set the
- * default egress vid
- */
+ ret = qca8k_rmw(priv, QCA8K_PORT_LOOKUP_CTRL(cpu_port),
+ BIT(port), BIT(port));
+ if (ret)
@@ -2106,6 +2107,9 @@ qca8k_setup(struct dsa_switch *ds)
/* Set max number of LAGs supported */
ds->num_lag_ids = QCA8K_NUM_LAGS;
#define QCA8K_QCA832X_MIB_COUNT 39
#define QCA8K_QCA833X_MIB_COUNT 41
-@@ -265,6 +269,7 @@
+@@ -266,6 +270,7 @@
#define QCA8K_PORT_LOOKUP_STATE_LEARNING QCA8K_PORT_LOOKUP_STATE(0x3)
#define QCA8K_PORT_LOOKUP_STATE_FORWARD QCA8K_PORT_LOOKUP_STATE(0x4)
#define QCA8K_PORT_LOOKUP_LEARN BIT(20)
#define QCA8K_PORT_LOOKUP_ING_MIRROR_EN BIT(25)
#define QCA8K_REG_GOL_TRUNK_CTRL0 0x700
-@@ -341,6 +346,53 @@
+@@ -342,6 +347,53 @@
#define MII_ATH_MMD_ADDR 0x0d
#define MII_ATH_MMD_DATA 0x0e
enum {
QCA8K_PORT_SPEED_10M = 0,
QCA8K_PORT_SPEED_100M = 1,
-@@ -468,6 +520,10 @@ struct qca8k_priv {
+@@ -469,6 +521,10 @@ struct qca8k_priv {
struct qca8k_pcs pcs_port_6;
const struct qca8k_match_data *info;
struct qca8k_led ports_led[QCA8K_LED_COUNT];
#define QCA8K_QCA832X_MIB_COUNT 39
#define QCA8K_QCA833X_MIB_COUNT 41
-@@ -265,6 +269,7 @@
+@@ -266,6 +270,7 @@
#define QCA8K_PORT_LOOKUP_STATE_LEARNING QCA8K_PORT_LOOKUP_STATE(0x3)
#define QCA8K_PORT_LOOKUP_STATE_FORWARD QCA8K_PORT_LOOKUP_STATE(0x4)
#define QCA8K_PORT_LOOKUP_LEARN BIT(20)
#define QCA8K_PORT_LOOKUP_ING_MIRROR_EN BIT(25)
#define QCA8K_REG_GOL_TRUNK_CTRL0 0x700
-@@ -341,6 +346,53 @@
+@@ -342,6 +347,53 @@
#define MII_ATH_MMD_ADDR 0x0d
#define MII_ATH_MMD_DATA 0x0e
enum {
QCA8K_PORT_SPEED_10M = 0,
QCA8K_PORT_SPEED_100M = 1,
-@@ -468,6 +520,10 @@ struct qca8k_priv {
+@@ -469,6 +521,10 @@ struct qca8k_priv {
struct qca8k_pcs pcs_port_6;
const struct qca8k_match_data *info;
struct qca8k_led ports_led[QCA8K_LED_COUNT];
+++ /dev/null
-From patchwork Fri Jun 5 08:11:29 2026
-Content-Type: text/plain; charset="utf-8"
-MIME-Version: 1.0
-Content-Transfer-Encoding: 7bit
-X-Patchwork-Submitter: George Moussalem <george.moussalem@outlook.com>
-X-Patchwork-Id: 14612861
-X-Patchwork-Delegate: kuba@kernel.org
-Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org
- [10.30.226.201])
- (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits))
- (No client certificate requested)
- by smtp.subspace.kernel.org (Postfix) with ESMTPS id A30B6495510;
- Fri, 5 Jun 2026 08:11:31 +0000 (UTC)
-Authentication-Results: smtp.subspace.kernel.org;
- arc=none smtp.client-ip=10.30.226.201
-ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116;
- t=1780647091; cv=none;
- b=izElTZnAl4VU4jMwjDaYCFw9xgx5Jdm3nvzLj6yKtIxqBkHhndvHgZTl1ps4ZXGZY7WuzqrPlnZzKvjqbmLl9zMojNhz9bfdRUSxAHckbIK7FvDAqJkdGnW7vUVhM2Y2vqBfHF8y99Y7jlplG33EUJvsGus3U3Nwb3XrMMA+TEk=
-ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org;
- s=arc-20240116; t=1780647091; c=relaxed/simple;
- bh=wVsP7qwG8BJu2wprX0HV2M1fTkH7tQV3G1mR8NtSBaw=;
- h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:To:Cc;
- b=YkSoB8rVHO59IG66zmiHuYuyYWLzbyGnZ4+Pyj30JYpjAyBu7MB3zgNmma5FE0yJ7yiQycoKrPcRux8qX7HpzavyQwPsDNmhSJ3eRqrE70gLAOVn8Aia4AnJW0rE5tcNyS6PhhYfflZLwAMqrMF9wuhVOgl8HQ/nT/H2Xry/x8Q=
-ARC-Authentication-Results: i=1; smtp.subspace.kernel.org;
- dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org
- header.b=Hbrnqrg9; arc=none smtp.client-ip=10.30.226.201
-Authentication-Results: smtp.subspace.kernel.org;
- dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org
- header.b="Hbrnqrg9"
-Received: by smtp.kernel.org (Postfix) with ESMTPS id 4CA61C2BCC6;
- Fri, 5 Jun 2026 08:11:31 +0000 (UTC)
-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org;
- s=k20201202; t=1780647091;
- bh=wVsP7qwG8BJu2wprX0HV2M1fTkH7tQV3G1mR8NtSBaw=;
- h=From:Date:Subject:To:Cc:Reply-To:From;
- b=Hbrnqrg9RhUWrD93DON6WYHKiUY/P9JoQoOKcBb0/KJWXzbzKUzy1aiEPRDNCz74l
- unOk46jE02c/euDZYR1gDWxh9Uxacss0xlUunTTj86g+Uk7iSNaM04wzH4DGgAqWuP
- QOK6c9mcDNCgEQ6otxAJVE8JDdQ6Rci/uyPf3YSW6lk2Xc5jca5/hSBn1Ed49K5Ycp
- e3VdODdZns9EJlfW2FyX1pLCLijdAZOhdw0+BdfL7Jl8v1NW3lPx68kX0BRG1rAFRs
- fIOgpgls+EJeJW0zsiaY65LScSz0F1I6vkcMUyGlEv/qY6jPK7TwZ9GbG+DUyC7SF3
- jl5FVBD61KZUQ==
-Received: from aws-us-west-2-korg-lkml-1.web.codeaurora.org
- (localhost.localdomain [127.0.0.1])
- by smtp.lore.kernel.org (Postfix) with ESMTP id 305B6CD6E5D;
- Fri, 5 Jun 2026 08:11:31 +0000 (UTC)
-Date: Fri, 05 Jun 2026 12:11:29 +0400
-Subject: [PATCH next-next v2] net: dsa: qca8k: Add support for force mode
- for fixed link topology
-Precedence: bulk
-X-Mailing-List: netdev@vger.kernel.org
-List-Id: <netdev.vger.kernel.org>
-List-Subscribe: <mailto:netdev+subscribe@vger.kernel.org>
-List-Unsubscribe: <mailto:netdev+unsubscribe@vger.kernel.org>
-MIME-Version: 1.0
-Message-Id: <20260605-qca8337-force-mode-v2-1-d9a6b6545bfa@outlook.com>
-X-B4-Tracking: v=1; b=H4sIAAAAAAAC/32NTQ6CMBCFr0Jm7ZjSYlFX3sOwKGUqjdLRFgmGc
- HcrB3Dzki/vb4FE0VOCc7FApMknzyGD3BVgexNuhL7LDFJILbRQ+LLmqFSNjqMlHLgjdK1tzUG
- edFVpyMVnJOfnbfQKgeYRfwJNtnqfRo6f7W4qt8C/5anEEq1xta6Fc6ZVF36PD+b73vIAzbquX
- 0/JJvLBAAAA
-X-Change-ID: 20260603-qca8337-force-mode-fbcba5296446
-To: Andrew Lunn <andrew@lunn.ch>, Vladimir Oltean <olteanv@gmail.com>,
- "David S. Miller" <davem@davemloft.net>, Eric Dumazet <edumazet@google.com>,
- Jakub Kicinski <kuba@kernel.org>, Paolo Abeni <pabeni@redhat.com>
-Cc: netdev@vger.kernel.org, linux-kernel@vger.kernel.org,
- George Moussalem <george.moussalem@outlook.com>
-X-Mailer: b4 0.14.3
-X-Developer-Signature: v=1; a=ed25519-sha256; t=1780647089; l=3187;
- i=george.moussalem@outlook.com; s=20250321; h=from:subject:message-id;
- bh=/1K49g/Q0+wrB8ffa0QJHaW9eANv5Yae+dqQkwbdI4U=;
- b=Xn7RwqPiLUqBSMkfjxaoyatDwqn+EhlJZVxXog3mgyAtwLsVAImKiBsVWVvKVPlXJ//K6hwlv
- IPyVfdFw1CBAI5t89drX8j9nLQevPmF9+g75OyKnuqOsk4jD9ZFIckZ
-X-Developer-Key: i=george.moussalem@outlook.com; a=ed25519;
- pk=/PuRTSI9iYiHwcc6Nrde8qF4ZDhJBlUgpHdhsIjnqIk=
-X-Endpoint-Received: by B4 Relay for george.moussalem@outlook.com/20250321
- with auth_id=364
-X-Original-From: George Moussalem <george.moussalem@outlook.com>
-Reply-To: george.moussalem@outlook.com
-From: George Moussalem <george.moussalem@outlook.com>
-X-Patchwork-Delegate: kuba@kernel.org
-
-From: George Moussalem <george.moussalem@outlook.com>
-
-A fixed link topology is commonly used to connect this switch (on port
-0 or 6) to a SoC's MAC over SGMII. When inband negotiation is not used,
-the switch needs to be configured to operate in force mode. As such,
-enable support for force mode.
-
-Reviewed-by: Andrew Lunn <andrew@lunn.ch>
-Signed-off-by: George Moussalem <george.moussalem@outlook.com>
----
-Changes in v2:
-- Added check for QCA8337 switch ID and build up the register mask
- accordingly before writing the force mode bit to avoid overwriting the
- bit on switches other than QCA8337 for which there's no documentation
- on whether force mode is supported or not.
-- Added comment to state that regardless of port used (0 or 6), the
- force mode bit is written to the PORT0 PAD register as per vendor SDK.
-- Link to v1: https://lore.kernel.org/r/20260603-qca8337-force-mode-v1-1-caf7670ffab3@outlook.com
----
- drivers/net/dsa/qca/qca8k-8xxx.c | 22 ++++++++++++++++------
- drivers/net/dsa/qca/qca8k.h | 1 +
- 2 files changed, 17 insertions(+), 6 deletions(-)
-
-
----
-base-commit: b7bee4ca5688e30ca50fbc87b1b8f7eed7006c17
-change-id: 20260603-qca8337-force-mode-fbcba5296446
-
-Best regards,
-
---- a/drivers/net/dsa/qca/qca8k-8xxx.c
-+++ b/drivers/net/dsa/qca/qca8k-8xxx.c
-@@ -1538,7 +1538,7 @@ static int qca8k_pcs_config(struct phyli
- {
- struct qca8k_priv *priv = pcs_to_qca8k_pcs(pcs)->priv;
- int cpu_port_index, ret, port;
-- u32 reg, val;
-+ u32 mask, reg, val;
-
- port = pcs_to_qca8k_pcs(pcs)->port;
- switch (port) {
-@@ -1611,11 +1611,21 @@ static int qca8k_pcs_config(struct phyli
- if (priv->ports_config.sgmii_tx_clk_falling_edge)
- val |= QCA8K_PORT0_PAD_SGMII_TXCLK_FALLING_EDGE;
-
-- if (val)
-- ret = qca8k_rmw(priv, reg,
-- QCA8K_PORT0_PAD_SGMII_RXCLK_FALLING_EDGE |
-- QCA8K_PORT0_PAD_SGMII_TXCLK_FALLING_EDGE,
-- val);
-+ mask = (val) ? (QCA8K_PORT0_PAD_SGMII_RXCLK_FALLING_EDGE |
-+ QCA8K_PORT0_PAD_SGMII_TXCLK_FALLING_EDGE) : 0;
-+
-+ /*
-+ * (Un)set force mode on QCA8337 only, don't include it in the mask for
-+ * others. It is written to the PORT0 PAD register for both port 0 and 6.
-+ */
-+ if (priv->switch_id == QCA8K_ID_QCA8337) {
-+ if (neg_mode == PHYLINK_PCS_NEG_OUTBAND)
-+ val |= QCA8K_PORT_PAD_SGMII_FORCE_MODE;
-+ mask |= QCA8K_PORT_PAD_SGMII_FORCE_MODE;
-+ }
-+
-+ if (mask)
-+ ret = qca8k_rmw(priv, reg, mask, val);
-
- return 0;
- }
---- a/drivers/net/dsa/qca/qca8k.h
-+++ b/drivers/net/dsa/qca/qca8k.h
-@@ -58,6 +58,7 @@
- #define QCA8K_PORT_PAD_RGMII_TX_DELAY_EN BIT(25)
- #define QCA8K_PORT_PAD_RGMII_RX_DELAY_EN BIT(24)
- #define QCA8K_PORT_PAD_SGMII_EN BIT(7)
-+#define QCA8K_PORT_PAD_SGMII_FORCE_MODE BIT(3)
- #define QCA8K_REG_PWS 0x010
- #define QCA8K_PWS_POWER_ON_SEL BIT(31)
- /* This reg is only valid for QCA832x and toggle the package