From 1f8d578a936173ce81a4ab589f91185616bc6707 Mon Sep 17 00:00:00 2001 From: Owen Huang Date: Sat, 14 Jun 2025 13:10:24 -0500 Subject: [PATCH] non-upstream: cleanup fws in link down while SDIO bus error happened Some FW signal information might not correct while SDIO bus error happened, so apply a workaround in link down to cleanup related queue and variables. Fixes SWWLAN-153899 Signed-off-by: Owen Huang --- .../broadcom/brcm80211/brcmfmac/bcmsdh.c | 4 +- .../broadcom/brcm80211/brcmfmac/cfg80211.c | 1 + .../broadcom/brcm80211/brcmfmac/fwsignal.c | 62 ++++++++++++++++++- .../broadcom/brcm80211/brcmfmac/fwsignal.h | 3 +- 4 files changed, 65 insertions(+), 5 deletions(-) diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcmsdh.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcmsdh.c index 501480cd89540..b41185544ca77 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcmsdh.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcmsdh.c @@ -345,8 +345,8 @@ static int brcmf_sdiod_skbuff_read(struct brcmf_sdio_dev *sdiodev, if (err == -ENOMEDIUM) brcmf_sdiod_change_state(sdiodev, BRCMF_SDIOD_NOMEDIUM); - if (err) - brcmf_fws_set_credit_recover(sdiodev->bus_if->drvr); + if (err && sdiodev->func2->device == SDIO_DEVICE_ID_CYPRESS_55572) + brcmf_fws_recv_err(sdiodev->bus_if->drvr); return err; } diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c index 2589d57301182..6eb48e033dea5 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c @@ -8285,6 +8285,7 @@ brcmf_notify_connect_status(struct brcmf_if *ifp, complete(&cfg->vif_disabled); brcmf_net_setcarrier(ifp, false); } + brcmf_fws_wa_cleanup_by_ifidx(ifp->drvr, ifp->ifidx); } else if (brcmf_is_nonetwork(cfg, e)) { if (brcmf_is_ibssmode(ifp->vif)) clear_bit(BRCMF_VIF_STATUS_CONNECTING, diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwsignal.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwsignal.c index 9ef4c8c8cdf1f..097ac6eeeb269 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwsignal.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwsignal.c @@ -507,6 +507,7 @@ struct brcmf_fws_info { bool bus_flow_blocked; bool creditmap_received; bool credit_recover; + bool sdio_recv_error; u8 mode; bool avoid_queueing; #if (KERNEL_VERSION(4, 16, 0) > LINUX_VERSION_CODE) @@ -745,6 +746,25 @@ static void brcmf_fws_macdesc_init(struct brcmf_fws_mac_descriptor *desc, memcpy(&desc->ea[0], addr, ETH_ALEN); } +static void brcmf_fws_macdesc_reset(struct brcmf_fws_mac_descriptor *entry) +{ + int i; + + brcmf_fws_macdesc_init(entry, entry->ea, entry->interface_id); + entry->mac_handle = 0; + entry->suppressed = 0; + entry->transit_count = 0; + entry->suppr_transit_count = 0; + entry->generation = 0; + + for (i = 0; i < BRCMF_FWS_FIFO_COUNT; i++) + entry->seq[i] = 0; + + entry->send_tim_signal = 0; + entry->traffic_pending_bmp = 0; + entry->traffic_lastreported_bmp = 0; +} + static void brcmf_fws_macdesc_deinit(struct brcmf_fws_mac_descriptor *desc) { @@ -1588,7 +1608,7 @@ static void brcmf_fws_credit_auto_recover(struct brcmf_fws_info *fws, u8 *data) fws->fifo_credit[3], fws->fifo_credit[4]); } -void brcmf_fws_set_credit_recover(struct brcmf_pub *drvr) +void brcmf_fws_recv_err(struct brcmf_pub *drvr) { struct brcmf_fws_info *fws = NULL; @@ -1600,10 +1620,11 @@ void brcmf_fws_set_credit_recover(struct brcmf_pub *drvr) if (!fws) return; - brcmf_err("Trigger credit recover\n"); + brcmf_dbg(SDIO, "Enter\n"); brcmf_fws_lock(fws); fws->credit_recover = true; + fws->sdio_recv_error = true; brcmf_fws_unlock(fws); } @@ -2672,3 +2693,40 @@ void brcmf_fws_bus_blocked(struct brcmf_pub *drvr, bool flow_blocked) fws->stats.bus_flow_block++; } } + +void brcmf_fws_wa_cleanup_by_ifidx(struct brcmf_pub *drvr, int ifidx) +{ + struct brcmf_fws_info *fws = drvr_to_fws(drvr); + struct brcmf_fws_mac_descriptor *entry; + struct brcmf_fws_mac_descriptor *table; + bool (*matchfn)(struct sk_buff *, void *) = brcmf_fws_ifidx_match; + int i; + + if (!fws->sdio_recv_error) + return; + + brcmf_dbg(SDIO, "Enter\n"); + + brcmf_fws_lock(fws); + + fws->sdio_recv_error = false; + + entry = &fws->desc.iface[ifidx]; + brcmf_dbg(SDIO, "iface[%d] mac %pM if %d psq len %d\n", + ifidx, entry->ea, entry->interface_id, entry->psq.len); + + /* cleanup interface */ + brcmf_fws_psq_flush(fws, &entry->psq, ifidx); + brcmf_fws_macdesc_reset(entry); + + /* cleanup individual nodes */ + table = &fws->desc.nodes[0]; + for (i = 0; i < ARRAY_SIZE(fws->desc.nodes); i++) + brcmf_fws_macdesc_cleanup(fws, &table[i], ifidx); + + /* cleanup txq and hanger */ + brcmf_fws_bus_txq_cleanup(fws, matchfn, ifidx); + brcmf_fws_hanger_cleanup(fws, matchfn, ifidx); + + brcmf_fws_unlock(fws); +} diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwsignal.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwsignal.h index aa8b59930275f..0add173ebe358 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwsignal.h +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwsignal.h @@ -44,6 +44,7 @@ void brcmf_fws_bustxcomplete(struct brcmf_fws_info *fws, struct sk_buff *skb, bool success); void brcmf_fws_bus_blocked(struct brcmf_pub *drvr, bool flow_blocked); void brcmf_fws_rxreorder(struct brcmf_if *ifp, struct sk_buff *skb, bool inirq); -void brcmf_fws_set_credit_recover(struct brcmf_pub *drvr); +void brcmf_fws_recv_err(struct brcmf_pub *drvr); +void brcmf_fws_wa_cleanup_by_ifidx(struct brcmf_pub *drvr, int ifidx); #endif /* FWSIGNAL_H_ */