mmc: core: Validate suspend prerequisites for SDIO at SUSPEND_PREPARE
authorUlf Hansson <ulf.hansson@linaro.org>
Mon, 10 Jun 2013 15:03:37 +0000 (17:03 +0200)
committerChris Ball <cjb@laptop.org>
Thu, 27 Jun 2013 16:39:15 +0000 (12:39 -0400)
This patch moves the validation for all the suspend prerequisites to be
done at SUSPEND_PREPARE notification. Previously in the SDIO case parts
of the validation was done from mmc_suspend_host.

This patch invents a new pre_suspend bus_ops callback and implements it
for SDIO. Returning an error code from it, will mean at SUSPEND_PREPARE
notification, the card will be removed before proceeding with the
suspend sequence.

Signed-off-by: Ulf Hansson <ulf.hansson@linaro.org>
Tested-by: Jaehoon Chung <jh80.chung@samsung.com>
Signed-off-by: Chris Ball <cjb@laptop.org>
drivers/mmc/core/core.c
drivers/mmc/core/core.h
drivers/mmc/core/sdio.c

index d2ee2829b313c6552dc47b5def1020f9622a4284..7a8a42d3a0394d776eeeea3ebe9fb69018cd8310 100644 (file)
@@ -2628,22 +2628,6 @@ int mmc_suspend_host(struct mmc_host *host)
        if (host->bus_ops && !host->bus_dead) {
                if (host->bus_ops->suspend)
                        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.  (Calling
-                        * bus_ops->remove() with a claimed host can
-                        * deadlock.)
-                        */
-                       host->bus_ops->remove(host);
-                       mmc_claim_host(host);
-                       mmc_detach_bus(host);
-                       mmc_power_off(host);
-                       mmc_release_host(host);
-                       host->pm_flags = 0;
-                       err = 0;
-               }
        }
        mmc_bus_put(host);
 
@@ -2706,6 +2690,7 @@ int mmc_pm_notify(struct notifier_block *notify_block,
        struct mmc_host *host = container_of(
                notify_block, struct mmc_host, pm_notify);
        unsigned long flags;
+       int err = 0;
 
        switch (mode) {
        case PM_HIBERNATION_PREPARE:
@@ -2715,7 +2700,13 @@ int mmc_pm_notify(struct notifier_block *notify_block,
                spin_unlock_irqrestore(&host->lock, flags);
                cancel_delayed_work_sync(&host->detect);
 
-               if (!host->bus_ops || host->bus_ops->suspend)
+               if (!host->bus_ops)
+                       break;
+
+               /* Validate prerequisites for suspend */
+               if (host->bus_ops->pre_suspend)
+                       err = host->bus_ops->pre_suspend(host);
+               if (!err && host->bus_ops->suspend)
                        break;
 
                /* Calling bus_ops->remove() with a claimed host can deadlock */
index 52a3650307afcdd64f0b48a21a5ca70138297ce4..79f37cfc373b80d14b0e9a68ca11ede7f0953fdb 100644 (file)
@@ -18,6 +18,7 @@
 struct mmc_bus_ops {
        void (*remove)(struct mmc_host *);
        void (*detect)(struct mmc_host *);
+       int (*pre_suspend)(struct mmc_host *);
        int (*suspend)(struct mmc_host *);
        int (*resume)(struct mmc_host *);
        int (*runtime_suspend)(struct mmc_host *);
index 1fbbd1bc55ec047cf5b317384d9a4f6312692342..be8cca8d30246ad60779ee2193a05ff95819aef1 100644 (file)
@@ -910,11 +910,11 @@ out:
 }
 
 /*
- * SDIO suspend.  We need to suspend all functions separately.
+ * SDIO pre_suspend.  We need to suspend all functions separately.
  * Therefore all registered functions must have drivers with suspend
  * and resume methods.  Failing that we simply remove the whole card.
  */
-static int mmc_sdio_suspend(struct mmc_host *host)
+static int mmc_sdio_pre_suspend(struct mmc_host *host)
 {
        int i, err = 0;
 
@@ -925,8 +925,26 @@ static int mmc_sdio_suspend(struct mmc_host *host)
                        if (!pmops || !pmops->suspend || !pmops->resume) {
                                /* force removal of entire card in that case */
                                err = -ENOSYS;
-                       } else
-                               err = pmops->suspend(&func->dev);
+                               break;
+                       }
+               }
+       }
+
+       return err;
+}
+
+/*
+ * SDIO suspend.  Suspend all functions separately.
+ */
+static int mmc_sdio_suspend(struct mmc_host *host)
+{
+       int i, err = 0;
+
+       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) {
+                       const struct dev_pm_ops *pmops = func->dev.driver->pm;
+                       err = pmops->suspend(&func->dev);
                        if (err)
                                break;
                }
@@ -1076,6 +1094,7 @@ static int mmc_sdio_runtime_resume(struct mmc_host *host)
 static const struct mmc_bus_ops mmc_sdio_ops = {
        .remove = mmc_sdio_remove,
        .detect = mmc_sdio_detect,
+       .pre_suspend = mmc_sdio_pre_suspend,
        .suspend = mmc_sdio_suspend,
        .resume = mmc_sdio_resume,
        .runtime_suspend = mmc_sdio_runtime_suspend,