eafc96b9015d59b5cf259c928e360cdcaf9a2403
[oweals/openwrt.git] /
1 From e0ae4bac22effbd644add326f658a3aeeb8d45ee Mon Sep 17 00:00:00 2001
2 From: Adrian Ratiu <adrian.ratiu@collabora.com>
3 Date: Wed, 25 Sep 2019 16:44:58 +0300
4 Subject: [PATCH] brcmfmac: fix suspend/resume when power is cut off
5
6 brcmfmac assumed the wifi device always remains powered on and thus
7 hardcoded the MMC_PM_KEEP_POWER flag expecting the wifi device to
8 remain on even during suspend/resume cycles.
9
10 This is not always the case, some appliances cut power to everything
11 connected via SDIO for efficiency reasons and this leads to wifi not
12 being usable after coming out of suspend because the device was not
13 correctly reinitialized.
14
15 So we check for the keep_power capability and if it's not present then
16 we remove the device and probe it again during resume to mirror what's
17 happening in hardware and ensure correct reinitialization in the case
18 when MMC_PM_KEEP_POWER is not supported.
19
20 Suggested-by: Gustavo Padovan <gustavo.padovan@collabora.com>
21 Signed-off-by: Adrian Ratiu <adrian.ratiu@collabora.com>
22 Signed-off-by: Kalle Valo <kvalo@codeaurora.org>
23 ---
24  .../broadcom/brcm80211/brcmfmac/bcmsdh.c      | 53 ++++++++++++++-----
25  1 file changed, 39 insertions(+), 14 deletions(-)
26
27 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcmsdh.c
28 +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcmsdh.c
29 @@ -1108,7 +1108,8 @@ static int brcmf_ops_sdio_suspend(struct
30         struct sdio_func *func;
31         struct brcmf_bus *bus_if;
32         struct brcmf_sdio_dev *sdiodev;
33 -       mmc_pm_flag_t sdio_flags;
34 +       mmc_pm_flag_t pm_caps, sdio_flags;
35 +       int ret = 0;
36  
37         func = container_of(dev, struct sdio_func, dev);
38         brcmf_dbg(SDIO, "Enter: F%d\n", func->num);
39 @@ -1119,19 +1120,33 @@ static int brcmf_ops_sdio_suspend(struct
40         bus_if = dev_get_drvdata(dev);
41         sdiodev = bus_if->bus_priv.sdio;
42  
43 -       brcmf_sdiod_freezer_on(sdiodev);
44 -       brcmf_sdio_wd_timer(sdiodev->bus, 0);
45 +       pm_caps = sdio_get_host_pm_caps(func);
46  
47 -       sdio_flags = MMC_PM_KEEP_POWER;
48 -       if (sdiodev->wowl_enabled) {
49 -               if (sdiodev->settings->bus.sdio.oob_irq_supported)
50 -                       enable_irq_wake(sdiodev->settings->bus.sdio.oob_irq_nr);
51 -               else
52 -                       sdio_flags |= MMC_PM_WAKE_SDIO_IRQ;
53 +       if (pm_caps & MMC_PM_KEEP_POWER) {
54 +               /* preserve card power during suspend */
55 +               brcmf_sdiod_freezer_on(sdiodev);
56 +               brcmf_sdio_wd_timer(sdiodev->bus, 0);
57 +
58 +               sdio_flags = MMC_PM_KEEP_POWER;
59 +               if (sdiodev->wowl_enabled) {
60 +                       if (sdiodev->settings->bus.sdio.oob_irq_supported)
61 +                               enable_irq_wake(sdiodev->settings->bus.sdio.oob_irq_nr);
62 +                       else
63 +                               sdio_flags |= MMC_PM_WAKE_SDIO_IRQ;
64 +               }
65 +
66 +               if (sdio_set_host_pm_flags(sdiodev->func1, sdio_flags))
67 +                       brcmf_err("Failed to set pm_flags %x\n", sdio_flags);
68 +
69 +       } else {
70 +               /* power will be cut so remove device, probe again in resume */
71 +               brcmf_sdiod_intr_unregister(sdiodev);
72 +               ret = brcmf_sdiod_remove(sdiodev);
73 +               if (ret)
74 +                       brcmf_err("Failed to remove device on suspend\n");
75         }
76 -       if (sdio_set_host_pm_flags(sdiodev->func1, sdio_flags))
77 -               brcmf_err("Failed to set pm_flags %x\n", sdio_flags);
78 -       return 0;
79 +
80 +       return ret;
81  }
82  
83  static int brcmf_ops_sdio_resume(struct device *dev)
84 @@ -1139,13 +1154,23 @@ static int brcmf_ops_sdio_resume(struct
85         struct brcmf_bus *bus_if = dev_get_drvdata(dev);
86         struct brcmf_sdio_dev *sdiodev = bus_if->bus_priv.sdio;
87         struct sdio_func *func = container_of(dev, struct sdio_func, dev);
88 +       mmc_pm_flag_t pm_caps = sdio_get_host_pm_caps(func);
89 +       int ret = 0;
90  
91         brcmf_dbg(SDIO, "Enter: F%d\n", func->num);
92         if (func->num != 2)
93                 return 0;
94  
95 -       brcmf_sdiod_freezer_off(sdiodev);
96 -       return 0;
97 +       if (!(pm_caps & MMC_PM_KEEP_POWER)) {
98 +               /* bus was powered off and device removed, probe again */
99 +               ret = brcmf_sdiod_probe(sdiodev);
100 +               if (ret)
101 +                       brcmf_err("Failed to probe device on resume\n");
102 +       } else {
103 +               brcmf_sdiod_freezer_off(sdiodev);
104 +       }
105 +
106 +       return ret;
107  }
108  
109  static const struct dev_pm_ops brcmf_sdio_pm_ops = {