89 lines
3.0 KiB
Diff
89 lines
3.0 KiB
Diff
From: Christian Daudt <csd@broadcom.com>
|
|
Date: Wed, 11 May 2016 15:06:48 -0700
|
|
Subject: [PATCH] brcmfmac: Fix kernel oops in failed chip_attach
|
|
|
|
When chip attach fails, brcmf_sdiod_intr_unregister is being called
|
|
but that is too early as sdiodev->settings has not been set yet
|
|
nor has brcmf_sdiod_intr_register been called.
|
|
Change to use oob_irq_requested + newly created sd_irq_requested
|
|
to decide on what to unregister at intr_unregister time.
|
|
|
|
Steps to reproduce problem:
|
|
- modprobe brcmfmac using buggy FW
|
|
- rmmod brcmfmac
|
|
- modprobe brcmfmac again.
|
|
|
|
If done with a buggy firmware, brcm_chip_attach will fail on the
|
|
2nd modprobe triggering the call to intr_unregister and the
|
|
kernel oops when attempting to de-reference sdiodev->settings->bus.sdio
|
|
which has not yet been set.
|
|
|
|
Signed-off-by: Christian Daudt <csd@broadcom.com>
|
|
Signed-off-by: Kalle Valo <kvalo@codeaurora.org>
|
|
---
|
|
|
|
--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcmsdh.c
|
|
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcmsdh.c
|
|
@@ -166,6 +166,7 @@ int brcmf_sdiod_intr_register(struct brc
|
|
sdio_claim_irq(sdiodev->func[1], brcmf_sdiod_ib_irqhandler);
|
|
sdio_claim_irq(sdiodev->func[2], brcmf_sdiod_dummy_irqhandler);
|
|
sdio_release_host(sdiodev->func[1]);
|
|
+ sdiodev->sd_irq_requested = true;
|
|
}
|
|
|
|
return 0;
|
|
@@ -173,27 +174,30 @@ int brcmf_sdiod_intr_register(struct brc
|
|
|
|
int brcmf_sdiod_intr_unregister(struct brcmf_sdio_dev *sdiodev)
|
|
{
|
|
- struct brcmfmac_sdio_pd *pdata;
|
|
|
|
- brcmf_dbg(SDIO, "Entering\n");
|
|
+ brcmf_dbg(SDIO, "Entering oob=%d sd=%d\n",
|
|
+ sdiodev->oob_irq_requested,
|
|
+ sdiodev->sd_irq_requested);
|
|
|
|
- pdata = &sdiodev->settings->bus.sdio;
|
|
- if (pdata->oob_irq_supported) {
|
|
+ if (sdiodev->oob_irq_requested) {
|
|
+ struct brcmfmac_sdio_pd *pdata;
|
|
+
|
|
+ pdata = &sdiodev->settings->bus.sdio;
|
|
sdio_claim_host(sdiodev->func[1]);
|
|
brcmf_sdiod_regwb(sdiodev, SDIO_CCCR_BRCM_SEPINT, 0, NULL);
|
|
brcmf_sdiod_regwb(sdiodev, SDIO_CCCR_IENx, 0, NULL);
|
|
sdio_release_host(sdiodev->func[1]);
|
|
|
|
- if (sdiodev->oob_irq_requested) {
|
|
- sdiodev->oob_irq_requested = false;
|
|
- if (sdiodev->irq_wake) {
|
|
- disable_irq_wake(pdata->oob_irq_nr);
|
|
- sdiodev->irq_wake = false;
|
|
- }
|
|
- free_irq(pdata->oob_irq_nr, &sdiodev->func[1]->dev);
|
|
- sdiodev->irq_en = false;
|
|
+ sdiodev->oob_irq_requested = false;
|
|
+ if (sdiodev->irq_wake) {
|
|
+ disable_irq_wake(pdata->oob_irq_nr);
|
|
+ sdiodev->irq_wake = false;
|
|
}
|
|
- } else {
|
|
+ free_irq(pdata->oob_irq_nr, &sdiodev->func[1]->dev);
|
|
+ sdiodev->irq_en = false;
|
|
+ }
|
|
+
|
|
+ if (sdiodev->sd_irq_requested) {
|
|
sdio_claim_host(sdiodev->func[1]);
|
|
sdio_release_irq(sdiodev->func[2]);
|
|
sdio_release_irq(sdiodev->func[1]);
|
|
--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.h
|
|
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.h
|
|
@@ -186,6 +186,7 @@ struct brcmf_sdio_dev {
|
|
struct brcmf_bus *bus_if;
|
|
struct brcmf_mp_device *settings;
|
|
bool oob_irq_requested;
|
|
+ bool sd_irq_requested;
|
|
bool irq_en; /* irq enable flags */
|
|
spinlock_t irq_en_lock;
|
|
bool irq_wake; /* irq wake enable flags */
|