All of lore.kernel.org
 help / color / mirror / Atom feed
From: Adrian Hunter <adrian.hunter@nokia.com>
To: Pierre Ossman <pierre@ossman.eu>
Cc: Jarkko Lavinen <jarkko.lavinen@nokia.com>,
	Denis Karpov <ext-denis.2.karpov@nokia.com>,
	Adrian Hunter <adrian.hunter@nokia.com>,
	lkml <linux-kernel@vger.kernel.org>,
	linux-omap Mailing List <linux-omap@vger.kernel.org>
Subject: [PATCH 18/32] omap_hsmmc: support for deeper power saving states
Date: Fri, 10 Jul 2009 15:42:13 +0300	[thread overview]
Message-ID: <20090710124213.1262.38739.sendpatchset@ahunter-tower> (raw)
In-Reply-To: <20090710124004.1262.10422.sendpatchset@ahunter-tower>

>From a86c083ca740b4311daf3dc8e219506b63756896 Mon Sep 17 00:00:00 2001
From: Denis Karpov <ext-denis.2.karpov@nokia.com>
Date: Mon, 11 May 2009 14:41:30 +0300
Subject: [PATCH] omap_hsmmc: support for deeper power saving states

Support for multi-level dynamic power saving states in omap_hsmmc
(ENABLED->DISABLED->OFF).
In the "deepest" state (OFF) we switch off the voltage regulators.

Signed-off-by: Denis Karpov <ext-denis.2.karpov@nokia.com>
Signed-off-by: Adrian Hunter <adrian.hunter@nokia.com>
---
 arch/arm/mach-omap2/mmc-twl4030.c     |    3 +
 arch/arm/mach-omap2/mmc-twl4030.h     |    1 +
 arch/arm/plat-omap/include/mach/mmc.h |    3 +
 drivers/mmc/host/omap_hsmmc.c         |  245 +++++++++++++++++++++++++++++----
 4 files changed, 222 insertions(+), 30 deletions(-)

diff --git a/arch/arm/mach-omap2/mmc-twl4030.c b/arch/arm/mach-omap2/mmc-twl4030.c
index 67c22c6..3678f0e 100644
--- a/arch/arm/mach-omap2/mmc-twl4030.c
+++ b/arch/arm/mach-omap2/mmc-twl4030.c
@@ -412,6 +412,9 @@ void __init twl4030_mmc_init(struct twl4030_hsmmc_info *controllers)
 		if (c->nonremovable)
 			mmc->slots[0].nonremovable = 1;
 
+		if (c->power_saving)
+			mmc->slots[0].power_saving = 1;
+
 		/* NOTE:  MMC slots should have a Vcc regulator set up.
 		 * This may be from a TWL4030-family chip, another
 		 * controllable regulator, or a fixed supply.
diff --git a/arch/arm/mach-omap2/mmc-twl4030.h b/arch/arm/mach-omap2/mmc-twl4030.h
index 75b0c64..a47e685 100644
--- a/arch/arm/mach-omap2/mmc-twl4030.h
+++ b/arch/arm/mach-omap2/mmc-twl4030.h
@@ -13,6 +13,7 @@ struct twl4030_hsmmc_info {
 	bool	ext_clock;	/* use external pin for input clock */
 	bool	cover_only;	/* No card detect - just cover switch */
 	bool	nonremovable;	/* Nonremovable e.g. eMMC */
+	bool	power_saving;	/* Try to sleep or power off when possible */
 	int	gpio_cd;	/* or -EINVAL */
 	int	gpio_wp;	/* or -EINVAL */
 	char	*name;		/* or NULL for default */
diff --git a/arch/arm/plat-omap/include/mach/mmc.h b/arch/arm/plat-omap/include/mach/mmc.h
index bab486c..82f1e29 100644
--- a/arch/arm/plat-omap/include/mach/mmc.h
+++ b/arch/arm/plat-omap/include/mach/mmc.h
@@ -86,6 +86,9 @@ struct omap_mmc_platform_data {
 		/* nonremovable e.g. eMMC */
 		unsigned nonremovable:1;
 
+		/* Try to sleep or power off when possible */
+		unsigned power_saving:1;
+
 		int switch_pin;			/* gpio (card detect) */
 		int gpio_wp;			/* gpio (write protect) */
 
diff --git a/drivers/mmc/host/omap_hsmmc.c b/drivers/mmc/host/omap_hsmmc.c
index 7b56988..c69ab89 100644
--- a/drivers/mmc/host/omap_hsmmc.c
+++ b/drivers/mmc/host/omap_hsmmc.c
@@ -111,6 +111,10 @@
 #define OMAP_MMC_MASTER_CLOCK	96000000
 #define DRIVER_NAME		"mmci-omap-hs"
 
+/* Timeouts for entering power saving states on inactivity, msec */
+#define OMAP_MMC_DISABLED_TIMEOUT	100
+#define OMAP_MMC_OFF_TIMEOUT		1000
+
 /*
  * One controller can have multiple slots, like on some omap boards using
  * omap.c controller driver. Luckily this is not currently done on any known
@@ -155,6 +159,7 @@ struct mmc_omap_host {
 	int			dbclk_enabled;
 	int			response_busy;
 	int			context_loss;
+	int			dpm_state;
 
 	struct	omap_mmc_platform_data	*pdata;
 };
@@ -985,29 +990,6 @@ mmc_omap_prepare_data(struct mmc_omap_host *host, struct mmc_request *req)
 	return 0;
 }
 
-static int omap_mmc_enable(struct mmc_host *mmc)
-{
-	struct mmc_omap_host *host = mmc_priv(mmc);
-	int err;
-
-	err = clk_enable(host->fclk);
-	if (err)
-		return err;
-	dev_dbg(mmc_dev(host->mmc), "mmc_fclk: enabled\n");
-	omap_mmc_restore_ctx(host);
-	return 0;
-}
-
-static int omap_mmc_disable(struct mmc_host *mmc, int lazy)
-{
-	struct mmc_omap_host *host = mmc_priv(mmc);
-
-	omap_mmc_save_ctx(host);
-	clk_disable(host->fclk);
-	dev_dbg(mmc_dev(host->mmc), "mmc_fclk: disabled\n");
-	return 0;
-}
-
 /*
  * Request function. for read/write operation
  */
@@ -1061,6 +1043,8 @@ static void omap_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
 		host->power_mode = ios->power_mode;
 	}
 
+	/* FIXME: set registers based only on changes to ios */
+
 	con = OMAP_HSMMC_READ(host->base, CON);
 	switch (mmc->ios.bus_width) {
 	case MMC_BUS_WIDTH_8:
@@ -1133,7 +1117,10 @@ static void omap_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
 	else
 		OMAP_HSMMC_WRITE(host->base, CON, con & ~OD);
 
-	mmc_host_lazy_disable(host->mmc);
+	if (host->power_mode == MMC_POWER_OFF)
+		mmc_host_disable(host->mmc);
+	else
+		mmc_host_lazy_disable(host->mmc);
 }
 
 static int omap_hsmmc_get_cd(struct mmc_host *mmc)
@@ -1183,7 +1170,191 @@ static void omap_hsmmc_init(struct mmc_omap_host *host)
 	set_sd_bus_power(host);
 }
 
-static struct mmc_host_ops mmc_omap_ops = {
+/*
+ * Dynamic power saving handling, FSM:
+ *   ENABLED -> DISABLED -> OFF
+ *     ^___________|          |
+ *     |______________________|
+ *
+ * ENABLED:   mmc host is fully functional
+ * DISABLED:  fclk is off
+ * 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, OFF};
+
+/* Handler for [ENABLED -> DISABLED] transition */
+static int omap_mmc_enabled_to_disabled(struct mmc_omap_host *host)
+{
+	omap_mmc_save_ctx(host);
+	clk_disable(host->fclk);
+	host->dpm_state = DISABLED;
+
+	dev_dbg(mmc_dev(host->mmc), "ENABLED -> DISABLED\n");
+
+	if (host->power_mode == MMC_POWER_OFF)
+		return 0;
+
+	return msecs_to_jiffies(OMAP_MMC_OFF_TIMEOUT);
+}
+
+/* Handler for [DISABLED -> OFF] transition */
+static int omap_mmc_disabled_to_off(struct mmc_omap_host *host)
+{
+	int new_state;
+
+	dev_dbg(mmc_dev(host->mmc), "DISABLED -> OFF\n");
+
+	if (!mmc_try_claim_host(host->mmc))
+		return 0;
+
+	clk_enable(host->fclk);
+
+	omap_mmc_restore_ctx(host);
+
+	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
+		new_state = DISABLED;
+
+	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);
+
+	clk_disable(host->fclk);
+	clk_disable(host->iclk);
+	clk_disable(host->dbclk);
+
+	host->dpm_state = new_state;
+
+	mmc_release_host(host->mmc);
+
+	return 0;
+}
+
+/* Handler for [DISABLED -> ENABLED] transition */
+static int omap_mmc_disabled_to_enabled(struct mmc_omap_host *host)
+{
+	int err;
+
+	err = clk_enable(host->fclk);
+	if (err < 0)
+		return err;
+
+	omap_mmc_restore_ctx(host);
+
+	host->dpm_state = ENABLED;
+
+	dev_dbg(mmc_dev(host->mmc), "DISABLED -> ENABLED\n");
+
+	return 0;
+}
+
+/* Handler for [OFF -> ENABLED] transition */
+static int omap_mmc_off_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");
+
+	omap_mmc_restore_ctx(host);
+	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;
+}
+
+/*
+ * Bring MMC host to ENABLED from any other PM state.
+ */
+static int omap_mmc_enable(struct mmc_host *mmc)
+{
+	struct mmc_omap_host *host = mmc_priv(mmc);
+
+	switch (host->dpm_state) {
+	case DISABLED:
+		return omap_mmc_disabled_to_enabled(host);
+	case OFF:
+		return omap_mmc_off_to_enabled(host);
+	default:
+		dev_dbg(mmc_dev(host->mmc), "UNKNOWN state\n");
+		return -EINVAL;
+	}
+}
+
+/*
+ * Bring MMC host in PM state (one level deeper).
+ */
+static int omap_mmc_disable(struct mmc_host *mmc, int lazy)
+{
+	struct mmc_omap_host *host = mmc_priv(mmc);
+
+	switch (host->dpm_state) {
+	case ENABLED: {
+		int delay;
+
+		delay = omap_mmc_enabled_to_disabled(host);
+		if (lazy || delay < 0)
+			return delay;
+		return 0;
+	}
+	case DISABLED:
+		return omap_mmc_disabled_to_off(host);
+	default:
+		dev_dbg(mmc_dev(host->mmc), "UNKNOWN state\n");
+		return -EINVAL;
+	}
+}
+
+static int omap_mmc_enable_fclk(struct mmc_host *mmc)
+{
+	struct mmc_omap_host *host = mmc_priv(mmc);
+	int err;
+
+	err = clk_enable(host->fclk);
+	if (err)
+		return err;
+	dev_dbg(mmc_dev(host->mmc), "mmc_fclk: enabled\n");
+	omap_mmc_restore_ctx(host);
+	return 0;
+}
+
+static int omap_mmc_disable_fclk(struct mmc_host *mmc, int lazy)
+{
+	struct mmc_omap_host *host = mmc_priv(mmc);
+
+	omap_mmc_save_ctx(host);
+	clk_disable(host->fclk);
+	dev_dbg(mmc_dev(host->mmc), "mmc_fclk: disabled\n");
+	return 0;
+}
+
+static const struct mmc_host_ops mmc_omap_ops = {
+	.enable = omap_mmc_enable_fclk,
+	.disable = omap_mmc_disable_fclk,
+	.request = omap_mmc_request,
+	.set_ios = omap_mmc_set_ios,
+	.get_cd = omap_hsmmc_get_cd,
+	.get_ro = omap_hsmmc_get_ro,
+	/* NYET -- enable_sdio_irq */
+};
+
+static const struct mmc_host_ops mmc_omap_ps_ops = {
 	.enable = omap_mmc_enable,
 	.disable = omap_mmc_disable,
 	.request = omap_mmc_request,
@@ -1207,15 +1378,22 @@ static int mmc_regs_show(struct seq_file *s, void *data)
 
 	seq_printf(s, "mmc%d:\n"
 			" enabled:\t%d\n"
+			" dpm_state:\t%d\n"
 			" nesting_cnt:\t%d\n"
 			" ctx_loss:\t%d:%d\n"
 			"\nregs:\n",
-			mmc->index, mmc->enabled ? 1 : 0, mmc->nesting_cnt,
+			mmc->index, mmc->enabled ? 1 : 0,
+			host->dpm_state, mmc->nesting_cnt,
 			host->context_loss, context_loss);
 
+	if (host->suspended || host->dpm_state == OFF) {
+		seq_printf(s, "host suspended, can't read registers\n");
+		return 0;
+	}
+
 	if (clk_enable(host->fclk) != 0) {
 		seq_printf(s, "can't read the regs\n");
-		goto err;
+		return 0;
 	}
 
 	seq_printf(s, "SYSCONFIG:\t0x%08x\n",
@@ -1234,7 +1412,7 @@ static int mmc_regs_show(struct seq_file *s, void *data)
 			OMAP_HSMMC_READ(host->base, CAPA));
 
 	clk_disable(host->fclk);
-err:
+
 	return 0;
 }
 
@@ -1316,7 +1494,11 @@ static int __init omap_mmc_probe(struct platform_device *pdev)
 	platform_set_drvdata(pdev, host);
 	INIT_WORK(&host->mmc_carddetect_work, mmc_omap_detect);
 
-	mmc->ops	= &mmc_omap_ops;
+	if (pdata->slots[host->slot_id].power_saving)
+		mmc->ops	= &mmc_omap_ps_ops;
+	else
+		mmc->ops	= &mmc_omap_ops;
+
 	mmc->f_min	= 400000;
 	mmc->f_max	= 52000000;
 
@@ -1339,7 +1521,10 @@ static int __init omap_mmc_probe(struct platform_device *pdev)
 	omap_mmc_save_ctx(host);
 
 	mmc->caps |= MMC_CAP_DISABLE;
-	mmc_set_disable_delay(mmc, msecs_to_jiffies(100));
+	mmc_set_disable_delay(mmc, msecs_to_jiffies(OMAP_MMC_DISABLED_TIMEOUT));
+	/* we start off in DISABLED state */
+	host->dpm_state = DISABLED;
+
 	if (mmc_host_enable(host->mmc) != 0) {
 		clk_put(host->iclk);
 		clk_put(host->fclk);
-- 
1.5.6.3


  parent reply	other threads:[~2009-07-10 12:41 UTC|newest]

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

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=20090710124213.1262.38739.sendpatchset@ahunter-tower \
    --to=adrian.hunter@nokia.com \
    --cc=ext-denis.2.karpov@nokia.com \
    --cc=jarkko.lavinen@nokia.com \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-omap@vger.kernel.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.