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>,
	linux-omap Mailing List <linux-omap@vger.kernel.org>,
	lkml <linux-kernel@vger.kernel.org>
Subject: [PATCH 5/32] mmc: add mmc card sleep and awake support
Date: Fri, 10 Jul 2009 15:40:41 +0300	[thread overview]
Message-ID: <20090710124041.1262.30914.sendpatchset@ahunter-tower> (raw)
In-Reply-To: <20090710124004.1262.10422.sendpatchset@ahunter-tower>

>From b2f023bed880e3560aaa7de5caa6f899d79840f5 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] mmc: add mmc card sleep and awake support

Add support for the new MMC command SLEEP_AWAKE.

Signed-off-by: Jarkko Lavinen <jarkko.lavinen@nokia.com>
Signed-off-by: Adrian Hunter <adrian.hunter@nokia.com>
---
 drivers/mmc/core/core.c    |   40 ++++++++++++++++++++++++++++++++
 drivers/mmc/core/core.h    |    2 +
 drivers/mmc/core/mmc.c     |   54 +++++++++++++++++++++++++++++++++++++++----
 drivers/mmc/core/mmc_ops.c |   36 +++++++++++++++++++++++++++++
 drivers/mmc/core/mmc_ops.h |    1 +
 include/linux/mmc/card.h   |    2 +
 include/linux/mmc/host.h   |    5 ++++
 include/linux/mmc/mmc.h    |    2 +
 8 files changed, 137 insertions(+), 5 deletions(-)

diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c
index b76a0f7..1461025 100644
--- a/drivers/mmc/core/core.c
+++ b/drivers/mmc/core/core.c
@@ -1176,6 +1176,46 @@ void mmc_power_restore_host(struct mmc_host *host)
 }
 EXPORT_SYMBOL(mmc_power_restore_host);
 
+int mmc_card_awake(struct mmc_host *host)
+{
+	int err = -ENOSYS;
+
+	mmc_bus_get(host);
+
+	if (host->bus_ops && !host->bus_dead && host->bus_ops->awake)
+		err = host->bus_ops->awake(host);
+
+	mmc_bus_put(host);
+
+	return err;
+}
+EXPORT_SYMBOL(mmc_card_awake);
+
+int mmc_card_sleep(struct mmc_host *host)
+{
+	int err = -ENOSYS;
+
+	mmc_bus_get(host);
+
+	if (host->bus_ops && !host->bus_dead && host->bus_ops->awake)
+		err = host->bus_ops->sleep(host);
+
+	mmc_bus_put(host);
+
+	return err;
+}
+EXPORT_SYMBOL(mmc_card_sleep);
+
+int mmc_card_can_sleep(struct mmc_host *host)
+{
+	struct mmc_card *card = host->card;
+
+	if (card && mmc_card_mmc(card) && card->ext_csd.rev >= 3)
+		return 1;
+	return 0;
+}
+EXPORT_SYMBOL(mmc_card_can_sleep);
+
 #ifdef CONFIG_PM
 
 /**
diff --git a/drivers/mmc/core/core.h b/drivers/mmc/core/core.h
index f7eb4c4..c386348 100644
--- a/drivers/mmc/core/core.h
+++ b/drivers/mmc/core/core.h
@@ -16,6 +16,8 @@
 #define MMC_CMD_RETRIES        3
 
 struct mmc_bus_ops {
+	int (*awake)(struct mmc_host *);
+	int (*sleep)(struct mmc_host *);
 	void (*remove)(struct mmc_host *);
 	void (*detect)(struct mmc_host *);
 	void (*suspend)(struct mmc_host *);
diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c
index 01f7226..8e2e3d2 100644
--- a/drivers/mmc/core/mmc.c
+++ b/drivers/mmc/core/mmc.c
@@ -160,7 +160,6 @@ static int mmc_read_ext_csd(struct mmc_card *card)
 {
 	int err;
 	u8 *ext_csd;
-	unsigned int ext_csd_struct;
 
 	BUG_ON(!card);
 
@@ -207,16 +206,16 @@ static int mmc_read_ext_csd(struct mmc_card *card)
 		goto out;
 	}
 
-	ext_csd_struct = ext_csd[EXT_CSD_REV];
-	if (ext_csd_struct > 3) {
+	card->ext_csd.rev = ext_csd[EXT_CSD_REV];
+	if (card->ext_csd.rev > 3) {
 		printk(KERN_ERR "%s: unrecognised EXT_CSD structure "
 			"version %d\n", mmc_hostname(card->host),
-			ext_csd_struct);
+			card->ext_csd.rev);
 		err = -EINVAL;
 		goto out;
 	}
 
-	if (ext_csd_struct >= 2) {
+	if (card->ext_csd.rev >= 2) {
 		card->ext_csd.sectors =
 			ext_csd[EXT_CSD_SEC_CNT + 0] << 0 |
 			ext_csd[EXT_CSD_SEC_CNT + 1] << 8 |
@@ -241,6 +240,15 @@ static int mmc_read_ext_csd(struct mmc_card *card)
 		goto out;
 	}
 
+	if (card->ext_csd.rev >= 3) {
+		u8 sa_shift = ext_csd[EXT_CSD_S_A_TIMEOUT];
+
+		/* Sleep / awake timeout in 100ns units */
+		if (sa_shift > 0 && sa_shift <= 0x17)
+			card->ext_csd.sa_timeout =
+					1 << ext_csd[EXT_CSD_S_A_TIMEOUT];
+	}
+
 out:
 	kfree(ext_csd);
 
@@ -557,9 +565,41 @@ static void mmc_power_restore(struct mmc_host *host)
 	mmc_release_host(host);
 }
 
+static int mmc_sleep(struct mmc_host *host)
+{
+	struct mmc_card *card = host->card;
+	int err = -ENOSYS;
+
+	if (card && card->ext_csd.rev >= 3) {
+		err = mmc_card_sleepawake(host, 1);
+		if (err < 0)
+			pr_debug("%s: Error %d while putting card into sleep",
+				 mmc_hostname(host), err);
+	}
+
+	return err;
+}
+
+static int mmc_awake(struct mmc_host *host)
+{
+	struct mmc_card *card = host->card;
+	int err = -ENOSYS;
+
+	if (card && card->ext_csd.rev >= 3) {
+		err = mmc_card_sleepawake(host, 0);
+		if (err < 0)
+			pr_debug("%s: Error %d while awaking sleeping card",
+				 mmc_hostname(host), err);
+	}
+
+	return err;
+}
+
 #ifdef CONFIG_MMC_UNSAFE_RESUME
 
 static const struct mmc_bus_ops mmc_ops = {
+	.awake = mmc_awake,
+	.sleep = mmc_sleep,
 	.remove = mmc_remove,
 	.detect = mmc_detect,
 	.suspend = mmc_suspend,
@@ -575,6 +615,8 @@ static void mmc_attach_bus_ops(struct mmc_host *host)
 #else
 
 static const struct mmc_bus_ops mmc_ops = {
+	.awake = mmc_awake,
+	.sleep = mmc_sleep,
 	.remove = mmc_remove,
 	.detect = mmc_detect,
 	.suspend = NULL,
@@ -583,6 +625,8 @@ static const struct mmc_bus_ops mmc_ops = {
 };
 
 static const struct mmc_bus_ops mmc_ops_unsafe = {
+	.awake = mmc_awake,
+	.sleep = mmc_sleep,
 	.remove = mmc_remove,
 	.detect = mmc_detect,
 	.suspend = mmc_suspend,
diff --git a/drivers/mmc/core/mmc_ops.c b/drivers/mmc/core/mmc_ops.c
index 34ce270..355c604 100644
--- a/drivers/mmc/core/mmc_ops.c
+++ b/drivers/mmc/core/mmc_ops.c
@@ -57,6 +57,42 @@ int mmc_deselect_cards(struct mmc_host *host)
 	return _mmc_select_card(host, NULL);
 }
 
+int mmc_card_sleepawake(struct mmc_host *host, int sleep)
+{
+	struct mmc_command cmd;
+	struct mmc_card *card = host->card;
+	int err;
+
+	if (sleep)
+		mmc_deselect_cards(host);
+
+	memset(&cmd, 0, sizeof(struct mmc_command));
+
+	cmd.opcode = MMC_SLEEP_AWAKE;
+	cmd.arg = card->rca << 16;
+	if (sleep)
+		cmd.arg |= 1 << 15;
+
+	cmd.flags = MMC_RSP_R1B | MMC_CMD_AC;
+	err = mmc_wait_for_cmd(host, &cmd, 0);
+	if (err)
+		return err;
+
+	/*
+	 * If the host does not wait while the card signals busy, then we will
+	 * will have to wait the sleep/awake timeout.  Note, we cannot use the
+	 * SEND_STATUS command to poll the status because that command (and most
+	 * others) is invalid while the card sleeps.
+	 */
+	if (!(host->caps & MMC_CAP_WAIT_WHILE_BUSY))
+		mmc_delay(DIV_ROUND_UP(card->ext_csd.sa_timeout, 10000));
+
+	if (!sleep)
+		err = mmc_select_card(card);
+
+	return err;
+}
+
 int mmc_go_idle(struct mmc_host *host)
 {
 	int err;
diff --git a/drivers/mmc/core/mmc_ops.h b/drivers/mmc/core/mmc_ops.h
index 17854bf..653eb8e 100644
--- a/drivers/mmc/core/mmc_ops.h
+++ b/drivers/mmc/core/mmc_ops.h
@@ -25,6 +25,7 @@ int mmc_send_status(struct mmc_card *card, u32 *status);
 int mmc_send_cid(struct mmc_host *host, u32 *cid);
 int mmc_spi_read_ocr(struct mmc_host *host, int highcap, u32 *ocrp);
 int mmc_spi_set_crc(struct mmc_host *host, int use_crc);
+int mmc_card_sleepawake(struct mmc_host *host, int sleep);
 
 #endif
 
diff --git a/include/linux/mmc/card.h b/include/linux/mmc/card.h
index 403aa50..58f5917 100644
--- a/include/linux/mmc/card.h
+++ b/include/linux/mmc/card.h
@@ -40,6 +40,8 @@ struct mmc_csd {
 };
 
 struct mmc_ext_csd {
+	u8			rev;
+	unsigned int		sa_timeout;		/* Units: 100ns */
 	unsigned int		hs_max_dtr;
 	unsigned int		sectors;
 };
diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h
index e24d992..0a60b02 100644
--- a/include/linux/mmc/host.h
+++ b/include/linux/mmc/host.h
@@ -149,6 +149,7 @@ struct mmc_host {
 #define MMC_CAP_8_BIT_DATA	(1 << 6)	/* Can the host do 8 bit transfers */
 #define MMC_CAP_DISABLE		(1 << 7)	/* Can the host be disabled */
 #define MMC_CAP_NONREMOVABLE	(1 << 8)	/* Nonremovable e.g. eMMC */
+#define MMC_CAP_WAIT_WHILE_BUSY	(1 << 9)	/* Waits while card is busy */
 
 	/* host specific block data */
 	unsigned int		max_seg_size;	/* see blk_queue_max_segment_size */
@@ -240,6 +241,10 @@ struct regulator;
 int mmc_regulator_get_ocrmask(struct regulator *supply);
 int mmc_regulator_set_ocr(struct regulator *supply, unsigned short vdd_bit);
 
+int mmc_card_awake(struct mmc_host *host);
+int mmc_card_sleep(struct mmc_host *host);
+int mmc_card_can_sleep(struct mmc_host *host);
+
 int mmc_host_enable(struct mmc_host *host);
 int mmc_host_disable(struct mmc_host *host);
 int mmc_host_lazy_disable(struct mmc_host *host);
diff --git a/include/linux/mmc/mmc.h b/include/linux/mmc/mmc.h
index 14b81f3..b2b4095 100644
--- a/include/linux/mmc/mmc.h
+++ b/include/linux/mmc/mmc.h
@@ -31,6 +31,7 @@
 #define MMC_ALL_SEND_CID          2   /* bcr                     R2  */
 #define MMC_SET_RELATIVE_ADDR     3   /* ac   [31:16] RCA        R1  */
 #define MMC_SET_DSR               4   /* bc   [31:16] RCA            */
+#define MMC_SLEEP_AWAKE		  5   /* ac   [31:16] RCA 15:flg R1b */
 #define MMC_SWITCH                6   /* ac   [31:0] See below   R1b */
 #define MMC_SELECT_CARD           7   /* ac   [31:16] RCA        R1  */
 #define MMC_SEND_EXT_CSD          8   /* adtc                    R1  */
@@ -254,6 +255,7 @@ struct _mmc_csd {
 #define EXT_CSD_CARD_TYPE	196	/* RO */
 #define EXT_CSD_REV		192	/* RO */
 #define EXT_CSD_SEC_CNT		212	/* RO, 4 bytes */
+#define EXT_CSD_S_A_TIMEOUT	217
 
 /*
  * EXT_CSD field definitions
-- 
1.5.6.3


  parent reply	other threads:[~2009-07-10 12:39 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 ` Adrian Hunter [this message]
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 ` [PATCH 18/32] omap_hsmmc: support for deeper power saving states Adrian Hunter
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=20090710124041.1262.30914.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.