diff mbox

[19/32] staging: brcm80211: Fix for suspend issue in brcmfmac driver

Message ID 1305280792-9475-19-git-send-email-arend@broadcom.com (mailing list archive)
State Not Applicable, archived
Headers show

Commit Message

Arend van Spriel May 13, 2011, 9:59 a.m. UTC
From: Sukesh Srikakula <sukeshs@broadcom.com>

Currently, there are 2 callbacks registered with OS for getting
notifications when system goes to suspend/resume. Racing between
these 2 callbacks results in random suspend failures. With this fix,
we avoid registering dhd callback for suspend/resume notification
when cfg80211 is used. Relevent functionality in dhd suspend/resume
callback function is moved to cfg80211 suspend/resume functions.

Cc: devel@linuxdriverproject.org
Cc: linux-wireless@vger.kernel.org
Cc: Grant Grundler <grundler@chromium.org>
Reviewed-by: Franky (Zhenhui) Lin <frankyl@broadcom.com>
Reviewed-by: Brett Rudley <brudley@broadcom.com>
Signed-off-by: Arend van Spriel <arend@broadcom.com>
---
 drivers/staging/brcm80211/brcmfmac/bcmsdh_sdmmc.c |    5 +-
 drivers/staging/brcm80211/brcmfmac/dhd.h          |   22 ++++---
 drivers/staging/brcm80211/brcmfmac/dhd_linux.c    |   20 ++++--
 drivers/staging/brcm80211/brcmfmac/wl_cfg80211.c  |   77 ++++++++++++++++++---
 drivers/staging/brcm80211/brcmfmac/wl_cfg80211.h  |    1 +
 5 files changed, 97 insertions(+), 28 deletions(-)

Comments

Grant Grundler May 13, 2011, 3:30 p.m. UTC | #1
On Fri, May 13, 2011 at 2:59 AM, Arend van Spriel <arend@broadcom.com> wrote:
> From: Sukesh Srikakula <sukeshs@broadcom.com>
>
> Currently, there are 2 callbacks registered with OS for getting
> notifications when system goes to suspend/resume. Racing between
> these 2 callbacks results in random suspend failures. With this fix,
> we avoid registering dhd callback for suspend/resume notification
> when cfg80211 is used. Relevent functionality in dhd suspend/resume
> callback function is moved to cfg80211 suspend/resume functions.
>
> Cc: devel@linuxdriverproject.org
> Cc: linux-wireless@vger.kernel.org
> Cc: Grant Grundler <grundler@chromium.org>

Thanks guys again! :)

Except for use of atomic_t instead of volatile, this appears to be the same as:
    http://codereview.chromium.org/6802002/

which I tested and fixes bug:
    http://crosbug.com/12337

thanks,
grant

