summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJin Qian <jqian@nvidia.com>2011-06-17 12:24:20 -0700
committerNiket Sirsi <nsirsi@nvidia.com>2011-06-22 11:44:53 -0700
commitf7820484ea6558798c652c420507520de2eeeeec (patch)
tree10a25f41a96c57440ee5744906b700ec958ab436
parent5f6c159f73520b187a58cdc1235e5e31d204ca1a (diff)
Revert "mmc: revert deferred resume"
This reverts commit 40c8a6e8551e81ad4d7647029e0d85f7b0a7e496. Original change broke LP1 suspend/resume Bug 840959,841139 Change-Id: I0fd50729cb81a5e56eca397428ee2c8b04f10e23 Reviewed-on: http://git-master/r/37353 Reviewed-by: Narendra Damahe <ndamahe@nvidia.com> Reviewed-by: Daniel Willemsen <dwillemsen@nvidia.com> Tested-by: Jin Qian <jqian@nvidia.com>
-rw-r--r--drivers/mmc/card/Kconfig9
-rw-r--r--drivers/mmc/card/block.c23
-rw-r--r--drivers/mmc/core/core.c47
-rw-r--r--include/linux/mmc/host.h16
4 files changed, 94 insertions, 1 deletions
diff --git a/drivers/mmc/card/Kconfig b/drivers/mmc/card/Kconfig
index 3f2a912659af..86948f90c3ff 100644
--- a/drivers/mmc/card/Kconfig
+++ b/drivers/mmc/card/Kconfig
@@ -32,6 +32,15 @@ config MMC_BLOCK_BOUNCE
If unsure, say Y here.
+config MMC_BLOCK_DEFERRED_RESUME
+ bool "Deferr MMC layer resume until I/O is requested"
+ depends on MMC_BLOCK
+ default n
+ help
+ Say Y here to enable deferred MMC resume until I/O
+ is requested. This will reduce overall resume latency and
+ save power when theres an SD card inserted but not being used.
+
config SDIO_UART
tristate "SDIO UART/GPS class support"
help
diff --git a/drivers/mmc/card/block.c b/drivers/mmc/card/block.c
index 7c136c29a11a..e6f74718e06f 100644
--- a/drivers/mmc/card/block.c
+++ b/drivers/mmc/card/block.c
@@ -575,8 +575,21 @@ static int mmc_blk_issue_rw_rq(struct mmc_queue *mq, struct request *req)
return 0;
}
+static int
+mmc_blk_set_blksize(struct mmc_blk_data *md, struct mmc_card *card);
+
static int mmc_blk_issue_rq(struct mmc_queue *mq, struct request *req)
{
+#ifdef CONFIG_MMC_BLOCK_DEFERRED_RESUME
+ struct mmc_blk_data *md = mq->data;
+ struct mmc_card *card = md->queue.card;
+
+ if (mmc_bus_needs_resume(card->host)) {
+ mmc_resume_bus(card->host);
+ mmc_blk_set_blksize(md, card);
+ }
+#endif
+
if (req->cmd_flags & REQ_DISCARD) {
if (req->cmd_flags & REQ_SECURE)
return mmc_blk_issue_secdiscard_rq(mq, req);
@@ -699,7 +712,7 @@ mmc_blk_set_blksize(struct mmc_blk_data *md, struct mmc_card *card)
if (err) {
printk(KERN_ERR "%s: unable to set block size to %d: %d\n",
- md->disk->disk_name, cmd.arg, err);
+ md->disk->disk_name, cmd.arg, err);
return -EINVAL;
}
@@ -743,6 +756,9 @@ static int mmc_blk_probe(struct mmc_card *card)
mmc_set_drvdata(card, md);
mmc_fixup_device(card, blk_fixups);
+#ifdef CONFIG_MMC_BLOCK_DEFERRED_RESUME
+ mmc_set_bus_resume_policy(card->host, 1);
+#endif
add_disk(md->disk);
return 0;
@@ -767,6 +783,9 @@ static void mmc_blk_remove(struct mmc_card *card)
mmc_blk_put(md);
}
mmc_set_drvdata(card, NULL);
+#ifdef CONFIG_MMC_BLOCK_DEFERRED_RESUME
+ mmc_set_bus_resume_policy(card->host, 0);
+#endif
}
#ifdef CONFIG_PM
@@ -785,7 +804,9 @@ static int mmc_blk_resume(struct mmc_card *card)
struct mmc_blk_data *md = mmc_get_drvdata(card);
if (md) {
+#ifndef CONFIG_MMC_BLOCK_DEFERRED_RESUME
mmc_blk_set_blksize(md, card);
+#endif
mmc_queue_resume(&md->queue);
}
return 0;
diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c
index c8384a8e6819..c3225d68d9a5 100644
--- a/drivers/mmc/core/core.c
+++ b/drivers/mmc/core/core.c
@@ -1040,6 +1040,36 @@ static inline void mmc_bus_put(struct mmc_host *host)
spin_unlock_irqrestore(&host->lock, flags);
}
+int mmc_resume_bus(struct mmc_host *host)
+{
+ unsigned long flags;
+
+ if (!mmc_bus_needs_resume(host))
+ return -EINVAL;
+
+ printk("%s: Starting deferred resume\n", mmc_hostname(host));
+ spin_lock_irqsave(&host->lock, flags);
+ host->bus_resume_flags &= ~MMC_BUSRESUME_NEEDS_RESUME;
+ host->rescan_disable = 0;
+ spin_unlock_irqrestore(&host->lock, flags);
+
+ 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_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.
@@ -1700,6 +1730,9 @@ int mmc_suspend_host(struct mmc_host *host)
{
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);
@@ -1742,6 +1775,12 @@ int mmc_resume_host(struct mmc_host *host)
int err = 0;
mmc_bus_get(host);
+ if (mmc_bus_manual_resume(host)) {
+ host->bus_resume_flags |= MMC_BUSRESUME_NEEDS_RESUME;
+ mmc_bus_put(host);
+ return 0;
+ }
+
if (host->bus_ops && !host->bus_dead) {
if (!(host->pm_flags & MMC_PM_KEEP_POWER)) {
mmc_power_up(host);
@@ -1779,6 +1818,10 @@ int mmc_pm_notify(struct notifier_block *notify_block,
case PM_SUSPEND_PREPARE:
spin_lock_irqsave(&host->lock, flags);
+ if (mmc_bus_needs_resume(host)) {
+ spin_unlock_irqrestore(&host->lock, flags);
+ break;
+ }
host->rescan_disable = 1;
spin_unlock_irqrestore(&host->lock, flags);
cancel_delayed_work_sync(&host->detect);
@@ -1802,6 +1845,10 @@ int mmc_pm_notify(struct notifier_block *notify_block,
spin_lock_irqsave(&host->lock, flags);
host->rescan_disable = 0;
+ if (mmc_bus_manual_resume(host)) {
+ spin_unlock_irqrestore(&host->lock, flags);
+ break;
+ }
spin_unlock_irqrestore(&host->lock, flags);
mmc_detect_change(host, 0);
diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h
index 7076851d9c2c..62ffcb05fb78 100644
--- a/include/linux/mmc/host.h
+++ b/include/linux/mmc/host.h
@@ -230,6 +230,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 tuning_status;
#define MMC_SD_TUNING_COMPLETED (1 << 0)
#define MMC_SD_SAMPLING_CLOCK_SELECT_SET (1 << 1)
@@ -283,6 +287,18 @@ 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)
+#define mmc_bus_manual_resume(host) ((host)->bus_resume_flags & MMC_BUSRESUME_MANUAL_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 *);
extern int mmc_resume_host(struct mmc_host *);