From 51f80078dc405f9e6ab104a2842501e11b10ea69 Mon Sep 17 00:00:00 2001
From: JasonHuang <Jason.Huang2@infineon.com>
Date: Fri, 11 Aug 2023 04:01:08 -0500
Subject: [PATCH] brcmfmac: OOB irq support on 43022 DS2

Issue: Observed bus is down when device is in ds2 with oob setting

Root cause:
1) In the redownload fw flow, host will try to request_irq again.
However, the irq already be registered in the beginning

2) If host readframe too early in the fw redownload flow, we will
see "mmc0: Timeout waiting for hardware interrupt"
which cause the OS hang or unstable.

Fix: Ignore request_irq again. Add re_download_fw flag and some delay
after brcmf_sdio_ulp_reinit_fw to make sure re-download fw done

Signed-off-by: JasonHuang <Jason.Huang2@infineon.com>
---
 .../broadcom/brcm80211/brcmfmac/bcmsdh.c      | 30 ++++++++++---------
 .../broadcom/brcm80211/brcmfmac/core.c        |  2 ++
 .../broadcom/brcm80211/brcmfmac/sdio.c        | 11 +++++--
 .../broadcom/brcm80211/brcmfmac/sdio.h        |  1 +
 4 files changed, 28 insertions(+), 16 deletions(-)

diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcmsdh.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcmsdh.c
index ede14e6b9..f3fea902e 100644
--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcmsdh.c
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcmsdh.c
@@ -107,25 +107,27 @@ int brcmf_sdiod_intr_register(struct brcmf_sdio_dev *sdiodev)
 	if (pdata->oob_irq_supported) {
 		brcmf_dbg(SDIO, "Enter, register OOB IRQ %d\n",
 			  pdata->oob_irq_nr);
-		spin_lock_init(&sdiodev->irq_en_lock);
-		sdiodev->irq_en = true;
+		if (!sdiodev->oob_irq_requested) {
+			spin_lock_init(&sdiodev->irq_en_lock);
+			sdiodev->irq_en = true;
 
-		ret = request_irq(pdata->oob_irq_nr, brcmf_sdiod_oob_irqhandler,
+			ret = request_irq(pdata->oob_irq_nr,
+					  brcmf_sdiod_oob_irqhandler,
 				  pdata->oob_irq_flags, "brcmf_oob_intr",
 				  &sdiodev->func1->dev);
-		if (ret != 0) {
-			brcmf_err("request_irq failed %d\n", ret);
-			return ret;
-		}
-		sdiodev->oob_irq_requested = true;
+			if (ret != 0) {
+				brcmf_err("request_irq failed %d\n", ret);
+				return ret;
+			}
+			sdiodev->oob_irq_requested = true;
 
-		ret = enable_irq_wake(pdata->oob_irq_nr);
-		if (ret != 0) {
-			brcmf_err("enable_irq_wake failed %d\n", ret);
-			return ret;
+			ret = enable_irq_wake(pdata->oob_irq_nr);
+			if (ret != 0) {
+				brcmf_err("enable_irq_wake failed %d\n", ret);
+				return ret;
+			}
+			disable_irq_wake(pdata->oob_irq_nr);
 		}
-		disable_irq_wake(pdata->oob_irq_nr);
-
 		sdio_claim_host(sdiodev->func1);
 
 		if (sdiodev->bus_if->chip == BRCM_CC_43362_CHIP_ID) {
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c
index a723452b4..4b8d08977 100644
--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c
@@ -683,6 +683,8 @@ static int brcmf_rx_hdrpull(struct brcmf_pub *drvr, struct sk_buff *skb,
 	}
 
 	skb->protocol = eth_type_trans(skb, (*ifp)->ndev);
+	brcmf_dbg(DATA, "protocol: 0x%04X\n", skb->protocol);
+
 	return 0;
 }
 
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c
index 07ff520fe..d2a4e3625 100644
--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c
@@ -3021,8 +3021,13 @@ static void brcmf_sdio_dpc(struct brcmf_sdio *bus)
 		u32 hmb_data = 0;
 		intstatus &= ~I_HMB_HOST_INT;
 		intstatus |= brcmf_sdio_hostmail(bus, &hmb_data);
-		if (brcmf_sdio_ulp_pre_redownload_check(bus, hmb_data))
+		if (brcmf_sdio_ulp_pre_redownload_check(bus, hmb_data)) {
+			/* Will toggle redownload_fw, after F2 enabled and
+			 * register IRQ.
+			 */
+			sdiod->redownload_fw = true;
 			brcmf_sdio_ulp_reinit_fw(bus);
+		}
 	}
 
 	sdio_release_host(bus->sdiodev->func1);
@@ -3054,7 +3059,8 @@ static void brcmf_sdio_dpc(struct brcmf_sdio *bus)
 		intstatus &= ~I_HMB_FRAME_IND;
 
 	/* On frame indication, read available frames */
-	if ((intstatus & I_HMB_FRAME_IND) && (bus->clkstate == CLK_AVAIL)) {
+	if ((intstatus & I_HMB_FRAME_IND) && bus->clkstate == CLK_AVAIL &&
+	    !sdiod->redownload_fw) {
 		brcmf_sdio_readframes(bus, bus->rxbound);
 		if (!bus->rxpending)
 			intstatus &= ~I_HMB_FRAME_IND;
@@ -4888,6 +4894,7 @@ static void brcmf_sdio_firmware_callback(struct device *dev, int err,
 		err = brcmf_sdiod_intr_register(sdiod);
 		if (err != 0)
 			brcmf_err("intr register failed:%d\n", err);
+		bus->sdiodev->redownload_fw = false;
 	}
 
 	/* If we didn't come up, turn off backplane clock */
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.h
index a312b9e78..4a3556b57 100644
--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.h
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.h
@@ -250,6 +250,7 @@ struct brcmf_sdio_dev {
 	const struct firmware *clm_fw;
 	struct brcmf_ulp fmac_ulp;
 	bool ulp;
+	bool redownload_fw;
 };
 
 /* sdio core registers */
-- 
2.39.5 (Apple Git-154)

