Commit e6e8f894 authored by Sukesh Srikakula's avatar Sukesh Srikakula Committed by Greg Kroah-Hartman

staging: brcm80211: Fix for suspend issue in brcmfmac driver

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: default avatarFranky (Zhenhui) Lin <frankyl@broadcom.com>
Reviewed-by: default avatarBrett Rudley <brudley@broadcom.com>
Signed-off-by: default avatarArend van Spriel <arend@broadcom.com>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@suse.de>
parent 22d5d59b
......@@ -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);
......
......@@ -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 { \
......
......@@ -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)
{
......
......@@ -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
......
......@@ -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_ */
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment