All of lore.kernel.org
 help / color / mirror / Atom feed
From: Adrian Hunter <adrian.hunter@nokia.com>
To: Andrew Morton <akpm@linux-foundation.org>
Cc: Jarkko Lavinen <jarkko.lavinen@nokia.com>,
	Adrian Hunter <adrian.hunter@nokia.com>,
	Madhusudhan Chikkature <madhu.cr@ti.com>,
	linux-mmc Mailing List <linux-mmc@vger.kernel.org>,
	linux-omap Mailing List <linux-omap@vger.kernel.org>,
	Pierre Ossman <pierre@ossman.eu>,
	Matt Fleming <matt@console-pimps.org>
Subject: [PATCH V3 20/30] omap_hsmmc: add mmc card sleep and awake support
Date: Wed, 09 Sep 2009 14:59:05 +0300	[thread overview]
Message-ID: <20090909115905.12833.31035.sendpatchset@ahunter-laptop> (raw)
In-Reply-To: <20090909115633.12833.39619.sendpatchset@ahunter-laptop>

>From ea55481d3f16bcc2d7bda941972c722895afc7ee Mon Sep 17 00:00:00 2001
From: Jarkko Lavinen <jarkko.lavinen@nokia.com>
Date: Tue, 12 May 2009 19:46:14 +0300
Subject: [PATCH] omap_hsmmc: add mmc card sleep and awake support

After 1 second of inactivity, put card and/or regulator
to sleep.  After 8 seconds of inactivity, turn off the
power.

Signed-off-by: Jarkko Lavinen <jarkko.lavinen@nokia.com>
Signed-off-by: Adrian Hunter <adrian.hunter@nokia.com>
---
 drivers/mmc/host/omap_hsmmc.c |  162 ++++++++++++++++++++++-------------------
 1 files changed, 88 insertions(+), 74 deletions(-)

diff --git a/drivers/mmc/host/omap_hsmmc.c b/drivers/mmc/host/omap_hsmmc.c
index cf6023b..d6bf65b 100644
--- a/drivers/mmc/host/omap_hsmmc.c
+++ b/drivers/mmc/host/omap_hsmmc.c
@@ -27,6 +27,7 @@
 #include <linux/timer.h>
 #include <linux/clk.h>
 #include <linux/mmc/host.h>
+#include <linux/mmc/core.h>
 #include <linux/io.h>
 #include <linux/semaphore.h>
 #include <mach/dma.h>
@@ -113,7 +114,8 @@
 
 /* Timeouts for entering power saving states on inactivity, msec */
 #define OMAP_MMC_DISABLED_TIMEOUT	100
-#define OMAP_MMC_OFF_TIMEOUT		1000
+#define OMAP_MMC_SLEEP_TIMEOUT		1000
+#define OMAP_MMC_OFF_TIMEOUT		8000
 
 /*
  * One controller can have multiple slots, like on some omap boards using
@@ -1175,20 +1177,21 @@ static void omap_hsmmc_init(struct mmc_omap_host *host)
 
 /*
  * Dynamic power saving handling, FSM:
- *   ENABLED -> DISABLED -> OFF / REGSLEEP
- *     ^___________|          |
- *     |______________________|
+ *   ENABLED -> DISABLED -> CARDSLEEP / REGSLEEP -> OFF
+ *     ^___________|          |                      |
+ *     |______________________|______________________|
  *
  * ENABLED:   mmc host is fully functional
  * DISABLED:  fclk is off
- * OFF:       fclk is off,voltage regulator is off
- * REGSLEEP:  fclk is off,voltage regulator is asleep
+ * CARDSLEEP: fclk is off, card is asleep, voltage regulator is asleep
+ * REGSLEEP:  fclk is off, voltage regulator is asleep
+ * OFF:       fclk is off, voltage regulator is off
  *
  * Transition handlers return the timeout for the next state transition
  * or negative error.
  */
 
-enum {ENABLED = 0, DISABLED, REGSLEEP, OFF};
+enum {ENABLED = 0, DISABLED, CARDSLEEP, REGSLEEP, OFF};
 
 /* Handler for [ENABLED -> DISABLED] transition */
 static int omap_mmc_enabled_to_disabled(struct mmc_omap_host *host)
@@ -1202,46 +1205,72 @@ static int omap_mmc_enabled_to_disabled(struct mmc_omap_host *host)
 	if (host->power_mode == MMC_POWER_OFF)
 		return 0;
 
-	return msecs_to_jiffies(OMAP_MMC_OFF_TIMEOUT);
+	return msecs_to_jiffies(OMAP_MMC_SLEEP_TIMEOUT);
 }
 
