Browse Source

brcmfmac: wait for driver to go idle during suspend

Before going in suspend state the watchdog thread needs to put the
device in bus sleep state, which assures it can go in deep-sleep
state during D3 state.

Reviewed-by: Hante Meuleman <meuleman@broadcom.com>
Reviewed-by: Daniel (Deognyoun) Kim <dekim@broadcom.com>
Reviewed-by: Franky (Zhenhui) Lin <frankyl@broadcom.com>
Reviewed-by: Pieter-Paul Giesberts <pieterpg@broadcom.com>
Signed-off-by: Arend van Spriel <arend@broadcom.com>
Signed-off-by: Kalle Valo <kvalo@codeaurora.org>
Arend van Spriel 11 years ago
parent
commit
8982cd40ac

+ 15 - 5
drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c

@@ -1045,7 +1045,9 @@ static int brcmf_ops_sdio_probe(struct sdio_func *func,
 		bus_if->wowl_supported = true;
 		bus_if->wowl_supported = true;
 #endif
 #endif
 
 
+	sdiodev->sleeping = false;
 	atomic_set(&sdiodev->suspend, false);
 	atomic_set(&sdiodev->suspend, false);
+	init_waitqueue_head(&sdiodev->idle_wait);
 
 
 	brcmf_dbg(SDIO, "F2 found, calling brcmf_sdiod_probe...\n");
 	brcmf_dbg(SDIO, "F2 found, calling brcmf_sdiod_probe...\n");
 	err = brcmf_sdiod_probe(sdiodev);
 	err = brcmf_sdiod_probe(sdiodev);
@@ -1107,12 +1109,23 @@ void brcmf_sdio_wowl_config(struct device *dev, bool enabled)
 #ifdef CONFIG_PM_SLEEP
 #ifdef CONFIG_PM_SLEEP
 static int brcmf_ops_sdio_suspend(struct device *dev)
 static int brcmf_ops_sdio_suspend(struct device *dev)
 {
 {
-	struct brcmf_bus *bus_if = dev_get_drvdata(dev);
-	struct brcmf_sdio_dev *sdiodev = bus_if->bus_priv.sdio;
+	struct brcmf_bus *bus_if;
+	struct brcmf_sdio_dev *sdiodev;
 	mmc_pm_flag_t sdio_flags;
 	mmc_pm_flag_t sdio_flags;
 
 
 	brcmf_dbg(SDIO, "Enter\n");
 	brcmf_dbg(SDIO, "Enter\n");
 
 
+	bus_if = dev_get_drvdata(dev);
+	sdiodev = bus_if->bus_priv.sdio;
+
+	/* wait for watchdog to go idle */
+	if (wait_event_timeout(sdiodev->idle_wait, sdiodev->sleeping,
+			       msecs_to_jiffies(3 * BRCMF_WD_POLL_MS)) == 0) {
+		brcmf_err("bus still active\n");
+		return -EBUSY;
+	}
+	/* disable watchdog */
+	brcmf_sdio_wd_timer(sdiodev->bus, 0);
 	atomic_set(&sdiodev->suspend, true);
 	atomic_set(&sdiodev->suspend, true);
 
 
 	if (sdiodev->wowl_enabled) {
 	if (sdiodev->wowl_enabled) {
@@ -1124,9 +1137,6 @@ static int brcmf_ops_sdio_suspend(struct device *dev)
 		if (sdio_set_host_pm_flags(sdiodev->func[1], sdio_flags))
 		if (sdio_set_host_pm_flags(sdiodev->func[1], sdio_flags))
 			brcmf_err("Failed to set pm_flags %x\n", sdio_flags);
 			brcmf_err("Failed to set pm_flags %x\n", sdio_flags);
 	}
 	}
-
-	brcmf_sdio_wd_timer(sdiodev->bus, 0);
-
 	return 0;
 	return 0;
 }
 }
 
 

+ 8 - 10
drivers/net/wireless/brcm80211/brcmfmac/sdio.c

@@ -515,7 +515,6 @@ struct brcmf_sdio {
 	bool txoff;		/* Transmit flow-controlled */
 	bool txoff;		/* Transmit flow-controlled */
 	struct brcmf_sdio_count sdcnt;
 	struct brcmf_sdio_count sdcnt;
 	bool sr_enabled; /* SaveRestore enabled */
 	bool sr_enabled; /* SaveRestore enabled */
-	bool sleeping; /* SDIO bus sleeping */
 
 
 	u8 tx_hdrlen;		/* sdio bus header length for tx packet */
 	u8 tx_hdrlen;		/* sdio bus header length for tx packet */
 	bool txglom;		/* host tx glomming enable flag */
 	bool txglom;		/* host tx glomming enable flag */
@@ -1014,12 +1013,12 @@ brcmf_sdio_bus_sleep(struct brcmf_sdio *bus, bool sleep, bool pendok)
 
 
 	brcmf_dbg(SDIO, "Enter: request %s currently %s\n",
 	brcmf_dbg(SDIO, "Enter: request %s currently %s\n",
 		  (sleep ? "SLEEP" : "WAKE"),
 		  (sleep ? "SLEEP" : "WAKE"),
-		  (bus->sleeping ? "SLEEP" : "WAKE"));
+		  (bus->sdiodev->sleeping ? "SLEEP" : "WAKE"));
 
 
 	/* If SR is enabled control bus state with KSO */
 	/* If SR is enabled control bus state with KSO */
 	if (bus->sr_enabled) {
 	if (bus->sr_enabled) {
 		/* Done if we're already in the requested state */
 		/* Done if we're already in the requested state */
-		if (sleep == bus->sleeping)
+		if (sleep == bus->sdiodev->sleeping)
 			goto end;
 			goto end;
 
 
 		/* Going to sleep */
 		/* Going to sleep */
@@ -1051,12 +1050,7 @@ brcmf_sdio_bus_sleep(struct brcmf_sdio *bus, bool sleep, bool pendok)
 			bus->idlecount = 0;
 			bus->idlecount = 0;
 			err = brcmf_sdio_kso_control(bus, true);
 			err = brcmf_sdio_kso_control(bus, true);
 		}
 		}
-		if (!err) {
-			/* Change state */
-			bus->sleeping = sleep;
-			brcmf_dbg(SDIO, "new state %s\n",
-				  (sleep ? "SLEEP" : "WAKE"));
-		} else {
+		if (err) {
 			brcmf_err("error while changing bus sleep state %d\n",
 			brcmf_err("error while changing bus sleep state %d\n",
 				  err);
 				  err);
 			goto done;
 			goto done;
@@ -1071,6 +1065,11 @@ end:
 	} else {
 	} else {
 		brcmf_sdio_clkctl(bus, CLK_AVAIL, pendok);
 		brcmf_sdio_clkctl(bus, CLK_AVAIL, pendok);
 	}
 	}
+	bus->sdiodev->sleeping = sleep;
+	if (sleep)
+		wake_up(&bus->sdiodev->idle_wait);
+	brcmf_dbg(SDIO, "new state %s\n",
+		  (sleep ? "SLEEP" : "WAKE"));
 done:
 done:
 	brcmf_dbg(SDIO, "Exit: err=%d\n", err);
 	brcmf_dbg(SDIO, "Exit: err=%d\n", err);
 	return err;
 	return err;
@@ -4215,7 +4214,6 @@ struct brcmf_sdio *brcmf_sdio_probe(struct brcmf_sdio_dev *sdiodev)
 	bus->idleclock = BRCMF_IDLE_ACTIVE;
 	bus->idleclock = BRCMF_IDLE_ACTIVE;
 
 
 	/* SR state */
 	/* SR state */
-	bus->sleeping = false;
 	bus->sr_enabled = false;
 	bus->sr_enabled = false;
 
 
 	brcmf_sdio_debugfs_create(bus);
 	brcmf_sdio_debugfs_create(bus);

+ 2 - 0
drivers/net/wireless/brcm80211/brcmfmac/sdio.h

@@ -169,6 +169,8 @@ struct brcmf_sdio_dev {
 	u32 sbwad;			/* Save backplane window address */
 	u32 sbwad;			/* Save backplane window address */
 	struct brcmf_sdio *bus;
 	struct brcmf_sdio *bus;
 	atomic_t suspend;		/* suspend flag */
 	atomic_t suspend;		/* suspend flag */
+	bool sleeping;
+	wait_queue_head_t idle_wait;
 	struct device *dev;
 	struct device *dev;
 	struct brcmf_bus *bus_if;
 	struct brcmf_bus *bus_if;
 	struct brcmfmac_sdio_platform_data *pdata;
 	struct brcmfmac_sdio_platform_data *pdata;