Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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: Adrian Hunter <adrian.hunter@nokia.com>
Acked-by: Matt 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: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>

authored by

Adrian Hunter and committed by
Linus Torvalds
eae1aeee 9feae246

+61
+34
drivers/mmc/core/core.c
··· 1151 1151 mmc_power_off(host); 1152 1152 } 1153 1153 1154 + void mmc_power_save_host(struct mmc_host *host) 1155 + { 1156 + mmc_bus_get(host); 1157 + 1158 + if (!host->bus_ops || host->bus_dead || !host->bus_ops->power_restore) { 1159 + mmc_bus_put(host); 1160 + return; 1161 + } 1162 + 1163 + if (host->bus_ops->power_save) 1164 + host->bus_ops->power_save(host); 1165 + 1166 + mmc_bus_put(host); 1167 + 1168 + mmc_power_off(host); 1169 + } 1170 + EXPORT_SYMBOL(mmc_power_save_host); 1171 + 1172 + void mmc_power_restore_host(struct mmc_host *host) 1173 + { 1174 + mmc_bus_get(host); 1175 + 1176 + if (!host->bus_ops || host->bus_dead || !host->bus_ops->power_restore) { 1177 + mmc_bus_put(host); 1178 + return; 1179 + } 1180 + 1181 + mmc_power_up(host); 1182 + host->bus_ops->power_restore(host); 1183 + 1184 + mmc_bus_put(host); 1185 + } 1186 + EXPORT_SYMBOL(mmc_power_restore_host); 1187 + 1154 1188 #ifdef CONFIG_PM 1155 1189 1156 1190 /**
+2
drivers/mmc/core/core.h
··· 20 20 void (*detect)(struct mmc_host *); 21 21 void (*suspend)(struct mmc_host *); 22 22 void (*resume)(struct mmc_host *); 23 + void (*power_save)(struct mmc_host *); 24 + void (*power_restore)(struct mmc_host *); 23 25 }; 24 26 25 27 void mmc_attach_bus(struct mmc_host *host, const struct mmc_bus_ops *ops);
+11
drivers/mmc/core/mmc.c
··· 549 549 550 550 } 551 551 552 + static void mmc_power_restore(struct mmc_host *host) 553 + { 554 + host->card->state &= ~MMC_STATE_HIGHSPEED; 555 + mmc_claim_host(host); 556 + mmc_init_card(host, host->ocr, host->card); 557 + mmc_release_host(host); 558 + } 559 + 552 560 #ifdef CONFIG_MMC_UNSAFE_RESUME 553 561 554 562 static const struct mmc_bus_ops mmc_ops = { ··· 564 556 .detect = mmc_detect, 565 557 .suspend = mmc_suspend, 566 558 .resume = mmc_resume, 559 + .power_restore = mmc_power_restore, 567 560 }; 568 561 569 562 static void mmc_attach_bus_ops(struct mmc_host *host) ··· 579 570 .detect = mmc_detect, 580 571 .suspend = NULL, 581 572 .resume = NULL, 573 + .power_restore = mmc_power_restore, 582 574 }; 583 575 584 576 static const struct mmc_bus_ops mmc_ops_unsafe = { ··· 587 577 .detect = mmc_detect, 588 578 .suspend = mmc_suspend, 589 579 .resume = mmc_resume, 580 + .power_restore = mmc_power_restore, 590 581 }; 591 582 592 583 static void mmc_attach_bus_ops(struct mmc_host *host)
+11
drivers/mmc/core/sd.c
··· 603 603 604 604 } 605 605 606 + static void mmc_sd_power_restore(struct mmc_host *host) 607 + { 608 + host->card->state &= ~MMC_STATE_HIGHSPEED; 609 + mmc_claim_host(host); 610 + mmc_sd_init_card(host, host->ocr, host->card); 611 + mmc_release_host(host); 612 + } 613 + 606 614 #ifdef CONFIG_MMC_UNSAFE_RESUME 607 615 608 616 static const struct mmc_bus_ops mmc_sd_ops = { ··· 618 610 .detect = mmc_sd_detect, 619 611 .suspend = mmc_sd_suspend, 620 612 .resume = mmc_sd_resume, 613 + .power_restore = mmc_sd_power_restore, 621 614 }; 622 615 623 616 static void mmc_sd_attach_bus_ops(struct mmc_host *host) ··· 633 624 .detect = mmc_sd_detect, 634 625 .suspend = NULL, 635 626 .resume = NULL, 627 + .power_restore = mmc_sd_power_restore, 636 628 }; 637 629 638 630 static const struct mmc_bus_ops mmc_sd_ops_unsafe = { ··· 641 631 .detect = mmc_sd_detect, 642 632 .suspend = mmc_sd_suspend, 643 633 .resume = mmc_sd_resume, 634 + .power_restore = mmc_sd_power_restore, 644 635 }; 645 636 646 637 static void mmc_sd_attach_bus_ops(struct mmc_host *host)
+3
include/linux/mmc/host.h
··· 223 223 extern int mmc_suspend_host(struct mmc_host *, pm_message_t); 224 224 extern int mmc_resume_host(struct mmc_host *); 225 225 226 + extern void mmc_power_save_host(struct mmc_host *host); 227 + extern void mmc_power_restore_host(struct mmc_host *host); 228 + 226 229 extern void mmc_detect_change(struct mmc_host *, unsigned long delay); 227 230 extern void mmc_request_done(struct mmc_host *, struct mmc_request *); 228 231