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 |  * 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 |  * @supply: regulator to use | ||||||
|  |  * @vdd_bit: zero for power off, else a bit number (host->ios.vdd) | ||||||
|  * |  * | ||||||
|  * Returns zero on success, else negative errno. |  * 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 |  * a particular supply voltage.  This would normally be called from the | ||||||
|  * set_ios() method. |  * 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			result = 0; | ||||||
| 	int			min_uV, max_uV; | 	int			min_uV, max_uV; | ||||||
| 	int			enabled; |  | ||||||
| 
 |  | ||||||
| 	enabled = regulator_is_enabled(supply); |  | ||||||
| 	if (enabled < 0) |  | ||||||
| 		return enabled; |  | ||||||
| 
 | 
 | ||||||
| 	if (vdd_bit) { | 	if (vdd_bit) { | ||||||
| 		int		tmp; | 		int		tmp; | ||||||
|  | @ -820,17 +818,25 @@ int mmc_regulator_set_ocr(struct regulator *supply, unsigned short vdd_bit) | ||||||
| 		else | 		else | ||||||
| 			result = 0; | 			result = 0; | ||||||
| 
 | 
 | ||||||
| 		if (result == 0 && !enabled) | 		if (result == 0 && !mmc->regulator_enabled) { | ||||||
| 			result = regulator_enable(supply); | 			result = regulator_enable(supply); | ||||||
| 	} else if (enabled) { | 			if (!result) | ||||||
|  | 				mmc->regulator_enabled = true; | ||||||
|  | 		} | ||||||
|  | 	} else if (mmc->regulator_enabled) { | ||||||
| 		result = regulator_disable(supply); | 		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; | 	return result; | ||||||
| } | } | ||||||
| EXPORT_SYMBOL(mmc_regulator_set_ocr); | EXPORT_SYMBOL(mmc_regulator_set_ocr); | ||||||
| 
 | 
 | ||||||
| #endif | #endif /* CONFIG_REGULATOR */ | ||||||
| 
 | 
 | ||||||
| /*
 | /*
 | ||||||
|  * Mask off any voltages we don't support and select |  * 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); | 	struct mmci_host *host = mmc_priv(mmc); | ||||||
| 	u32 pwr = 0; | 	u32 pwr = 0; | ||||||
| 	unsigned long flags; | 	unsigned long flags; | ||||||
|  | 	int ret; | ||||||
| 
 | 
 | ||||||
| 	switch (ios->power_mode) { | 	switch (ios->power_mode) { | ||||||
| 	case MMC_POWER_OFF: | 	case MMC_POWER_OFF: | ||||||
| 		if(host->vcc && | 		if (host->vcc) | ||||||
| 		   regulator_is_enabled(host->vcc)) | 			ret = mmc_regulator_set_ocr(mmc, host->vcc, 0); | ||||||
| 			regulator_disable(host->vcc); |  | ||||||
| 		break; | 		break; | ||||||
| 	case MMC_POWER_UP: | 	case MMC_POWER_UP: | ||||||
| #ifdef CONFIG_REGULATOR | 		if (host->vcc) { | ||||||
| 		if (host->vcc) | 			ret = mmc_regulator_set_ocr(mmc, host->vcc, ios->vdd); | ||||||
| 			/* This implicitly enables the regulator */ | 			if (ret) { | ||||||
| 			mmc_regulator_set_ocr(host->vcc, ios->vdd); | 				dev_err(mmc_dev(mmc), "unable to set OCR\n"); | ||||||
| #endif | 				/*
 | ||||||
|  | 				 * 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) | 		if (host->plat->vdd_handler) | ||||||
| 			pwr |= host->plat->vdd_handler(mmc_dev(mmc), ios->vdd, | 			pwr |= host->plat->vdd_handler(mmc_dev(mmc), ios->vdd, | ||||||
| 						       ios->power_mode); | 						       ios->power_mode); | ||||||
|  | @ -869,8 +877,8 @@ static int __devexit mmci_remove(struct amba_device *dev) | ||||||
| 		clk_disable(host->clk); | 		clk_disable(host->clk); | ||||||
| 		clk_put(host->clk); | 		clk_put(host->clk); | ||||||
| 
 | 
 | ||||||
| 		if (regulator_is_enabled(host->vcc)) | 		if (host->vcc) | ||||||
| 			regulator_disable(host->vcc); | 			mmc_regulator_set_ocr(mmc, host->vcc, 0); | ||||||
| 		regulator_put(host->vcc); | 		regulator_put(host->vcc); | ||||||
| 
 | 
 | ||||||
| 		mmc_free_host(mmc); | 		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); | 		mmc_slot(host).before_set_reg(dev, slot, power_on, vdd); | ||||||
| 
 | 
 | ||||||
