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".

Signed-off-by: Nicolas Pitre <[email protected]>
---
 drivers/mmc/core/core.c |   35 ++++++++++++++++++++++++++++-------
 drivers/mmc/core/core.h |    4 ++--
 drivers/mmc/core/mmc.c  |   15 +++++----------
 drivers/mmc/core/sd.c   |   15 +++++----------
 drivers/mmc/core/sdio.c |   41 +++++++++++++++++------------------------
 5 files changed, 57 insertions(+), 53 deletions(-)

diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c
index 2649117..2853f58 100644
--- a/drivers/mmc/core/core.c
+++ b/drivers/mmc/core/core.c
@@ -958,27 +958,34 @@ void mmc_stop_host(struct mmc_host *host)
  */
 int mmc_suspend_host(struct mmc_host *host, pm_message_t state)
 {
+       int err = 0;
+
        cancel_delayed_work(&host->detect);
        mmc_flush_scheduled_work();
 
        mmc_bus_get(host);
        if (host->bus_ops && !host->bus_dead) {
                if (host->bus_ops->suspend)
-                       host->bus_ops->suspend(host);
-               if (!host->bus_ops->resume) {
+                       err = host->bus_ops->suspend(host);
+               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)
                                host->bus_ops->remove(host);
-
                        mmc_claim_host(host);
                        mmc_detach_bus(host);
                        mmc_release_host(host);
+                       err = 0;
                }
        }
        mmc_bus_put(host);
 
-       mmc_power_off(host);
+       if (!err)
+               mmc_power_off(host);
 
-       return 0;
+       return err;
 }
 
 EXPORT_SYMBOL(mmc_suspend_host);
@@ -989,12 +996,26 @@ EXPORT_SYMBOL(mmc_suspend_host);
  */
 int mmc_resume_host(struct mmc_host *host)
 {
+       int err = 0;
+
        mmc_bus_get(host);
        if (host->bus_ops && !host->bus_dead) {
                mmc_power_up(host);
                mmc_select_voltage(host, host->ocr);
                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);
 
@@ -1004,7 +1025,7 @@ int mmc_resume_host(struct mmc_host *host)
         */
        mmc_detect_change(host, 1);
 
-       return 0;
+       return err;
 }
 
 EXPORT_SYMBOL(mmc_resume_host);
diff --git a/drivers/mmc/core/core.h b/drivers/mmc/core/core.h
index c819eff..767d212 100644
--- a/drivers/mmc/core/core.h
+++ b/drivers/mmc/core/core.h
@@ -18,8 +18,8 @@
 struct mmc_bus_ops {
        void (*remove)(struct mmc_host *);
        void (*detect)(struct mmc_host *);
-       void (*suspend)(struct mmc_host *);
-       void (*resume)(struct mmc_host *);
+       int (*suspend)(struct mmc_host *);
+       int (*resume)(struct mmc_host *);
 };
 
 void mmc_attach_bus(struct mmc_host *host, const struct mmc_bus_ops *ops);
diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c
index 06084db..188ae06 100644
--- a/drivers/mmc/core/mmc.c
+++ b/drivers/mmc/core/mmc.c
@@ -512,7 +512,7 @@ static void mmc_detect(struct mmc_host *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->card);
@@ -522,6 +522,8 @@ static void mmc_suspend(struct mmc_host *host)
                mmc_deselect_cards(host);
        host->card->state &= ~MMC_STATE_HIGHSPEED;
        mmc_release_host(host);
+
+       return 0;
 }
 
 /*
@@ -530,7 +532,7 @@ static void mmc_suspend(struct mmc_host *host)
  * This function tries to determine if the same card is still present
  * 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;
 
@@ -541,14 +543,7 @@ static void mmc_resume(struct mmc_host *host)
        err = mmc_init_card(host, host->ocr, host->card);
        mmc_release_host(host);
 
-       if (err) {
-               mmc_remove(host);
-
-               mmc_claim_host(host);
-               mmc_detach_bus(host);
-               mmc_release_host(host);
-       }
-
+       return err;
 }
 
 #else
diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c
index cd81c39..4539a46 100644
--- a/drivers/mmc/core/sd.c
+++ b/drivers/mmc/core/sd.c
@@ -566,7 +566,7 @@ static void mmc_sd_detect(struct mmc_host *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->card);
@@ -576,6 +576,8 @@ static void mmc_sd_suspend(struct mmc_host *host)
                mmc_deselect_cards(host);
        host->card->state &= ~MMC_STATE_HIGHSPEED;
        mmc_release_host(host);
+
+       return 0;
 }
 
 /*
@@ -584,7 +586,7 @@ static void mmc_sd_suspend(struct mmc_host *host)
  * This function tries to determine if the same card is still present
  * 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;
 
@@ -595,14 +597,7 @@ static void mmc_sd_resume(struct mmc_host *host)
        err = mmc_sd_init_card(host, host->ocr, host->card);
        mmc_release_host(host);
 
-       if (err) {
-               mmc_sd_remove(host);
-
-               mmc_claim_host(host);
-               mmc_detach_bus(host);
-               mmc_release_host(host);
-       }
-
+       return err;
 }
 
 #else
diff --git a/drivers/mmc/core/sdio.c b/drivers/mmc/core/sdio.c
index 4c21b74..d592423 100644
--- a/drivers/mmc/core/sdio.c
+++ b/drivers/mmc/core/sdio.c
@@ -376,37 +376,35 @@ static void mmc_sdio_detect(struct mmc_host *host)
  * Therefore all registered functions must have drivers with suspend
  * 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++) {
                struct sdio_func *func = host->card->sdio_func[i];
                if (func && sdio_func_present(func) && func->dev.driver) {
                        struct dev_pm_ops *pmops = func->dev.driver->pm;
                        if (!pmops || !pmops->suspend || !pmops->resume) {
-                               /* just remove the entire card in that case */
-                               mmc_sdio_remove(host);
-                               mmc_claim_host(host);
-                               mmc_detach_bus(host);
-                               mmc_release_host(host);
-                               return;
-                       }
+                               /* force removal of entire card in that case */
+                               err = -ENOSYS;
+                       } else
+                               err = pmops->suspend(&func->dev);
+                       if (err)
+                               break;
                }
        }
-
-       /* now suspend them */
-       for (i = 0; i < host->card->sdio_funcs; i++) {
+       while (err && --i >= 0) {
                struct sdio_func *func = host->card->sdio_func[i];
                if (func && sdio_func_present(func) && func->dev.driver) {
                        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;
 
@@ -416,22 +414,17 @@ static void mmc_sdio_resume(struct mmc_host *host)
        mmc_claim_host(host);
        err = mmc_sdio_init_card(host, host->ocr, host->card);
        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++) {
+       for (i = 0; !err && i < host->card->sdio_funcs; i++) {
                struct sdio_func *func = host->card->sdio_func[i];
                if (func && sdio_func_present(func) && func->dev.driver) {
                        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 = {
-- 
1.6.4.1.315.g5106d.dirty

--
To unsubscribe from this list: send the line "unsubscribe linux-mmc" in
the body of a message to [email protected]
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to