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

mmc: sdio: fix runtime PM anomalies by introducing MMC_CAP_POWER_OFF_CARD

Some board/card/host configurations are not capable of powering off the
card after boot.

To support such configurations, and to allow smoother transition to
runtime PM behavior, MMC_CAP_POWER_OFF_CARD is added, so hosts need to
explicitly indicate whether it's OK to power off their cards after boot.

SDIO core will enable runtime PM for a card only if that cap is set.
As a result, the card will be powered down after boot, and will only
be powered up again when a driver is loaded (and then it's up to the
driver to decide whether power will be kept or not).

This will prevent sdio_bus_probe() failures with setups that do not
support powering off the card.

Reported-and-tested-by: Daniel Drake <dsd@laptop.org>
Reported-and-tested-by: Arnd Hannemann <arnd@arndnet.de>
Signed-off-by: Ohad Ben-Cohen <ohad@wizery.com>
Signed-off-by: Chris Ball <cjb@laptop.org>

authored by

Ohad Ben-Cohen and committed by
Chris Ball
ed919b01 4d0812c3

+46 -25
+23 -14
drivers/mmc/core/sdio.c
··· 547 547 BUG_ON(!host->card); 548 548 549 549 /* Make sure card is powered before detecting it */ 550 - err = pm_runtime_get_sync(&host->card->dev); 551 - if (err < 0) 552 - goto out; 550 + if (host->caps & MMC_CAP_POWER_OFF_CARD) { 551 + err = pm_runtime_get_sync(&host->card->dev); 552 + if (err < 0) 553 + goto out; 554 + } 553 555 554 556 mmc_claim_host(host); 555 557 ··· 573 571 * is about to show up at this point, the _sync variant is 574 572 * desirable anyway. 575 573 */ 576 - pm_runtime_put_sync(&host->card->dev); 574 + if (host->caps & MMC_CAP_POWER_OFF_CARD) 575 + pm_runtime_put_sync(&host->card->dev); 577 576 578 577 out: 579 578 if (err) { ··· 731 728 card = host->card; 732 729 733 730 /* 734 - * Let runtime PM core know our card is active 731 + * Enable runtime PM only if supported by host+card+board 735 732 */ 736 - err = pm_runtime_set_active(&card->dev); 737 - if (err) 738 - goto remove; 733 + if (host->caps & MMC_CAP_POWER_OFF_CARD) { 734 + /* 735 + * Let runtime PM core know our card is active 736 + */ 737 + err = pm_runtime_set_active(&card->dev); 738 + if (err) 739 + goto remove; 739 740 740 - /* 741 - * Enable runtime PM for this card 742 - */ 743 - pm_runtime_enable(&card->dev); 741 + /* 742 + * Enable runtime PM for this card 743 + */ 744 + pm_runtime_enable(&card->dev); 745 + } 744 746 745 747 /* 746 748 * The number of functions on the card is encoded inside ··· 763 755 goto remove; 764 756 765 757 /* 766 - * Enable Runtime PM for this func 758 + * Enable Runtime PM for this func (if supported) 767 759 */ 768 - pm_runtime_enable(&card->sdio_func[i]->dev); 760 + if (host->caps & MMC_CAP_POWER_OFF_CARD) 761 + pm_runtime_enable(&card->sdio_func[i]->dev); 769 762 } 770 763 771 764 mmc_release_host(host);
+22 -11
drivers/mmc/core/sdio_bus.c
··· 17 17 #include <linux/pm_runtime.h> 18 18 19 19 #include <linux/mmc/card.h> 20 + #include <linux/mmc/host.h> 20 21 #include <linux/mmc/sdio_func.h> 21 22 22 23 #include "sdio_cis.h" ··· 133 132 * it should call pm_runtime_put_noidle() in its probe routine and 134 133 * pm_runtime_get_noresume() in its remove routine. 135 134 */ 136 - ret = pm_runtime_get_sync(dev); 137 - if (ret < 0) 138 - goto out; 135 + if (func->card->host->caps & MMC_CAP_POWER_OFF_CARD) { 136 + ret = pm_runtime_get_sync(dev); 137 + if (ret < 0) 138 + goto out; 139 + } 139 140 140 141 /* Set the default block size so the driver is sure it's something 141 142 * sensible. */ ··· 154 151 return 0; 155 152 156 153 disable_runtimepm: 157 - pm_runtime_put_noidle(dev); 154 + if (func->card->host->caps & MMC_CAP_POWER_OFF_CARD) 155 + pm_runtime_put_noidle(dev); 158 156 out: 159 157 return ret; 160 158 } ··· 164 160 { 165 161 struct sdio_driver *drv = to_sdio_driver(dev->driver); 166 162 struct sdio_func *func = dev_to_sdio_func(dev); 167 - int ret; 163 + int ret = 0; 168 164 169 165 /* Make sure card is powered before invoking ->remove() */ 170 - ret = pm_runtime_get_sync(dev); 171 - if (ret < 0) 172 - goto out; 166 + if (func->card->host->caps & MMC_CAP_POWER_OFF_CARD) { 167 + ret = pm_runtime_get_sync(dev); 168 + if (ret < 0) 169 + goto out; 170 + } 173 171 174 172 drv->remove(func); 175 173 ··· 184 178 } 185 179 186 180 /* First, undo the increment made directly above */ 187 - pm_runtime_put_noidle(dev); 181 + if (func->card->host->caps & MMC_CAP_POWER_OFF_CARD) 182 + pm_runtime_put_noidle(dev); 188 183 189 184 /* Then undo the runtime PM settings in sdio_bus_probe() */ 190 - pm_runtime_put_noidle(dev); 185 + if (func->card->host->caps & MMC_CAP_POWER_OFF_CARD) 186 + pm_runtime_put_noidle(dev); 191 187 192 188 out: 193 189 return ret; ··· 199 191 200 192 static int sdio_bus_pm_prepare(struct device *dev) 201 193 { 194 + struct sdio_func *func = dev_to_sdio_func(dev); 195 + 202 196 /* 203 197 * Resume an SDIO device which was suspended at run time at this 204 198 * point, in order to allow standard SDIO suspend/resume paths ··· 222 212 * since there is little point in failing system suspend if a 223 213 * device can't be resumed. 224 214 */ 225 - pm_runtime_resume(dev); 215 + if (func->card->host->caps & MMC_CAP_POWER_OFF_CARD) 216 + pm_runtime_resume(dev); 226 217 227 218 return 0; 228 219 }
+1
include/linux/mmc/host.h
··· 168 168 /* DDR mode at 1.8V */ 169 169 #define MMC_CAP_1_2V_DDR (1 << 12) /* can support */ 170 170 /* DDR mode at 1.2V */ 171 + #define MMC_CAP_POWER_OFF_CARD (1 << 13) /* Can power off after boot */ 171 172 172 173 mmc_pm_flag_t pm_caps; /* supported pm features */ 173 174