All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 0/4] power management support for SDIO cards
@ 2009-09-02 18:37 Nicolas Pitre
  2009-09-02 18:37 ` [PATCH 1/4] [MMC] core SDIO suspend/resume support Nicolas Pitre
                   ` (3 more replies)
  0 siblings, 4 replies; 7+ messages in thread
From: Nicolas Pitre @ 2009-09-02 18:37 UTC (permalink / raw)
  To: linux-mmc

This series is the core changes needed to support suspend/resume with
SDIO cards, and allow for special power management features needed to
implement things like the equivalent of wake-on-land for SDIO wireless
cards.  Further patches will eventually be posted for the SDHCI driver
and for the Libertas driver.

[PATCH 1/4] [MMC] core SDIO suspend/resume support
[PATCH 2/4] [MMC] propagate error codes back from bus drivers' suspend/resume methods
[PATCH 3/4] [MMC] make SDIO device/driver struct accessors public
[PATCH 4/4] [MMC] introduce API for special power management features


 drivers/mmc/core/core.c       |   45 +++++-
 drivers/mmc/core/core.h       |    4 +-
 drivers/mmc/core/mmc.c        |   15 +--
 drivers/mmc/core/sd.c         |   15 +--
 drivers/mmc/core/sdio.c       |  278 ++++++++++++++++++++++++-----------
 drivers/mmc/core/sdio_bus.c   |    3 -
 drivers/mmc/core/sdio_io.c    |   49 ++++++
 include/linux/mmc/host.h      |    5 +
 include/linux/mmc/pm.h        |   30 ++++
 include/linux/mmc/sdio_func.h |    8 +
 10 files changed, 329 insertions(+), 123 deletions(-)


Nicolas


^ permalink raw reply	[flat|nested] 7+ messages in thread

* [PATCH 1/4] [MMC] core SDIO suspend/resume support
  2009-09-02 18:37 [PATCH 0/4] power management support for SDIO cards Nicolas Pitre
@ 2009-09-02 18:37 ` Nicolas Pitre
  2009-09-06 14:17   ` Pierre Ossman
  2009-09-02 18:37 ` [PATCH 2/4] [MMC] propagate error codes back from bus drivers' suspend/resume methods Nicolas Pitre
                   ` (2 subsequent siblings)
  3 siblings, 1 reply; 7+ messages in thread
From: Nicolas Pitre @ 2009-09-02 18:37 UTC (permalink / raw)
  To: linux-mmc

Currently, all SDIO cards are virtually removed upon a suspend, and
completely reprobed upon a resume.  This adds the suspend and resume
methods to the SDIO bus driver so to be able to dispatch those events
to the actual SDIO function drivers for real suspend/resume instead.

All active functions on a card must have a driver with both a suspend
and a resume method though.  Failing that, we fall back to the current
behavior of simply "removing" the card when suspending.

When resuming, we make sure the same card is still inserted by comparing
the vendor and product IDs.  If there is a mismatch, or if there is simply
no card anymore in the slot, then the previous card is "removed" and the
new card is detected.

Signed-off-by: Nicolas Pitre <nico@marvell.com>
---
 drivers/mmc/core/sdio.c |  285 ++++++++++++++++++++++++++++++++---------------
 1 files changed, 196 insertions(+), 89 deletions(-)

