From da68c4eb258cd9f3f0b8aeb7e46b8118bb6358b6 Mon Sep 17 00:00:00 2001 From: Nicolas Pitre Date: Fri, 5 Mar 2010 13:43:31 -0800 Subject: 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 Cc: Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/mmc/core/core.c | 12 +++++++++--- drivers/mmc/core/sdio_io.c | 49 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 58 insertions(+), 3 deletions(-) (limited to 'drivers/mmc/core') diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c index 30acd526582..f4b97d3c3d0 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) 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->remove) @@ -1273,12 +1276,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; @@ -1296,8 +1300,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 87c618904ee..ff27c8c7135 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, *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); -- cgit v1.2.3-70-g09d2