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>
This commit is contained in:
		
					parent
					
						
							
								4d0812c37f
							
						
					
				
			
			
				commit
				
					
						ed919b0125
					
				
			
		
					 3 changed files with 46 additions and 25 deletions
				
			
		|  | @ -547,9 +547,11 @@ static void mmc_sdio_detect(struct mmc_host *host) | ||||||
| 	BUG_ON(!host->card); | 	BUG_ON(!host->card); | ||||||
| 
 | 
 | ||||||
| 	/* Make sure card is powered before detecting it */ | 	/* Make sure card is powered before detecting it */ | ||||||
| 	err = pm_runtime_get_sync(&host->card->dev); | 	if (host->caps & MMC_CAP_POWER_OFF_CARD) { | ||||||
| 	if (err < 0) | 		err = pm_runtime_get_sync(&host->card->dev); | ||||||
| 		goto out; | 		if (err < 0) | ||||||
|  | 			goto out; | ||||||
|  | 	} | ||||||
| 
 | 
 | ||||||
| 	mmc_claim_host(host); | 	mmc_claim_host(host); | ||||||
| 
 | 
 | ||||||
|  | @ -571,7 +573,8 @@ static void mmc_sdio_detect(struct mmc_host *host) | ||||||
| 	 * is about to show up at this point, the _sync variant is | 	 * is about to show up at this point, the _sync variant is | ||||||
| 	 * desirable anyway. | 	 * desirable anyway. | ||||||
| 	 */ | 	 */ | ||||||
| 	pm_runtime_put_sync(&host->card->dev); | 	if (host->caps & MMC_CAP_POWER_OFF_CARD) | ||||||
|  | 		pm_runtime_put_sync(&host->card->dev); | ||||||
| 
 | 
 | ||||||
