Skip to content

Commit

Permalink
libertas: fix suspend and resume for SDIO connected cards
Browse files Browse the repository at this point in the history
Prior to commit 573185c ("mmc: core: Invoke sdio func driver's PM
callbacks from the sdio bus"), the MMC core used to call into the power
management functions of SDIO clients itself and removed the card if the
return code was non-zero. IOW, the mmc handled errors gracefully and didn't
upchain them to the pm core.

Since this change, the mmc core relies on generic power management
functions which treat all errors as a reason to cancel the suspend
immediately. This causes suspend attempts to fail when the libertas
driver is loaded.

To fix this, power down the card explicitly in if_sdio_suspend() when we
know we're about to lose power and return success. Also set a flag in these
cases, and power up the card again in if_sdio_resume().

Fixes: 573185c ("mmc: core: Invoke sdio func driver's PM callbacks from the sdio bus")
Cc: <stable@vger.kernel.org>
Signed-off-by: Daniel Mack <daniel@zonque.org>
Reviewed-by: Chris Ball <chris@printf.net>
Reviewed-by: Ulf Hansson <ulf.hansson@linaro.org>
Signed-off-by: Kalle Valo <kvalo@codeaurora.org>
  • Loading branch information
Daniel Mack authored and Kalle Valo committed Jul 4, 2018
1 parent 07b1ae4 commit 7444a80
Show file tree
Hide file tree
Showing 2 changed files with 25 additions and 6 deletions.
1 change: 1 addition & 0 deletions drivers/net/wireless/marvell/libertas/dev.h
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,7 @@ struct lbs_private {
u8 fw_ready;
u8 surpriseremoved;
u8 setup_fw_on_resume;
u8 power_up_on_resume;
int (*hw_host_to_card) (struct lbs_private *priv, u8 type, u8 *payload, u16 nb);
void (*reset_card) (struct lbs_private *priv);
int (*power_save) (struct lbs_private *priv);
Expand Down
30 changes: 24 additions & 6 deletions drivers/net/wireless/marvell/libertas/if_sdio.c
Original file line number Diff line number Diff line change
Expand Up @@ -1290,25 +1290,38 @@ static void if_sdio_remove(struct sdio_func *func)
static int if_sdio_suspend(struct device *dev)
{
struct sdio_func *func = dev_to_sdio_func(dev);
int ret;
struct if_sdio_card *card = sdio_get_drvdata(func);
struct lbs_private *priv = card->priv;
int ret;

mmc_pm_flag_t flags = sdio_get_host_pm_caps(func);
priv->power_up_on_resume = false;

/* If we're powered off anyway, just let the mmc layer remove the
* card. */
if (!lbs_iface_active(card->priv))
return -ENOSYS;
if (!lbs_iface_active(priv)) {
if (priv->fw_ready) {
priv->power_up_on_resume = true;
if_sdio_power_off(card);
}

return 0;
}

dev_info(dev, "%s: suspend: PM flags = 0x%x\n",
sdio_func_id(func), flags);

/* If we aren't being asked to wake on anything, we should bail out
* and let the SD stack power down the card.
*/
if (card->priv->wol_criteria == EHS_REMOVE_WAKEUP) {
if (priv->wol_criteria == EHS_REMOVE_WAKEUP) {
dev_info(dev, "Suspend without wake params -- powering down card\n");
return -ENOSYS;
if (priv->fw_ready) {
priv->power_up_on_resume = true;
if_sdio_power_off(card);
}

return 0;
}

if (!(flags & MMC_PM_KEEP_POWER)) {
Expand All @@ -1321,7 +1334,7 @@ static int if_sdio_suspend(struct device *dev)
if (ret)
return ret;

ret = lbs_suspend(card->priv);
ret = lbs_suspend(priv);
if (ret)
return ret;

Expand All @@ -1336,6 +1349,11 @@ static int if_sdio_resume(struct device *dev)

dev_info(dev, "%s: resume: we're back\n", sdio_func_id(func));

if (card->priv->power_up_on_resume) {
if_sdio_power_on(card);
wait_event(card->pwron_waitq, card->priv->fw_ready);
}

ret = lbs_resume(card->priv);

return ret;
Expand Down

0 comments on commit 7444a80

Please sign in to comment.