brcmfmac: wait for driver to go idle during suspend
authorArend van Spriel <arend@broadcom.com>
Sun, 25 Jan 2015 19:31:34 +0000 (20:31 +0100)
committerKalle Valo <kvalo@codeaurora.org>
Thu, 29 Jan 2015 07:59:00 +0000 (09:59 +0200)
Before going in suspend state the watchdog thread needs to put the
device in bus sleep state, which assures it can go in deep-sleep
state during D3 state.

Reviewed-by: Hante Meuleman <meuleman@broadcom.com>
Reviewed-by: Daniel (Deognyoun) Kim <dekim@broadcom.com>
Reviewed-by: Franky (Zhenhui) Lin <frankyl@broadcom.com>
Reviewed-by: Pieter-Paul Giesberts <pieterpg@broadcom.com>
Signed-off-by: Arend van Spriel <arend@broadcom.com>
Signed-off-by: Kalle Valo <kvalo@codeaurora.org>
drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c
drivers/net/wireless/brcm80211/brcmfmac/sdio.c
drivers/net/wireless/brcm80211/brcmfmac/sdio.h

index 8ba60f6f491510636a426d5532e31790735389fc..9afaeb6d2af5df6821ecd74aa74d270f4866df63 100644 (file)
@@ -1045,7 +1045,9 @@ static int brcmf_ops_sdio_probe(struct sdio_func *func,
                bus_if->wowl_supported = true;
 #endif
 
+       sdiodev->sleeping = false;
        atomic_set(&sdiodev->suspend, false);
+       init_waitqueue_head(&sdiodev->idle_wait);
 
        brcmf_dbg(SDIO, "F2 found, calling brcmf_sdiod_probe...\n");
        err = brcmf_sdiod_probe(sdiodev);
@@ -1107,12 +1109,23 @@ void brcmf_sdio_wowl_config(struct device *dev, bool enabled)
 #ifdef CONFIG_PM_SLEEP
 static int brcmf_ops_sdio_suspend(struct device *dev)
 {
-       struct brcmf_bus *bus_if = dev_get_drvdata(dev);
-       struct brcmf_sdio_dev *sdiodev = bus_if->bus_priv.sdio;
+       struct brcmf_bus *bus_if;
+       struct brcmf_sdio_dev *sdiodev;
        mmc_pm_flag_t sdio_flags;
 
        brcmf_dbg(SDIO, "Enter\n");
 
+       bus_if = dev_get_drvdata(dev);
+       sdiodev = bus_if->bus_priv.sdio;
+
+       /* wait for watchdog to go idle */
+       if (wait_event_timeout(sdiodev->idle_wait, sdiodev->sleeping,
+                              msecs_to_jiffies(3 * BRCMF_WD_POLL_MS)) == 0) {
+               brcmf_err("bus still active\n");
+               return -EBUSY;
+       }
+       /* disable watchdog */
+       brcmf_sdio_wd_timer(sdiodev->bus, 0);
        atomic_set(&sdiodev->suspend, true);
 
        if (sdiodev->wowl_enabled) {
@@ -1124,9 +1137,6 @@ static int brcmf_ops_sdio_suspend(struct device *dev)
                if (sdio_set_host_pm_flags(sdiodev->func[1], sdio_flags))
                        brcmf_err("Failed to set pm_flags %x\n", sdio_flags);
        }
-
-       brcmf_sdio_wd_timer(sdiodev->bus, 0);
-
        return 0;
 }
 
index b4f5fe5d99a61185753608dd0337a875ab6f1650..488a0f648e185376601573d48e37a9e02754a87c 100644 (file)
@@ -515,7 +515,6 @@ struct brcmf_sdio {
        bool txoff;             /* Transmit flow-controlled */
        struct brcmf_sdio_count sdcnt;
        bool sr_enabled; /* SaveRestore enabled */
-       bool sleeping; /* SDIO bus sleeping */
 
        u8 tx_hdrlen;           /* sdio bus header length for tx packet */
        bool txglom;            /* host tx glomming enable flag */
@@ -1014,12 +1013,12 @@ brcmf_sdio_bus_sleep(struct brcmf_sdio *bus, bool sleep, bool pendok)
 
        brcmf_dbg(SDIO, "Enter: request %s currently %s\n",
                  (sleep ? "SLEEP" : "WAKE"),
-                 (bus->sleeping ? "SLEEP" : "WAKE"));
+                 (bus->sdiodev->sleeping ? "SLEEP" : "WAKE"));
 
        /* If SR is enabled control bus state with KSO */
        if (bus->sr_enabled) {
                /* Done if we're already in the requested state */
-               if (sleep == bus->sleeping)
+               if (sleep == bus->sdiodev->sleeping)
                        goto end;
 
                /* Going to sleep */
@@ -1051,12 +1050,7 @@ brcmf_sdio_bus_sleep(struct brcmf_sdio *bus, bool sleep, bool pendok)
                        bus->idlecount = 0;
                        err = brcmf_sdio_kso_control(bus, true);
                }
-               if (!err) {
-                       /* Change state */
-                       bus->sleeping = sleep;
-                       brcmf_dbg(SDIO, "new state %s\n",
-                                 (sleep ? "SLEEP" : "WAKE"));
-               } else {
+               if (err) {
                        brcmf_err("error while changing bus sleep state %d\n",
                                  err);
                        goto done;
@@ -1071,6 +1065,11 @@ end:
        } else {
                brcmf_sdio_clkctl(bus, CLK_AVAIL, pendok);
        }
+       bus->sdiodev->sleeping = sleep;
+       if (sleep)
+               wake_up(&bus->sdiodev->idle_wait);
+       brcmf_dbg(SDIO, "new state %s\n",
+                 (sleep ? "SLEEP" : "WAKE"));
 done:
        brcmf_dbg(SDIO, "Exit: err=%d\n", err);
        return err;
@@ -4215,7 +4214,6 @@ struct brcmf_sdio *brcmf_sdio_probe(struct brcmf_sdio_dev *sdiodev)
        bus->idleclock = BRCMF_IDLE_ACTIVE;
 
        /* SR state */
-       bus->sleeping = false;
        bus->sr_enabled = false;
 
        brcmf_sdio_debugfs_create(bus);
index 82494df541d43150ff2248a67c9713f50662821b..9c5d42d20b4891e3eec059a092ad7118af344587 100644 (file)
@@ -169,6 +169,8 @@ struct brcmf_sdio_dev {
        u32 sbwad;                      /* Save backplane window address */
        struct brcmf_sdio *bus;
        atomic_t suspend;               /* suspend flag */
+       bool sleeping;
+       wait_queue_head_t idle_wait;
        struct device *dev;
        struct brcmf_bus *bus_if;
        struct brcmfmac_sdio_platform_data *pdata;