diff --git a/drivers/mmc/core/sdio.c b/drivers/mmc/core/sdio.c
index fb99ccf..4c21b74 100644
--- a/drivers/mmc/core/sdio.c
+++ b/drivers/mmc/core/sdio.c
@@ -195,6 +195,134 @@ static int sdio_enable_hs(struct mmc_card *card)
 }
 
 /*
+ * Handle the detection and initialisation of a card.
+ *
+ * In the case of a resume, "oldcard" will contain the card
+ * we're trying to reinitialise.
+ */
+static int mmc_sdio_init_card(struct mmc_host *host, u32 ocr,
+			      struct mmc_card *oldcard)
+{
+	struct mmc_card *card;
+	int err;
+
+	BUG_ON(!host);
+	WARN_ON(!host->claimed);
+
+	/*
+	 * Inform the card of the voltage
+	 */
+	err = mmc_send_io_op_cond(host, host->ocr, &ocr);
+	if (err)
+		goto err;
+
+	/*
+	 * For SPI, enable CRC as appropriate.
+	 */
+	if (mmc_host_is_spi(host)) {
+		err = mmc_spi_set_crc(host, use_spi_crc);
+		if (err)
+			goto err;
+	}
+
+	/*
+	 * Allocate card structure.
+	 */
+	card = mmc_alloc_card(host, NULL);
+	if (IS_ERR(card)) {
+		err = PTR_ERR(card);
+		goto err;
+	}
+
+	card->type = MMC_TYPE_SDIO;
+
+	/*
+	 * For native busses:  set card RCA and quit open drain mode.
+	 */
+	if (!mmc_host_is_spi(host)) {
+		err = mmc_send_relative_addr(host, &card->rca);
+		if (err)
+			goto remove;
+
+		mmc_set_bus_mode(host, MMC_BUSMODE_PUSHPULL);
+	}
+
+	/*
+	 * Select card, as all following commands rely on that.
+	 */
+	if (!mmc_host_is_spi(host)) {
+		err = mmc_select_card(card);
+		if (err)
+			goto remove;
+	}
+
+	/*
+	 * Read the common registers.
+	 */
+	err = sdio_read_cccr(card);
+	if (err)
+		goto remove;
+
+	/*
+	 * Read the common CIS tuples.
+	 */
+	err = sdio_read_common_cis(card);
+	if (err)
+		goto remove;
+
+	if (oldcard) {
+		int same = (card->cis.vendor == oldcard->cis.vendor &&
+			    card->cis.device == oldcard->cis.device);
+		mmc_remove_card(card);
+		if (!same) {
+			err = -ENOENT;
+			goto err;
+		}
+		card = oldcard;
+	}
+
+	/*
+	 * Switch to high-speed (if supported).
+	 */
+	err = sdio_enable_hs(card);
+	if (err)
+		goto remove;
+
+	/*
+	 * Change to the card's maximum speed.
+	 */
+	if (mmc_card_highspeed(card)) {
+		/*
+		 * The SDIO specification doesn't mention how
+		 * the CIS transfer speed register relates to
+		 * high-speed, but it seems that 50 MHz is
+		 * mandatory.
+		 */
+		mmc_set_clock(host, 50000000);
+	} else {
+		mmc_set_clock(host, card->cis.max_dtr);
+	}
+
+	/*
+	 * Switch to wider bus (if supported).
+	 */
+	err = sdio_enable_wide(card);
+	if (err)
+		goto remove;
+
+	if (!oldcard)
+		host->card = card;
+	return 0;
+
+remove:
+	if (!oldcard)
+		mmc_remove_card(card);
+
+err:
+	return err;
+}
+
+/*
  * Host is being removed. Free up the current card.
  */
 static void mmc_sdio_remove(struct mmc_host *host)
@@ -243,10 +371,74 @@ static void mmc_sdio_detect(struct mmc_host *host)
 	}
 }
 
+/*
+ * SDIO suspend.  We need to suspend all functions separately.
+ * Therefore all registered functions must have drivers with suspend
+ * and resume methods.  Failing that we simply remove the whole card.
+ */
+static void mmc_sdio_suspend(struct mmc_host *host)
+{
+	int i;
+
+	/* make sure all registered functions can suspend/resume */
+	for (i = 0; i < host->card->sdio_funcs; i++) {
+		struct sdio_func *func = host->card->sdio_func[i];
+		if (func && sdio_func_present(func) && func->dev.driver) {
+			struct dev_pm_ops *pmops = func->dev.driver->pm;
+			if (!pmops || !pmops->suspend || !pmops->resume) {
+				/* just remove the entire card in that case */
+				mmc_sdio_remove(host);
+				mmc_claim_host(host);
+				mmc_detach_bus(host);
+				mmc_release_host(host);
+				return;
+			}
+		}
+	}
+
+	/* now suspend them */
+	for (i = 0; i < host->card->sdio_funcs; i++) {
+		struct sdio_func *func = host->card->sdio_func[i];
+		if (func && sdio_func_present(func) && func->dev.driver) {
+			struct dev_pm_ops *pmops = func->dev.driver->pm;
+			pmops->suspend(&func->dev);
+		}
+	}
+}
+
+static void mmc_sdio_resume(struct mmc_host *host)
+{
+	int i, err;
+
+	BUG_ON(!host);
+	BUG_ON(!host->card);
+
+	mmc_claim_host(host);
+	err = mmc_sdio_init_card(host, host->ocr, host->card);
+	mmc_release_host(host);
+	if (err) {
+		mmc_sdio_remove(host);
+		mmc_claim_host(host);
+		mmc_detach_bus(host);
+		mmc_release_host(host);
+		return;
+	}
+
+	/* resume all functions */
+	for (i = 0; i < host->card->sdio_funcs; i++) {
+		struct sdio_func *func = host->card->sdio_func[i];
+		if (func && sdio_func_present(func) && func->dev.driver) {
+			struct dev_pm_ops *pmops = func->dev.driver->pm;
+			pmops->resume(&func->dev);
+		}
+	}
+}
 
 static const struct mmc_bus_ops mmc_sdio_ops = {
 	.remove = mmc_sdio_remove,
 	.detect = mmc_sdio_detect,
+	.suspend = mmc_sdio_suspend,
+	.resume = mmc_sdio_resume,
 };
 
 
@@ -293,103 +485,18 @@ int mmc_attach_sdio(struct mmc_host *host, u32 ocr)
 	}
 
 	/*
-	 * Inform the card of the voltage
+	 * Detect and init the card.
 	 */
-	err = mmc_send_io_op_cond(host, host->ocr, &ocr);
+	err = mmc_sdio_init_card(host, host->ocr, NULL);
 	if (err)
 		goto err;
-
-	/*
-	 * For SPI, enable CRC as appropriate.
-	 */
-	if (mmc_host_is_spi(host)) {
-		err = mmc_spi_set_crc(host, use_spi_crc);
-		if (err)
-			goto err;
-	}
+	card = host->card;
 
 	/*
 	 * The number of functions on the card is encoded inside
 	 * the ocr.
 	 */
-	funcs = (ocr & 0x70000000) >> 28;
-
-	/*
-	 * Allocate card structure.
-	 */
-	card = mmc_alloc_card(host, NULL);
-	if (IS_ERR(card)) {
-		err = PTR_ERR(card);
-		goto err;
-	}
-
-	card->type = MMC_TYPE_SDIO;
-	card->sdio_funcs = funcs;
-
-	host->card = card;
-
-	/*
-	 * For native busses:  set card RCA and quit open drain mode.
-	 */
-	if (!mmc_host_is_spi(host)) {
-		err = mmc_send_relative_addr(host, &card->rca);
-		if (err)
-			goto remove;
-
-		mmc_set_bus_mode(host, MMC_BUSMODE_PUSHPULL);
-	}
-
-	/*
-	 * Select card, as all following commands rely on that.
-	 */
-	if (!mmc_host_is_spi(host)) {
-		err = mmc_select_card(card);
-		if (err)
-			goto remove;
-	}
-
-	/*
-	 * Read the common registers.
-	 */
-	err = sdio_read_cccr(card);
-	if (err)
-		goto remove;
-
-	/*
-	 * Read the common CIS tuples.
-	 */
-	err = sdio_read_common_cis(card);
-	if (err)
-		goto remove;
-
-	/*
-	 * Switch to high-speed (if supported).
-	 */
-	err = sdio_enable_hs(card);
-	if (err)
-		goto remove;
-
-	/*
-	 * Change to the card's maximum speed.
-	 */
-	if (mmc_card_highspeed(card)) {
-		/*
-		 * The SDIO specification doesn't mention how
-		 * the CIS transfer speed register relates to
-		 * high-speed, but it seems that 50 MHz is
-		 * mandatory.
-		 */
-		mmc_set_clock(host, 50000000);
-	} else {
-		mmc_set_clock(host, card->cis.max_dtr);
-	}
-
-	/*
-	 * Switch to wider bus (if supported).
-	 */
-	err = sdio_enable_wide(card);
-	if (err)
-		goto remove;
+	card->sdio_funcs = funcs = (ocr & 0x70000000) >> 28;
 
 	/*
 	 * Initialize (but don't add) all present functions.
-- 
1.6.4.1.315.g5106d.dirty


^ permalink raw reply related	[flat|nested] 7+ messages in thread

* [PATCH 2/4] [MMC] propagate error codes back from bus drivers' suspend/resume methods
  2009-09-02 18:37 [PATCH 0/4] power management support for SDIO cards Nicolas Pitre
  2009-09-02 18:37 ` [PATCH 1/4] [MMC] core SDIO suspend/resume support Nicolas Pitre
@ 2009-09-02 18:37 ` Nicolas Pitre
  2009-09-02 18:37 ` [PATCH 3/4] [MMC] make SDIO device/driver struct accessors public Nicolas Pitre
  2009-09-02 18:37 ` [PATCH 4/4] [MMC] introduce API for special power management features Nicolas Pitre
  3 siblings, 0 replies; 7+ messages in thread
From: Nicolas Pitre @ 2009-09-02 18:37 UTC (permalink / raw)
  To: linux-mmc

Especially for SDIO drivers which may have special conditions/errors
to report, it is a good thing to relay the returned error code back to
upper layers.

This also allows for the rationalization of the resume path where code
to "remove" a no-longer-existing or replaced card was duplicated into
the MMC, SD and SDIO bus drivers.

In the SDIO case, if a function suspend method returns an error, then
all previously suspended functions are resumed and the error returned.
An exception is made for -ENOSYS which the core interprets as "we don't
support suspend so just kick the card out for suspend and return success".

Signed-off-by: Nicolas Pitre <nico@marvell.com>
---
 drivers/mmc/core/core.c |   35 ++++++++++++++++++++++++++++-------
 drivers/mmc/core/core.h |    4 ++--
 drivers/mmc/core/mmc.c  |   15 +++++----------
 drivers/mmc/core/sd.c   |   15 +++++----------
 drivers/mmc/core/sdio.c |   41 +++++++++++++++++------------------------
 5 files changed, 57 insertions(+), 53 deletions(-)

diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c
index 2649117..2853f58 100644
--- a/drivers/mmc/core/core.c
+++ b/drivers/mmc/core/core.c
@@ -958,27 +958,34 @@ void mmc_stop_host(struct mmc_host *host)
  */
 int mmc_suspend_host(struct mmc_host *host, pm_message_t state)
 {
+	int err = 0;
+
 	cancel_delayed_work(&host->detect);
 	mmc_flush_scheduled_work();
 
 	mmc_bus_get(host);
 	if (host->bus_ops && !host->bus_dead) {
 		if (host->bus_ops->suspend)
-			host->bus_ops->suspend(host);
-		if (!host->bus_ops->resume) {
+			err = host->bus_ops->suspend(host);
+		if (err == -ENOSYS || !host->bus_ops->resume) {
+			/*
+			 * We simply "remove" the card in this case.
+			 * It will be redetected on resume.
+			 */
 			if (host->bus_ops->remove)
 				host->bus_ops->remove(host);
-
 			mmc_claim_host(host);
 			mmc_detach_bus(host);
 			mmc_release_host(host);
+			err = 0;
 		}
 	}
 	mmc_bus_put(host);
 
-	mmc_power_off(host);
+	if (!err)
+		mmc_power_off(host);
 
-	return 0;
+	return err;
 }
 
 EXPORT_SYMBOL(mmc_suspend_host);
@@ -989,12 +996,26 @@ EXPORT_SYMBOL(mmc_suspend_host);
  */
 int mmc_resume_host(struct mmc_host *host)
 {
+	int err = 0;
+
 	mmc_bus_get(host);
 	if (host->bus_ops && !host->bus_dead) {
 		mmc_power_up(host);
 		mmc_select_voltage(host, host->ocr);
 		BUG_ON(!host->bus_ops->resume);
-		host->bus_ops->resume(host);
+		err = host->bus_ops->resume(host);
+		if (err) {
+			printk(KERN_WARNING "%s: error %d during resume "
+					    "(card was removed?)\n",
+					    mmc_hostname(host), err);
+			if (host->bus_ops->remove)
+				host->bus_ops->remove(host);
+			mmc_claim_host(host);
+			mmc_detach_bus(host);
+			mmc_release_host(host);
+			/* no need to bother upper layers */
+			err = 0;
+		}
 	}
 	mmc_bus_put(host);
 
@@ -1004,7 +1025,7 @@ int mmc_resume_host(struct mmc_host *host)
 	 */
 	mmc_detect_change(host, 1);
 
-	return 0;
+	return err;
 }
 
 EXPORT_SYMBOL(mmc_resume_host);
diff --git a/drivers/mmc/core/core.h b/drivers/mmc/core/core.h
index c819eff..767d212 100644
--- a/drivers/mmc/core/core.h
+++ b/drivers/mmc/core/core.h
@@ -18,8 +18,8 @@
 struct mmc_bus_ops {
 	void (*remove)(struct mmc_host *);
 	void (*detect)(struct mmc_host *);
-	void (*suspend)(struct mmc_host *);
-	void (*resume)(struct mmc_host *);
+	int (*suspend)(struct mmc_host *);
+	int (*resume)(struct mmc_host *);
 };
 
 void mmc_attach_bus(struct mmc_host *host, const struct mmc_bus_ops *ops);
diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c
index 06084db..188ae06 100644
--- a/drivers/mmc/core/mmc.c
+++ b/drivers/mmc/core/mmc.c
@@ -512,7 +512,7 @@ static void mmc_detect(struct mmc_host *host)
 /*
  * Suspend callback from host.
  */
-static void mmc_suspend(struct mmc_host *host)
+static int mmc_suspend(struct mmc_host *host)
 {
 	BUG_ON(!host);
 	BUG_ON(!host->card);
@@ -522,6 +522,8 @@ static void mmc_suspend(struct mmc_host *host)
 		mmc_deselect_cards(host);
 	host->card->state &= ~MMC_STATE_HIGHSPEED;
 	mmc_release_host(host);
+
+	return 0;
 }
 
 /*
@@ -530,7 +532,7 @@ static void mmc_suspend(struct mmc_host *host)
  * This function tries to determine if the same card is still present
  * and, if so, restore all state to it.
  */
-static void mmc_resume(struct mmc_host *host)
+static int mmc_resume(struct mmc_host *host)
 {
 	int err;
 
@@ -541,14 +543,7 @@ static void mmc_resume(struct mmc_host *host)
 	err = mmc_init_card(host, host->ocr, host->card);
 	mmc_release_host(host);
 
-	if (err) {
-		mmc_remove(host);
-
-		mmc_claim_host(host);
-		mmc_detach_bus(host);
-		mmc_release_host(host);
-	}
-
+	return err;
 }
 
 #else
diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c
index cd81c39..4539a46 100644
--- a/drivers/mmc/core/sd.c
+++ b/drivers/mmc/core/sd.c
@@ -566,7 +566,7 @@ static void mmc_sd_detect(struct mmc_host *host)
 /*
  * Suspend callback from host.
  */
-static void mmc_sd_suspend(struct mmc_host *host)
+static int mmc_sd_suspend(struct mmc_host *host)
 {
 	BUG_ON(!host);
 	BUG_ON(!host->card);
@@ -576,6 +576,8 @@ static void mmc_sd_suspend(struct mmc_host *host)
 		mmc_deselect_cards(host);
 	host->card->state &= ~MMC_STATE_HIGHSPEED;
 	mmc_release_host(host);
+
+	return 0;
 }
 
 /*
@@ -584,7 +586,7 @@ static void mmc_sd_suspend(struct mmc_host *host)
  * This function tries to determine if the same card is still present
  * and, if so, restore all state to it.
  */
-static void mmc_sd_resume(struct mmc_host *host)
+static int mmc_sd_resume(struct mmc_host *host)
 {
 	int err;
 
@@ -595,14 +597,7 @@ static void mmc_sd_resume(struct mmc_host *host)
 	err = mmc_sd_init_card(host, host->ocr, host->card);
 	mmc_release_host(host);
 
-	if (err) {
-		mmc_sd_remove(host);
-
-		mmc_claim_host(host);
-		mmc_detach_bus(host);
-		mmc_release_host(host);
-	}
-
+	return err;
 }
 
 #else
diff --git a/drivers/mmc/core/sdio.c b/drivers/mmc/core/sdio.c
index 4c21b74..d592423 100644
--- a/drivers/mmc/core/sdio.c
+++ b/drivers/mmc/core/sdio.c
@@ -376,37 +376,35 @@ static void mmc_sdio_detect(struct mmc_host *host)
  * Therefore all registered functions must have drivers with suspend
  * and resume methods.  Failing that we simply remove the whole card.
  */
-static void mmc_sdio_suspend(struct mmc_host *host)
+static int mmc_sdio_suspend(struct mmc_host *host)
 {
-	int i;
+	int i, err = 0;
 
-	/* make sure all registered functions can suspend/resume */
 	for (i = 0; i < host->card->sdio_funcs; i++) {
 		struct sdio_func *func = host->card->sdio_func[i];
 		if (func && sdio_func_present(func) && func->dev.driver) {
 			struct dev_pm_ops *pmops = func->dev.driver->pm;
 			if (!pmops || !pmops->suspend || !pmops->resume) {
-				/* just remove the entire card in that case */
-				mmc_sdio_remove(host);
-				mmc_claim_host(host);
-				mmc_detach_bus(host);
-				mmc_release_host(host);
-				return;
-			}
+				/* force removal of entire card in that case */
+				err = -ENOSYS;
+			} else
+				err = pmops->suspend(&func->dev);
+			if (err)
+				break;
 		}
 	}
-
-	/* now suspend them */
-	for (i = 0; i < host->card->sdio_funcs; i++) {
+	while (err && --i >= 0) {
 		struct sdio_func *func = host->card->sdio_func[i];
 		if (func && sdio_func_present(func) && func->dev.driver) {
 			struct dev_pm_ops *pmops = func->dev.driver->pm;
-			pmops->suspend(&func->dev);
+			pmops->resume(&func->dev);
 		}
 	}
+
+	return err;
 }
 
-static void mmc_sdio_resume(struct mmc_host *host)
+static int mmc_sdio_resume(struct mmc_host *host)
 {
 	int i, err;
 
@@ -416,22 +414,17 @@ static void mmc_sdio_resume(struct mmc_host *host)
 	mmc_claim_host(host);
 	err = mmc_sdio_init_card(host, host->ocr, host->card);
 	mmc_release_host(host);
-	if (err) {
-		mmc_sdio_remove(host);
-		mmc_claim_host(host);
-		mmc_detach_bus(host);
-		mmc_release_host(host);
-		return;
-	}
 
 	/* resume all functions */
-	for (i = 0; i < host->card->sdio_funcs; i++) {
+	for (i = 0; !err && i < host->card->sdio_funcs; i++) {
 		struct sdio_func *func = host->card->sdio_func[i];
 		if (func && sdio_func_present(func) && func->dev.driver) {
 			struct dev_pm_ops *pmops = func->dev.driver->pm;
-			pmops->resume(&func->dev);
+			err = pmops->resume(&func->dev);
 		}
 	}
+
+	return err;
 }
 
 static const struct mmc_bus_ops mmc_sdio_ops = {
-- 
1.6.4.1.315.g5106d.dirty


^ permalink raw reply related	[flat|nested] 7+ messages in thread

* [PATCH 3/4] [MMC] make SDIO device/driver struct accessors public
  2009-09-02 18:37 [PATCH 0/4] power management support for SDIO cards Nicolas Pitre
  2009-09-02 18:37 ` [PATCH 1/4] [MMC] core SDIO suspend/resume support Nicolas Pitre
  2009-09-02 18:37 ` [PATCH 2/4] [MMC] propagate error codes back from bus drivers' suspend/resume methods Nicolas Pitre
@ 2009-09-02 18:37 ` Nicolas Pitre
  2009-09-02 18:37 ` [PATCH 4/4] [MMC] introduce API for special power management features Nicolas Pitre
  3 siblings, 0 replies; 7+ messages in thread
From: Nicolas Pitre @ 2009-09-02 18:37 UTC (permalink / raw)
  To: linux-mmc

Especially with the PM callbacks, those are quite handy in driver code.

Signed-off-by: Nicolas Pitre <nico@marvell.com>
---
 drivers/mmc/core/sdio_bus.c   |    3 ---
 include/linux/mmc/sdio_func.h |    3 +++
 2 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/drivers/mmc/core/sdio_bus.c b/drivers/mmc/core/sdio_bus.c
index 46284b5..d37464e 100644
--- a/drivers/mmc/core/sdio_bus.c
+++ b/drivers/mmc/core/sdio_bus.c
@@ -20,9 +20,6 @@
 #include "sdio_cis.h"
 #include "sdio_bus.h"
 
-#define dev_to_sdio_func(d)	container_of(d, struct sdio_func, dev)
-#define to_sdio_driver(d)      container_of(d, struct sdio_driver, drv)
-
 /* show configuration fields */
 #define sdio_config_attr(field, format_string)				\
 static ssize_t								\
diff --git a/include/linux/mmc/sdio_func.h b/include/linux/mmc/sdio_func.h
index 451bdfc..ac3ab68 100644
--- a/include/linux/mmc/sdio_func.h
+++ b/include/linux/mmc/sdio_func.h
@@ -67,6 +67,7 @@ struct sdio_func {
 
 #define sdio_get_drvdata(f)	dev_get_drvdata(&(f)->dev)
 #define sdio_set_drvdata(f,d)	dev_set_drvdata(&(f)->dev, d)
+#define dev_to_sdio_func(d)	container_of(d, struct sdio_func, dev)
 
 /*
  * SDIO function device driver
@@ -81,6 +82,8 @@ struct sdio_driver {
 	struct device_driver drv;
 };
 
+#define to_sdio_driver(d)	container_of(d, struct sdio_driver, drv)
+
 /**
  * SDIO_DEVICE - macro used to describe a specific SDIO device
  * @vend: the 16 bit manufacturer code
-- 
1.6.4.1.315.g5106d.dirty


^ permalink raw reply related	[flat|nested] 7+ messages in thread

* [PATCH 4/4] [MMC] introduce API for special power management features
  2009-09-02 18:37 [PATCH 0/4] power management support for SDIO cards Nicolas Pitre
                   ` (2 preceding siblings ...)
  2009-09-02 18:37 ` [PATCH 3/4] [MMC] make SDIO device/driver struct accessors public Nicolas Pitre
@ 2009-09-02 18:37 ` Nicolas Pitre
  3 siblings, 0 replies; 7+ messages in thread
From: Nicolas Pitre @ 2009-09-02 18:37 UTC (permalink / raw)
  To: linux-mmc

Some SDIO cards have the ability to keep on running autonomously when
the host system is suspended, and wake it up when needed.  This however
requires that the host controller preserve power to the card, and
configure itself appropriately for wake-up.

There is however 4 layers of abstractions involved: the host controller
driver, the MMC core code, the SDIO card management code, and the actual
SDIO function driver.  To make things simple and manageable, host drivers
must advertise their PM capabilities with a feature bitmask, then function
drivers can query and set those features from their suspend method.
Then each layer in the suspend call chain is expected to act upon those
bits accordingly.

Signed-off-by: Nicolas Pitre <nico@marvell.com>
---
 drivers/mmc/core/core.c       |   12 +++++++--
 drivers/mmc/core/sdio_io.c    |   49 +++++++++++++++++++++++++++++++++++++++++
 include/linux/mmc/host.h      |    5 ++++
 include/linux/mmc/pm.h        |   30 +++++++++++++++++++++++++
 include/linux/mmc/sdio_func.h |    5 ++++
 5 files changed, 98 insertions(+), 3 deletions(-)
 create mode 100644 include/linux/mmc/pm.h

diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c
index 2853f58..4bcbd5e 100644
--- a/drivers/mmc/core/core.c
+++ b/drivers/mmc/core/core.c
@@ -963,6 +963,9 @@ int mmc_suspend_host(struct mmc_host *host, pm_message_t state)
 	cancel_delayed_work(&host->detect);
 	mmc_flush_scheduled_work();
 
+	/* clear pm flags now and let card drivers set them as needed */
+	host->pm_flags = 0;
+
 	mmc_bus_get(host);
 	if (host->bus_ops && !host->bus_dead) {
 		if (host->bus_ops->suspend)
@@ -977,12 +980,13 @@ int mmc_suspend_host(struct mmc_host *host, pm_message_t state)
 			mmc_claim_host(host);
 			mmc_detach_bus(host);
 			mmc_release_host(host);
+			host->pm_flags = 0;
 			err = 0;
 		}
 	}
 	mmc_bus_put(host);
 
-	if (!err)
+	if (!err && !(host->pm_flags & MMC_PM_KEEP_POWER))
 		mmc_power_off(host);
 
 	return err;
@@ -1000,8 +1004,10 @@ int mmc_resume_host(struct mmc_host *host)
 
 	mmc_bus_get(host);
 	if (host->bus_ops && !host->bus_dead) {
-		mmc_power_up(host);
-		mmc_select_voltage(host, host->ocr);
+		if (!(host->pm_flags & MMC_PM_KEEP_POWER)) {
+			mmc_power_up(host);
+			mmc_select_voltage(host, host->ocr);
+		}
 		BUG_ON(!host->bus_ops->resume);
 		err = host->bus_ops->resume(host);
 		if (err) {
diff --git a/drivers/mmc/core/sdio_io.c b/drivers/mmc/core/sdio_io.c
index f61fc2d..468746b 100644
--- a/drivers/mmc/core/sdio_io.c
+++ b/drivers/mmc/core/sdio_io.c
@@ -635,3 +635,52 @@ void sdio_f0_writeb(struct sdio_func *func, unsigned char b, unsigned int addr,
 		*err_ret = ret;
 }
 EXPORT_SYMBOL_GPL(sdio_f0_writeb);
+
+/**
+ *	sdio_get_host_pm_caps - get host power management capabilities
+ *	@func: SDIO function attached to host
+ *
+ *	REturns a capability bitmask corresponding to power management
+ *	features supported by the host controller that the card function
+ *	might rely upon during a system suspend.  The host doesn't need
+ *	to be claimed, nor the function active, for this information to be
+ *	obtained.
+ */
+mmc_pm_flag_t sdio_get_host_pm_caps(struct sdio_func *func)
+{
+	BUG_ON(!func);
+	BUG_ON(!func->card);
+
+	return func->card->host->pm_caps;
+}
+EXPORT_SYMBOL_GPL(sdio_get_host_pm_caps);
+
+/**
+ *	sdio_set_host_pm_flags - set wanted host power management capabilities
+ *	@func: SDIO function attached to host
+ *
+ *	Set a capability bitmask corresponding to wanted host controller
+ *	power management features for the upcoming suspend state.
+ *	This must be called, if needed, each time the suspend method of
+ *	the function driver is called, and must contain only bits that
+ *	were returned by sdio_get_host_pm_caps().
+ *	The host doesn't need to be claimed, nor the function active,
+ *	for this information to be set.
+ */
+int sdio_set_host_pm_flags(struct sdio_func *func, mmc_pm_flag_t flags)
+{
+	struct mmc_host *host;
+
+	BUG_ON(!func);
+	BUG_ON(!func->card);
+
+	host = func->card->host;
+
+	if (flags & ~host->pm_caps)
+		return -EINVAL;
+
+	/* function suspend methods are serialized, hence no lock needed */
+	host->pm_flags |= flags;
+	return 0;
+}
+EXPORT_SYMBOL_GPL(sdio_set_host_pm_flags);
diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h
index 3e7615e..fe321d3 100644
--- a/include/linux/mmc/host.h
+++ b/include/linux/mmc/host.h
@@ -13,6 +13,7 @@
 #include <linux/leds.h>
 
 #include <linux/mmc/core.h>
+#include <linux/mmc/pm.h>
 
 struct mmc_ios {
 	unsigned int	clock;			/* clock rate */
@@ -119,6 +120,8 @@ struct mmc_host {
 #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 */
 
+	mmc_pm_flag_t		pm_caps;	/* supported pm features */
+
 	/* host specific block data */
 	unsigned int		max_seg_size;	/* see blk_queue_max_segment_size */
 	unsigned short		max_hw_segs;	/* see blk_queue_max_hw_segments */
@@ -155,6 +158,8 @@ struct mmc_host {
 	struct task_struct	*sdio_irq_thread;
 	atomic_t		sdio_irq_thread_abort;
 
+	mmc_pm_flag_t		pm_flags;	/* requested pm features */
+
 #ifdef CONFIG_LEDS_TRIGGERS
 	struct led_trigger	*led;		/* activity led */
 #endif
diff --git a/include/linux/mmc/pm.h b/include/linux/mmc/pm.h
new file mode 100644
index 0000000..d37aac4
--- /dev/null
+++ b/include/linux/mmc/pm.h
@@ -0,0 +1,30 @@
+/*
+ * linux/include/linux/mmc/pm.h
+ *
+ * Author:	Nicolas Pitre
+ * Copyright:	(C) 2009 Marvell Technology Group Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef LINUX_MMC_PM_H
+#define LINUX_MMC_PM_H
+
+/*
+ * These flags are used to describe power management features that
+ * some cards (typically SDIO cards) might wish to benefit from when
+ * the host system is being suspended.  There are several layers of
+ * abstractions involved, from the host controller driver, to the MMC core
+ * code, to the SDIO core code, to finally get to the actual SDIO function
+ * driver.  This file is therefore used for common definitions shared across
+ * all those layers.
+ */
+
+typedef unsigned int mmc_pm_flag_t;
+
+#define MMC_PM_KEEP_POWER	(1 << 0)	/* preserve card power during suspend */
+#define MMC_PM_WAKE_SDIO_IRQ	(1 << 1)	/* wake up host system on SDIO IRQ assertion */
+
+#endif
diff --git a/include/linux/mmc/sdio_func.h b/include/linux/mmc/sdio_func.h
index ac3ab68..c6c0cce 100644
--- a/include/linux/mmc/sdio_func.h
+++ b/include/linux/mmc/sdio_func.h
@@ -15,6 +15,8 @@
 #include <linux/device.h>
 #include <linux/mod_devicetable.h>
 
+#include <linux/mmc/pm.h>
+
 struct mmc_card;
 struct sdio_func;
 
@@ -153,5 +155,8 @@ extern unsigned char sdio_f0_readb(struct sdio_func *func,
 extern void sdio_f0_writeb(struct sdio_func *func, unsigned char b,
 	unsigned int addr, int *err_ret);
 
+extern mmc_pm_flag_t sdio_get_host_pm_caps(struct sdio_func *func);
+extern int sdio_set_host_pm_flags(struct sdio_func *func, mmc_pm_flag_t flags);
+
 #endif
 
-- 
1.6.4.1.315.g5106d.dirty


^ permalink raw reply related	[flat|nested] 7+ messages in thread

* Re: [PATCH 1/4] [MMC] core SDIO suspend/resume support
  2009-09-02 18:37 ` [PATCH 1/4] [MMC] core SDIO suspend/resume support Nicolas Pitre
@ 2009-09-06 14:17   ` Pierre Ossman
  2009-09-08  1:53     ` Nicolas Pitre
  0 siblings, 1 reply; 7+ messages in thread
From: Pierre Ossman @ 2009-09-06 14:17 UTC (permalink / raw)
  To: Nicolas Pitre; +Cc: linux-mmc

[-- Attachment #1: Type: text/plain, Size: 1104 bytes --]

On Wed, 02 Sep 2009 14:37:23 -0400
Nicolas Pitre <nico@cam.org> wrote:

> 
> When resuming, we make sure the same card is still inserted by comparing
> the vendor and product IDs.  If there is a mismatch, or if there is simply
> no card anymore in the slot, then the previous card is "removed" and the
> new card is detected.
> 

I know this is an area with a lot of differing views, but I see this as
totally insufficient. Like storage cards, this card might have a
non-volatile state that drivers and upper layers rely on being the same
before and after suspend.

(Then there's also the issue of e.g. having two "identical" wifi cards
but with different MAC addresses).

I think the right way to go is to retain enough power to the card for
it to stay initialised and then poke it at resume to see if it is still
running in the same state we left it.

Rgds
-- 
     -- Pierre Ossman

  WARNING: This correspondence is being monitored by the
  Swedish government. Make sure your server uses encryption
  for SMTP traffic and consider using PGP for end-to-end
  encryption.

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 198 bytes --]

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [PATCH 1/4] [MMC] core SDIO suspend/resume support
  2009-09-06 14:17   ` Pierre Ossman
@ 2009-09-08  1:53     ` Nicolas Pitre
  0 siblings, 0 replies; 7+ messages in thread
From: Nicolas Pitre @ 2009-09-08  1:53 UTC (permalink / raw)
  To: Pierre Ossman; +Cc: linux-mmc

On Sun, 6 Sep 2009, Pierre Ossman wrote:

> On Wed, 02 Sep 2009 14:37:23 -0400
> Nicolas Pitre <nico@cam.org> wrote:
> 
> > 
> > When resuming, we make sure the same card is still inserted by comparing
> > the vendor and product IDs.  If there is a mismatch, or if there is simply
> > no card anymore in the slot, then the previous card is "removed" and the
> > new card is detected.
> > 
> 
> I know this is an area with a lot of differing views, but I see this as
> totally insufficient. Like storage cards, this card might have a
> non-volatile state that drivers and upper layers rely on being the same
> before and after suspend.
> 
> (Then there's also the issue of e.g. having two "identical" wifi cards
> but with different MAC addresses).
> 
> I think the right way to go is to retain enough power to the card for
> it to stay initialised and then poke it at resume to see if it is still
> running in the same state we left it.

IMHO the right way to go is instead to consider patch2/4 that goes along 
with this series and defer such additional intelligence to each function 
driver's resume method.  After all that's where knowledge of MAC 
addresses and so on should be, not in the core code.

And in the context of power management, always having to keep power to 
the card is somehow against the point, isn't it?  Especially considering 
that this simply is not always possible.


Nicolas

^ permalink raw reply	[flat|nested] 7+ messages in thread

end of thread, other threads:[~2009-09-08  1:53 UTC | newest]

Thread overview: 7+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2009-09-02 18:37 [PATCH 0/4] power management support for SDIO cards Nicolas Pitre
2009-09-02 18:37 ` [PATCH 1/4] [MMC] core SDIO suspend/resume support Nicolas Pitre
2009-09-06 14:17   ` Pierre Ossman
2009-09-08  1:53     ` Nicolas Pitre
2009-09-02 18:37 ` [PATCH 2/4] [MMC] propagate error codes back from bus drivers' suspend/resume methods Nicolas Pitre
2009-09-02 18:37 ` [PATCH 3/4] [MMC] make SDIO device/driver struct accessors public Nicolas Pitre
2009-09-02 18:37 ` [PATCH 4/4] [MMC] introduce API for special power management features Nicolas Pitre

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.