mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2026-05-09 17:17:04 -04:00
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: Franky (Zhenhui) Lin <frankyl@broadcom.com> Reviewed-by: Brett Rudley <brudley@broadcom.com> Signed-off-by: Arend van Spriel <arend@broadcom.com> Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
This commit is contained in:
committed by
Greg Kroah-Hartman
parent
22d5d59b8c
commit
e6e8f894aa
@@ -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_ */
|
||||
|
||||
Reference in New Issue
Block a user