> Reviewed-by: Franky (Zhenhui) Lin <frankyl@broadcom.com>
> Reviewed-by: Brett Rudley <brudley@broadcom.com>
> Signed-off-by: Arend van Spriel <arend@broadcom.com>
> ---
>  drivers/staging/brcm80211/brcmfmac/bcmsdh_sdmmc.c |    5 +-
>  drivers/staging/brcm80211/brcmfmac/dhd.h          |   22 ++++---
>  drivers/staging/brcm80211/brcmfmac/dhd_linux.c    |   20 ++++--
>  drivers/staging/brcm80211/brcmfmac/wl_cfg80211.c  |   77 ++++++++++++++++++---
>  drivers/staging/brcm80211/brcmfmac/wl_cfg80211.h  |    1 +
>  5 files changed, 97 insertions(+), 28 deletions(-)
>
> diff --git a/drivers/staging/brcm80211/brcmfmac/bcmsdh_sdmmc.c b/drivers/staging/brcm80211/brcmfmac/bcmsdh_sdmmc.c
> index 43aebfd..c0ffbd3 100644
> --- a/drivers/staging/brcm80211/brcmfmac/bcmsdh_sdmmc.c
> +++ b/drivers/staging/brcm80211/brcmfmac/bcmsdh_sdmmc.c
> @@ -26,14 +26,11 @@
>  #include <linux/mmc/core.h>
>  #include <linux/mmc/sdio_func.h>
>  #include <linux/mmc/sdio_ids.h>
> +#include <linux/suspend.h>
>
>  #include <dngl_stats.h>
>  #include <dhd.h>
>
> -#if defined(CONFIG_PM_SLEEP)
> -#include <linux/suspend.h>
> -extern volatile bool dhd_mmc_suspend;
> -#endif
>  #include "bcmsdh_sdmmc.h"
>
>  extern int sdio_function_init(void);
> diff --git a/drivers/staging/brcm80211/brcmfmac/dhd.h b/drivers/staging/brcm80211/brcmfmac/dhd.h
> index 99c38dd..a726b49 100644
> --- a/drivers/staging/brcm80211/brcmfmac/dhd.h
> +++ b/drivers/staging/brcm80211/brcmfmac/dhd.h
> @@ -31,6 +31,7 @@
>  #include <linux/random.h>
>  #include <linux/spinlock.h>
>  #include <linux/ethtool.h>
> +#include <linux/suspend.h>
>  #include <asm/uaccess.h>
>  #include <asm/unaligned.h>
>  /* The kernel threading is sdio-specific */
> @@ -122,19 +123,22 @@ typedef struct dhd_pub {
>  } dhd_pub_t;
>
>  #if defined(CONFIG_PM_SLEEP)
> -
> +extern atomic_t dhd_mmc_suspend;
>  #define DHD_PM_RESUME_WAIT_INIT(a) DECLARE_WAIT_QUEUE_HEAD(a);
> -#define _DHD_PM_RESUME_WAIT(a, b) do {\
> -                       int retry = 0; \
> -                       while (dhd_mmc_suspend && retry++ != b) { \
> -                               wait_event_timeout(a, false, HZ/100); \
> -                       } \
> -               }       while (0)
> +#define _DHD_PM_RESUME_WAIT(a, b) do { \
> +               int retry = 0; \
> +               while (atomic_read(&dhd_mmc_suspend) && retry++ != b) { \
> +                       wait_event_timeout(a, false, HZ/100); \
> +               } \
> +       }       while (0)
>  #define DHD_PM_RESUME_WAIT(a)  _DHD_PM_RESUME_WAIT(a, 30)
>  #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)
> -#define DHD_PM_RESUME_RETURN   do { if (dhd_mmc_suspend) return; } while (0)
> +       do { if (atomic_read(&dhd_mmc_suspend)) return a; } while (0)
> +#define DHD_PM_RESUME_RETURN   do { \
> +       if (atomic_read(&dhd_mmc_suspend)) \
> +               return; \
> +       } while (0)
>
>  #define DHD_SPINWAIT_SLEEP_INIT(a) DECLARE_WAIT_QUEUE_HEAD(a);
>  #define SPINWAIT_SLEEP(a, exp, us) do { \
> diff --git a/drivers/staging/brcm80211/brcmfmac/dhd_linux.c b/drivers/staging/brcm80211/brcmfmac/dhd_linux.c
> index ba73ce0..31a5ca0 100644
> --- a/drivers/staging/brcm80211/brcmfmac/dhd_linux.c
> +++ b/drivers/staging/brcm80211/brcmfmac/dhd_linux.c
> @@ -166,7 +166,7 @@ void wifi_del_dev(void)
>
>  #if defined(CONFIG_PM_SLEEP)
>  #include <linux/suspend.h>
> -volatile bool dhd_mmc_suspend = false;
> +atomic_t dhd_mmc_suspend;
>  DECLARE_WAIT_QUEUE_HEAD(dhd_dpc_wait);
>  #endif /*  defined(CONFIG_PM_SLEEP) */
>
> @@ -407,11 +407,11 @@ static int dhd_sleep_pm_callback(struct notifier_block *nfb,
>        switch (action) {
>        case PM_HIBERNATION_PREPARE:
>        case PM_SUSPEND_PREPARE:
> -               dhd_mmc_suspend = true;
> +               atomic_set(&dhd_mmc_suspend, true);
>                return NOTIFY_OK;
>        case PM_POST_HIBERNATION:
>        case PM_POST_SUSPEND:
> -               dhd_mmc_suspend = false;
> +               atomic_set(&dhd_mmc_suspend, false);
>                return NOTIFY_OK;
>        }
>        return 0;
> @@ -2011,7 +2011,9 @@ dhd_pub_t *dhd_attach(struct dhd_bus *bus, uint bus_hdrlen)
>        g_bus = bus;
>  #endif
>  #if defined(CONFIG_PM_SLEEP)
> -       register_pm_notifier(&dhd_sleep_pm_notifier);
> +       atomic_set(&dhd_mmc_suspend, false);
> +       if (!IS_CFG80211_FAVORITE())
> +               register_pm_notifier(&dhd_sleep_pm_notifier);
>  #endif /* defined(CONFIG_PM_SLEEP) */
>        /* && defined(DHD_GPL) */
>        /* Init lock suspend to prevent kernel going to suspend */
> @@ -2305,7 +2307,8 @@ void dhd_detach(dhd_pub_t *dhdp)
>                                wl_cfg80211_detach();
>
>  #if defined(CONFIG_PM_SLEEP)
> -                       unregister_pm_notifier(&dhd_sleep_pm_notifier);
> +                       if (!IS_CFG80211_FAVORITE())
> +                               unregister_pm_notifier(&dhd_sleep_pm_notifier);
>  #endif /* defined(CONFIG_PM_SLEEP) */
>                        /* && defined(DHD_GPL) */
>                        free_netdev(ifp->net);
> @@ -2816,6 +2819,13 @@ int dhd_wait_pend8021x(struct net_device *dev)
>        return pend;
>  }
>
> +void wl_os_wd_timer(struct net_device *ndev, uint wdtick)
> +{
> +       dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(ndev);
> +
> +       dhd_os_wd_timer(&dhd->pub, wdtick);
> +}
> +
>  #ifdef DHD_DEBUG
>  int write_to_file(dhd_pub_t *dhd, u8 *buf, int size)
>  {
> diff --git a/drivers/staging/brcm80211/brcmfmac/wl_cfg80211.c b/drivers/staging/brcm80211/brcmfmac/wl_cfg80211.c
> index c60fc7c..2d67048 100644
> --- a/drivers/staging/brcm80211/brcmfmac/wl_cfg80211.c
> +++ b/drivers/staging/brcm80211/brcmfmac/wl_cfg80211.c
> @@ -1969,34 +1969,91 @@ wl_cfg80211_set_bitrate_mask(struct wiphy *wiphy, struct net_device *dev,
>
>  static s32 wl_cfg80211_resume(struct wiphy *wiphy)
>  {
> -       s32 err = 0;
> +       struct wl_priv *wl = wiphy_to_wl(wiphy);
> +       struct net_device *ndev = wl_to_ndev(wl);
>
> -       CHECK_SYS_UP();
> -       wl_invoke_iscan(wiphy_to_wl(wiphy));
> +       /*
> +        * Check for WL_STATUS_READY before any function call which
> +        * could result is bus access. Don't block the resume for
> +        * any driver error conditions
> +        */
>
> -       return err;
> +#if defined(CONFIG_PM_SLEEP)
> +       atomic_set(&dhd_mmc_suspend, false);
> +#endif /*  defined(CONFIG_PM_SLEEP) */
> +
> +       if (test_bit(WL_STATUS_READY, &wl->status)) {
> +               /* Turn on Watchdog timer */
> +               wl_os_wd_timer(ndev, dhd_watchdog_ms);
> +               wl_invoke_iscan(wiphy_to_wl(wiphy));
> +       }
> +
> +       return 0;
>  }
>
>  static s32 wl_cfg80211_suspend(struct wiphy *wiphy)
>  {
>        struct wl_priv *wl = wiphy_to_wl(wiphy);
>        struct net_device *ndev = wl_to_ndev(wl);
> -       s32 err = 0;
> +
> +
> +       /*
> +        * Check for WL_STATUS_READY before any function call which
> +        * could result is bus access. Don't block the suspend for
> +        * any driver error conditions
> +        */
> +
> +       /*
> +        * While going to suspend if associated with AP disassociate
> +        * from AP to save power while system is in suspended state
> +        */
> +       if (test_bit(WL_STATUS_CONNECTED, &wl->status) &&
> +               test_bit(WL_STATUS_READY, &wl->status)) {
> +               WL_INFO("Disassociating from AP"
> +                       " while entering suspend state\n");
> +               wl_link_down(wl);
> +
> +               /*
> +                * Make sure WPA_Supplicant receives all the event
> +                * generated due to DISASSOC call to the fw to keep
> +                * the state fw and WPA_Supplicant state consistent
> +                */
> +               rtnl_unlock();
> +               wl_delay(500);
> +               rtnl_lock();
> +       }
>
>        set_bit(WL_STATUS_SCAN_ABORTING, &wl->status);
> -       wl_term_iscan(wl);
> +       if (test_bit(WL_STATUS_READY, &wl->status))
> +               wl_term_iscan(wl);
> +
>        if (wl->scan_request) {
> -               cfg80211_scan_done(wl->scan_request, true);     /* true means
> -                                                                abort */
> -               wl_set_mpc(ndev, 1);
> +               /* Indidate scan abort to cfg80211 layer */
> +               WL_INFO("Terminating scan in progress\n");
> +               cfg80211_scan_done(wl->scan_request, true);
>                wl->scan_request = NULL;
>        }
>        clear_bit(WL_STATUS_SCANNING, &wl->status);
>        clear_bit(WL_STATUS_SCAN_ABORTING, &wl->status);
> +       clear_bit(WL_STATUS_CONNECTING, &wl->status);
> +       clear_bit(WL_STATUS_CONNECTED, &wl->status);
>
> +       /* Inform SDIO stack not to switch off power to the chip */
>        sdioh_sdio_set_host_pm_flags(MMC_PM_KEEP_POWER);
>
> -       return err;
> +       /* Turn off watchdog timer */
> +       if (test_bit(WL_STATUS_READY, &wl->status)) {
> +               WL_INFO("Terminate watchdog timer and enable MPC\n");
> +               wl_set_mpc(ndev, 1);
> +               wl_os_wd_timer(ndev, 0);
> +       }
> +
> +#if defined(CONFIG_PM_SLEEP)
> +       atomic_set(&dhd_mmc_suspend, true);
> +#endif /*  defined(CONFIG_PM_SLEEP) */
> +
> +
> +       return 0;
>  }
>
>  static __used s32
> diff --git a/drivers/staging/brcm80211/brcmfmac/wl_cfg80211.h b/drivers/staging/brcm80211/brcmfmac/wl_cfg80211.h
> index 5112160..3c8b902 100644
> --- a/drivers/staging/brcm80211/brcmfmac/wl_cfg80211.h
> +++ b/drivers/staging/brcm80211/brcmfmac/wl_cfg80211.h
> @@ -375,5 +375,6 @@ extern s8 *wl_cfg80211_get_fwname(void);    /* get firmware name for
>                                                 the dongle */
>  extern s8 *wl_cfg80211_get_nvramname(void);    /* get nvram name for
>                                                 the dongle */
> +extern void wl_os_wd_timer(struct net_device *ndev, uint wdtick);
>
>  #endif                         /* _wl_cfg80211_h_ */
> --
> 1.7.4.1
>
>
>
--
To unsubscribe from this list: send the line "unsubscribe linux-wireless" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
diff mbox

Patch

diff --git a/drivers/staging/brcm80211/brcmfmac/bcmsdh_sdmmc.c b/drivers/staging/brcm80211/brcmfmac/bcmsdh_sdmmc.c
index 43aebfd..c0ffbd3 100644
--- a/drivers/staging/brcm80211/brcmfmac/bcmsdh_sdmmc.c
+++ b/drivers/staging/brcm80211/brcmfmac/bcmsdh_sdmmc.c
@@ -26,14 +26,11 @@ 
 #include <linux/mmc/core.h>
 #include <linux/mmc/sdio_func.h>
 #include <linux/mmc/sdio_ids.h>
+#include <linux/suspend.h>
 
 #include <dngl_stats.h>
 #include <dhd.h>
 
-#if defined(CONFIG_PM_SLEEP)
-#include <linux/suspend.h>
-extern volatile bool dhd_mmc_suspend;
-#endif
 #include "bcmsdh_sdmmc.h"
 
 extern int sdio_function_init(void);
diff --git a/drivers/staging/brcm80211/brcmfmac/dhd.h b/drivers/staging/brcm80211/brcmfmac/dhd.h
index 99c38dd..a726b49 100644
--- a/drivers/staging/brcm80211/brcmfmac/dhd.h
+++ b/drivers/staging/brcm80211/brcmfmac/dhd.h
@@ -31,6 +31,7 @@ 
 #include <linux/random.h>
 #include <linux/spinlock.h>
 #include <linux/ethtool.h>
+#include <linux/suspend.h>
 #include <asm/uaccess.h>
 #include <asm/unaligned.h>
 /* The kernel threading is sdio-specific */
@@ -122,19 +123,22 @@  typedef struct dhd_pub {
 } dhd_pub_t;
 
 #if defined(CONFIG_PM_SLEEP)
-
+extern atomic_t dhd_mmc_suspend;
 #define DHD_PM_RESUME_WAIT_INIT(a) DECLARE_WAIT_QUEUE_HEAD(a);
-#define _DHD_PM_RESUME_WAIT(a, b) do {\
-			int retry = 0; \
-			while (dhd_mmc_suspend && retry++ != b) { \
-				wait_event_timeout(a, false, HZ/100); \
-			} \
-		}	while (0)
+#define _DHD_PM_RESUME_WAIT(a, b) do { \
+		int retry = 0; \
+		while (atomic_read(&dhd_mmc_suspend) && retry++ != b) { \
+			wait_event_timeout(a, false, HZ/100); \
+		} \
+	}	while (0)
 #define DHD_PM_RESUME_WAIT(a)	_DHD_PM_RESUME_WAIT(a, 30)
 #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)
-#define DHD_PM_RESUME_RETURN	do { if (dhd_mmc_suspend) return; } while (0)
+	do { if (atomic_read(&dhd_mmc_suspend)) return a; } while (0)
+#define DHD_PM_RESUME_RETURN	do { \
+	if (atomic_read(&dhd_mmc_suspend)) \
+		return; \
+	} while (0)
 
 #define DHD_SPINWAIT_SLEEP_INIT(a) DECLARE_WAIT_QUEUE_HEAD(a);
 #define SPINWAIT_SLEEP(a, exp, us) do { \
diff --git a/drivers/staging/brcm80211/brcmfmac/dhd_linux.c b/drivers/staging/brcm80211/brcmfmac/dhd_linux.c
index ba73ce0..31a5ca0 100644
--- a/drivers/staging/brcm80211/brcmfmac/dhd_linux.c
+++ b/drivers/staging/brcm80211/brcmfmac/dhd_linux.c
@@ -166,7 +166,7 @@  void wifi_del_dev(void)
 
 #if defined(CONFIG_PM_SLEEP)
 #include <linux/suspend.h>
-volatile bool dhd_mmc_suspend = false;
+atomic_t dhd_mmc_suspend;
 DECLARE_WAIT_QUEUE_HEAD(dhd_dpc_wait);
 #endif	/*  defined(CONFIG_PM_SLEEP) */
 
