aboutsummaryrefslogtreecommitdiffstats
path: root/package/kernel/mac80211/patches/brcm/401-v5.5-0002-brcmfmac-fix-suspend-resume-when-power-is-cut-off.patch
diff options
context:
space:
mode:
Diffstat (limited to 'package/kernel/mac80211/patches/brcm/401-v5.5-0002-brcmfmac-fix-suspend-resume-when-power-is-cut-off.patch')
-rw-r--r--package/kernel/mac80211/patches/brcm/401-v5.5-0002-brcmfmac-fix-suspend-resume-when-power-is-cut-off.patch109
1 files changed, 109 insertions, 0 deletions
diff --git a/package/kernel/mac80211/patches/brcm/401-v5.5-0002-brcmfmac-fix-suspend-resume-when-power-is-cut-off.patch b/package/kernel/mac80211/patches/brcm/401-v5.5-0002-brcmfmac-fix-suspend-resume-when-power-is-cut-off.patch
new file mode 100644
index 0000000000..49ea85bf97
--- /dev/null
+++ b/package/kernel/mac80211/patches/brcm/401-v5.5-0002-brcmfmac-fix-suspend-resume-when-power-is-cut-off.patch
@@ -0,0 +1,109 @@
+From e0ae4bac22effbd644add326f658a3aeeb8d45ee Mon Sep 17 00:00:00 2001
+From: Adrian Ratiu <adrian.ratiu@collabora.com>
+Date: Wed, 25 Sep 2019 16:44:58 +0300
+Subject: [PATCH] brcmfmac: fix suspend/resume when power is cut off
+
+brcmfmac assumed the wifi device always remains powered on and thus
+hardcoded the MMC_PM_KEEP_POWER flag expecting the wifi device to
+remain on even during suspend/resume cycles.
+
+This is not always the case, some appliances cut power to everything
+connected via SDIO for efficiency reasons and this leads to wifi not
+being usable after coming out of suspend because the device was not
+correctly reinitialized.
+
+So we check for the keep_power capability and if it's not present then
+we remove the device and probe it again during resume to mirror what's
+happening in hardware and ensure correct reinitialization in the case
+when MMC_PM_KEEP_POWER is not supported.
+
+Suggested-by: Gustavo Padovan <gustavo.padovan@collabora.com>
+Signed-off-by: Adrian Ratiu <adrian.ratiu@collabora.com>
+Signed-off-by: Kalle Valo <kvalo@codeaurora.org>
+---
+ .../broadcom/brcm80211/brcmfmac/bcmsdh.c | 53 ++++++++++++++-----
+ 1 file changed, 39 insertions(+), 14 deletions(-)
+
+--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcmsdh.c
++++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcmsdh.c
+@@ -1097,7 +1097,8 @@ static int brcmf_ops_sdio_suspend(struct
+ struct sdio_func *func;
+ struct brcmf_bus *bus_if;
+ struct brcmf_sdio_dev *sdiodev;
+- mmc_pm_flag_t sdio_flags;
++ mmc_pm_flag_t pm_caps, sdio_flags;
++ int ret = 0;
+
+ func = container_of(dev, struct sdio_func, dev);
+ brcmf_dbg(SDIO, "Enter: F%d\n", func->num);
+@@ -1108,19 +1109,33 @@ static int brcmf_ops_sdio_suspend(struct
+ bus_if = dev_get_drvdata(dev);
+ sdiodev = bus_if->bus_priv.sdio;
+
+- brcmf_sdiod_freezer_on(sdiodev);
+- brcmf_sdio_wd_timer(sdiodev->bus, 0);
++ pm_caps = sdio_get_host_pm_caps(func);
+
+- sdio_flags = MMC_PM_KEEP_POWER;
+- if (sdiodev->wowl_enabled) {
+- if (sdiodev->settings->bus.sdio.oob_irq_supported)
+- enable_irq_wake(sdiodev->settings->bus.sdio.oob_irq_nr);
+- else
+- sdio_flags |= MMC_PM_WAKE_SDIO_IRQ;
++ if (pm_caps & MMC_PM_KEEP_POWER) {
++ /* preserve card power during suspend */
++ brcmf_sdiod_freezer_on(sdiodev);
++ brcmf_sdio_wd_timer(sdiodev->bus, 0);
++
++ sdio_flags = MMC_PM_KEEP_POWER;
++ if (sdiodev->wowl_enabled) {
++ if (sdiodev->settings->bus.sdio.oob_irq_supported)
++ enable_irq_wake(sdiodev->settings->bus.sdio.oob_irq_nr);
++ else
++ sdio_flags |= MMC_PM_WAKE_SDIO_IRQ;
++ }
++
++ if (sdio_set_host_pm_flags(sdiodev->func1, sdio_flags))
++ brcmf_err("Failed to set pm_flags %x\n", sdio_flags);
++
++ } else {
++ /* power will be cut so remove device, probe again in resume */
++ brcmf_sdiod_intr_unregister(sdiodev);
++ ret = brcmf_sdiod_remove(sdiodev);
++ if (ret)
++ brcmf_err("Failed to remove device on suspend\n");
+ }
+- if (sdio_set_host_pm_flags(sdiodev->func1, sdio_flags))
+- brcmf_err("Failed to set pm_flags %x\n", sdio_flags);
+- return 0;
++
++ return ret;
+ }
+
+ static int brcmf_ops_sdio_resume(struct device *dev)
+@@ -1128,13 +1143,23 @@ static int brcmf_ops_sdio_resume(struct
+ struct brcmf_bus *bus_if = dev_get_drvdata(dev);
+ struct brcmf_sdio_dev *sdiodev = bus_if->bus_priv.sdio;
+ struct sdio_func *func = container_of(dev, struct sdio_func, dev);
++ mmc_pm_flag_t pm_caps = sdio_get_host_pm_caps(func);
++ int ret = 0;
+
+ brcmf_dbg(SDIO, "Enter: F%d\n", func->num);
+ if (func->num != 2)
+ return 0;
+
+- brcmf_sdiod_freezer_off(sdiodev);
+- return 0;
++ if (!(pm_caps & MMC_PM_KEEP_POWER)) {
++ /* bus was powered off and device removed, probe again */
++ ret = brcmf_sdiod_probe(sdiodev);
++ if (ret)
++ brcmf_err("Failed to probe device on resume\n");
++ } else {
++ brcmf_sdiod_freezer_off(sdiodev);
++ }
++
++ return ret;
+ }
+
+ static const struct dev_pm_ops brcmf_sdio_pm_ops = {