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

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
...@@ -772,8 +772,9 @@ EXPORT_SYMBOL(mmc_regulator_get_ocrmask); ...@@ -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); ...@@ -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) ...@@ -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) ...@@ -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) ...@@ -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, ...@@ -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, ...@@ -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, ...@@ -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) ...@@ -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) ...@@ -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) ...@@ -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) ...@@ -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 { ...@@ -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) ...@@ -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);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment