Linux-OMAP Archive on lore.kernel.org
 help / color / 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 1/32] mmc: add 'enable' and 'disable' methods to mmc host
Date: Fri, 10 Jul 2009 15:40:11 +0300
Message-ID: <20090710124011.1262.73298.sendpatchset@ahunter-tower> (raw)
In-Reply-To: <20090710124004.1262.10422.sendpatchset@ahunter-tower>

>From a0164897276e4d1f972fd90b1e9499e1ab8d221e Mon Sep 17 00:00:00 2001
From: Adrian Hunter <adrian.hunter@nokia.com>
Date: Wed, 22 Apr 2009 12:50:45 +0300
Subject: [PATCH] mmc: add 'enable' and 'disable' methods to mmc host

MMC hosts that support power saving can use the 'enable' and
'disable' methods to exit and enter power saving states.
An explanation of their use is provided in the comments
added to include/linux/mmc/host.h.

Signed-off-by: Adrian Hunter <adrian.hunter@nokia.com>
---
 drivers/mmc/core/core.c  |  174 ++++++++++++++++++++++++++++++++++++++++++++--
 drivers/mmc/core/host.c  |    1 +
 drivers/mmc/core/host.h  |    2 +
 include/linux/mmc/host.h |   47 ++++++++++++
 4 files changed, 218 insertions(+), 6 deletions(-)

diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c
index d84c880..41fd127 100644
--- a/drivers/mmc/core/core.c
+++ b/drivers/mmc/core/core.c
@@ -344,6 +344,98 @@ unsigned int mmc_align_data_size(struct mmc_card *card, unsigned int sz)
 EXPORT_SYMBOL(mmc_align_data_size);
 
 /**
+ *	mmc_host_enable - enable a host.
+ *	@host: mmc host to enable
+ *
+ *	Hosts that support power saving can use the 'enable' and 'disable'
+ *	methods to exit and enter power saving states. For more information
+ *	see comments for struct mmc_host_ops.
+ */
+int mmc_host_enable(struct mmc_host *host)
+{
+	if (!(host->caps & MMC_CAP_DISABLE))
+		return 0;
+
+	if (host->en_dis_recurs)
+		return 0;
+
+	if (host->nesting_cnt++)
+		return 0;
+
+	cancel_delayed_work_sync(&host->disable);
+
+	if (host->enabled)
+		return 0;
+
+	if (host->ops->enable) {
+		int err;
+
+		host->en_dis_recurs = 1;
+		err = host->ops->enable(host);
+		host->en_dis_recurs = 0;
+
+		if (err) {
+			pr_debug("%s: enable error %d\n",
+				 mmc_hostname(host), err);
+			return err;
+		}
+	}
+	host->enabled = 1;
+	return 0;
+}
+EXPORT_SYMBOL(mmc_host_enable);
+
+static int mmc_host_do_disable(struct mmc_host *host, int lazy)
+{
+	if (host->ops->disable) {
+		int err;
+
+		host->en_dis_recurs = 1;
+		err = host->ops->disable(host, lazy);
+		host->en_dis_recurs = 0;
+
+		if (err < 0) {
+			pr_debug("%s: disable error %d\n",
+				 mmc_hostname(host), err);
+			return err;
+		}
+		if (err > 0)
+			mmc_schedule_delayed_work(&host->disable, err);
+	}
+	host->enabled = 0;
+	return 0;
+}
+
+/**
+ *	mmc_host_disable - disable a host.
+ *	@host: mmc host to disable
+ *
+ *	Hosts that support power saving can use the 'enable' and 'disable'
+ *	methods to exit and enter power saving states. For more information
+ *	see comments for struct mmc_host_ops.
+ */
+int mmc_host_disable(struct mmc_host *host)
+{
+	int err;
+
+	if (!(host->caps & MMC_CAP_DISABLE))
+		return 0;
+
+	if (host->en_dis_recurs)
+		return 0;
+
+	if (--host->nesting_cnt)
+		return 0;
+
+	if (!host->enabled)
+		return 0;
+
+	err = mmc_host_do_disable(host, 0);
+	return err;
+}
+EXPORT_SYMBOL(mmc_host_disable);
+
+/**
  *	__mmc_claim_host - exclusively claim a host
  *	@host: mmc host to claim
  *	@abort: whether or not the operation should be aborted
@@ -379,11 +471,81 @@ int __mmc_claim_host(struct mmc_host *host, atomic_t *abort)
 		wake_up(&host->wq);
 	spin_unlock_irqrestore(&host->lock, flags);
 	remove_wait_queue(&host->wq, &wait);
+	if (!stop)
+		mmc_host_enable(host);
 	return stop;
 }
 
 EXPORT_SYMBOL(__mmc_claim_host);
 
+static int mmc_try_claim_host(struct mmc_host *host)
+{
+	int claimed_host = 0;
+	unsigned long flags;
+
+	spin_lock_irqsave(&host->lock, flags);
+	if (!host->claimed) {
+		host->claimed = 1;
+		claimed_host = 1;
+	}
+	spin_unlock_irqrestore(&host->lock, flags);
+	return claimed_host;
+}
+
+static void mmc_do_release_host(struct mmc_host *host)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&host->lock, flags);
+	host->claimed = 0;
+	spin_unlock_irqrestore(&host->lock, flags);
+
+	wake_up(&host->wq);
+}
+
+void mmc_host_deeper_disable(struct work_struct *work)
+{
+	struct mmc_host *host =
+		container_of(work, struct mmc_host, disable.work);
+
+	/* If the host is claimed then we do not want to disable it anymore */
+	if (!mmc_try_claim_host(host))
+		return;
+	mmc_host_do_disable(host, 1);
+	mmc_do_release_host(host);
+}
+
+/**
+ *	mmc_host_lazy_disable - lazily disable a host.
+ *	@host: mmc host to disable
+ *
+ *	Hosts that support power saving can use the 'enable' and 'disable'
+ *	methods to exit and enter power saving states. For more information
+ *	see comments for struct mmc_host_ops.
+ */
+int mmc_host_lazy_disable(struct mmc_host *host)
+{
+	if (!(host->caps & MMC_CAP_DISABLE))
+		return 0;
+
+	if (host->en_dis_recurs)
+		return 0;
+
+	if (--host->nesting_cnt)
+		return 0;
+
+	if (!host->enabled)
+		return 0;
+
+	if (host->disable_delay) {
+		mmc_schedule_delayed_work(&host->disable,
+				msecs_to_jiffies(host->disable_delay));
+		return 0;
+	} else
+		return mmc_host_do_disable(host, 1);
+}
+EXPORT_SYMBOL(mmc_host_lazy_disable);
+
 /**
  *	mmc_release_host - release a host
  *	@host: mmc host to release
@@ -393,15 +555,11 @@ EXPORT_SYMBOL(__mmc_claim_host);
  */
 void mmc_release_host(struct mmc_host *host)
 {
-	unsigned long flags;
-
 	WARN_ON(!host->claimed);
 
-	spin_lock_irqsave(&host->lock, flags);
-	host->claimed = 0;
-	spin_unlock_irqrestore(&host->lock, flags);
+	mmc_host_lazy_disable(host);
 
-	wake_up(&host->wq);
+	mmc_do_release_host(host);
 }
 
 EXPORT_SYMBOL(mmc_release_host);
@@ -947,6 +1105,8 @@ void mmc_stop_host(struct mmc_host *host)
 	spin_unlock_irqrestore(&host->lock, flags);
 #endif
 
+	if (host->caps & MMC_CAP_DISABLE)
+		cancel_delayed_work(&host->disable);
 	cancel_delayed_work(&host->detect);
 	mmc_flush_scheduled_work();
 
@@ -975,6 +1135,8 @@ void mmc_stop_host(struct mmc_host *host)
  */
 int mmc_suspend_host(struct mmc_host *host, pm_message_t state)
 {
+	if (host->caps & MMC_CAP_DISABLE)
+		cancel_delayed_work(&host->disable);
 	cancel_delayed_work(&host->detect);
 	mmc_flush_scheduled_work();
 
diff --git a/drivers/mmc/core/host.c b/drivers/mmc/core/host.c
index 5e945e6..a268d12 100644
--- a/drivers/mmc/core/host.c
+++ b/drivers/mmc/core/host.c
@@ -83,6 +83,7 @@ struct mmc_host *mmc_alloc_host(int extra, struct device *dev)
 	spin_lock_init(&host->lock);
 	init_waitqueue_head(&host->wq);
 	INIT_DELAYED_WORK(&host->detect, mmc_rescan);
+	INIT_DELAYED_WORK_DEFERRABLE(&host->disable, mmc_host_deeper_disable);
 
 	/*
 	 * By default, hosts do not support SGIO or large requests.
diff --git a/drivers/mmc/core/host.h b/drivers/mmc/core/host.h
index c2dc3d2..8c87e11 100644
--- a/drivers/mmc/core/host.h
+++ b/drivers/mmc/core/host.h
@@ -14,5 +14,7 @@
 int mmc_register_host_class(void);
 void mmc_unregister_host_class(void);
 
+void mmc_host_deeper_disable(struct work_struct *work);
+
 #endif
 
diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h
index 3e7615e..583c068 100644
--- a/include/linux/mmc/host.h
+++ b/include/linux/mmc/host.h
@@ -51,6 +51,35 @@ struct mmc_ios {
 };
 
 struct mmc_host_ops {
+	/*
+	 * Hosts that support power saving can use the 'enable' and 'disable'
+	 * methods to exit and enter power saving states. 'enable' is called
+	 * when the host is claimed and 'disable' is called (or scheduled with
+	 * a delay) when the host is released. The 'disable' is scheduled if
+	 * the disable delay set by 'mmc_set_disable_delay()' is non-zero,
+	 * otherwise 'disable' is called immediately. 'disable' may be
+	 * scheduled repeatedly, to permit ever greater power saving at the
+	 * expense of ever greater latency to re-enable. Rescheduling is
+	 * determined by the return value of the 'disable' method. A positive
+	 * value gives the delay in jiffies.
+	 *
+	 * In the case where a host function (like set_ios) may be called
+	 * with or without the host claimed, enabling and disabling can be
+	 * done directly and will nest correctly. Call 'mmc_host_enable()' and
+	 * 'mmc_host_lazy_disable()' for this purpose, but note that these
+	 * functions must be paired.
+	 *
+	 * Alternatively, 'mmc_host_enable()' may be paired with
+	 * 'mmc_host_disable()' which calls 'disable' immediately.  In this
+	 * case the 'disable' method will be called with 'lazy' set to 0.
+	 * This is mainly useful for error paths.
+	 *
+	 * Because lazy disble may be called from a work queue, the 'disable'
+	 * method must claim the host when 'lazy' != 0, which will work
+	 * correctly because recursion is detected and handled.
+	 */
+	int (*enable)(struct mmc_host *host);
+	int (*disable)(struct mmc_host *host, int lazy);
 	void	(*request)(struct mmc_host *host, struct mmc_request *req);
 	/*
 	 * Avoid calling these three functions too often or in a "fast path",
@@ -118,6 +147,7 @@ struct mmc_host {
 #define MMC_CAP_SPI		(1 << 4)	/* Talks only SPI protocols */
 #define MMC_CAP_NEEDS_POLL	(1 << 5)	/* Needs polling for card-detection */
 #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 */
 
 	/* host specific block data */
 	unsigned int		max_seg_size;	/* see blk_queue_max_segment_size */
@@ -142,6 +172,13 @@ struct mmc_host {
 	unsigned int		removed:1;	/* host is being removed */
 #endif
 
+	/* Only used with MMC_CAP_DISABLE */
+	int			enabled;	/* host is enabled */
+	int			nesting_cnt;	/* "enable" nesting count */
+	int			en_dis_recurs;	/* detect recursion */
+	unsigned int		disable_delay;	/* disable delay in msecs */
+	struct delayed_work	disable;	/* disabling work */
+
 	struct mmc_card		*card;		/* device attached to this host */
 
 	wait_queue_head_t	wq;
@@ -197,5 +234,15 @@ struct regulator;
 int mmc_regulator_get_ocrmask(struct regulator *supply);
 int mmc_regulator_set_ocr(struct regulator *supply, unsigned short vdd_bit);
 
+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);
+
+static inline void mmc_set_disable_delay(struct mmc_host *host,
+					 unsigned int disable_delay)
+{
+	host->disable_delay = disable_delay;
+}
+
 #endif
 
-- 
1.5.6.3


  reply index

Thread overview: 55+ 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 ` Adrian Hunter [this message]
2009-07-10 17:15   ` [PATCH 1/32] mmc: add 'enable' and 'disable' methods to mmc host Madhusudhan
2009-07-10 19:12     ` Adrian Hunter
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 ` [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-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-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=20090710124011.1262.73298.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

Linux-OMAP Archive on lore.kernel.org

Archives are clonable:
	git clone --mirror https://lore.kernel.org/linux-omap/0 linux-omap/git/0.git

	# If you have public-inbox 1.1+ installed, you may
	# initialize and index your mirror using the following commands:
	public-inbox-init -V2 linux-omap linux-omap/ https://lore.kernel.org/linux-omap \
		linux-omap@vger.kernel.org
	public-inbox-index linux-omap

Example config snippet for mirrors

Newsgroup available over NNTP:
	nntp://nntp.lore.kernel.org/org.kernel.vger.linux-omap


AGPL code for this site: git clone https://public-inbox.org/public-inbox.git