mmc: Move regulator handling closer to core
After discovering a problem in regulator reference counting I took Mark Brown's advice to move the reference count into the MMC core by making the regulator status a member of struct mmc_host. I took this opportunity to also implement NULL versions of the regulator functions so as to rid the driver code from some ugly #ifdef CONFIG_REGULATOR clauses. Signed-off-by: Linus Walleij <linus.walleij@stericsson.com> Reviewed-by: Mark Brown <broonie@opensource.wolfsonmicro.com> Cc: Liam Girdwood <lrg@slimlogic.co.uk> Cc: Tony Lindgren <tony@atomide.com> Cc: Adrian Hunter <adrian.hunter@nokia.com> Cc: Robert Jarzmik <robert.jarzmik@free.fr> Cc: Sundar Iyer <sundar.iyer@stericsson.com> Cc: Daniel Mack <daniel@caiaq.de> Cc: Pierre Ossman <pierre@ossman.eu> Cc: Matt Fleming <matt@console-pimps.org> Cc: David Brownell <dbrownell@users.sourceforge.net> Cc: Russell King <rmk+kernel@arm.linux.org.uk> Cc: Eric Miao <eric.y.miao@gmail.com> Cc: Cliff Brake <cbrake@bec-systems.com> Cc: Jarkko Lavinen <jarkko.lavinen@nokia.com> Cc: <linux-mmc@vger.kernel.org> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Chris Ball <cjb@laptop.org>
This commit is contained in:
		
					parent
					
						
							
								4d0b8611cd
							
						
					
				
			
			
				commit
				
					
						99fc513101
					
				
			
		
					 5 changed files with 101 additions and 37 deletions
				
			
		|  | @ -772,8 +772,9 @@ EXPORT_SYMBOL(mmc_regulator_get_ocrmask); | |||
| 
 | ||||
| /**
 | ||||
|  * mmc_regulator_set_ocr - set regulator to match host->ios voltage | ||||
|  * @vdd_bit: zero for power off, else a bit number (host->ios.vdd) | ||||
|  * @mmc: the host to regulate | ||||
|  * @supply: regulator to use | ||||
|  * @vdd_bit: zero for power off, else a bit number (host->ios.vdd) | ||||
|  * | ||||
|  * Returns zero on success, else negative errno. | ||||
|  * | ||||
|  | @ -781,15 +782,12 @@ EXPORT_SYMBOL(mmc_regulator_get_ocrmask); | |||
|  * a particular supply voltage.  This would normally be called from the | ||||
|  * set_ios() method. | ||||
|  */ | ||||
| int mmc_regulator_set_ocr(struct regulator *supply, unsigned short vdd_bit) | ||||
| int mmc_regulator_set_ocr(struct mmc_host *mmc, | ||||
| 			struct regulator *supply, | ||||
| 			unsigned short vdd_bit) | ||||
| { | ||||
| 	int			result = 0; | ||||
| 	int			min_uV, max_uV; | ||||
| 	int			enabled; | ||||
| 
 | ||||
| 	enabled = regulator_is_enabled(supply); | ||||
| 	if (enabled < 0) | ||||
| 		return enabled; | ||||
| 
 | ||||
| 	if (vdd_bit) { | ||||
| 		int		tmp; | ||||
|  | @ -820,17 +818,25 @@ int mmc_regulator_set_ocr(struct regulator *supply, unsigned short vdd_bit) | |||
| 		else | ||||
| 			result = 0; | ||||
| 
 | ||||
| 		if (result == 0 && !enabled) | ||||
| 		if (result == 0 && !mmc->regulator_enabled) { | ||||
| 			result = regulator_enable(supply); | ||||
| 	} else if (enabled) { | ||||
| 			if (!result) | ||||
| 				mmc->regulator_enabled = true; | ||||
| 		} | ||||
| 	} else if (mmc->regulator_enabled) { | ||||
| 		result = regulator_disable(supply); | ||||
| 		if (result == 0) | ||||
| 			mmc->regulator_enabled = false; | ||||
| 	} | ||||
| 
 | ||||
| 	if (result) | ||||
| 		dev_err(mmc_dev(mmc), | ||||
| 			"could not set regulator OCR (%d)\n", result); | ||||
| 	return result; | ||||
| } | ||||
| EXPORT_SYMBOL(mmc_regulator_set_ocr); | ||||
| 
 | ||||