-/* Handler for [DISABLED -> OFF] transition */
-static int omap_mmc_disabled_to_off(struct mmc_omap_host *host)
+/* Handler for [DISABLED -> REGSLEEP / CARDSLEEP] transition */
+static int omap_mmc_disabled_to_sleep(struct mmc_omap_host *host)
 {
-	int new_state;
-
-	dev_dbg(mmc_dev(host->mmc), "DISABLED -> OFF\n");
+	int err, new_state;
 
 	if (!mmc_try_claim_host(host->mmc))
 		return 0;
 
 	clk_enable(host->fclk);
-
 	omap_mmc_restore_ctx(host);
+	if (mmc_card_can_sleep(host->mmc)) {
+		err = mmc_card_sleep(host->mmc);
+		if (err < 0) {
+			clk_disable(host->fclk);
+			mmc_release_host(host->mmc);
+			return err;
+		}
+		new_state = CARDSLEEP;
+	} else
+		new_state = REGSLEEP;
+	if (mmc_slot(host).set_sleep)
+		mmc_slot(host).set_sleep(host->dev, host->slot_id, 1, 0,
+					 new_state == CARDSLEEP);
+	/* FIXME: turn off bus power and perhaps interrupts too */
+	clk_disable(host->fclk);
+	host->dpm_state = new_state;
+
+	mmc_release_host(host->mmc);
+
+	dev_dbg(mmc_dev(host->mmc), "DISABLED -> %s\n",
+		host->dpm_state == CARDSLEEP ? "CARDSLEEP" : "REGSLEEP");
 
 	if ((host->mmc->caps & MMC_CAP_NONREMOVABLE) ||
 	    mmc_slot(host).card_detect ||
 	    (mmc_slot(host).get_cover_state &&
-	     mmc_slot(host).get_cover_state(host->dev, host->slot_id))) {
-		mmc_power_save_host(host->mmc);
-		new_state = OFF;
-	} else {
-		if (mmc_slot(host).set_sleep)
-			mmc_slot(host).set_sleep(host->dev, host->slot_id,
-						 1, 0, 0);
-		new_state = REGSLEEP;
+	     mmc_slot(host).get_cover_state(host->dev, host->slot_id)))
+		return msecs_to_jiffies(OMAP_MMC_OFF_TIMEOUT);
+
+	return 0;
+}
+
+/* Handler for [REGSLEEP / CARDSLEEP -> OFF] transition */
+static int omap_mmc_sleep_to_off(struct mmc_omap_host *host)
+{
+	if (!mmc_try_claim_host(host->mmc))
+		return 0;
+
+	if (!((host->mmc->caps & MMC_CAP_NONREMOVABLE) ||
+	      mmc_slot(host).card_detect ||
+	      (mmc_slot(host).get_cover_state &&
+	       mmc_slot(host).get_cover_state(host->dev, host->slot_id)))) {
+		mmc_release_host(host->mmc);
+		return 0;
 	}
 
-	OMAP_HSMMC_WRITE(host->base, ISE, 0);
-	OMAP_HSMMC_WRITE(host->base, IE, 0);
-	OMAP_HSMMC_WRITE(host->base, HCTL,
-		 OMAP_HSMMC_READ(host->base, HCTL) & ~SDBP);
+	mmc_slot(host).set_power(host->dev, host->slot_id, 0, 0);
+	host->vdd = 0;
+	host->power_mode = MMC_POWER_OFF;
 
-	clk_disable(host->fclk);
-	clk_disable(host->iclk);
-	clk_disable(host->dbclk);
+	dev_dbg(mmc_dev(host->mmc), "%s -> OFF\n",
+		host->dpm_state == CARDSLEEP ? "CARDSLEEP" : "REGSLEEP");
 
-	host->dpm_state = new_state;
+	host->dpm_state = OFF;
 
 	mmc_release_host(host->mmc);
 
@@ -1266,62 +1295,43 @@ static int omap_mmc_disabled_to_enabled(struct mmc_omap_host *host)
 	return 0;
 }
 
-/* Handler for [OFF -> ENABLED] transition */
-static int omap_mmc_off_to_enabled(struct mmc_omap_host *host)
+/* Handler for [SLEEP -> ENABLED] transition */
+static int omap_mmc_sleep_to_enabled(struct mmc_omap_host *host)
 {
-	clk_enable(host->fclk);
-	clk_enable(host->iclk);
-
-	if (clk_enable(host->dbclk))
-		dev_dbg(mmc_dev(host->mmc),
-			"Enabling debounce clk failed\n");
+	if (!mmc_try_claim_host(host->mmc))
+		return 0;
 
+	clk_enable(host->fclk);
 	omap_mmc_restore_ctx(host);
-	omap_hsmmc_init(host);
-	mmc_power_restore_host(host->mmc);
+	if (mmc_slot(host).set_sleep)
+		mmc_slot(host).set_sleep(host->dev, host->slot_id, 0,
+			 host->vdd, host->dpm_state == CARDSLEEP);
+	if (mmc_card_can_sleep(host->mmc))
+		mmc_card_awake(host->mmc);
+
+	dev_dbg(mmc_dev(host->mmc), "%s -> ENABLED\n",
+		host->dpm_state == CARDSLEEP ? "CARDSLEEP" : "REGSLEEP");
 
 	host->dpm_state = ENABLED;
 
-	dev_dbg(mmc_dev(host->mmc), "OFF -> ENABLED\n");
+	mmc_release_host(host->mmc);
 
 	return 0;
 }
 