| 	if (power_on) | 	if (power_on) | ||||||
| 		ret = mmc_regulator_set_ocr(host->vcc, vdd); | 		ret = mmc_regulator_set_ocr(host->mmc, host->vcc, vdd); | ||||||
| 	else | 	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) | 	if (mmc_slot(host).after_set_reg) | ||||||
| 		mmc_slot(host).after_set_reg(dev, slot, power_on, vdd); | 		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. | 	 * chips/cards need an interface voltage rail too. | ||||||
| 	 */ | 	 */ | ||||||
| 	if (power_on) { | 	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 */ | 		/* Enable interface voltage rail, if needed */ | ||||||
| 		if (ret == 0 && host->vcc_aux) { | 		if (ret == 0 && host->vcc_aux) { | ||||||
| 			ret = regulator_enable(host->vcc_aux); | 			ret = regulator_enable(host->vcc_aux); | ||||||
| 			if (ret < 0) | 			if (ret < 0) | ||||||
| 				ret = mmc_regulator_set_ocr(host->vcc, 0); | 				ret = mmc_regulator_set_ocr(host->mmc, | ||||||
|  | 							host->vcc, 0); | ||||||
| 		} | 		} | ||||||
| 	} else { | 	} else { | ||||||
|  | 		/* Shut down the rail */ | ||||||
| 		if (host->vcc_aux) | 		if (host->vcc_aux) | ||||||
| 			ret = regulator_disable(host->vcc_aux); | 			ret = regulator_disable(host->vcc_aux); | ||||||
| 		if (ret == 0) | 		if (!ret) { | ||||||
| 			ret = mmc_regulator_set_ocr(host->vcc, 0); | 			/* 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) | 	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) { | 	if (cardsleep) { | ||||||
| 		/* VCC can be turned off if card is asleep */ | 		/* VCC can be turned off if card is asleep */ | ||||||
| 		if (sleep) | 		if (sleep) | ||||||
| 			err = mmc_regulator_set_ocr(host->vcc, 0); | 			err = mmc_regulator_set_ocr(host->mmc, host->vcc, 0); | ||||||
| 		else | 		else | ||||||
| 			err = mmc_regulator_set_ocr(host->vcc, vdd); | 			err = mmc_regulator_set_ocr(host->mmc, host->vcc, vdd); | ||||||
| 	} else | 	} else | ||||||
| 		err = regulator_set_mode(host->vcc, mode); | 		err = regulator_set_mode(host->vcc, mode); | ||||||
| 	if (err) | 	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; | 	int on; | ||||||
| 
 | 
 | ||||||
| #ifdef CONFIG_REGULATOR | 	if (host->vcc) { | ||||||
| 	if (host->vcc) | 		int ret; | ||||||
| 		mmc_regulator_set_ocr(host->vcc, vdd); | 
 | ||||||
| #endif | 		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 && | 	if (!host->vcc && host->pdata && | ||||||
| 	    gpio_is_valid(host->pdata->gpio_power)) { | 	    gpio_is_valid(host->pdata->gpio_power)) { | ||||||
| 		on = ((1 << vdd) & host->pdata->ocr_mask); | 		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) | 	if (!host->vcc && host->pdata && host->pdata->setpower) | ||||||
| 		host->pdata->setpower(mmc_dev(host->mmc), vdd); | 		host->pdata->setpower(mmc_dev(host->mmc), vdd); | ||||||
|  | 
 | ||||||
|  | 	return 0; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static void pxamci_stop_clock(struct pxamci_host *host) | 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) { | 	if (host->power_mode != ios->power_mode) { | ||||||
|  | 		int ret; | ||||||
|  | 
 | ||||||
| 		host->power_mode = ios->power_mode; | 		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) | 		if (ios->power_mode == MMC_POWER_ON) | ||||||
| 			host->cmdat |= CMDAT_INIT; | 			host->cmdat |= CMDAT_INIT; | ||||||
|  | @ -503,8 +528,8 @@ static void pxamci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) | ||||||
| 	else | 	else | ||||||
| 		host->cmdat &= ~CMDAT_SD_4DAT; | 		host->cmdat &= ~CMDAT_SD_4DAT; | ||||||
| 
 | 
 | ||||||
| 	pr_debug("PXAMCI: clkrt = %x cmdat = %x\n", | 	dev_dbg(mmc_dev(mmc), "PXAMCI: clkrt = %x cmdat = %x\n", | ||||||
| 		 host->clkrt, host->cmdat); | 		host->clkrt, host->cmdat); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static void pxamci_enable_sdio_irq(struct mmc_host *host, int enable) | 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 */ | 	struct led_trigger	*led;		/* activity led */ | ||||||
| #endif | #endif | ||||||
| 
 | 
 | ||||||
|  | #ifdef CONFIG_REGULATOR | ||||||
|  | 	bool			regulator_enabled; /* regulator state */ | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
| 	struct dentry		*debugfs_root; | 	struct dentry		*debugfs_root; | ||||||
| 
 | 
 | ||||||
| 	unsigned long		private[0] ____cacheline_aligned; | 	unsigned long		private[0] ____cacheline_aligned; | ||||||
|  | @ -250,8 +254,24 @@ static inline void mmc_signal_sdio_irq(struct mmc_host *host) | ||||||
| 
 | 
 | ||||||
| struct regulator; | struct regulator; | ||||||
| 
 | 
 | ||||||
|  | #ifdef CONFIG_REGULATOR | ||||||
| int mmc_regulator_get_ocrmask(struct regulator *supply); | 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_awake(struct mmc_host *host); | ||||||
| int mmc_card_sleep(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