Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 99fc5131 authored by Linus Walleij's avatar Linus Walleij Committed by Chris Ball
Browse files

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: default avatarLinus Walleij <linus.walleij@stericsson.com>
Reviewed-by: default avatarMark 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: default avatarAndrew Morton <akpm@linux-foundation.org>
Signed-off-by: default avatarChris Ball <cjb@laptop.org>
parent 4d0b8611
Loading
Loading
Loading
Loading
+16 −10
Original line number Original line Diff line number Diff line
@@ -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
+18 −10
Original line number Original line Diff line number Diff line
@@ -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);
+13 −8
Original line number Original line Diff line number Diff line
@@ -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)
+33 −8
Original line number Original line Diff line number Diff line
@@ -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,7 +528,7 @@ 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);
}
}


+21 −1
Original line number Original line Diff line number Diff line
@@ -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);