mmc: core: Add deferred bus resume policy.
authorSan Mehat <san@google.com>
Thu, 30 Jul 2009 14:55:28 +0000 (07:55 -0700)
committerArve Hjønnevåg <arve@android.com>
Mon, 8 Feb 2010 23:08:29 +0000 (15:08 -0800)
A card driver can now specify that the underlying bus should *not*
auto-resume with the rest of the system. This is useful for reducing resume
latency as well as saving power when the card driver is not using the
bus. In the future, we'll add support for manual suspend

Signed-off-by: San Mehat <san@google.com>
drivers/mmc/core/core.c
include/linux/mmc/host.h

index 08d08e4699e7380524f5001834d965f2f0d0297d..80994cf10a36983334c59d0db251a10e31f9ce85 100644 (file)
@@ -964,6 +964,30 @@ static inline void mmc_bus_put(struct mmc_host *host)
        spin_unlock_irqrestore(&host->lock, flags);
 }
 
+int mmc_resume_bus(struct mmc_host *host)
+{
+       if (!mmc_bus_needs_resume(host))
+               return -EINVAL;
+
+       printk("%s: Starting deferred resume\n", mmc_hostname(host));
+       host->bus_resume_flags &= ~MMC_BUSRESUME_NEEDS_RESUME;
+       mmc_bus_get(host);
+       if (host->bus_ops && !host->bus_dead) {
+               mmc_power_up(host);
+               BUG_ON(!host->bus_ops->resume);
+               host->bus_ops->resume(host);
+       }
+
+       if (host->bus_ops->detect && !host->bus_dead)
+               host->bus_ops->detect(host);
+
+       mmc_bus_put(host);
+       printk("%s: Deferred resume completed\n", mmc_hostname(host));
+       return 0;
+}
+
+EXPORT_SYMBOL(mmc_resume_bus);
+
 /*
  * Assign a mmc bus handler to a host. Only one bus handler may control a
  * host at any given time.
@@ -1250,6 +1274,9 @@ int mmc_suspend_host(struct mmc_host *host, pm_message_t state)
 {
        int err = 0;
 
+       if (mmc_bus_needs_resume(host))
+               return 0;
+
        if (host->caps & MMC_CAP_DISABLE)
                cancel_delayed_work(&host->disable);
        cancel_delayed_work(&host->detect);
@@ -1291,6 +1318,12 @@ int mmc_resume_host(struct mmc_host *host)
        int err = 0;
 
        mmc_bus_get(host);
+       if (host->bus_resume_flags & MMC_BUSRESUME_MANUAL_RESUME) {
+               host->bus_resume_flags |= MMC_BUSRESUME_NEEDS_RESUME;
+               mmc_bus_put(host);
+               return 0;
+       }
+
        if (host->bus_ops && !host->bus_dead) {
                mmc_power_up(host);
                mmc_select_voltage(host, host->ocr);
index f59f2b268c0f26644ed6a23f182401c3c35d5b6f..c3b88ff5654f47b6a368b472c5b95502d7f253ff 100644 (file)
@@ -193,6 +193,10 @@ struct mmc_host {
        const struct mmc_bus_ops *bus_ops;      /* current bus driver */
        unsigned int            bus_refs;       /* reference counter */
 
+       unsigned int            bus_resume_flags;
+#define MMC_BUSRESUME_MANUAL_RESUME    (1 << 0)
+#define MMC_BUSRESUME_NEEDS_RESUME     (1 << 1)
+
        unsigned int            sdio_irqs;
        struct task_struct      *sdio_irq_thread;
        atomic_t                sdio_irq_thread_abort;
@@ -238,6 +242,17 @@ static inline void *mmc_priv(struct mmc_host *host)
 #define mmc_dev(x)     ((x)->parent)
 #define mmc_classdev(x)        (&(x)->class_dev)
 #define mmc_hostname(x)        (dev_name(&(x)->class_dev))
+#define mmc_bus_needs_resume(host) ((host)->bus_resume_flags & MMC_BUSRESUME_NEEDS_RESUME)
+
+static inline void mmc_set_bus_resume_policy(struct mmc_host *host, int manual)
+{
+       if (manual)
+               host->bus_resume_flags |= MMC_BUSRESUME_MANUAL_RESUME;
+       else
+               host->bus_resume_flags &= ~MMC_BUSRESUME_MANUAL_RESUME;
+}
+
+extern int mmc_resume_bus(struct mmc_host *host);
 
 extern int mmc_suspend_host(struct mmc_host *, pm_message_t);
 extern int mmc_resume_host(struct mmc_host *);