Commit 645865fc authored by Ido Yariv's avatar Ido Yariv Committed by Luciano Coelho

wlcore: Fix sdio out-of-sync power state

wl12xx_sdio_power_off() manually powers down the card regardless of the
runtime pm state. If wl12xx_sdio_power_on() is called before the card
was suspended by runtime PM, it will not power up the card.

As part of the HW detection, the chip's power is toggled. Since this
happens in the context of probing sdio, the power reference counter will
be higher than zero. As a result, when wl12xx_sdio_power_off() is
called, the chip will be powered down while still having a positive
power reference counter. If the interface is quickly activated, the
driver might try to transfer data to a powered off chip.

Fix this by ensuring that wl12xx_sdio_power_on() explicitly powers on
the chip in case runtime pm claims the chip is already powered on. To
avoid cases in which it is not possible to determine if the chip was
really powered on (card's power reference counter is positive), operate
on the mmc_card instead of the function.

Also verify that the chip is indeed powered on before powering off, to
avoid wrong reference counter values in error cases.
Signed-off-by: default avatarIdo Yariv <ido@wizery.com>
Signed-off-by: default avatarLuciano Coelho <coelho@ti.com>
parent 41844076
...@@ -160,7 +160,13 @@ static inline void wlcore_write_reg(struct wl1271 *wl, int reg, u32 val) ...@@ -160,7 +160,13 @@ static inline void wlcore_write_reg(struct wl1271 *wl, int reg, u32 val)
static inline void wl1271_power_off(struct wl1271 *wl) static inline void wl1271_power_off(struct wl1271 *wl)
{ {
wl->if_ops->power(wl->dev, false); int ret;
if (!test_bit(WL1271_FLAG_GPIO_POWER, &wl->flags))
return;
ret = wl->if_ops->power(wl->dev, false);
if (!ret)
clear_bit(WL1271_FLAG_GPIO_POWER, &wl->flags); clear_bit(WL1271_FLAG_GPIO_POWER, &wl->flags);
} }
......
...@@ -147,18 +147,21 @@ static int wl12xx_sdio_power_on(struct wl12xx_sdio_glue *glue) ...@@ -147,18 +147,21 @@ static int wl12xx_sdio_power_on(struct wl12xx_sdio_glue *glue)
{ {
int ret; int ret;
struct sdio_func *func = dev_to_sdio_func(glue->dev); struct sdio_func *func = dev_to_sdio_func(glue->dev);
struct mmc_card *card = func->card;
/* If enabled, tell runtime PM not to power off the card */ ret = pm_runtime_get_sync(&card->dev);
if (pm_runtime_enabled(&func->dev)) { if (ret) {
ret = pm_runtime_get_sync(&func->dev); /*
if (ret < 0) * Runtime PM might be temporarily disabled, or the device
goto out; * might have a positive reference counter. Make sure it is
} else { * really powered on.
/* Runtime PM is disabled: power up the card manually */ */
ret = mmc_power_restore_host(func->card->host); ret = mmc_power_restore_host(card->host);
if (ret < 0) if (ret < 0) {
pm_runtime_put_sync(&card->dev);
goto out; goto out;
} }
}
sdio_claim_host(func); sdio_claim_host(func);
sdio_enable_func(func); sdio_enable_func(func);
...@@ -172,20 +175,21 @@ static int wl12xx_sdio_power_off(struct wl12xx_sdio_glue *glue) ...@@ -172,20 +175,21 @@ static int wl12xx_sdio_power_off(struct wl12xx_sdio_glue *glue)
{ {
int ret; int ret;
struct sdio_func *func = dev_to_sdio_func(glue->dev); struct sdio_func *func = dev_to_sdio_func(glue->dev);
struct mmc_card *card = func->card;
sdio_claim_host(func); sdio_claim_host(func);
sdio_disable_func(func); sdio_disable_func(func);
sdio_release_host(func); sdio_release_host(func);
/* Power off the card manually, even if runtime PM is enabled. */ /* Power off the card manually in case it wasn't powered off above */
ret = mmc_power_save_host(func->card->host); ret = mmc_power_save_host(card->host);
if (ret < 0) if (ret < 0)
return ret; goto out;
/* If enabled, let runtime PM know the card is powered off */ /* Let runtime PM know the card is powered off */
if (pm_runtime_enabled(&func->dev)) pm_runtime_put_sync(&card->dev);
ret = pm_runtime_put_sync(&func->dev);
out:
return ret; return ret;
} }
......
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