net: wireless: bcmdhd: Improve suspend/resume processing
authorDmitry Shmidt <dimitrysh@google.com>
Wed, 2 Nov 2011 23:51:29 +0000 (16:51 -0700)
committerDmitry Shmidt <dimitrysh@google.com>
Fri, 4 Nov 2011 16:56:20 +0000 (09:56 -0700)
Signed-off-by: Dmitry Shmidt <dimitrysh@google.com>
drivers/net/wireless/bcmdhd/bcmsdh_linux.c
drivers/net/wireless/bcmdhd/bcmsdh_sdmmc_linux.c
drivers/net/wireless/bcmdhd/dhd.h
drivers/net/wireless/bcmdhd/dhd_linux.c
drivers/net/wireless/bcmdhd/wl_android.c

index a4dc6ff40429b12e6882d173be7c60ac50b6dacb..04c43a3225cfedaeeaba87968a1323c13840e973 100644 (file)
@@ -611,6 +611,13 @@ static irqreturn_t wlan_oob_irq(int irq, void *dev_id)
        return IRQ_HANDLED;
 }
 
+void *bcmsdh_get_drvdata(void)
+{
+       if (!sdhcinfo)
+               return NULL;
+       return dev_get_drvdata(sdhcinfo->dev);
+}
+
 int bcmsdh_register_oob_intr(void * dhdp)
 {
        int error = 0;
index 8baa60bf24f2042e1536e80d94aaa581d1b95fc0..726b6391353d9e1bf5424ff72c8787f94714cc58 100644 (file)
@@ -66,6 +66,9 @@ extern void wl_cfg80211_set_sdio_func(void *func);
 
 extern void sdioh_sdmmc_devintr_off(sdioh_info_t *sd);
 extern void sdioh_sdmmc_devintr_on(sdioh_info_t *sd);
+extern int dhd_os_check_wakelock(void *dhdp);
+extern int dhd_os_check_if_up(void *dhdp);
+extern void *bcmsdh_get_drvdata(void);
 
 int sdio_function_init(void);
 void sdio_function_cleanup(void);
@@ -87,6 +90,8 @@ PBCMSDH_SDMMC_INSTANCE gInstance;
 extern int bcmsdh_probe(struct device *dev);
 extern int bcmsdh_remove(struct device *dev);
 
+extern volatile bool dhd_mmc_suspend;
+
 static int bcmsdh_sdmmc_probe(struct sdio_func *func,
                               const struct sdio_device_id *id)
 {
@@ -154,12 +159,56 @@ static const struct sdio_device_id bcmsdh_sdmmc_ids[] = {
 
 MODULE_DEVICE_TABLE(sdio, bcmsdh_sdmmc_ids);
 
+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 39)) && defined(CONFIG_PM)
+static int bcmsdh_sdmmc_suspend(struct device *pdev)
+{
+       struct sdio_func *func = dev_to_sdio_func(pdev);
+
+       if (func->num != 2)
+               return 0;
+       if (dhd_os_check_wakelock(bcmsdh_get_drvdata()))
+               return -EBUSY;
+#if defined(OOB_INTR_ONLY)
+       bcmsdh_oob_intr_set(0);
+#endif
+       dhd_mmc_suspend = TRUE;
+       smp_mb();
+
+       return 0;
+}
+
+static int bcmsdh_sdmmc_resume(struct device *pdev)
+{
+       struct sdio_func *func = dev_to_sdio_func(pdev);
+
+       if (func->num != 2)
+               return 0;
+       dhd_mmc_suspend = FALSE;
+#if defined(OOB_INTR_ONLY)
+       if (dhd_os_check_if_up(bcmsdh_get_drvdata()))
+               bcmsdh_oob_intr_set(1);
+#endif
+       smp_mb();
+       return 0;
+}
+
+static const struct dev_pm_ops bcmsdh_sdmmc_pm_ops = {
+       .suspend        = bcmsdh_sdmmc_suspend,
+       .resume         = bcmsdh_sdmmc_resume,
+};
+#endif
+
 static struct sdio_driver bcmsdh_sdmmc_driver = {
        .probe          = bcmsdh_sdmmc_probe,
        .remove         = bcmsdh_sdmmc_remove,
        .name           = "bcmsdh_sdmmc",
        .id_table       = bcmsdh_sdmmc_ids,
-       };
+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 39)) && defined(CONFIG_PM)
+       .drv = {
+               .pm     = &bcmsdh_sdmmc_pm_ops,
+       },
+#endif
+};
 
 struct sdos_info {
        sdioh_info_t *sd;
index e4732612759de162454e830c6b8ae63e07c63056..c87f6cf37d9c09fd44e62bbd77472a7895c13c1e 100644 (file)
@@ -235,11 +235,12 @@ typedef struct dhd_cmn {
        #define DHD_PM_RESUME_WAIT_INIT(a) DECLARE_WAIT_QUEUE_HEAD(a);
        #define _DHD_PM_RESUME_WAIT(a, b) do {\
                        int retry = 0; \
-                       smp_mb(); \
+                       SMP_RD_BARRIER_DEPENDS(); \
                        while (dhd_mmc_suspend && retry++ != b) { \
-                               wait_event_interruptible_timeout(a, FALSE, HZ/100); \
+                               SMP_RD_BARRIER_DEPENDS(); \
+                               wait_event_interruptible_timeout(a, !dhd_mmc_suspend, HZ/100); \
                        } \
-               }       while (0)
+               } while (0)
        #define DHD_PM_RESUME_WAIT(a)           _DHD_PM_RESUME_WAIT(a, 200)
        #define DHD_PM_RESUME_WAIT_FOREVER(a)   _DHD_PM_RESUME_WAIT(a, ~0)
        #define DHD_PM_RESUME_RETURN_ERROR(a)   do { if (dhd_mmc_suspend) return a; } while (0)