| #endif | ||||
| #endif /* CONFIG_REGULATOR */ | ||||
| 
 | ||||
| /*
 | ||||
|  * Mask off any voltages we don't support and select | ||||
|  |  | |||
|  | @ -523,19 +523,27 @@ static void mmci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) | |||
| 	struct mmci_host *host = mmc_priv(mmc); | ||||
| 	u32 pwr = 0; | ||||
| 	unsigned long flags; | ||||
| 	int ret; | ||||
| 
 | ||||
| 	switch (ios->power_mode) { | ||||
| 	case MMC_POWER_OFF: | ||||
| 		if(host->vcc && | ||||
| 		   regulator_is_enabled(host->vcc)) | ||||
| 			regulator_disable(host->vcc); | ||||
| 		if (host->vcc) | ||||
| 			ret = mmc_regulator_set_ocr(mmc, host->vcc, 0); | ||||
| 		break; | ||||
| 	case MMC_POWER_UP: | ||||
| #ifdef CONFIG_REGULATOR | ||||
| 		if (host->vcc) | ||||
| 			/* This implicitly enables the regulator */ | ||||
| 			mmc_regulator_set_ocr(host->vcc, ios->vdd); | ||||
| #endif | ||||
| 		if (host->vcc) { | ||||
| 			ret = mmc_regulator_set_ocr(mmc, host->vcc, ios->vdd); | ||||
| 			if (ret) { | ||||
| 				dev_err(mmc_dev(mmc), "unable to set OCR\n"); | ||||
| 				/*
 | ||||
| 				 * The .set_ios() function in the mmc_host_ops | ||||
| 				 * struct return void, and failing to set the | ||||
| 				 * power should be rare so we print an error | ||||
| 				 * and return here. | ||||
| 				 */ | ||||
| 				return; | ||||
| 			} | ||||
| 		} | ||||
| 		if (host->plat->vdd_handler) | ||||
| 			pwr |= host->plat->vdd_handler(mmc_dev(mmc), ios->vdd, | ||||
| 						       ios->power_mode); | ||||
|  | @ -869,8 +877,8 @@ static int __devexit mmci_remove(struct amba_device *dev) | |||
| 		clk_disable(host->clk); | ||||
| 		clk_put(host->clk); | ||||
| 
 | ||||
| 		if (regulator_is_enabled(host->vcc)) | ||||
| 			regulator_disable(host->vcc); | ||||
| 		if (host->vcc) | ||||
| 			mmc_regulator_set_ocr(mmc, host->vcc, 0); | ||||
| 		regulator_put(host->vcc); | ||||
| 
 | ||||
