brcmfmac: Add wowl support for SDIO devices.
authorHante Meuleman <meuleman@broadcom.com>
Tue, 28 Oct 2014 13:56:05 +0000 (14:56 +0100)
committerJohn W. Linville <linville@tuxdriver.com>
Thu, 30 Oct 2014 19:24:49 +0000 (15:24 -0400)
This patch adds wowl support for SDIO bus devices. This feature
requires FW which has support for wowl built in.

Reviewed-by: Arend Van Spriel <arend@broadcom.com>
Reviewed-by: Franky (Zhenhui) Lin <frankyl@broadcom.com>
Reviewed-by: Pieter-Paul Giesberts <pieterpg@broadcom.com>
Signed-off-by: Hante Meuleman <meuleman@broadcom.com>
Signed-off-by: Arend van Spriel <arend@broadcom.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c
drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c
drivers/net/wireless/brcm80211/brcmfmac/sdio_host.h

index 8dbd5dbb78fd14c8b697c04379e88bdf7442784f..79b2c7e3f7e71d39f74c5b8f74035552ee88c7ad 100644 (file)
@@ -1064,6 +1064,16 @@ static int brcmf_ops_sdio_probe(struct sdio_func *func,
        if (!sdiodev->pdata)
                brcmf_of_probe(sdiodev);
 
+#ifdef CONFIG_PM_SLEEP
+       /* wowl can be supported when KEEP_POWER is true and (WAKE_SDIO_IRQ
+        * is true or when platform data OOB irq is true).
+        */
+       if ((sdio_get_host_pm_caps(sdiodev->func[1]) & MMC_PM_KEEP_POWER) &&
+           ((sdio_get_host_pm_caps(sdiodev->func[1]) & MMC_PM_WAKE_SDIO_IRQ) ||
+            (sdiodev->pdata->oob_irq_supported)))
+               bus_if->wowl_supported = true;
+#endif
+
        atomic_set(&sdiodev->suspend, false);
        init_waitqueue_head(&sdiodev->request_word_wait);
        init_waitqueue_head(&sdiodev->request_buffer_wait);
@@ -1116,34 +1126,39 @@ static void brcmf_ops_sdio_remove(struct sdio_func *func)
        brcmf_dbg(SDIO, "Exit\n");
 }
 
+void brcmf_sdio_wowl_config(struct device *dev, bool enabled)
+{
+       struct brcmf_bus *bus_if = dev_get_drvdata(dev);
+       struct brcmf_sdio_dev *sdiodev = bus_if->bus_priv.sdio;
+
+       brcmf_dbg(SDIO, "Configuring WOWL, enabled=%d\n", enabled);
+       sdiodev->wowl_enabled = enabled;
+}
+
 #ifdef CONFIG_PM_SLEEP
 static int brcmf_ops_sdio_suspend(struct device *dev)
 {
-       mmc_pm_flag_t sdio_flags;
        struct brcmf_bus *bus_if = dev_get_drvdata(dev);
        struct brcmf_sdio_dev *sdiodev = bus_if->bus_priv.sdio;
-       int ret = 0;
+       mmc_pm_flag_t sdio_flags;
 
        brcmf_dbg(SDIO, "Enter\n");
 
-       sdio_flags = sdio_get_host_pm_caps(sdiodev->func[1]);
-       if (!(sdio_flags & MMC_PM_KEEP_POWER)) {
-               brcmf_err("Host can't keep power while suspended\n");
-               return -EINVAL;
-       }
-
        atomic_set(&sdiodev->suspend, true);
 
-       ret = sdio_set_host_pm_flags(sdiodev->func[1], MMC_PM_KEEP_POWER);
-       if (ret) {
-               brcmf_err("Failed to set pm_flags\n");
-               atomic_set(&sdiodev->suspend, false);
-               return ret;
+       if (sdiodev->wowl_enabled) {
+               sdio_flags = MMC_PM_KEEP_POWER;
+               if (sdiodev->pdata->oob_irq_supported)
+                       enable_irq_wake(sdiodev->pdata->oob_irq_nr);
+               else
+                       sdio_flags = MMC_PM_WAKE_SDIO_IRQ;
+               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 ret;
+       return 0;
 }
 
 static int brcmf_ops_sdio_resume(struct device *dev)
@@ -1152,6 +1167,8 @@ static int brcmf_ops_sdio_resume(struct device *dev)
        struct brcmf_sdio_dev *sdiodev = bus_if->bus_priv.sdio;
 
        brcmf_dbg(SDIO, "Enter\n");
+       if (sdiodev->pdata->oob_irq_supported)
+               disable_irq_wake(sdiodev->pdata->oob_irq_nr);
        brcmf_sdio_wd_timer(sdiodev->bus, BRCMF_WD_POLL_MS);
        atomic_set(&sdiodev->suspend, false);
        return 0;
index f55f625fd06b4aab850ffa16761714a5dff196f6..079187c511707d6ac053e4bb3e138fb1c0356f8b 100644 (file)
@@ -3949,6 +3949,7 @@ static struct brcmf_bus_ops brcmf_sdio_bus_ops = {
        .txctl = brcmf_sdio_bus_txctl,
        .rxctl = brcmf_sdio_bus_rxctl,
        .gettxq = brcmf_sdio_bus_gettxq,
+       .wowl_config = brcmf_sdio_wowl_config
 };
 
 static void brcmf_sdio_firmware_callback(struct device *dev,
index f2d06cae366af25d440cc5d7cd0b2572911c4fd5..262aedfeaa30a98f1ec5702e3706232ce82695f1 100644 (file)
@@ -186,6 +186,7 @@ struct brcmf_sdio_dev {
        struct sg_table sgtable;
        char fw_name[BRCMF_FW_PATH_LEN + BRCMF_FW_NAME_LEN];
        char nvram_name[BRCMF_FW_PATH_LEN + BRCMF_FW_NAME_LEN];
+       bool wowl_enabled;
 };
 
 /* sdio core registers */
@@ -334,5 +335,6 @@ void brcmf_sdio_remove(struct brcmf_sdio *bus);
 void brcmf_sdio_isr(struct brcmf_sdio *bus);
 
 void brcmf_sdio_wd_timer(struct brcmf_sdio *bus, uint wdtick);
+void brcmf_sdio_wowl_config(struct device *dev, bool enabled);
 
 #endif                         /* _BRCM_SDH_H_ */