@@ -26,6 +26,7 @@
#include <linux/suspend.h>
#include <linux/fault-inject.h>
#include <linux/random.h>
+#include <linux/pm_wakeup.h>
#include <linux/mmc/card.h>
#include <linux/mmc/host.h>
@@ -1358,6 +1359,8 @@ void mmc_detect_change(struct mmc_host *host, unsigned long delay)
spin_unlock_irqrestore(&host->lock, flags);
#endif
host->detect_change = 1;
+
+ __pm_stay_awake(&host->detect_wakeup_source);
mmc_schedule_delayed_work(&host->detect, delay);
}
@@ -2016,6 +2019,7 @@ void mmc_rescan(struct work_struct *work)
struct mmc_host *host =
container_of(work, struct mmc_host, detect.work);
int i;
+ bool extend_wakeup = false;
if (host->rescan_disable)
return;
@@ -2060,16 +2064,25 @@ void mmc_rescan(struct work_struct *work)
mmc_claim_host(host);
for (i = 0; i < ARRAY_SIZE(freqs); i++) {
- if (!mmc_rescan_try_freq(host, max(freqs[i], host->f_min)))
+ if (!mmc_rescan_try_freq(host, max(freqs[i], host->f_min))) {
+ extend_wakeup = true;
break;
+ }
if (freqs[i] <= host->f_min)
break;
}
mmc_release_host(host);
out:
- if (host->caps & MMC_CAP_NEEDS_POLL)
+ if (extend_wakeup)
+ __pm_wakeup_event(&host->detect_wakeup_source, 500);
+ else
+ __pm_relax(&host->detect_wakeup_source);
+
+ if (host->caps & MMC_CAP_NEEDS_POLL) {
+ __pm_stay_awake(&host->detect_wakeup_source);
mmc_schedule_delayed_work(&host->detect, HZ);
+ }
}
void mmc_start_host(struct mmc_host *host)
@@ -2088,7 +2101,8 @@ void mmc_stop_host(struct mmc_host *host)
spin_unlock_irqrestore(&host->lock, flags);
#endif
- cancel_delayed_work_sync(&host->detect);
+ if (cancel_delayed_work_sync(&host->detect))
+ __pm_relax(&host->detect_wakeup_source);
mmc_flush_scheduled_work();
/* clear pm flags now and let card drivers set them as needed */
@@ -2284,7 +2298,8 @@ int mmc_suspend_host(struct mmc_host *host)
{
int err = 0;
- cancel_delayed_work(&host->detect);
+ if (cancel_delayed_work(&host->detect))
+ __pm_relax(&host->detect_wakeup_source);
mmc_flush_scheduled_work();
err = mmc_cache_ctrl(host, 0);
@@ -2387,7 +2402,8 @@ int mmc_pm_notify(struct notifier_block *notify_block,
host->rescan_disable = 1;
host->power_notify_type = MMC_HOST_PW_NOTIFY_SHORT;
spin_unlock_irqrestore(&host->lock, flags);
- cancel_delayed_work_sync(&host->detect);
+ if (cancel_delayed_work_sync(&host->detect))
+ __pm_relax(&host->detect_wakeup_source);
if (!host->bus_ops || host->bus_ops->suspend)
break;
@@ -329,6 +329,9 @@ struct mmc_host *mmc_alloc_host(int extra, struct device *dev)
spin_lock_init(&host->lock);
init_waitqueue_head(&host->wq);
+ wakeup_source_init(&host->detect_wakeup_source,
+ kasprintf(GFP_KERNEL, "%s_detect",
+ mmc_hostname(host)));
INIT_DELAYED_WORK(&host->detect, mmc_rescan);
#ifdef CONFIG_PM
host->pm_notify.notifier_call = mmc_pm_notify;
@@ -425,6 +428,7 @@ void mmc_free_host(struct mmc_host *host)
spin_lock(&mmc_host_lock);
idr_remove(&mmc_host_idr, host->index);
spin_unlock(&mmc_host_lock);
+ wakeup_source_trash(&host->detect_wakeup_source);
put_device(&host->class_dev);
}
@@ -14,6 +14,7 @@
#include <linux/sched.h>
#include <linux/device.h>
#include <linux/fault-inject.h>
+#include <linux/pm_wakeup.h>
#include <linux/mmc/core.h>
#include <linux/mmc/pm.h>
@@ -289,6 +290,7 @@ struct mmc_host {
int claim_cnt; /* "claim" nesting count */
struct delayed_work detect;
+ struct wakeup_source detect_wakeup_source;
int detect_change; /* card detect flag */
struct mmc_hotplug hotplug;
Hey San, Colin, I just wanted to send this your way for comment before possibly sending it to the mmc maintainer & lkml. Let me know if you have any objections. thanks -john This is a reworked implementation using wakeup_sources of two patches by San Mehat and Colin Cross that enabled wakelocks for the mmc core. CC: San Mehat <san@google.com> CC: Colin Cross <ccross@android.com> Signed-off-by: John Stultz <john.stultz@linaro.org> --- drivers/mmc/core/core.c | 26 +++++++++++++++++++++----- drivers/mmc/core/host.c | 4 ++++ include/linux/mmc/host.h | 2 ++ 3 files changed, 27 insertions(+), 5 deletions(-)