| 		mmc_free_host(mmc); | ||||
|  |  | |||
|  | @ -250,9 +250,9 @@ static int omap_hsmmc_1_set_power(struct device *dev, int slot, int power_on, | |||
| 		mmc_slot(host).before_set_reg(dev, slot, power_on, vdd); | ||||
| 
 | ||||
| 	if (power_on) | ||||
| 		ret = mmc_regulator_set_ocr(host->vcc, vdd); | ||||
| 		ret = mmc_regulator_set_ocr(host->mmc, host->vcc, vdd); | ||||
| 	else | ||||
| 		ret = mmc_regulator_set_ocr(host->vcc, 0); | ||||
| 		ret = mmc_regulator_set_ocr(host->mmc, host->vcc, 0); | ||||
| 
 | ||||
| 	if (mmc_slot(host).after_set_reg) | ||||
| 		mmc_slot(host).after_set_reg(dev, slot, power_on, vdd); | ||||
|  | @ -291,18 +291,23 @@ static int omap_hsmmc_23_set_power(struct device *dev, int slot, int power_on, | |||
| 	 * chips/cards need an interface voltage rail too. | ||||
| 	 */ | ||||
| 	if (power_on) { | ||||
| 		ret = mmc_regulator_set_ocr(host->vcc, vdd); | ||||
| 		ret = mmc_regulator_set_ocr(host->mmc, host->vcc, vdd); | ||||
| 		/* Enable interface voltage rail, if needed */ | ||||
| 		if (ret == 0 && host->vcc_aux) { | ||||
| 			ret = regulator_enable(host->vcc_aux); | ||||
| 			if (ret < 0) | ||||
| 				ret = mmc_regulator_set_ocr(host->vcc, 0); | ||||
| 				ret = mmc_regulator_set_ocr(host->mmc, | ||||
| 							host->vcc, 0); | ||||
| 		} | ||||
| 	} else { | ||||
| 		/* Shut down the rail */ | ||||
| 		if (host->vcc_aux) | ||||
| 			ret = regulator_disable(host->vcc_aux); | ||||
| 		if (ret == 0) | ||||
| 			ret = mmc_regulator_set_ocr(host->vcc, 0); | ||||
| 		if (!ret) { | ||||
| 			/* Then proceed to shut down the local regulator */ | ||||
| 			ret = mmc_regulator_set_ocr(host->mmc, | ||||
| 						host->vcc, 0); | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	if (mmc_slot(host).after_set_reg) | ||||
|  | @ -343,9 +348,9 @@ static int omap_hsmmc_23_set_sleep(struct device *dev, int slot, int sleep, | |||
| 	if (cardsleep) { | ||||
| 		/* VCC can be turned off if card is asleep */ | ||||
| 		if (sleep) | ||||
| 			err = mmc_regulator_set_ocr(host->vcc, 0); | ||||
| 			err = mmc_regulator_set_ocr(host->mmc, host->vcc, 0); | ||||
| 		else | ||||
| 			err = mmc_regulator_set_ocr(host->vcc, vdd); | ||||
| 			err = mmc_regulator_set_ocr(host->mmc, host->vcc, vdd); | ||||
| 	} else | ||||
| 		err = regulator_set_mode(host->vcc, mode); | ||||
| 	if (err) | ||||
|  |  | |||
|  | @ -99,14 +99,25 @@ static inline void pxamci_init_ocr(struct pxamci_host *host) | |||
| 	} | ||||
| } | ||||
| 
 | ||||
| static inline void pxamci_set_power(struct pxamci_host *host, unsigned int vdd) | ||||
| static inline int pxamci_set_power(struct pxamci_host *host, | ||||
| 				    unsigned char power_mode, | ||||
| 				    unsigned int vdd) | ||||
| { | ||||
| 	int on; | ||||
| 
 | ||||
| #ifdef CONFIG_REGULATOR | ||||
| 	if (host->vcc) | ||||
| 		mmc_regulator_set_ocr(host->vcc, vdd); | ||||
| #endif | ||||
| 	if (host->vcc) { | ||||
| 		int ret; | ||||
| 
 | ||||
| 		if (power_mode == MMC_POWER_UP) { | ||||
| 			ret = mmc_regulator_set_ocr(host->mmc, host->vcc, vdd); | ||||
| 			if (ret) | ||||
| 				return ret; | ||||
| 		} else if (power_mode == MMC_POWER_OFF) { | ||||
| 			ret = mmc_regulator_set_ocr(host->mmc, host->vcc, 0); | ||||
| 			if (ret) | ||||
| 				return ret; | ||||
| 		} | ||||
| 	} | ||||
| 	if (!host->vcc && host->pdata && | ||||
| 	    gpio_is_valid(host->pdata->gpio_power)) { | ||||
| 		on = ((1 << vdd) & host->pdata->ocr_mask); | ||||
|  | @ -115,6 +126,8 @@ static inline void pxamci_set_power(struct pxamci_host *host, unsigned int vdd) | |||
| 	} | ||||
| 	if (!host->vcc && host->pdata && host->pdata->setpower) | ||||
| 		host->pdata->setpower(mmc_dev(host->mmc), vdd); | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static void pxamci_stop_clock(struct pxamci_host *host) | ||||
|  | @ -490,9 +503,21 @@ static void pxamci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) | |||
| 	} | ||||
| 
 | ||||
| 	if (host->power_mode != ios->power_mode) { | ||||
| 		int ret; | ||||
| 
 | ||||
| 		host->power_mode = ios->power_mode; | ||||
| 
 | ||||
| 		pxamci_set_power(host, ios->vdd); | ||||
| 		ret = pxamci_set_power(host, ios->power_mode, ios->vdd); | ||||
| 		if (ret) { | ||||
| 			dev_err(mmc_dev(mmc), "unable to set power\n"); | ||||
| 			/*
 | ||||
| 			 * The .set_ios() function in the mmc_host_ops | ||||
| 			 * struct return void, and failing to set the | ||||
| 			 * power should be rare so we print an error and | ||||
| 			 * return here. | ||||
| 			 */ | ||||
| 			return; | ||||
| 		} | ||||
| 
 | ||||
| 		if (ios->power_mode == MMC_POWER_ON) | ||||
| 			host->cmdat |= CMDAT_INIT; | ||||
|  | @ -503,8 +528,8 @@ static void pxamci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) | |||
| 	else | ||||
| 		host->cmdat &= ~CMDAT_SD_4DAT; | ||||
| 
 | ||||
| 	pr_debug("PXAMCI: clkrt = %x cmdat = %x\n", | ||||
| 		 host->clkrt, host->cmdat); | ||||
| 	dev_dbg(mmc_dev(mmc), "PXAMCI: clkrt = %x cmdat = %x\n", | ||||
| 		host->clkrt, host->cmdat); | ||||
| } | ||||
| 
 | ||||
| static void pxamci_enable_sdio_irq(struct mmc_host *host, int enable) | ||||
|  |  | |||
|  | @ -212,6 +212,10 @@ struct mmc_host { | |||
| 	struct led_trigger	*led;		/* activity led */ | ||||
| #endif | ||||
| 
 | ||||
| #ifdef CONFIG_REGULATOR | ||||
| 	bool			regulator_enabled; /* regulator state */ | ||||
| #endif | ||||
| 
 | ||||
| 	struct dentry		*debugfs_root; | ||||
| 
 | ||||
| 	unsigned long		private[0] ____cacheline_aligned; | ||||
|  | @ -250,8 +254,24 @@ static inline void mmc_signal_sdio_irq(struct mmc_host *host) | |||
| 
 | ||||
| struct regulator; | ||||
| 
 | ||||
| #ifdef CONFIG_REGULATOR | ||||
| int mmc_regulator_get_ocrmask(struct regulator *supply); | ||||
| int mmc_regulator_set_ocr(struct regulator *supply, unsigned short vdd_bit); | ||||
| int mmc_regulator_set_ocr(struct mmc_host *mmc, | ||||
| 			struct regulator *supply, | ||||
| 			unsigned short vdd_bit); | ||||
| #else | ||||
| static inline int mmc_regulator_get_ocrmask(struct regulator *supply) | ||||
| { | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static inline int mmc_regulator_set_ocr(struct mmc_host *mmc, | ||||
| 				 struct regulator *supply, | ||||
| 				 unsigned short vdd_bit) | ||||
| { | ||||
| 	return 0; | ||||
| } | ||||
| #endif | ||||
| 
 | ||||
| int mmc_card_awake(struct mmc_host *host); | ||||
| int mmc_card_sleep(struct mmc_host *host); | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Linus Walleij
				Linus Walleij