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);
|
BUG_ON(!host->card);
|
||||||
|
|
||||||
/* Make sure card is powered before detecting it */
|
/* Make sure card is powered before detecting it */
|
||||||
err = pm_runtime_get_sync(&host->card->dev);
|
if (host->caps & MMC_CAP_POWER_OFF_CARD) {
|
||||||
if (err < 0)
|
err = pm_runtime_get_sync(&host->card->dev);
|
||||||
goto out;
|
if (err < 0)
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
mmc_claim_host(host);
|
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
|
* is about to show up at this point, the _sync variant is
|
||||||
* desirable anyway.
|
* 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:
|
out:
|
||||||
if (err) {
|
if (err) {
|
||||||
@ -728,16 +731,21 @@ int mmc_attach_sdio(struct mmc_host *host, u32 ocr)
|
|||||||
card = host->card;
|
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 (host->caps & MMC_CAP_POWER_OFF_CARD) {
|
||||||
if (err)
|
/*
|
||||||
goto remove;
|
* 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
|
* Enable runtime PM for this card
|
||||||
*/
|
*/
|
||||||
pm_runtime_enable(&card->dev);
|
pm_runtime_enable(&card->dev);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* The number of functions on the card is encoded inside
|
* 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;
|
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);
|
mmc_release_host(host);
|
||||||
|
@ -17,6 +17,7 @@
|
|||||||
#include <linux/pm_runtime.h>
|
#include <linux/pm_runtime.h>
|
||||||
|
|
||||||
#include <linux/mmc/card.h>
|
#include <linux/mmc/card.h>
|
||||||
|
#include <linux/mmc/host.h>
|
||||||
#include <linux/mmc/sdio_func.h>
|
#include <linux/mmc/sdio_func.h>
|
||||||
|
|
||||||
#include "sdio_cis.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
|
* it should call pm_runtime_put_noidle() in its probe routine and
|
||||||
* pm_runtime_get_noresume() in its remove routine.
|
* pm_runtime_get_noresume() in its remove routine.
|
||||||
*/
|
*/
|
||||||
ret = pm_runtime_get_sync(dev);
|
if (func->card->host->caps & MMC_CAP_POWER_OFF_CARD) {
|
||||||
if (ret < 0)
|
ret = pm_runtime_get_sync(dev);
|
||||||
goto out;
|
if (ret < 0)
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
/* Set the default block size so the driver is sure it's something
|
/* Set the default block size so the driver is sure it's something
|
||||||
* sensible. */
|
* sensible. */
|
||||||
@ -151,7 +154,8 @@ static int sdio_bus_probe(struct device *dev)
|
|||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
disable_runtimepm:
|
disable_runtimepm:
|
||||||
pm_runtime_put_noidle(dev);
|
if (func->card->host->caps & MMC_CAP_POWER_OFF_CARD)
|
||||||
|
pm_runtime_put_noidle(dev);
|
||||||
out:
|
out:
|
||||||
return ret;
|
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_driver *drv = to_sdio_driver(dev->driver);
|
||||||
struct sdio_func *func = dev_to_sdio_func(dev);
|
struct sdio_func *func = dev_to_sdio_func(dev);
|
||||||
int ret;
|
int ret = 0;
|
||||||
|
|
||||||
/* Make sure card is powered before invoking ->remove() */
|
/* Make sure card is powered before invoking ->remove() */
|
||||||
ret = pm_runtime_get_sync(dev);
|
if (func->card->host->caps & MMC_CAP_POWER_OFF_CARD) {
|
||||||
if (ret < 0)
|
ret = pm_runtime_get_sync(dev);
|
||||||
goto out;
|
if (ret < 0)
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
drv->remove(func);
|
drv->remove(func);
|
||||||
|
|
||||||
@ -178,10 +184,12 @@ static int sdio_bus_remove(struct device *dev)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* First, undo the increment made directly above */
|
/* 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() */
|
/* 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:
|
out:
|
||||||
return ret;
|
return ret;
|
||||||
@ -191,6 +199,8 @@ out:
|
|||||||
|
|
||||||
static int sdio_bus_pm_prepare(struct device *dev)
|
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
|
* Resume an SDIO device which was suspended at run time at this
|
||||||
* point, in order to allow standard SDIO suspend/resume paths
|
* 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
|
* since there is little point in failing system suspend if a
|
||||||
* device can't be resumed.
|
* 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;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -168,6 +168,7 @@ struct mmc_host {
|
|||||||
/* DDR mode at 1.8V */
|
/* DDR mode at 1.8V */
|
||||||
#define MMC_CAP_1_2V_DDR (1 << 12) /* can support */
|
#define MMC_CAP_1_2V_DDR (1 << 12) /* can support */
|
||||||
/* DDR mode at 1.2V */
|
/* 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 */
|
mmc_pm_flag_t pm_caps; /* supported pm features */
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user