-/* Handler for [REGSLEEP -> ENABLED] transition */
-static int omap_mmc_regsleep_to_enabled(struct mmc_omap_host *host)
+/* Handler for [OFF -> ENABLED] transition */
+static int omap_mmc_off_to_enabled(struct mmc_omap_host *host)
 {
-	unsigned long timeout;
-
-	dev_dbg(mmc_dev(host->mmc), "REGSLEEP -> ENABLED\n");
-
 	clk_enable(host->fclk);
-	clk_enable(host->iclk);
-
-	if (clk_enable(host->dbclk))
-		dev_dbg(mmc_dev(host->mmc),
-			"Enabling debounce clk failed\n");
 
 	omap_mmc_restore_ctx(host);
-
-	/*
-	 * We turned off interrupts and bus power.  Interrupts
-	 * are turned on by 'mmc_omap_start_command()' so we
-	 * just need to turn on the bus power here.
-	 */
-	OMAP_HSMMC_WRITE(host->base, HCTL,
-			 OMAP_HSMMC_READ(host->base, HCTL) | SDBP);
-
-	timeout = jiffies + msecs_to_jiffies(MMC_TIMEOUT_MS);
-	while ((OMAP_HSMMC_READ(host->base, HCTL) & SDBP) != SDBP &&
-	       time_before(jiffies, timeout))
-		;
-
-	if (mmc_slot(host).set_sleep)
-		mmc_slot(host).set_sleep(host->dev, host->slot_id,
-					 0, host->vdd, 0);
+	omap_hsmmc_init(host);
+	mmc_power_restore_host(host->mmc);
 
 	host->dpm_state = ENABLED;
 
+	dev_dbg(mmc_dev(host->mmc), "OFF -> ENABLED\n");
+
 	return 0;
 }
 
@@ -1335,8 +1345,9 @@ static int omap_mmc_enable(struct mmc_host *mmc)
 	switch (host->dpm_state) {
 	case DISABLED:
 		return omap_mmc_disabled_to_enabled(host);
+	case CARDSLEEP:
 	case REGSLEEP:
-		return omap_mmc_regsleep_to_enabled(host);
+		return omap_mmc_sleep_to_enabled(host);
 	case OFF:
 		return omap_mmc_off_to_enabled(host);
 	default:
@@ -1362,7 +1373,10 @@ static int omap_mmc_disable(struct mmc_host *mmc, int lazy)
 		return 0;
 	}
 	case DISABLED:
-		return omap_mmc_disabled_to_off(host);
+		return omap_mmc_disabled_to_sleep(host);
+	case CARDSLEEP:
+	case REGSLEEP:
+		return omap_mmc_sleep_to_off(host);
 	default:
 		dev_dbg(mmc_dev(host->mmc), "UNKNOWN state\n");
 		return -EINVAL;
@@ -1434,8 +1448,7 @@ static int mmc_regs_show(struct seq_file *s, void *data)
 			host->dpm_state, mmc->nesting_cnt,
 			host->context_loss, context_loss);
 
-	if (host->suspended || host->dpm_state == OFF ||
-	    host->dpm_state == REGSLEEP) {
+	if (host->suspended || host->dpm_state == OFF) {
 		seq_printf(s, "host suspended, can't read registers\n");
 		return 0;
 	}
@@ -1610,7 +1623,8 @@ static int __init omap_mmc_probe(struct platform_device *pdev)
 	mmc->max_req_size = mmc->max_blk_size * mmc->max_blk_count;
 	mmc->max_seg_size = mmc->max_req_size;
 
-	mmc->caps |= MMC_CAP_MMC_HIGHSPEED | MMC_CAP_SD_HIGHSPEED;
+	mmc->caps |= MMC_CAP_MMC_HIGHSPEED | MMC_CAP_SD_HIGHSPEED |
+		     MMC_CAP_WAIT_WHILE_BUSY;
 
 	if (pdata->slots[host->slot_id].wires >= 8)
 		mmc->caps |= MMC_CAP_8_BIT_DATA;
-- 
1.5.6.3


  parent reply	other threads:[~2009-09-09 11:59 UTC|newest]

Thread overview: 33+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2009-09-09 11:56 [PATCH V3 0/30] mmc and omap_hsmmc patches Adrian Hunter
2009-09-09 11:56 ` [PATCH V3 1/30] mmc: add 'enable' and 'disable' methods to mmc host Adrian Hunter
2009-09-09 11:56 ` [PATCH V3 2/30] mmc: allow host claim / release nesting Adrian Hunter
2009-09-09 11:56 ` [PATCH V3 3/30] mmc: add MMC_CAP_NONREMOVABLE host capability Adrian Hunter
2009-09-09 11:57 ` [PATCH V3 4/30] mmc: add ability to save power by powering off cards Adrian Hunter
2009-09-09 11:57 ` [PATCH V3 5/30] mmc: add mmc card sleep and awake support Adrian Hunter
2009-09-09 11:57 ` [PATCH V3 6/30] mmc: power off once at removal Adrian Hunter
2009-09-09 11:57 ` [PATCH V3 7/30] mmc: check status after MMC SWITCH command Adrian Hunter
2009-09-09 11:57 ` [PATCH V3 8/30] omap_hsmmc: add debugfs entry (host registers) Adrian Hunter
2009-09-09 11:57 ` [PATCH V3 9/30] omap_hsmmc: make use of new enable/disable interface Adrian Hunter
2009-09-09 11:57 ` [PATCH V3 10/30] ARM: OMAP: mmc-twl4030: add context loss counter support Adrian Hunter
2009-09-09 11:57 ` [PATCH V3 11/30] omap_hsmmc: keep track of power mode Adrian Hunter
2009-09-09 11:58 ` [PATCH V3 12/30] omap_hsmmc: context save/restore support Adrian Hunter
2009-09-09 11:58 ` [PATCH V3 13/30] omap_hsmmc: set open drain bit correctly Adrian Hunter
2009-09-09 11:58 ` [PATCH V3 14/30] omap_hsmmc: ensure workqueues are empty before suspend Adrian Hunter
2009-09-09 11:58 ` [PATCH V3 15/30] omap_hsmmc: fix scatter-gather list sanity checking Adrian Hunter
2009-09-09 11:58 ` [PATCH V3 16/30] omap_hsmmc: make use of new MMC_CAP_NONREMOVABLE host capability Adrian Hunter
2009-09-09 11:58 ` [PATCH V3 17/30] omap_hsmmc: support for deeper power saving states Adrian Hunter
2009-09-09 11:58 ` [PATCH V3 18/30] ARM: OMAP: mmc-twl4030: add regulator sleep / wake function Adrian Hunter
2009-09-09 11:58 ` [PATCH V3 19/30] omap_hsmmc: put MMC regulator to sleep Adrian Hunter
2009-09-09 11:59 ` Adrian Hunter [this message]
2009-09-09 11:59 ` [PATCH V3 21/30] omap_hsmmc: fix NULL pointer dereference Adrian Hunter
2009-09-09 11:59 ` [PATCH V3 22/30] omap_hsmmc: cleanup macro usage Adrian Hunter
2009-09-09 11:59 ` [PATCH V3 23/30] omap_hsmmc: clear interrupt status after init sequence Adrian Hunter
2009-09-09 11:59 ` [PATCH V3 24/30] omap_hsmmc: cater for weird CMD6 behaviour Adrian Hunter
2009-09-09 11:59 ` [PATCH V3 25/30] omap_hsmmc: prevent races with irq handler Adrian Hunter
2009-09-09 11:59 ` [PATCH V3 26/30] omap_hsmmc: code refactoring Adrian Hunter
2009-09-09 11:59 ` [PATCH V3 27/30] omap_hsmmc: protect the card when the cover is open Adrian Hunter
2009-09-09 12:00 ` [PATCH V3 28/30] omap_hsmmc: ensure all clock enables and disables are paired Adrian Hunter
2009-09-10 22:02   ` Andrew Morton
2009-09-11  8:31     ` Adrian Hunter
2009-09-09 12:00 ` [PATCH V3 29/30] omap_hsmmc: set a large data timeout for commands with busy signal Adrian Hunter
2009-09-09 12:00 ` [PATCH V3 30/30] ARM: OMAP: RX51: set MMC capabilities and power-saving flag Adrian Hunter

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20090909115905.12833.31035.sendpatchset@ahunter-laptop \
    --to=adrian.hunter@nokia.com \
    --cc=akpm@linux-foundation.org \
    --cc=jarkko.lavinen@nokia.com \
    --cc=linux-mmc@vger.kernel.org \
    --cc=linux-omap@vger.kernel.org \
    --cc=madhu.cr@ti.com \
    --cc=matt@console-pimps.org \
    --cc=pierre@ossman.eu \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.