diff options
Diffstat (limited to 'drivers/mmc/core')
-rw-r--r-- | drivers/mmc/core/core.c | 35 | ||||
-rw-r--r-- | drivers/mmc/core/core.h | 1 | ||||
-rw-r--r-- | drivers/mmc/core/host.c | 1 |
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 |