[PATCH net] net: ti: icssg-prueth: Fix switch mode for offload and non-offload cases
From: Meghana Malladi
Date: Wed Sep 30 2026 - 08:58:45 EST
Non-offloaded switch mode is failing to forward packets. Following fixes
ensure smooth operation of switch mode in both offload and non-offload cases:
- Fix NULL dereference in icssg_enable_fw_offload() when second emac
slot is uninitialized during first port open
- Set port_vlan on both emacs when bridge is formed; previously only
the second port was set, causing TX stalls in switch offload mode
- Track UC/MC flooding state and restore it after firmware restart in
non-offload bridge mode; restart cleared IFF_PROMISC-driven flooding,
breaking unicast forwarding across bridge ports
- Defer emac_adjust_link() to after prueth_emac_start() so R30 FORWARD
commands don't time out with PRU not running, leaving stale R30 values
that caused prueth_emac_restart() to fail in non-offload bridge mode
Fixes: 161087db66d6b ("net: ti: icssg-prueth: Add Support for Multicast filtering with VLAN in HSR mode")
Signed-off-by: Meghana Malladi <m-malladi@xxxxxx>
---
drivers/net/ethernet/ti/icssg/icssg_prueth.c | 47 +++++++++++++++-----
drivers/net/ethernet/ti/icssg/icssg_prueth.h | 4 ++
2 files changed, 40 insertions(+), 11 deletions(-)
diff --git a/drivers/net/ethernet/ti/icssg/icssg_prueth.c b/drivers/net/ethernet/ti/icssg/icssg_prueth.c
index 591be5c8056b..9afda5da3ca3 100644
--- a/drivers/net/ethernet/ti/icssg/icssg_prueth.c
+++ b/drivers/net/ethernet/ti/icssg/icssg_prueth.c
@@ -213,6 +213,8 @@ static void icssg_enable_fw_offload(struct prueth *prueth)
for (mac = PRUETH_MAC0; mac < PRUETH_NUM_MACS; mac++) {
emac = prueth->emac[mac];
+ if (!emac)
+ continue;
if (prueth->is_hsr_offload_mode) {
if (emac->ndev->features & NETIF_F_HW_HSR_TAG_RM)
icssg_set_port_state(emac, ICSSG_EMAC_HSR_RX_OFFLOAD_ENABLE);
@@ -241,6 +243,16 @@ static void icssg_enable_fw_offload(struct prueth *prueth)
icssg_set_port_state(emac, ICSSG_EMAC_PORT_VLAN_AWARE_ENABLE);
}
}
+
+ /* Re-apply flooding state cleared by firmware restart.
+ * Not needed in switch mode; FDB handles forwarding there.
+ */
+ if (!prueth->is_switch_mode && netif_running(emac->ndev)) {
+ if (emac->uc_flooding_enabled)
+ icssg_set_port_state(emac, ICSSG_EMAC_PORT_UC_FLOODING_ENABLE);
+ if (emac->mc_flooding_enabled)
+ icssg_set_port_state(emac, ICSSG_EMAC_PORT_MC_FLOODING_ENABLE);
+ }
}
}
@@ -278,13 +290,22 @@ static int prueth_emac_common_start(struct prueth *prueth)
ret = icssg_config(prueth, emac, slice);
if (ret)
goto disable_class;
+ }
+
+ ret = prueth_emac_start(prueth);
+ if (ret)
+ goto disable_class;
+
+ /* Reset link state and reconfigure after firmware is running.
+ * Must be done after prueth_emac_start() so that R30 commands
+ * (e.g. ICSSG_EMAC_PORT_FORWARD from emac_adjust_link) reach
+ * the PRU firmware instead of timing out with stale R30 values.
+ */
+ for (slice = 0; slice < PRUETH_NUM_MACS; slice++) {
+ emac = prueth->emac[slice];
+ if (!emac)
+ continue;
- /* Reset link state to force reconfiguration in
- * emac_adjust_link(). Without this, if the link was already up
- * before restart, emac_adjust_link() won't detect any state
- * change and will skip critical configuration like writing
- * speed to firmware.
- */
emac->link = 0;
mutex_lock(&emac->ndev->phydev->lock);
@@ -292,10 +313,6 @@ static int prueth_emac_common_start(struct prueth *prueth)
mutex_unlock(&emac->ndev->phydev->lock);
}
- ret = prueth_emac_start(prueth);
- if (ret)
- goto disable_class;
-
emac = prueth->emac[ICSS_SLICE0] ? prueth->emac[ICSS_SLICE0] :
prueth->emac[ICSS_SLICE1];
ret = icss_iep_init(emac->iep, &prueth_iep_clockops,
@@ -1288,14 +1305,21 @@ static void emac_ndo_set_rx_mode_work(struct work_struct *work)
if (promisc) {
icssg_set_port_state(emac, ICSSG_EMAC_PORT_UC_FLOODING_ENABLE);
icssg_set_port_state(emac, ICSSG_EMAC_PORT_MC_FLOODING_ENABLE);
+ emac->uc_flooding_enabled = true;
+ emac->mc_flooding_enabled = true;
return;
}
if (allmulti) {
icssg_set_port_state(emac, ICSSG_EMAC_PORT_MC_FLOODING_ENABLE);
+ emac->mc_flooding_enabled = true;
+ emac->uc_flooding_enabled = false;
return;
}
+ emac->uc_flooding_enabled = false;
+ emac->mc_flooding_enabled = false;
+
if (emac->prueth->is_hsr_offload_mode) {
/* Track basic add/delete via callbacks */
__dev_mc_sync(ndev, icssg_prueth_hsr_add_mcast,
@@ -1919,7 +1943,8 @@ static int prueth_netdevice_port_link(struct net_device *ndev,
prueth->br_members & BIT(PRUETH_PORT_MII1)) {
prueth->is_switch_mode = true;
prueth->default_vlan = PRUETH_DFLT_VLAN_SW;
- emac->port_vlan = prueth->default_vlan;
+ prueth->emac[PRUETH_MAC0]->port_vlan = prueth->default_vlan;
+ prueth->emac[PRUETH_MAC1]->port_vlan = prueth->default_vlan;
icssg_change_mode(prueth);
}
}
diff --git a/drivers/net/ethernet/ti/icssg/icssg_prueth.h b/drivers/net/ethernet/ti/icssg/icssg_prueth.h
index df93d15c5b78..832e654a102c 100644
--- a/drivers/net/ethernet/ti/icssg/icssg_prueth.h
+++ b/drivers/net/ethernet/ti/icssg/icssg_prueth.h
@@ -242,6 +242,10 @@ struct prueth_emac {
bool offload_fwd_mark;
int port_vlan;
+ /* UC/MC flooding state - preserved across firmware restarts */
+ unsigned int uc_flooding_enabled : 1;
+ unsigned int mc_flooding_enabled : 1;
+
struct delayed_work stats_work;
u64 stats[ICSSG_NUM_MIIG_STATS];
u64 pa_stats[ICSSG_NUM_PA_STATS];
base-commit: 99b43ede9e355ba35244cc9470bf1819774ce39d
--
2.43.0