aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorNicolas Pitre <nico@fluxnic.net>2010-03-05 16:43:31 -0500
committerLinus Torvalds <torvalds@linux-foundation.org>2010-03-06 14:26:36 -0500
commitda68c4eb258cd9f3f0b8aeb7e46b8118bb6358b6 (patch)
treeb7767ce0b0d05d16dec6eeeadd21bd70cc458ac7
parent9e506f35b1dc327c448d4791bc098f07b9b2efe9 (diff)
sdio: introduce API for special power management features
This patch series provides the core changes needed to allow SDIO cards to remain powered and active while the host system is suspended, and let them wake up the host system when needed. This is used to implement wake-on-lan with SDIO wireless cards at the moment. Patches to add that support to the libertas driver will be posted separately. This patch: 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. [akpm@linux-foundation.org: fix typo in comment] Signed-off-by: Nicolas Pitre <nico@marvell.com> Cc: <linux-mmc@vger.kernel.org> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
-rw-r--r--drivers/mmc/core/core.c12
-rw-r--r--drivers/mmc/core/sdio_io.c49
-rw-r--r--include/linux/mmc/host.h5
-rw-r--r--include/linux/mmc/pm.h30
-rw-r--r--include/linux/mmc/sdio_func.h5
5 files changed, 98 insertions, 3 deletions
diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c
index 30acd5265821..f4b97d3c3d0f 100644
--- a/drivers/mmc/core/core.c
+++ b/drivers/mmc/core/core.c
@@ -1151,6 +1151,9 @@ void mmc_stop_host(struct mmc_host *host)
1151 cancel_delayed_work(&host->detect); 1151 cancel_delayed_work(&host->detect);
1152 mmc_flush_scheduled_work(); 1152 mmc_flush_scheduled_work();
1153 1153
1154 /* clear pm flags now and let card drivers set them as needed */
1155 host->pm_flags = 0;
1156
1154 mmc_bus_get(host); 1157 mmc_bus_get(host);
1155 if (host->bus_ops && !host->bus_dead) { 1158 if (host->bus_ops && !host->bus_dead) {
1156 if (host->bus_ops->remove) 1159 if (host->bus_ops->remove)
@@ -1273,12 +1276,13 @@ int mmc_suspend_host(struct mmc_host *host, pm_message_t state)
1273 mmc_claim_host(host); 1276 mmc_claim_host(host);
1274 mmc_detach_bus(host); 1277 mmc_detach_bus(host);
1275 mmc_release_host(host); 1278 mmc_release_host(host);
1279 host->pm_flags = 0;
1276 err = 0; 1280 err = 0;
1277 } 1281 }
1278 } 1282 }
1279 mmc_bus_put(host); 1283 mmc_bus_put(host);
1280 1284
1281 if (!err) 1285 if (!err && !(host->pm_flags & MMC_PM_KEEP_POWER))
1282 mmc_power_off(host); 1286 mmc_power_off(host);
1283 1287
1284 return err; 1288 return err;
@@ -1296,8 +1300,10 @@ int mmc_resume_host(struct mmc_host *host)
1296 1300
1297 mmc_bus_get(host); 1301 mmc_bus_get(host);
1298 if (host->bus_ops && !host->bus_dead) { 1302 if (host->bus_ops && !host->bus_dead) {
1299 mmc_power_up(host); 1303 if (!(host->pm_flags & MMC_PM_KEEP_POWER)) {
1300 mmc_select_voltage(host, host->ocr); 1304 mmc_power_up(host);
1305 mmc_select_voltage(host, host->ocr);
1306 }
1301 BUG_ON(!host->bus_ops->resume); 1307 BUG_ON(!host->bus_ops->resume);
1302 err = host->bus_ops->resume(host); 1308 err = host->bus_ops->resume(host);
1303 if (err) { 1309 if (err) {
diff --git a/drivers/mmc/core/sdio_io.c b/drivers/mmc/core/sdio_io.c
index 87c618904ee2..ff27c8c71355 100644
--- a/drivers/mmc/core/sdio_io.c
+++ b/drivers/mmc/core/sdio_io.c
@@ -640,3 +640,52 @@ void sdio_f0_writeb(struct sdio_func *func, unsigned char b, unsigned int addr,
640 *err_ret = ret; 640 *err_ret = ret;
641} 641}
642EXPORT_SYMBOL_GPL(sdio_f0_writeb); 642EXPORT_SYMBOL_GPL(sdio_f0_writeb);
643
644/**
645 * sdio_get_host_pm_caps - get host power management capabilities
646 * @func: SDIO function attached to host
647 *
648 * Returns a capability bitmask corresponding to power management
649 * features supported by the host controller that the card function
650 * might rely upon during a system suspend. The host doesn't need
651 * to be claimed, nor the function active, for this information to be
652 * obtained.
653 */
654mmc_pm_flag_t sdio_get_host_pm_caps(struct sdio_func *func)
655{
656 BUG_ON(!func);
657 BUG_ON(!func->card);
658
659 return func->card->host->pm_caps;
660}
661EXPORT_SYMBOL_GPL(sdio_get_host_pm_caps);
662
663/**
664 * sdio_set_host_pm_flags - set wanted host power management capabilities
665 * @func: SDIO function attached to host
666 *
667 * Set a capability bitmask corresponding to wanted host controller
668 * power management features for the upcoming suspend state.
669 * This must be called, if needed, each time the suspend method of
670 * the function driver is called, and must contain only bits that
671 * were returned by sdio_get_host_pm_caps().
672 * The host doesn't need to be claimed, nor the function active,
673 * for this information to be set.
674 */
675int sdio_set_host_pm_flags(struct sdio_func *func, mmc_pm_flag_t flags)
676{
677 struct mmc_host *host;
678
679 BUG_ON(!func);
680 BUG_ON(!func->card);
681
682 host = func->card->host;
683
684 if (flags & ~host->pm_caps)
685 return -EINVAL;
686
687 /* function suspend methods are serialized, hence no lock needed */
688 host->pm_flags |= flags;
689 return 0;
690}
691EXPORT_SYMBOL_GPL(sdio_set_host_pm_flags);
diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h
index eaf36364b7d4..43eaf5ca5848 100644
--- a/include/linux/mmc/host.h
+++ b/include/linux/mmc/host.h
@@ -14,6 +14,7 @@
14#include <linux/sched.h> 14#include <linux/sched.h>
15 15
16#include <linux/mmc/core.h> 16#include <linux/mmc/core.h>
17#include <linux/mmc/pm.h>
17 18
18struct mmc_ios { 19struct mmc_ios {
19 unsigned int clock; /* clock rate */ 20 unsigned int clock; /* clock rate */
@@ -152,6 +153,8 @@ struct mmc_host {
152#define MMC_CAP_NONREMOVABLE (1 << 8) /* Nonremovable e.g. eMMC */ 153#define MMC_CAP_NONREMOVABLE (1 << 8) /* Nonremovable e.g. eMMC */
153#define MMC_CAP_WAIT_WHILE_BUSY (1 << 9) /* Waits while card is busy */ 154#define MMC_CAP_WAIT_WHILE_BUSY (1 << 9) /* Waits while card is busy */
154 155
156 mmc_pm_flag_t pm_caps; /* supported pm features */
157
155 /* host specific block data */ 158 /* host specific block data */
156 unsigned int max_seg_size; /* see blk_queue_max_segment_size */ 159 unsigned int max_seg_size; /* see blk_queue_max_segment_size */
157 unsigned short max_hw_segs; /* see blk_queue_max_hw_segments */ 160 unsigned short max_hw_segs; /* see blk_queue_max_hw_segments */
@@ -197,6 +200,8 @@ struct mmc_host {
197 struct task_struct *sdio_irq_thread; 200 struct task_struct *sdio_irq_thread;
198 atomic_t sdio_irq_thread_abort; 201 atomic_t sdio_irq_thread_abort;
199 202
203 mmc_pm_flag_t pm_flags; /* requested pm features */
204
200#ifdef CONFIG_LEDS_TRIGGERS 205#ifdef CONFIG_LEDS_TRIGGERS
201 struct led_trigger *led; /* activity led */ 206 struct led_trigger *led; /* activity led */
202#endif 207#endif
diff --git a/include/linux/mmc/pm.h b/include/linux/mmc/pm.h
new file mode 100644
index 000000000000..d37aac49cf9a
--- /dev/null
+++ b/include/linux/mmc/pm.h
@@ -0,0 +1,30 @@
1/*
2 * linux/include/linux/mmc/pm.h
3 *
4 * Author: Nicolas Pitre
5 * Copyright: (C) 2009 Marvell Technology Group Ltd.
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License version 2 as
9 * published by the Free Software Foundation.
10 */
11
12#ifndef LINUX_MMC_PM_H
13#define LINUX_MMC_PM_H
14
15/*
16 * These flags are used to describe power management features that
17 * some cards (typically SDIO cards) might wish to benefit from when
18 * the host system is being suspended. There are several layers of
19 * abstractions involved, from the host controller driver, to the MMC core
20 * code, to the SDIO core code, to finally get to the actual SDIO function
21 * driver. This file is therefore used for common definitions shared across
22 * all those layers.
23 */
24
25typedef unsigned int mmc_pm_flag_t;
26
27#define MMC_PM_KEEP_POWER (1 << 0) /* preserve card power during suspend */
28#define MMC_PM_WAKE_SDIO_IRQ (1 << 1) /* wake up host system on SDIO IRQ assertion */
29
30#endif
diff --git a/include/linux/mmc/sdio_func.h b/include/linux/mmc/sdio_func.h
index ac3ab683fec6..c6c0cceba5fe 100644
--- a/include/linux/mmc/sdio_func.h
+++ b/include/linux/mmc/sdio_func.h
@@ -15,6 +15,8 @@
15#include <linux/device.h> 15#include <linux/device.h>
16#include <linux/mod_devicetable.h> 16#include <linux/mod_devicetable.h>
17 17
18#include <linux/mmc/pm.h>
19
18struct mmc_card; 20struct mmc_card;
19struct sdio_func; 21struct sdio_func;
20 22
@@ -153,5 +155,8 @@ extern unsigned char sdio_f0_readb(struct sdio_func *func,
153extern void sdio_f0_writeb(struct sdio_func *func, unsigned char b, 155extern void sdio_f0_writeb(struct sdio_func *func, unsigned char b,
154 unsigned int addr, int *err_ret); 156 unsigned int addr, int *err_ret);
155 157
158extern mmc_pm_flag_t sdio_get_host_pm_caps(struct sdio_func *func);
159extern int sdio_set_host_pm_flags(struct sdio_func *func, mmc_pm_flag_t flags);
160
156#endif 161#endif
157 162