@@ -407,11 +407,11 @@  static int dhd_sleep_pm_callback(struct notifier_block *nfb,
 	switch (action) {
 	case PM_HIBERNATION_PREPARE:
 	case PM_SUSPEND_PREPARE:
-		dhd_mmc_suspend = true;
+		atomic_set(&dhd_mmc_suspend, true);
 		return NOTIFY_OK;
 	case PM_POST_HIBERNATION:
 	case PM_POST_SUSPEND:
-		dhd_mmc_suspend = false;
+		atomic_set(&dhd_mmc_suspend, false);
 		return NOTIFY_OK;
 	}
 	return 0;
@@ -2011,7 +2011,9 @@  dhd_pub_t *dhd_attach(struct dhd_bus *bus, uint bus_hdrlen)
 	g_bus = bus;
 #endif
 #if defined(CONFIG_PM_SLEEP)
-	register_pm_notifier(&dhd_sleep_pm_notifier);
+	atomic_set(&dhd_mmc_suspend, false);
+	if (!IS_CFG80211_FAVORITE())
+		register_pm_notifier(&dhd_sleep_pm_notifier);
 #endif	/* defined(CONFIG_PM_SLEEP) */
 	/* && defined(DHD_GPL) */
 	/* Init lock suspend to prevent kernel going to suspend */
@@ -2305,7 +2307,8 @@  void dhd_detach(dhd_pub_t *dhdp)
 				wl_cfg80211_detach();
 
 #if defined(CONFIG_PM_SLEEP)
-			unregister_pm_notifier(&dhd_sleep_pm_notifier);
+			if (!IS_CFG80211_FAVORITE())
+				unregister_pm_notifier(&dhd_sleep_pm_notifier);
 #endif	/* defined(CONFIG_PM_SLEEP) */
 			/* && defined(DHD_GPL) */
 			free_netdev(ifp->net);
@@ -2816,6 +2819,13 @@  int dhd_wait_pend8021x(struct net_device *dev)
 	return pend;
 }
 
