Commit 95cdfb72 authored by Nicolas Pitre's avatar Nicolas Pitre Committed by Linus Torvalds

mmc: propagate error codes back from bus drivers' suspend/resume methods

Especially for SDIO drivers which may have special conditions/errors to
report, it is a good thing to relay the returned error code back to upper
layers.

This also allows for the rationalization of the resume path where code to
"remove" a no-longer-existing or replaced card was duplicated into the
MMC, SD and SDIO bus drivers.

In the SDIO case, if a function suspend method returns an error, then all
previously suspended functions are resumed and the error returned.  An
exception is made for -ENOSYS which the core interprets as "we don't
support suspend so just kick the card out for suspend and return success".

When resuming SDIO cards, the core code only validates the manufacturer
and product IDs to make sure the same kind of card is still present before
invoking functions resume methods.  It's the function driver's
responsibility to perform further tests to confirm that the actual same
card is present (same MAC address, etc.) and return an error otherwise.
Signed-off-by: default avatarNicolas Pitre <nico@marvell.com>
Cc: <linux-mmc@vger.kernel.org>
Signed-off-by: default avatarAndrew Morton <akpm@linux-foundation.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@linux-foundation.org>
parent 17d33e14
...@@ -1236,6 +1236,8 @@ EXPORT_SYMBOL(mmc_card_can_sleep); ...@@ -1236,6 +1236,8 @@ EXPORT_SYMBOL(mmc_card_can_sleep);
*/ */
int mmc_suspend_host(struct mmc_host *host, pm_message_t state) int mmc_suspend_host(struct mmc_host *host, pm_message_t state)
{ {
int err = 0;
if (host->caps & MMC_CAP_DISABLE) if (host->caps & MMC_CAP_DISABLE)
cancel_delayed_work(&host->disable); cancel_delayed_work(&host->disable);
cancel_delayed_work(&host->detect); cancel_delayed_work(&host->detect);
...@@ -1244,21 +1246,26 @@ int mmc_suspend_host(struct mmc_host *host, pm_message_t state) ...@@ -1244,21 +1246,26 @@ int mmc_suspend_host(struct mmc_host *host, pm_message_t state)
mmc_bus_get(host); mmc_bus_get(host);
if (host->bus_ops && !host->bus_dead) { if (host->bus_ops && !host->bus_dead) {
if (host->bus_ops->suspend) if (host->bus_ops->suspend)
host->bus_ops->suspend(host); err = host->bus_ops->suspend(host);
if (!host->bus_ops->resume) { if (err == -ENOSYS || !host->bus_ops->resume) {
/*
* We simply "remove" the card in this case.
* It will be redetected on resume.
*/
if (host->bus_ops->remove) if (host->bus_ops->remove)
host->bus_ops->remove(host); host->bus_ops->remove(host);
mmc_claim_host(host); mmc_claim_host(host);
mmc_detach_bus(host); mmc_detach_bus(host);
mmc_release_host(host); mmc_release_host(host);
err = 0;
} }
} }
mmc_bus_put(host); mmc_bus_put(host);
if (!err)
mmc_power_off(host); mmc_power_off(host);
return 0; return err;
} }
EXPORT_SYMBOL(mmc_suspend_host); EXPORT_SYMBOL(mmc_suspend_host);
...@@ -1269,12 +1276,26 @@ EXPORT_SYMBOL(mmc_suspend_host); ...@@ -1269,12 +1276,26 @@ EXPORT_SYMBOL(mmc_suspend_host);
*/ */
int mmc_resume_host(struct mmc_host *host) int mmc_resume_host(struct mmc_host *host)
{ {
int err = 0;
mmc_bus_get(host); mmc_bus_get(host);
if (host->bus_ops && !host->bus_dead) { if (host->bus_ops && !host->bus_dead) {
mmc_power_up(host); mmc_power_up(host);
mmc_select_voltage(host, host->ocr); mmc_select_voltage(host, host->ocr);
BUG_ON(!host->bus_ops->resume); BUG_ON(!host->bus_ops->resume);
host->bus_ops->resume(host); err = host->bus_ops->resume(host);
if (err) {
printk(KERN_WARNING "%s: error %d during resume "
"(card was removed?)\n",
mmc_hostname(host), err);
if (host->bus_ops->remove)
host->bus_ops->remove(host);
mmc_claim_host(host);
mmc_detach_bus(host);
mmc_release_host(host);
/* no need to bother upper layers */
err = 0;
}
} }
mmc_bus_put(host); mmc_bus_put(host);
...@@ -1284,7 +1305,7 @@ int mmc_resume_host(struct mmc_host *host) ...@@ -1284,7 +1305,7 @@ int mmc_resume_host(struct mmc_host *host)
*/ */
mmc_detect_change(host, 1); mmc_detect_change(host, 1);
return 0; return err;
} }
EXPORT_SYMBOL(mmc_resume_host); EXPORT_SYMBOL(mmc_resume_host);
......
...@@ -20,8 +20,8 @@ struct mmc_bus_ops { ...@@ -20,8 +20,8 @@ struct mmc_bus_ops {
int (*sleep)(struct mmc_host *); int (*sleep)(struct mmc_host *);
void (*remove)(struct mmc_host *); void (*remove)(struct mmc_host *);
void (*detect)(struct mmc_host *); void (*detect)(struct mmc_host *);
void (*suspend)(struct mmc_host *); int (*suspend)(struct mmc_host *);
void (*resume)(struct mmc_host *); int (*resume)(struct mmc_host *);
void (*power_save)(struct mmc_host *); void (*power_save)(struct mmc_host *);
void (*power_restore)(struct mmc_host *); void (*power_restore)(struct mmc_host *);
}; };
......
...@@ -530,7 +530,7 @@ static void mmc_detect(struct mmc_host *host) ...@@ -530,7 +530,7 @@ static void mmc_detect(struct mmc_host *host)
/* /*
* Suspend callback from host. * Suspend callback from host.
*/ */
static void mmc_suspend(struct mmc_host *host) static int mmc_suspend(struct mmc_host *host)
{ {
BUG_ON(!host); BUG_ON(!host);
BUG_ON(!host->card); BUG_ON(!host->card);
...@@ -540,6 +540,8 @@ static void mmc_suspend(struct mmc_host *host) ...@@ -540,6 +540,8 @@ static void mmc_suspend(struct mmc_host *host)
mmc_deselect_cards(host); mmc_deselect_cards(host);
host->card->state &= ~MMC_STATE_HIGHSPEED; host->card->state &= ~MMC_STATE_HIGHSPEED;
mmc_release_host(host); mmc_release_host(host);
return 0;
} }
/* /*
...@@ -548,7 +550,7 @@ static void mmc_suspend(struct mmc_host *host) ...@@ -548,7 +550,7 @@ static void mmc_suspend(struct mmc_host *host)
* This function tries to determine if the same card is still present * This function tries to determine if the same card is still present
* and, if so, restore all state to it. * and, if so, restore all state to it.
*/ */
static void mmc_resume(struct mmc_host *host) static int mmc_resume(struct mmc_host *host)
{ {
int err; int err;
...@@ -559,14 +561,7 @@ static void mmc_resume(struct mmc_host *host) ...@@ -559,14 +561,7 @@ static void mmc_resume(struct mmc_host *host)
err = mmc_init_card(host, host->ocr, host->card); err = mmc_init_card(host, host->ocr, host->card);
mmc_release_host(host); mmc_release_host(host);
if (err) { return err;
mmc_remove(host);
mmc_claim_host(host);
mmc_detach_bus(host);
mmc_release_host(host);
}
} }
static void mmc_power_restore(struct mmc_host *host) static void mmc_power_restore(struct mmc_host *host)
......
...@@ -564,7 +564,7 @@ static void mmc_sd_detect(struct mmc_host *host) ...@@ -564,7 +564,7 @@ static void mmc_sd_detect(struct mmc_host *host)
/* /*
* Suspend callback from host. * Suspend callback from host.
*/ */
static void mmc_sd_suspend(struct mmc_host *host) static int mmc_sd_suspend(struct mmc_host *host)
{ {
BUG_ON(!host); BUG_ON(!host);
BUG_ON(!host->card); BUG_ON(!host->card);
...@@ -574,6 +574,8 @@ static void mmc_sd_suspend(struct mmc_host *host) ...@@ -574,6 +574,8 @@ static void mmc_sd_suspend(struct mmc_host *host)
mmc_deselect_cards(host); mmc_deselect_cards(host);
host->card->state &= ~MMC_STATE_HIGHSPEED; host->card->state &= ~MMC_STATE_HIGHSPEED;
mmc_release_host(host); mmc_release_host(host);
return 0;
} }
/* /*
...@@ -582,7 +584,7 @@ static void mmc_sd_suspend(struct mmc_host *host) ...@@ -582,7 +584,7 @@ static void mmc_sd_suspend(struct mmc_host *host)
* This function tries to determine if the same card is still present * This function tries to determine if the same card is still present
* and, if so, restore all state to it. * and, if so, restore all state to it.
*/ */
static void mmc_sd_resume(struct mmc_host *host) static int mmc_sd_resume(struct mmc_host *host)
{ {
int err; int err;
...@@ -593,14 +595,7 @@ static void mmc_sd_resume(struct mmc_host *host) ...@@ -593,14 +595,7 @@ static void mmc_sd_resume(struct mmc_host *host)
err = mmc_sd_init_card(host, host->ocr, host->card); err = mmc_sd_init_card(host, host->ocr, host->card);
mmc_release_host(host); mmc_release_host(host);
if (err) { return err;
mmc_sd_remove(host);
mmc_claim_host(host);
mmc_detach_bus(host);
mmc_release_host(host);
}
} }
static void mmc_sd_power_restore(struct mmc_host *host) static void mmc_sd_power_restore(struct mmc_host *host)
......
...@@ -302,6 +302,7 @@ static int mmc_sdio_init_card(struct mmc_host *host, u32 ocr, ...@@ -302,6 +302,7 @@ static int mmc_sdio_init_card(struct mmc_host *host, u32 ocr,
goto err; goto err;
} }
card = oldcard; card = oldcard;
return 0;
} }
/* /*
...@@ -399,62 +400,65 @@ static void mmc_sdio_detect(struct mmc_host *host) ...@@ -399,62 +400,65 @@ static void mmc_sdio_detect(struct mmc_host *host)
* Therefore all registered functions must have drivers with suspend * Therefore all registered functions must have drivers with suspend
* and resume methods. Failing that we simply remove the whole card. * and resume methods. Failing that we simply remove the whole card.
*/ */
static void mmc_sdio_suspend(struct mmc_host *host) static int mmc_sdio_suspend(struct mmc_host *host)
{ {
int i; int i, err = 0;
/* make sure all registered functions can suspend/resume */
for (i = 0; i < host->card->sdio_funcs; i++) { for (i = 0; i < host->card->sdio_funcs; i++) {
struct sdio_func *func = host->card->sdio_func[i]; struct sdio_func *func = host->card->sdio_func[i];
if (func && sdio_func_present(func) && func->dev.driver) { if (func && sdio_func_present(func) && func->dev.driver) {
const struct dev_pm_ops *pmops = func->dev.driver->pm; const struct dev_pm_ops *pmops = func->dev.driver->pm;
if (!pmops || !pmops->suspend || !pmops->resume) { if (!pmops || !pmops->suspend || !pmops->resume) {
/* just remove the entire card in that case */ /* force removal of entire card in that case */
mmc_sdio_remove(host); err = -ENOSYS;
mmc_claim_host(host); } else
mmc_detach_bus(host); err = pmops->suspend(&func->dev);
mmc_release_host(host); if (err)
return; break;
}
} }
} }
while (err && --i >= 0) {
/* now suspend them */
for (i = 0; i < host->card->sdio_funcs; i++) {
struct sdio_func *func = host->card->sdio_func[i]; struct sdio_func *func = host->card->sdio_func[i];
if (func && sdio_func_present(func) && func->dev.driver) { if (func && sdio_func_present(func) && func->dev.driver) {
const struct dev_pm_ops *pmops = func->dev.driver->pm; const struct dev_pm_ops *pmops = func->dev.driver->pm;
pmops->suspend(&func->dev); pmops->resume(&func->dev);
} }
} }
return err;
} }
static void mmc_sdio_resume(struct mmc_host *host) static int mmc_sdio_resume(struct mmc_host *host)
{ {
int i, err; int i, err;
BUG_ON(!host); BUG_ON(!host);
BUG_ON(!host->card); BUG_ON(!host->card);
/* Basic card reinitialization. */
mmc_claim_host(host); mmc_claim_host(host);
err = mmc_sdio_init_card(host, host->ocr, host->card); err = mmc_sdio_init_card(host, host->ocr, host->card);
mmc_release_host(host); mmc_release_host(host);
if (err) {
mmc_sdio_remove(host);
mmc_claim_host(host);
mmc_detach_bus(host);
mmc_release_host(host);
return;
}
/* resume all functions */ /*
for (i = 0; i < host->card->sdio_funcs; i++) { * If the card looked to be the same as before suspending, then
* we proceed to resume all card functions. If one of them returns
* an error then we simply return that error to the core and the
* card will be redetected as new. It is the responsibility of
* the function driver to perform further tests with the extra
* knowledge it has of the card to confirm the card is indeed the
* same as before suspending (same MAC address for network cards,
* etc.) and return an error otherwise.
*/
for (i = 0; !err && i < host->card->sdio_funcs; i++) {
struct sdio_func *func = host->card->sdio_func[i]; struct sdio_func *func = host->card->sdio_func[i];
if (func && sdio_func_present(func) && func->dev.driver) { if (func && sdio_func_present(func) && func->dev.driver) {
const struct dev_pm_ops *pmops = func->dev.driver->pm; const struct dev_pm_ops *pmops = func->dev.driver->pm;
pmops->resume(&func->dev); err = pmops->resume(&func->dev);
} }
} }
return err;
} }
static const struct mmc_bus_ops mmc_sdio_ops = { static const struct mmc_bus_ops mmc_sdio_ops = {
......
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