Commit ed919b01 authored by Ohad Ben-Cohen's avatar Ohad Ben-Cohen Committed by Chris Ball

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: default avatarDaniel Drake <dsd@laptop.org>
Reported-and-tested-by: default avatarArnd Hannemann <arnd@arndnet.de>
Signed-off-by: default avatarOhad Ben-Cohen <ohad@wizery.com>
Signed-off-by: default avatarChris Ball <cjb@laptop.org>
parent 4d0812c3
...@@ -547,9 +547,11 @@ static void mmc_sdio_detect(struct mmc_host *host) ...@@ -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) ...@@ -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) ...@@ -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) ...@@ -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 @@ ...@@ -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) ...@@ -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) ...@@ -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) ...@@ -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) ...@@ -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 @@ static int sdio_bus_remove(struct device *dev) ...@@ -191,6 +199,8 @@ static int sdio_bus_remove(struct device *dev)
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) ...@@ -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 { ...@@ -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 */
......
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