From b0423895c62d563799c8e453e684fa8035d9019d Mon Sep 17 00:00:00 2001 From: Dmitry Shmidt Date: Wed, 2 Nov 2011 16:51:29 -0700 Subject: [PATCH] net: wireless: bcmdhd: Improve suspend/resume processing Signed-off-by: Dmitry Shmidt --- drivers/net/wireless/bcmdhd/bcmsdh_linux.c | 7 ++++ drivers/net/wireless/bcmdhd/bcmsdh_sdmmc_linux.c | 51 +++++++++++++++++++++++- drivers/net/wireless/bcmdhd/dhd.h | 7 ++-- drivers/net/wireless/bcmdhd/dhd_linux.c | 34 ++++++++++++++-- drivers/net/wireless/bcmdhd/wl_android.c | 15 ++++--- 5 files changed, 101 insertions(+), 13 deletions(-) diff --git a/drivers/net/wireless/bcmdhd/bcmsdh_linux.c b/drivers/net/wireless/bcmdhd/bcmsdh_linux.c index a4dc6ff..04c43a3 100644 --- a/drivers/net/wireless/bcmdhd/bcmsdh_linux.c +++ b/drivers/net/wireless/bcmdhd/bcmsdh_linux.c @@ -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; diff --git a/drivers/net/wireless/bcmdhd/bcmsdh_sdmmc_linux.c b/drivers/net/wireless/bcmdhd/bcmsdh_sdmmc_linux.c index 8baa60b..726b639 100644 --- a/drivers/net/wireless/bcmdhd/bcmsdh_sdmmc_linux.c +++ b/drivers/net/wireless/bcmdhd/bcmsdh_sdmmc_linux.c @@ -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; diff --git a/drivers/net/wireless/bcmdhd/dhd.h b/drivers/net/wireless/bcmdhd/dhd.h index e473261..c87f6cf 100644 --- a/drivers/net/wireless/bcmdhd/dhd.h +++ b/drivers/net/wireless/bcmdhd/dhd.h @@ -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) diff --git a/drivers/net/wireless/bcmdhd/dhd_linux.c b/drivers/net/wireless/bcmdhd/dhd_linux.c index 04152a0..e980442 100644 --- a/drivers/net/wireless/bcmdhd/dhd_linux.c +++ b/drivers/net/wireless/bcmdhd/dhd_linux.c @@ -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); diff --git a/drivers/net/wireless/bcmdhd/wl_android.c b/drivers/net/wireless/bcmdhd/wl_android.c index 76df647..7c6ab2f 100644 --- a/drivers/net/wireless/bcmdhd/wl_android.c +++ b/drivers/net/wireless/bcmdhd/wl_android.c @@ -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; } -- 2.7.4