+void wl_os_wd_timer(struct net_device *ndev, uint wdtick)
+{
+	dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(ndev);
+
+	dhd_os_wd_timer(&dhd->pub, wdtick);
+}
+
 #ifdef DHD_DEBUG
 int write_to_file(dhd_pub_t *dhd, u8 *buf, int size)
 {
diff --git a/drivers/staging/brcm80211/brcmfmac/wl_cfg80211.c b/drivers/staging/brcm80211/brcmfmac/wl_cfg80211.c
index c60fc7c..2d67048 100644
--- a/drivers/staging/brcm80211/brcmfmac/wl_cfg80211.c
+++ b/drivers/staging/brcm80211/brcmfmac/wl_cfg80211.c
@@ -1969,34 +1969,91 @@  wl_cfg80211_set_bitrate_mask(struct wiphy *wiphy, struct net_device *dev,
 
 static s32 wl_cfg80211_resume(struct wiphy *wiphy)
 {
-	s32 err = 0;
+	struct wl_priv *wl = wiphy_to_wl(wiphy);
+	struct net_device *ndev = wl_to_ndev(wl);
 
-	CHECK_SYS_UP();
-	wl_invoke_iscan(wiphy_to_wl(wiphy));
+	/*
+	 * Check for WL_STATUS_READY before any function call which
+	 * could result is bus access. Don't block the resume for
+	 * any driver error conditions
+	 */
 
-	return err;
+#if defined(CONFIG_PM_SLEEP)
+	atomic_set(&dhd_mmc_suspend, false);
+#endif	/*  defined(CONFIG_PM_SLEEP) */
+
+	if (test_bit(WL_STATUS_READY, &wl->status)) {
+		/* Turn on Watchdog timer */
+		wl_os_wd_timer(ndev, dhd_watchdog_ms);
+		wl_invoke_iscan(wiphy_to_wl(wiphy));
+	}
+
+	return 0;
 }
 
 static s32 wl_cfg80211_suspend(struct wiphy *wiphy)
 {
 	struct wl_priv *wl = wiphy_to_wl(wiphy);
 	struct net_device *ndev = wl_to_ndev(wl);
-	s32 err = 0;
+
+
+	/*
+	 * Check for WL_STATUS_READY before any function call which
+	 * could result is bus access. Don't block the suspend for
+	 * any driver error conditions
+	 */
+
+	/*
+	 * While going to suspend if associated with AP disassociate
+	 * from AP to save power while system is in suspended state
+	 */
+	if (test_bit(WL_STATUS_CONNECTED, &wl->status) &&
+		test_bit(WL_STATUS_READY, &wl->status)) {
+		WL_INFO("Disassociating from AP"
+			" while entering suspend state\n");
+		wl_link_down(wl);
+
+		/*
+		 * Make sure WPA_Supplicant receives all the event
+		 * generated due to DISASSOC call to the fw to keep
+		 * the state fw and WPA_Supplicant state consistent
+		 */
+		rtnl_unlock();
+		wl_delay(500);
+		rtnl_lock();
+	}
 
 	set_bit(WL_STATUS_SCAN_ABORTING, &wl->status);
-	wl_term_iscan(wl);
+	if (test_bit(WL_STATUS_READY, &wl->status))
+		wl_term_iscan(wl);
+
 	if (wl->scan_request) {
-		cfg80211_scan_done(wl->scan_request, true);	/* true means
-								 abort */
-		wl_set_mpc(ndev, 1);
+		/* Indidate scan abort to cfg80211 layer */
+		WL_INFO("Terminating scan in progress\n");
+		cfg80211_scan_done(wl->scan_request, true);
 		wl->scan_request = NULL;
 	}
 	clear_bit(WL_STATUS_SCANNING, &wl->status);
 	clear_bit(WL_STATUS_SCAN_ABORTING, &wl->status);
+	clear_bit(WL_STATUS_CONNECTING, &wl->status);
+	clear_bit(WL_STATUS_CONNECTED, &wl->status);
 
+	/* Inform SDIO stack not to switch off power to the chip */
 	sdioh_sdio_set_host_pm_flags(MMC_PM_KEEP_POWER);
 
-	return err;
+	/* Turn off watchdog timer */
+	if (test_bit(WL_STATUS_READY, &wl->status)) {
+		WL_INFO("Terminate watchdog timer and enable MPC\n");
+		wl_set_mpc(ndev, 1);
+		wl_os_wd_timer(ndev, 0);
+	}
+
+#if defined(CONFIG_PM_SLEEP)
+	atomic_set(&dhd_mmc_suspend, true);
+#endif	/*  defined(CONFIG_PM_SLEEP) */
+
+
+	return 0;
 }
 
 static __used s32
diff --git a/drivers/staging/brcm80211/brcmfmac/wl_cfg80211.h b/drivers/staging/brcm80211/brcmfmac/wl_cfg80211.h
index 5112160..3c8b902 100644
--- a/drivers/staging/brcm80211/brcmfmac/wl_cfg80211.h
+++ b/drivers/staging/brcm80211/brcmfmac/wl_cfg80211.h
@@ -375,5 +375,6 @@  extern s8 *wl_cfg80211_get_fwname(void);	/* get firmware name for
 						 the dongle */
 extern s8 *wl_cfg80211_get_nvramname(void);	/* get nvram name for
 						 the dongle */
+extern void wl_os_wd_timer(struct net_device *ndev, uint wdtick);
 
 #endif				/* _wl_cfg80211_h_ */