diff options
author | Ulf Hansson <ulf.hansson@linaro.org> | 2013-06-10 11:03:37 -0400 |
---|---|---|
committer | Chris Ball <cjb@laptop.org> | 2013-06-27 12:39:15 -0400 |
commit | 810caddba42a54fe5db4e2664757a9a334ba359c (patch) | |
tree | 12bd0ddf6a95e42bbc5f0438337b5492710760d6 /drivers/mmc/core | |
parent | 58a8a4a1a5da4694a3a069de5e0a8c15995e7b5f (diff) |
mmc: core: Validate suspend prerequisites for SDIO at SUSPEND_PREPARE
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>
Diffstat (limited to 'drivers/mmc/core')
-rw-r--r-- | drivers/mmc/core/core.c | 25 | ||||
-rw-r--r-- | drivers/mmc/core/core.h | 1 | ||||
-rw-r--r-- | drivers/mmc/core/sdio.c | 27 |
3 files changed, 32 insertions, 21 deletions
diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c index d2ee2829b313..7a8a42d3a039 100644 --- a/drivers/mmc/core/core.c +++ b/drivers/mmc/core/core.c | |||
@@ -2628,22 +2628,6 @@ int mmc_suspend_host(struct mmc_host *host) | |||
2628 | if (host->bus_ops && !host->bus_dead) { | 2628 | if (host->bus_ops && !host->bus_dead) { |
2629 | if (host->bus_ops->suspend) | 2629 | if (host->bus_ops->suspend) |
2630 | err = host->bus_ops->suspend(host); | 2630 | err = host->bus_ops->suspend(host); |
2631 | |||
2632 | if (err == -ENOSYS || !host->bus_ops->resume) { | ||
2633 | /* | ||
2634 | * We simply "remove" the card in this case. | ||
2635 | * It will be redetected on resume. (Calling | ||
2636 | * bus_ops->remove() with a claimed host can | ||
2637 | * deadlock.) | ||
2638 | */ | ||
2639 | host->bus_ops->remove(host); | ||
2640 | mmc_claim_host(host); | ||
2641 | mmc_detach_bus(host); | ||
2642 | mmc_power_off(host); | ||
2643 | mmc_release_host(host); | ||
2644 | host->pm_flags = 0; | ||
2645 | err = 0; | ||
2646 | } | ||
2647 | } | 2631 | } |
2648 | mmc_bus_put(host); | 2632 | mmc_bus_put(host); |
2649 | 2633 | ||
@@ -2706,6 +2690,7 @@ int mmc_pm_notify(struct notifier_block *notify_block, | |||
2706 | struct mmc_host *host = container_of( | 2690 | struct mmc_host *host = container_of( |
2707 | notify_block, struct mmc_host, pm_notify); | 2691 | notify_block, struct mmc_host, pm_notify); |
2708 | unsigned long flags; | 2692 | unsigned long flags; |
2693 | int err = 0; | ||
2709 | 2694 | ||
2710 | switch (mode) { | 2695 | switch (mode) { |
2711 | case PM_HIBERNATION_PREPARE: | 2696 | case PM_HIBERNATION_PREPARE: |
@@ -2715,7 +2700,13 @@ int mmc_pm_notify(struct notifier_block *notify_block, | |||
2715 | spin_unlock_irqrestore(&host->lock, flags); | 2700 | spin_unlock_irqrestore(&host->lock, flags); |
2716 | cancel_delayed_work_sync(&host->detect); | 2701 | cancel_delayed_work_sync(&host->detect); |
2717 | 2702 | ||
2718 | if (!host->bus_ops || host->bus_ops->suspend) | 2703 | if (!host->bus_ops) |
2704 | break; | ||
2705 | |||
2706 | /* Validate prerequisites for suspend */ | ||
2707 | if (host->bus_ops->pre_suspend) | ||
2708 | err = host->bus_ops->pre_suspend(host); | ||
2709 | if (!err && host->bus_ops->suspend) | ||
2719 | break; | 2710 | break; |
2720 | 2711 | ||
2721 | /* Calling bus_ops->remove() with a claimed host can deadlock */ | 2712 | /* Calling bus_ops->remove() with a claimed host can deadlock */ |
diff --git a/drivers/mmc/core/core.h b/drivers/mmc/core/core.h index 52a3650307af..79f37cfc373b 100644 --- a/drivers/mmc/core/core.h +++ b/drivers/mmc/core/core.h | |||
@@ -18,6 +18,7 @@ | |||
18 | struct mmc_bus_ops { | 18 | struct mmc_bus_ops { |
19 | void (*remove)(struct mmc_host *); | 19 | void (*remove)(struct mmc_host *); |
20 | void (*detect)(struct mmc_host *); | 20 | void (*detect)(struct mmc_host *); |
21 | int (*pre_suspend)(struct mmc_host *); | ||
21 | int (*suspend)(struct mmc_host *); | 22 | int (*suspend)(struct mmc_host *); |
22 | int (*resume)(struct mmc_host *); | 23 | int (*resume)(struct mmc_host *); |
23 | int (*runtime_suspend)(struct mmc_host *); | 24 | int (*runtime_suspend)(struct mmc_host *); |
diff --git a/drivers/mmc/core/sdio.c b/drivers/mmc/core/sdio.c index 1fbbd1bc55ec..be8cca8d3024 100644 --- a/drivers/mmc/core/sdio.c +++ b/drivers/mmc/core/sdio.c | |||
@@ -910,11 +910,11 @@ out: | |||
910 | } | 910 | } |
911 | 911 | ||
912 | /* | 912 | /* |
913 | * SDIO suspend. We need to suspend all functions separately. | 913 | * SDIO pre_suspend. We need to suspend all functions separately. |
914 | * Therefore all registered functions must have drivers with suspend | 914 | * Therefore all registered functions must have drivers with suspend |
915 | * and resume methods. Failing that we simply remove the whole card. | 915 | * and resume methods. Failing that we simply remove the whole card. |
916 | */ | 916 | */ |
917 | static int mmc_sdio_suspend(struct mmc_host *host) | 917 | static int mmc_sdio_pre_suspend(struct mmc_host *host) |
918 | { | 918 | { |
919 | int i, err = 0; | 919 | int i, err = 0; |
920 | 920 | ||
@@ -925,8 +925,26 @@ static int mmc_sdio_suspend(struct mmc_host *host) | |||
925 | if (!pmops || !pmops->suspend || !pmops->resume) { | 925 | if (!pmops || !pmops->suspend || !pmops->resume) { |
926 | /* force removal of entire card in that case */ | 926 | /* force removal of entire card in that case */ |
927 | err = -ENOSYS; | 927 | err = -ENOSYS; |
928 | } else | 928 | break; |
929 | err = pmops->suspend(&func->dev); | 929 | } |
930 | } | ||
931 | } | ||
932 | |||
933 | return err; | ||
934 | } | ||
935 | |||
936 | /* | ||
937 | * SDIO suspend. Suspend all functions separately. | ||
938 | */ | ||
939 | static int mmc_sdio_suspend(struct mmc_host *host) | ||
940 | { | ||
941 | int i, err = 0; | ||
942 | |||
943 | for (i = 0; i < host->card->sdio_funcs; i++) { | ||
944 | struct sdio_func *func = host->card->sdio_func[i]; | ||
945 | if (func && sdio_func_present(func) && func->dev.driver) { | ||
946 | const struct dev_pm_ops *pmops = func->dev.driver->pm; | ||
947 | err = pmops->suspend(&func->dev); | ||
930 | if (err) | 948 | if (err) |
931 | break; | 949 | break; |
932 | } | 950 | } |
@@ -1076,6 +1094,7 @@ static int mmc_sdio_runtime_resume(struct mmc_host *host) | |||
1076 | static const struct mmc_bus_ops mmc_sdio_ops = { | 1094 | static const struct mmc_bus_ops mmc_sdio_ops = { |
1077 | .remove = mmc_sdio_remove, | 1095 | .remove = mmc_sdio_remove, |
1078 | .detect = mmc_sdio_detect, | 1096 | .detect = mmc_sdio_detect, |
1097 | .pre_suspend = mmc_sdio_pre_suspend, | ||
1079 | .suspend = mmc_sdio_suspend, | 1098 | .suspend = mmc_sdio_suspend, |
1080 | .resume = mmc_sdio_resume, | 1099 | .resume = mmc_sdio_resume, |
1081 | .runtime_suspend = mmc_sdio_runtime_suspend, | 1100 | .runtime_suspend = mmc_sdio_runtime_suspend, |