| out: | out: | ||||||
| 	if (err) { | 	if (err) { | ||||||
|  | @ -728,16 +731,21 @@ int mmc_attach_sdio(struct mmc_host *host, u32 ocr) | ||||||
| 	card = host->card; | 	card = host->card; | ||||||
| 
 | 
 | ||||||
| 	/*
 | 	/*
 | ||||||
| 	 * Let runtime PM core know our card is active | 	 * Enable runtime PM only if supported by host+card+board | ||||||
| 	 */ | 	 */ | ||||||
| 	err = pm_runtime_set_active(&card->dev); | 	if (host->caps & MMC_CAP_POWER_OFF_CARD) { | ||||||
| 	if (err) | 		/*
 | ||||||
| 		goto remove; | 		 * Let runtime PM core know our card is active | ||||||
|  | 		 */ | ||||||
|  | 		err = pm_runtime_set_active(&card->dev); | ||||||
|  | 		if (err) | ||||||
|  | 			goto remove; | ||||||
| 
 | 
 | ||||||
| 	/*
 | 		/*
 | ||||||
| 	 * Enable runtime PM for this card | 		 * Enable runtime PM for this card | ||||||
| 	 */ | 		 */ | ||||||
| 	pm_runtime_enable(&card->dev); | 		pm_runtime_enable(&card->dev); | ||||||
|  | 	} | ||||||
| 
 | 
 | ||||||
| 	/*
 | 	/*
 | ||||||
| 	 * The number of functions on the card is encoded inside | 	 * The number of functions on the card is encoded inside | ||||||
|  | @ -755,9 +763,10 @@ int mmc_attach_sdio(struct mmc_host *host, u32 ocr) | ||||||
| 			goto remove; | 			goto remove; | ||||||
| 
 | 
 | ||||||
| 		/*
 | 		/*
 | ||||||
| 		 * Enable Runtime PM for this func | 		 * Enable Runtime PM for this func (if supported) | ||||||
| 		 */ | 		 */ | ||||||
| 		pm_runtime_enable(&card->sdio_func[i]->dev); | 		if (host->caps & MMC_CAP_POWER_OFF_CARD) | ||||||
|  | 			pm_runtime_enable(&card->sdio_func[i]->dev); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	mmc_release_host(host); | 	mmc_release_host(host); | ||||||
|  |  | ||||||
|  | @ -17,6 +17,7 @@ | ||||||
| #include <linux/pm_runtime.h> | #include <linux/pm_runtime.h> | ||||||
| 
 | 
 | ||||||
| #include <linux/mmc/card.h> | #include <linux/mmc/card.h> | ||||||
|  | #include <linux/mmc/host.h> | ||||||
| #include <linux/mmc/sdio_func.h> | #include <linux/mmc/sdio_func.h> | ||||||
| 
 | 
 | ||||||
| #include "sdio_cis.h" | #include "sdio_cis.h" | ||||||
|  | @ -132,9 +133,11 @@ static int sdio_bus_probe(struct device *dev) | ||||||
| 	 * it should call pm_runtime_put_noidle() in its probe routine and | 	 * it should call pm_runtime_put_noidle() in its probe routine and | ||||||
| 	 * pm_runtime_get_noresume() in its remove routine. | 	 * pm_runtime_get_noresume() in its remove routine. | ||||||
| 	 */ | 	 */ | ||||||
| 	ret = pm_runtime_get_sync(dev); | 	if (func->card->host->caps & MMC_CAP_POWER_OFF_CARD) { | ||||||
| 	if (ret < 0) | 		ret = pm_runtime_get_sync(dev); | ||||||
| 		goto out; | 		if (ret < 0) | ||||||
|  | 			goto out; | ||||||
|  | 	} | ||||||
| 
 | 
 | ||||||
| 	/* Set the default block size so the driver is sure it's something
 | 	/* Set the default block size so the driver is sure it's something
 | ||||||
| 	 * sensible. */ | 	 * sensible. */ | ||||||
|  | @ -151,7 +154,8 @@ static int sdio_bus_probe(struct device *dev) | ||||||
| 	return 0; | 	return 0; | ||||||
| 
 | 
 | ||||||
| disable_runtimepm: | disable_runtimepm: | ||||||
| 	pm_runtime_put_noidle(dev); | 	if (func->card->host->caps & MMC_CAP_POWER_OFF_CARD) | ||||||
|  | 		pm_runtime_put_noidle(dev); | ||||||
| out: | out: | ||||||
| 	return ret; | 	return ret; | ||||||
| } | } | ||||||
|  | @ -160,12 +164,14 @@ static int sdio_bus_remove(struct device *dev) | ||||||
| { | { | ||||||
| 	struct sdio_driver *drv = to_sdio_driver(dev->driver); | 	struct sdio_driver *drv = to_sdio_driver(dev->driver); | ||||||
| 	struct sdio_func *func = dev_to_sdio_func(dev); | 	struct sdio_func *func = dev_to_sdio_func(dev); | ||||||
| 	int ret; | 	int ret = 0; | ||||||
| 
 | 
 | ||||||
| 	/* Make sure card is powered before invoking ->remove() */ | 	/* Make sure card is powered before invoking ->remove() */ | ||||||
| 	ret = pm_runtime_get_sync(dev); | 	if (func->card->host->caps & MMC_CAP_POWER_OFF_CARD) { | ||||||
| 	if (ret < 0) | 		ret = pm_runtime_get_sync(dev); | ||||||
| 		goto out; | 		if (ret < 0) | ||||||
|  | 			goto out; | ||||||
|  | 	} | ||||||
| 
 | 
 | ||||||
| 	drv->remove(func); | 	drv->remove(func); | ||||||
| 
 | 
 | ||||||
|  | @ -178,10 +184,12 @@ static int sdio_bus_remove(struct device *dev) | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	/* First, undo the increment made directly above */ | 	/* First, undo the increment made directly above */ | ||||||
| 	pm_runtime_put_noidle(dev); | 	if (func->card->host->caps & MMC_CAP_POWER_OFF_CARD) | ||||||
|  | 		pm_runtime_put_noidle(dev); | ||||||
| 
 | 
 | ||||||
| 	/* Then undo the runtime PM settings in sdio_bus_probe() */ | 	/* Then undo the runtime PM settings in sdio_bus_probe() */ | ||||||
| 	pm_runtime_put_noidle(dev); | 	if (func->card->host->caps & MMC_CAP_POWER_OFF_CARD) | ||||||
|  | 		pm_runtime_put_noidle(dev); | ||||||
| 
 | 
 | ||||||
| out: | out: | ||||||
| 	return ret; | 	return ret; | ||||||
|  | @ -191,6 +199,8 @@ out: | ||||||
| 
 | 
 | ||||||
| static int sdio_bus_pm_prepare(struct device *dev) | static int sdio_bus_pm_prepare(struct device *dev) | ||||||
| { | { | ||||||
|  | 	struct sdio_func *func = dev_to_sdio_func(dev); | ||||||
|  | 
 | ||||||
| 	/*
 | 	/*
 | ||||||
| 	 * Resume an SDIO device which was suspended at run time at this | 	 * Resume an SDIO device which was suspended at run time at this | ||||||
| 	 * point, in order to allow standard SDIO suspend/resume paths | 	 * point, in order to allow standard SDIO suspend/resume paths | ||||||
|  | @ -212,7 +222,8 @@ static int sdio_bus_pm_prepare(struct device *dev) | ||||||
| 	 * since there is little point in failing system suspend if a | 	 * since there is little point in failing system suspend if a | ||||||
| 	 * device can't be resumed. | 	 * device can't be resumed. | ||||||
| 	 */ | 	 */ | ||||||
| 	pm_runtime_resume(dev); | 	if (func->card->host->caps & MMC_CAP_POWER_OFF_CARD) | ||||||
|  | 		pm_runtime_resume(dev); | ||||||
| 
 | 
 | ||||||
| 	return 0; | 	return 0; | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -168,6 +168,7 @@ struct mmc_host { | ||||||
| 						/* DDR mode at 1.8V */ | 						/* DDR mode at 1.8V */ | ||||||
| #define MMC_CAP_1_2V_DDR	(1 << 12)	/* can support */ | #define MMC_CAP_1_2V_DDR	(1 << 12)	/* can support */ | ||||||
| 						/* DDR mode at 1.2V */ | 						/* DDR mode at 1.2V */ | ||||||
|  | #define MMC_CAP_POWER_OFF_CARD	(1 << 13)	/* Can power off after boot */ | ||||||
| 
 | 
 | ||||||
| 	mmc_pm_flag_t		pm_caps;	/* supported pm features */ | 	mmc_pm_flag_t		pm_caps;	/* supported pm features */ | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Ohad Ben-Cohen
				Ohad Ben-Cohen