Commit eae1aeee authored by Adrian Hunter's avatar Adrian Hunter Committed by Linus Torvalds

mmc: add ability to save power by powering off cards

Power can be saved by powering off cards that are not in use.  This is
similar to suspend / resume except it is under the control of the driver,
and does not require any power management support.  It can only be used
when the driver can monitor whether the card is removed, otherwise it is
unsafe.  This is possible because, unlike suspend, the driver still
receives card detect and / or cover switch interrupts.
Signed-off-by: default avatarAdrian Hunter <adrian.hunter@nokia.com>
Acked-by: default avatarMatt Fleming <matt@console-pimps.org>
Cc: Ian Molton <ian@mnementh.co.uk>
Cc: "Roberto A. Foglietta" <roberto.foglietta@gmail.com>
Cc: Jarkko Lavinen <jarkko.lavinen@nokia.com>
Cc: Denis Karpov <ext-denis.2.karpov@nokia.com>
Cc: Pierre Ossman <pierre@ossman.eu>
Cc: Philip Langdale <philipl@overt.org>
Cc: "Madhusudhan" <madhu.cr@ti.com>
Cc: <linux-mmc@vger.kernel.org>
Signed-off-by: default avatarAndrew Morton <akpm@linux-foundation.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@linux-foundation.org>
parent 9feae246
...@@ -1151,6 +1151,40 @@ void mmc_stop_host(struct mmc_host *host) ...@@ -1151,6 +1151,40 @@ void mmc_stop_host(struct mmc_host *host)
mmc_power_off(host); mmc_power_off(host);
} }
void mmc_power_save_host(struct mmc_host *host)
{
mmc_bus_get(host);
if (!host->bus_ops || host->bus_dead || !host->bus_ops->power_restore) {
mmc_bus_put(host);
return;
}
if (host->bus_ops->power_save)
host->bus_ops->power_save(host);
mmc_bus_put(host);
mmc_power_off(host);
}
EXPORT_SYMBOL(mmc_power_save_host);
void mmc_power_restore_host(struct mmc_host *host)
{
mmc_bus_get(host);
if (!host->bus_ops || host->bus_dead || !host->bus_ops->power_restore) {
mmc_bus_put(host);
return;
}
mmc_power_up(host);
host->bus_ops->power_restore(host);
mmc_bus_put(host);
}
EXPORT_SYMBOL(mmc_power_restore_host);
#ifdef CONFIG_PM #ifdef CONFIG_PM
/** /**
......
...@@ -20,6 +20,8 @@ struct mmc_bus_ops { ...@@ -20,6 +20,8 @@ struct mmc_bus_ops {
void (*detect)(struct mmc_host *); void (*detect)(struct mmc_host *);
void (*suspend)(struct mmc_host *); void (*suspend)(struct mmc_host *);
void (*resume)(struct mmc_host *); void (*resume)(struct mmc_host *);
void (*power_save)(struct mmc_host *);
void (*power_restore)(struct mmc_host *);
}; };
void mmc_attach_bus(struct mmc_host *host, const struct mmc_bus_ops *ops); void mmc_attach_bus(struct mmc_host *host, const struct mmc_bus_ops *ops);
......
...@@ -549,6 +549,14 @@ static void mmc_resume(struct mmc_host *host) ...@@ -549,6 +549,14 @@ static void mmc_resume(struct mmc_host *host)
} }
static void mmc_power_restore(struct mmc_host *host)
{
host->card->state &= ~MMC_STATE_HIGHSPEED;
mmc_claim_host(host);
mmc_init_card(host, host->ocr, host->card);
mmc_release_host(host);
}
#ifdef CONFIG_MMC_UNSAFE_RESUME #ifdef CONFIG_MMC_UNSAFE_RESUME
static const struct mmc_bus_ops mmc_ops = { static const struct mmc_bus_ops mmc_ops = {
...@@ -556,6 +564,7 @@ static const struct mmc_bus_ops mmc_ops = { ...@@ -556,6 +564,7 @@ static const struct mmc_bus_ops mmc_ops = {
.detect = mmc_detect, .detect = mmc_detect,
.suspend = mmc_suspend, .suspend = mmc_suspend,
.resume = mmc_resume, .resume = mmc_resume,
.power_restore = mmc_power_restore,
}; };
static void mmc_attach_bus_ops(struct mmc_host *host) static void mmc_attach_bus_ops(struct mmc_host *host)
...@@ -570,6 +579,7 @@ static const struct mmc_bus_ops mmc_ops = { ...@@ -570,6 +579,7 @@ static const struct mmc_bus_ops mmc_ops = {
.detect = mmc_detect, .detect = mmc_detect,
.suspend = NULL, .suspend = NULL,
.resume = NULL, .resume = NULL,
.power_restore = mmc_power_restore,
}; };
static const struct mmc_bus_ops mmc_ops_unsafe = { static const struct mmc_bus_ops mmc_ops_unsafe = {
...@@ -577,6 +587,7 @@ static const struct mmc_bus_ops mmc_ops_unsafe = { ...@@ -577,6 +587,7 @@ static const struct mmc_bus_ops mmc_ops_unsafe = {
.detect = mmc_detect, .detect = mmc_detect,
.suspend = mmc_suspend, .suspend = mmc_suspend,
.resume = mmc_resume, .resume = mmc_resume,
.power_restore = mmc_power_restore,
}; };
static void mmc_attach_bus_ops(struct mmc_host *host) static void mmc_attach_bus_ops(struct mmc_host *host)
......
...@@ -603,6 +603,14 @@ static void mmc_sd_resume(struct mmc_host *host) ...@@ -603,6 +603,14 @@ static void mmc_sd_resume(struct mmc_host *host)
} }
static void mmc_sd_power_restore(struct mmc_host *host)
{
host->card->state &= ~MMC_STATE_HIGHSPEED;
mmc_claim_host(host);
mmc_sd_init_card(host, host->ocr, host->card);
mmc_release_host(host);
}
#ifdef CONFIG_MMC_UNSAFE_RESUME #ifdef CONFIG_MMC_UNSAFE_RESUME
static const struct mmc_bus_ops mmc_sd_ops = { static const struct mmc_bus_ops mmc_sd_ops = {
...@@ -610,6 +618,7 @@ static const struct mmc_bus_ops mmc_sd_ops = { ...@@ -610,6 +618,7 @@ static const struct mmc_bus_ops mmc_sd_ops = {
.detect = mmc_sd_detect, .detect = mmc_sd_detect,
.suspend = mmc_sd_suspend, .suspend = mmc_sd_suspend,
.resume = mmc_sd_resume, .resume = mmc_sd_resume,
.power_restore = mmc_sd_power_restore,
}; };
static void mmc_sd_attach_bus_ops(struct mmc_host *host) static void mmc_sd_attach_bus_ops(struct mmc_host *host)
...@@ -624,6 +633,7 @@ static const struct mmc_bus_ops mmc_sd_ops = { ...@@ -624,6 +633,7 @@ static const struct mmc_bus_ops mmc_sd_ops = {
.detect = mmc_sd_detect, .detect = mmc_sd_detect,
.suspend = NULL, .suspend = NULL,
.resume = NULL, .resume = NULL,
.power_restore = mmc_sd_power_restore,
}; };
static const struct mmc_bus_ops mmc_sd_ops_unsafe = { static const struct mmc_bus_ops mmc_sd_ops_unsafe = {
...@@ -631,6 +641,7 @@ static const struct mmc_bus_ops mmc_sd_ops_unsafe = { ...@@ -631,6 +641,7 @@ static const struct mmc_bus_ops mmc_sd_ops_unsafe = {
.detect = mmc_sd_detect, .detect = mmc_sd_detect,
.suspend = mmc_sd_suspend, .suspend = mmc_sd_suspend,
.resume = mmc_sd_resume, .resume = mmc_sd_resume,
.power_restore = mmc_sd_power_restore,
}; };
static void mmc_sd_attach_bus_ops(struct mmc_host *host) static void mmc_sd_attach_bus_ops(struct mmc_host *host)
......
...@@ -223,6 +223,9 @@ static inline void *mmc_priv(struct mmc_host *host) ...@@ -223,6 +223,9 @@ static inline void *mmc_priv(struct mmc_host *host)
extern int mmc_suspend_host(struct mmc_host *, pm_message_t); extern int mmc_suspend_host(struct mmc_host *, pm_message_t);
extern int mmc_resume_host(struct mmc_host *); extern int mmc_resume_host(struct mmc_host *);
extern void mmc_power_save_host(struct mmc_host *host);
extern void mmc_power_restore_host(struct mmc_host *host);
extern void mmc_detect_change(struct mmc_host *, unsigned long delay); extern void mmc_detect_change(struct mmc_host *, unsigned long delay);
extern void mmc_request_done(struct mmc_host *, struct mmc_request *); extern void mmc_request_done(struct mmc_host *, struct mmc_request *);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment