qualcommax: qca_ppe: leave the loopback port's transmit gate open

ppe_mac_hw_init() enables the loopback, waits 100 ms for it and opens that
port's PPE_PORT_BRIDGE_CTRL_TXMAC_EN, the gate the fabric needs before it
will hand a port a frame. Two paths shut it again. qca_ppe_setup() walks
every port and clears the bit for all but the CPU port, and the loopback
port is described by no board, so DSA also sees it as unused and calls
port_disable() on it. Nothing reopens it: the port has no phylink, so no
link event ever runs, and the loopback probe configured stays unreachable
for the life of the boot.

Give the bit one owner. ppe_port_bridge_txmac_set() refuses to close the
loopback port's gate, and the setup walk stops writing the bit at all - it
was only ever setting it for the CPU port, whose gate qca_ppe_port_enable()
opens a moment later when DSA enables that port. A user port's gate is
still opened by qca_ppe_mac_link_up() and closed by its link going down.

Tested on IPQ8074 (Xiaomi AX3600, kernel 6.18.44): PPE_PORT_BRIDGE_CTRL(7)
reads 0x0002ff08 before this change and 0x0003ff08 after it, the two
differing only in TXMAC_EN. The CPU port and the four user ports read the
bit set and the two ports no board describes read it clear. Forwarding,
offload and dmesg are unchanged.

Fixes: 142104bb90 ("qualcommax: add PPE driver")
Assisted-by: Claude:claude-opus-5
Signed-off-by: Julius Bairaktaris <julius@bairaktaris.de>
Link: https://github.com/openwrt/openwrt/pull/25084
Signed-off-by: Robert Marko <robimarko@gmail.com>
This commit is contained in:
Julius Bairaktaris authored and Robert Marko committed 2026-09-09 21:00:02 +02:00
1 parent 66344b9be8
commit 245f8d364f
1 file changed
+8 -4
@@ -61,6 +61,13 @@ static void ppe_port_xgmac_set(struct qca_ppe_priv *priv, int port,
static void ppe_port_bridge_txmac_set(struct qca_ppe_priv *priv, int port,
bool enable)
{
/* The loopback port is internal and has no link of its own: probe
* opens its gate once and nothing that walks the ports may close it,
* because nothing would open it again.
*/
if (!enable && port == priv->data->loopback_port)
return;
regmap_update_bits(priv->regmap, PPE_PORT_BRIDGE_CTRL(port),
PPE_PORT_BRIDGE_CTRL_TXMAC_EN,
enable ? PPE_PORT_BRIDGE_CTRL_TXMAC_EN : 0);
@@ -538,13 +545,10 @@ static int qca_ppe_setup(struct dsa_switch *ds)
val = PPE_BRIDGE_NEW_LRN_EN |
PPE_BRIDGE_STA_MOVE_EN |
FIELD_PREP(PPE_BRIDGE_PORT_ISOL, port_mask);
if (dsa_is_cpu_port(ds, i))
val |= PPE_PORT_BRIDGE_CTRL_TXMAC_EN;
regmap_update_bits(priv->regmap, PPE_PORT_BRIDGE_CTRL(i),
PPE_BRIDGE_NEW_LRN_EN |
PPE_BRIDGE_STA_MOVE_EN |
PPE_BRIDGE_PORT_ISOL |
PPE_PORT_BRIDGE_CTRL_TXMAC_EN,
PPE_BRIDGE_PORT_ISOL,
val);
ppe_port_cnt_enable(priv, i);