mmc: sdio: fix runtime PM anomalies by introducing MMC_CAP_POWER_OFF_CARD
Some board/card/host configurations are not capable of powering off the card after boot. To support such configurations, and to allow smoother transition to runtime PM behavior, MMC_CAP_POWER_OFF_CARD is added, so hosts need to explicitly indicate whether it's OK to power off their cards after boot. SDIO core will enable runtime PM for a card only if that cap is set. As a result, the card will be powered down after boot, and will only be powered up again when a driver is loaded (and then it's up to the driver to decide whether power will be kept or not). This will prevent sdio_bus_probe() failures with setups that do not support powering off the card. Reported-and-tested-by: Daniel Drake <dsd@laptop.org> Reported-and-tested-by: Arnd Hannemann <arnd@arndnet.de> Signed-off-by: Ohad Ben-Cohen <ohad@wizery.com> Signed-off-by: Chris Ball <cjb@laptop.org>
This commit is contained in:
parent
4d0812c37f
commit
ed919b0125
|
@ -547,9 +547,11 @@ static void mmc_sdio_detect(struct mmc_host *host)
|
|||
BUG_ON(!host->card);
|
||||
|
||||
/* Make sure card is powered before detecting it */
|
||||
err = pm_runtime_get_sync(&host->card->dev);
|
||||
if (err < 0)
|
||||
goto out;
|
||||
if (host->caps & MMC_CAP_POWER_OFF_CARD) {
|
||||
err = pm_runtime_get_sync(&host->card->dev);
|
||||
if (err < 0)
|
||||
goto out;
|
||||
}
|
||||
|
||||
mmc_claim_host(host);
|
||||
|
||||
|
@ -571,7 +573,8 @@ static void mmc_sdio_detect(struct mmc_host *host)
|
|||
* is about to show up at this point, the _sync variant is
|
||||
* desirable anyway.
|
||||
*/
|
||||
pm_runtime_put_sync(&host->card->dev);
|
||||
if (host->caps & MMC_CAP_POWER_OFF_CARD)
|
||||
pm_runtime_put_sync(&host->card->dev);
|
||||
|
||||
out:
|
||||
if (err) {
|
||||
|
@ -728,16 +731,21 @@ int mmc_attach_sdio(struct mmc_host *host, u32 ocr)
|
|||
card = host->card;
|
||||
|
||||
/*
|
||||
* Let runtime PM core know our card is active
|
||||
* Enable runtime PM only if supported by host+card+board
|
||||
*/
|
||||
err = pm_runtime_set_active(&card->dev);
|
||||
if (err)
|
||||
goto remove;
|
||||
if (host->caps & MMC_CAP_POWER_OFF_CARD) {
|
||||
/*
|
||||
* Let runtime PM core know our card is active
|
||||
*/
|
||||
err = pm_runtime_set_active(&card->dev);
|
||||
if (err)
|
||||
goto remove;
|
||||
|
||||
/*
|
||||
* Enable runtime PM for this card
|
||||
*/
|
||||
pm_runtime_enable(&card->dev);
|
||||
/*
|
||||
* Enable runtime PM for this card
|
||||
*/
|
||||
pm_runtime_enable(&card->dev);
|
||||
}
|
||||
|
||||
/*
|
||||
* The number of functions on the card is encoded inside
|
||||
|
@ -755,9 +763,10 @@ int mmc_attach_sdio(struct mmc_host *host, u32 ocr)
|
|||
goto remove;
|
||||
|
||||
/*
|
||||
* Enable Runtime PM for this func
|
||||
* Enable Runtime PM for this func (if supported)
|
||||
*/
|
||||
pm_runtime_enable(&card->sdio_func[i]->dev);
|
||||
if (host->caps & MMC_CAP_POWER_OFF_CARD)
|
||||
pm_runtime_enable(&card->sdio_func[i]->dev);
|
||||
}
|
||||
|
||||
mmc_release_host(host);
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
#include <linux/pm_runtime.h>
|
||||
|
||||
#include <linux/mmc/card.h>
|
||||
#include <linux/mmc/host.h>
|
||||
#include <linux/mmc/sdio_func.h>
|
||||
|
||||
#include "sdio_cis.h"
|
||||
|
@ -132,9 +133,11 @@ static int sdio_bus_probe(struct device *dev)
|
|||
* it should call pm_runtime_put_noidle() in its probe routine and
|
||||
* pm_runtime_get_noresume() in its remove routine.
|
||||
*/
|
||||
ret = pm_runtime_get_sync(dev);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
if (func->card->host->caps & MMC_CAP_POWER_OFF_CARD) {
|
||||
ret = pm_runtime_get_sync(dev);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Set the default block size so the driver is sure it's something
|
||||
* sensible. */
|
||||
|
@ -151,7 +154,8 @@ static int sdio_bus_probe(struct device *dev)
|
|||
return 0;
|
||||
|
||||
disable_runtimepm:
|
||||
pm_runtime_put_noidle(dev);
|
||||
if (func->card->host->caps & MMC_CAP_POWER_OFF_CARD)
|
||||
pm_runtime_put_noidle(dev);
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
@ -160,12 +164,14 @@ static int sdio_bus_remove(struct device *dev)
|
|||
{
|
||||
struct sdio_driver *drv = to_sdio_driver(dev->driver);
|
||||
struct sdio_func *func = dev_to_sdio_func(dev);
|
||||
int ret;
|
||||
int ret = 0;
|
||||
|
||||
/* Make sure card is powered before invoking ->remove() */
|
||||
ret = pm_runtime_get_sync(dev);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
if (func->card->host->caps & MMC_CAP_POWER_OFF_CARD) {
|
||||
ret = pm_runtime_get_sync(dev);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
}
|
||||
|
||||
drv->remove(func);
|
||||
|
||||
|
@ -178,10 +184,12 @@ static int sdio_bus_remove(struct device *dev)
|
|||
}
|
||||
|
||||
/* First, undo the increment made directly above */
|
||||
pm_runtime_put_noidle(dev);
|
||||
if (func->card->host->caps & MMC_CAP_POWER_OFF_CARD)
|
||||
pm_runtime_put_noidle(dev);
|
||||
|
||||
/* Then undo the runtime PM settings in sdio_bus_probe() */
|
||||
pm_runtime_put_noidle(dev);
|
||||
if (func->card->host->caps & MMC_CAP_POWER_OFF_CARD)
|
||||
pm_runtime_put_noidle(dev);
|
||||
|
||||
out:
|
||||
return ret;
|
||||
|
@ -191,6 +199,8 @@ out:
|
|||
|
||||
static int sdio_bus_pm_prepare(struct device *dev)
|
||||
{
|
||||
struct sdio_func *func = dev_to_sdio_func(dev);
|
||||
|
||||
/*
|
||||
* Resume an SDIO device which was suspended at run time at this
|
||||
* point, in order to allow standard SDIO suspend/resume paths
|
||||
|
@ -212,7 +222,8 @@ static int sdio_bus_pm_prepare(struct device *dev)
|
|||
* since there is little point in failing system suspend if a
|
||||
* device can't be resumed.
|
||||
*/
|
||||
pm_runtime_resume(dev);
|
||||
if (func->card->host->caps & MMC_CAP_POWER_OFF_CARD)
|
||||
pm_runtime_resume(dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -168,6 +168,7 @@ struct mmc_host {
|
|||
/* DDR mode at 1.8V */
|
||||
#define MMC_CAP_1_2V_DDR (1 << 12) /* can support */
|
||||
/* DDR mode at 1.2V */
|
||||
#define MMC_CAP_POWER_OFF_CARD (1 << 13) /* Can power off after boot */
|
||||
|
||||
mmc_pm_flag_t pm_caps; /* supported pm features */
|
||||
|
||||
|
|
Loading…
Reference in a new issue