summaryrefslogtreecommitdiff
path: root/drivers/net
diff options
context:
space:
mode:
authorDmitry Shmidt <dimitrysh@google.com>2011-11-02 16:51:29 -0700
committerDan Willemsen <dwillemsen@nvidia.com>2011-11-30 21:39:16 -0800
commit7dc754ae6d03b84c87d9c2cebdcae3715b2222ac (patch)
treeabd3f5ba4f1f96a22c6e953c1542d2ded82cc80d /drivers/net
parenta5c23bb9bbb64a820a17786ee3e18110850419b9 (diff)
net: wireless: bcmdhd: Improve suspend/resume processing
Signed-off-by: Dmitry Shmidt <dimitrysh@google.com>
Diffstat (limited to 'drivers/net')
-rw-r--r--drivers/net/wireless/bcmdhd/bcmsdh_linux.c7
-rw-r--r--drivers/net/wireless/bcmdhd/bcmsdh_sdmmc_linux.c51
-rw-r--r--drivers/net/wireless/bcmdhd/dhd.h7
-rw-r--r--drivers/net/wireless/bcmdhd/dhd_linux.c34
-rw-r--r--drivers/net/wireless/bcmdhd/wl_android.c15
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 a4dc6ff40429..04c43a3225cf 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 8baa60bf24f2..726b6391353d 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 e4732612759d..c87f6cf37d9c 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 04152a04320d..e98044279f23 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 76df647b6778..7c6ab2fb2821 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;
}