index 04152a04320de5d773e922fc79e38f23054971aa..e98044279f232340662682caad3f495ebe40e02f 100644 (file)
@@ -478,19 +478,21 @@ static int dhd_sleep_pm_callback(struct notifier_block *nfb, unsigned long actio
 {
        int ret = NOTIFY_DONE;
 
-       switch (action) {
+#if (LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 39))
+       switch (action) {
                case PM_HIBERNATION_PREPARE:
                case PM_SUSPEND_PREPARE:
                        dhd_mmc_suspend = TRUE;
-               ret = NOTIFY_OK;
+                       ret = NOTIFY_OK;
                break;
                case PM_POST_HIBERNATION:
                case PM_POST_SUSPEND:
                        dhd_mmc_suspend = FALSE;
-               ret = NOTIFY_OK;
+                       ret = NOTIFY_OK;
                break;
        }
        smp_mb();
+#endif
        return ret;
 }
 
@@ -2312,6 +2314,7 @@ dhd_open(struct net_device *net)
 #endif
        int ifidx;
        int32 ret = 0;
+
        DHD_OS_WAKE_LOCK(&dhd->pub);
        /* Update FW path if it was changed */
        if ((firmware_path != NULL) && (firmware_path[0] != '\0')) {
@@ -4510,6 +4513,31 @@ int dhd_os_wake_unlock(dhd_pub_t *pub)
        return ret;
 }
 
+int dhd_os_check_wakelock(void *dhdp)
+{
+#ifdef CONFIG_HAS_WAKELOCK
+       dhd_pub_t *pub = (dhd_pub_t *)dhdp;
+       dhd_info_t *dhd;
+
+       if (!pub)
+               return 0;
+       dhd = (dhd_info_t *)(pub->info);
+
+       if (dhd && wake_lock_active(&dhd->wl_wifi))
+               return 1;
+#endif
+       return 0;
+}
+
+int dhd_os_check_if_up(void *dhdp)
+{
+       dhd_pub_t *pub = (dhd_pub_t *)dhdp;
+
+       if (!pub)
+               return 0;
+       return pub->up;
+}
+
 int net_os_wake_unlock(struct net_device *dev)
 {
        dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(dev);
index 76df647b677883a784f1f4a0082ecd29ea3ea779..7c6ab2fb2821994d6d2dc22b9d47b1ca9e71ec81 100644 (file)
@@ -126,6 +126,8 @@ int wl_cfg80211_get_p2p_noa(struct net_device *net, char* buf, int len)
 int wl_cfg80211_set_p2p_ps(struct net_device *net, char* buf, int len)
 { return 0; }
 #endif
+extern int dhd_os_check_if_up(void *dhdp);
+extern void *bcmsdh_get_drvdata(void);
 
 extern bool ap_fw_loaded;
 #ifdef CUSTOMER_HW2
@@ -708,7 +710,7 @@ int wifi_set_power(int on, unsigned long msec)
                wifi_control_data->set_power(on);
        }
        if (msec)
-               mdelay(msec);
+               msleep(msec);
        return 0;
 }
 
@@ -784,18 +786,19 @@ static int wifi_remove(struct platform_device *pdev)
 static int wifi_suspend(struct platform_device *pdev, pm_message_t state)
 {
        DHD_TRACE(("##> %s\n", __FUNCTION__));
-#if defined(OOB_INTR_ONLY)
+#if (LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 39)) && defined(OOB_INTR_ONLY)
        bcmsdh_oob_intr_set(0);
-#endif /* (OOB_INTR_ONLY) */
+#endif
        return 0;
 }
 
 static int wifi_resume(struct platform_device *pdev)
 {
        DHD_TRACE(("##> %s\n", __FUNCTION__));
-#if defined(OOB_INTR_ONLY)
-       bcmsdh_oob_intr_set(1);
-#endif /* (OOB_INTR_ONLY) */
+#if (LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 39)) && defined(OOB_INTR_ONLY)
+       if (dhd_os_check_if_up(bcmsdh_get_drvdata()))
+               bcmsdh_oob_intr_set(1);
+#endif
        return 0;
 }