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

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
Loading
Loading
Loading
Loading
+1 −4
Original line number Diff line number Diff line
@@ -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);
+13 −9
Original line number Diff line number Diff line
@@ -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) { \
		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 { \
+15 −5
Original line number Diff line number Diff line
@@ -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,6 +2011,8 @@ dhd_pub_t *dhd_attach(struct dhd_bus *bus, uint bus_hdrlen)
	g_bus = bus;
#endif
#if defined(CONFIG_PM_SLEEP)
	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) */
@@ -2305,6 +2307,7 @@ void dhd_detach(dhd_pub_t *dhdp)
				wl_cfg80211_detach();

#if defined(CONFIG_PM_SLEEP)
			if (!IS_CFG80211_FAVORITE())
				unregister_pm_notifier(&dhd_sleep_pm_notifier);
#endif	/* defined(CONFIG_PM_SLEEP) */
			/* && defined(DHD_GPL) */
@@ -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)
{
+67 −10
Original line number Diff line number Diff line
@@ -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();
	/*
	 * 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
	 */

#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 err;
	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);
	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
+1 −0
Original line number Diff line number Diff line
@@ -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_ */