summaryrefslogtreecommitdiff
path: root/drivers/mmc/core
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/mmc/core')
-rw-r--r--drivers/mmc/core/core.c35
-rw-r--r--drivers/mmc/core/core.h1
-rw-r--r--drivers/mmc/core/host.c1
3 files changed, 36 insertions, 1 deletions
diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c
index ba821fe70bc..9cce415f1ff 100644
--- a/drivers/mmc/core/core.c
+++ b/drivers/mmc/core/core.c
@@ -2010,7 +2010,7 @@ void mmc_rescan(struct work_struct *work)
container_of(work, struct mmc_host, detect.work);
int i;
- if (host->rescan_disable)
+ if (host->rescan_disable || mmc_host_needs_resume(host))
return;
mmc_bus_get(host);
@@ -2273,8 +2273,13 @@ int mmc_suspend_host(struct mmc_host *host)
int err = 0;
cancel_delayed_work(&host->detect);
+ cancel_delayed_work_sync(&host->resume);
mmc_flush_scheduled_work();
+ /* Skip suspend, if deferred resume were scheduled but not completed. */
+ if (mmc_host_needs_resume(host))
+ return 0;
+
err = mmc_cache_ctrl(host, 0);
if (err)
goto out;
@@ -2300,6 +2305,10 @@ int mmc_suspend_host(struct mmc_host *host)
mmc_release_host(host);
host->pm_flags = 0;
err = 0;
+ } else if (mmc_card_mmc(host->card) ||
+ mmc_card_sd(host->card)) {
+ host->pm_state |= MMC_HOST_DEFERRED_RESUME |
+ MMC_HOST_NEEDS_RESUME;
}
}
mmc_bus_put(host);
@@ -2321,6 +2330,12 @@ int mmc_resume_host(struct mmc_host *host)
{
int err = 0;
+ if (mmc_host_deferred_resume(host)) {
+ mmc_schedule_delayed_work(&host->resume,
+ msecs_to_jiffies(3000));
+ return 0;
+ }
+
mmc_bus_get(host);
if (host->bus_ops && !host->bus_dead) {
if (!mmc_card_keep_power(host)) {
@@ -2355,6 +2370,24 @@ int mmc_resume_host(struct mmc_host *host)
}
EXPORT_SYMBOL(mmc_resume_host);
+void mmc_resume_work(struct work_struct *work)
+{
+ struct mmc_host *host =
+ container_of(work, struct mmc_host, resume.work);
+
+ host->pm_state &= ~MMC_HOST_DEFERRED_RESUME;
+ mmc_resume_host(host);
+ host->pm_state &= ~MMC_HOST_NEEDS_RESUME;
+
+ mmc_detect_change(host, 0);
+}
+
+void mmc_resume_host_sync(struct mmc_host *host)
+{
+ flush_delayed_work_sync(&host->resume);
+}
+EXPORT_SYMBOL(mmc_resume_host_sync);
+
/* Do the card removal on suspend if card is assumed removeable
* Do that in pm notifier while userspace isn't yet frozen, so we will be able
to sync the card.
diff --git a/drivers/mmc/core/core.h b/drivers/mmc/core/core.h
index 3bdafbca354..5796d2d85f4 100644
--- a/drivers/mmc/core/core.h
+++ b/drivers/mmc/core/core.h
@@ -59,6 +59,7 @@ static inline void mmc_delay(unsigned int ms)
void mmc_rescan(struct work_struct *work);
void mmc_start_host(struct mmc_host *host);
void mmc_stop_host(struct mmc_host *host);
+void mmc_resume_work(struct work_struct *work);
int _mmc_detect_card_removed(struct mmc_host *host);
diff --git a/drivers/mmc/core/host.c b/drivers/mmc/core/host.c
index 91c84c7a182..d87d1152ea2 100644
--- a/drivers/mmc/core/host.c
+++ b/drivers/mmc/core/host.c
@@ -330,6 +330,7 @@ struct mmc_host *mmc_alloc_host(int extra, struct device *dev)
spin_lock_init(&host->lock);
init_waitqueue_head(&host->wq);
INIT_DELAYED_WORK(&host->detect, mmc_rescan);
+ INIT_DELAYED_WORK(&host->resume, mmc_resume_work);
#ifdef CONFIG_PM
host->pm_notify.notifier_call = mmc_pm_notify;
#endif