diff options
author | Linus Walleij <linus.walleij@stericsson.com> | 2010-09-29 01:08:27 -0400 |
---|---|---|
committer | Chris Ball <cjb@laptop.org> | 2010-10-23 09:11:16 -0400 |
commit | 99fc5131018cbdc3cf42ce09fb394a4e8b053c74 (patch) | |
tree | 68638188b665af5add8d885b3e22a6edd537de7e /drivers/mmc | |
parent | 4d0b8611cd4da64f075b8e07a126f0eb498fb153 (diff) |
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>
Diffstat (limited to 'drivers/mmc')
-rw-r--r-- | drivers/mmc/core/core.c | 26 | ||||
-rw-r--r-- | drivers/mmc/host/mmci.c | 28 | ||||
-rw-r--r-- | drivers/mmc/host/omap_hsmmc.c | 21 | ||||
-rw-r--r-- | drivers/mmc/host/pxamci.c | 41 |
4 files changed, 80 insertions, 36 deletions
diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c index c5e3c9bf6fdd..46029d5c0364 100644 --- a/drivers/mmc/core/core.c +++ b/drivers/mmc/core/core.c | |||
@@ -772,8 +772,9 @@ EXPORT_SYMBOL(mmc_regulator_get_ocrmask); | |||
772 | 772 | ||
773 | /** | 773 | /** |
774 | * mmc_regulator_set_ocr - set regulator to match host->ios voltage | 774 | * mmc_regulator_set_ocr - set regulator to match host->ios voltage |
775 | * @vdd_bit: zero for power off, else a bit number (host->ios.vdd) | 775 | * @mmc: the host to regulate |
776 | * @supply: regulator to use | 776 | * @supply: regulator to use |
777 | * @vdd_bit: zero for power off, else a bit number (host->ios.vdd) | ||
777 | * | 778 | * |
778 | * Returns zero on success, else negative errno. | 779 | * Returns zero on success, else negative errno. |
779 | * | 780 | * |
@@ -781,15 +782,12 @@ EXPORT_SYMBOL(mmc_regulator_get_ocrmask); | |||
781 | * a particular supply voltage. This would normally be called from the | 782 | * a particular supply voltage. This would normally be called from the |
782 | * set_ios() method. | 783 | * set_ios() method. |
783 | */ | 784 | */ |
784 | int mmc_regulator_set_ocr(struct regulator *supply, unsigned short vdd_bit) | 785 | int mmc_regulator_set_ocr(struct mmc_host *mmc, |
786 | struct regulator *supply, | ||
787 | unsigned short vdd_bit) | ||
785 | { | 788 | { |
786 | int result = 0; | 789 | int result = 0; |
787 | int min_uV, max_uV; | 790 | int min_uV, max_uV; |
788 | int enabled; | ||
789 | |||
790 | enabled = regulator_is_enabled(supply); | ||
791 | if (enabled < 0) | ||
792 | return enabled; | ||
793 | 791 | ||
794 | if (vdd_bit) { | 792 | if (vdd_bit) { |
795 | int tmp; | 793 | int tmp; |
@@ -820,17 +818,25 @@ int mmc_regulator_set_ocr(struct regulator *supply, unsigned short vdd_bit) | |||
820 | else | 818 | else |
821 | result = 0; | 819 | result = 0; |
822 | 820 | ||
823 | if (result == 0 && !enabled) | 821 | if (result == 0 && !mmc->regulator_enabled) { |
824 | result = regulator_enable(supply); | 822 | result = regulator_enable(supply); |
825 | } else if (enabled) { | 823 | if (!result) |
824 | mmc->regulator_enabled = true; | ||
825 | } | ||
826 | } else if (mmc->regulator_enabled) { | ||
826 | result = regulator_disable(supply); | 827 | result = regulator_disable(supply); |
828 | if (result == 0) | ||
829 | mmc->regulator_enabled = false; | ||
827 | } | 830 | } |
828 | 831 | ||
832 | if (result) | ||
833 | dev_err(mmc_dev(mmc), | ||
834 | "could not set regulator OCR (%d)\n", result); | ||
829 | return result; | 835 | return result; |
830 | } | 836 | } |
831 | EXPORT_SYMBOL(mmc_regulator_set_ocr); | 837 | EXPORT_SYMBOL(mmc_regulator_set_ocr); |
832 | 838 | ||
833 | #endif | 839 | #endif /* CONFIG_REGULATOR */ |
834 | 840 | ||
835 | /* | 841 | /* |
836 | * Mask off any voltages we don't support and select | 842 | * Mask off any voltages we don't support and select |
diff --git a/drivers/mmc/host/mmci.c b/drivers/mmc/host/mmci.c index 5f2e72d38b5d..87b4fc6c98c2 100644 --- a/drivers/mmc/host/mmci.c +++ b/drivers/mmc/host/mmci.c | |||
@@ -523,19 +523,27 @@ static void mmci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) | |||
523 | struct mmci_host *host = mmc_priv(mmc); | 523 | struct mmci_host *host = mmc_priv(mmc); |
524 | u32 pwr = 0; | 524 | u32 pwr = 0; |
525 | unsigned long flags; | 525 | unsigned long flags; |
526 | int ret; | ||
526 | 527 | ||
527 | switch (ios->power_mode) { | 528 | switch (ios->power_mode) { |
528 | case MMC_POWER_OFF: | 529 | case MMC_POWER_OFF: |
529 | if(host->vcc && | 530 | if (host->vcc) |
530 | regulator_is_enabled(host->vcc)) | 531 | ret = mmc_regulator_set_ocr(mmc, host->vcc, 0); |
531 | regulator_disable(host->vcc); | ||
532 | break; | 532 | break; |
533 | case MMC_POWER_UP: | 533 | case MMC_POWER_UP: |
534 | #ifdef CONFIG_REGULATOR | 534 | if (host->vcc) { |
535 | if (host->vcc) | 535 | ret = mmc_regulator_set_ocr(mmc, host->vcc, ios->vdd); |
536 | /* This implicitly enables the regulator */ | 536 | if (ret) { |
537 | mmc_regulator_set_ocr(host->vcc, ios->vdd); | 537 | dev_err(mmc_dev(mmc), "unable to set OCR\n"); |
538 | #endif | 538 | /* |
539 | * The .set_ios() function in the mmc_host_ops | ||
540 | * struct return void, and failing to set the | ||
541 | * power should be rare so we print an error | ||
542 | * and return here. | ||
543 | */ | ||
544 | return; | ||
545 | } | ||
546 | } | ||
539 | if (host->plat->vdd_handler) | 547 | if (host->plat->vdd_handler) |
540 | pwr |= host->plat->vdd_handler(mmc_dev(mmc), ios->vdd, | 548 | pwr |= host->plat->vdd_handler(mmc_dev(mmc), ios->vdd, |
541 | ios->power_mode); | 549 | ios->power_mode); |
@@ -869,8 +877,8 @@ static int __devexit mmci_remove(struct amba_device *dev) | |||
869 | clk_disable(host->clk); | 877 | clk_disable(host->clk); |
870 | clk_put(host->clk); | 878 | clk_put(host->clk); |
871 | 879 | ||
872 | if (regulator_is_enabled(host->vcc)) | 880 | if (host->vcc) |
873 | regulator_disable(host->vcc); | 881 | mmc_regulator_set_ocr(mmc, host->vcc, 0); |
874 | regulator_put(host->vcc); | 882 | regulator_put(host->vcc); |
875 | 883 | ||
876 | mmc_free_host(mmc); | 884 | mmc_free_host(mmc); |
diff --git a/drivers/mmc/host/omap_hsmmc.c b/drivers/mmc/host/omap_hsmmc.c index 03c26e026506..8c863ccb1bfe 100644 --- a/drivers/mmc/host/omap_hsmmc.c +++ b/drivers/mmc/host/omap_hsmmc.c | |||
@@ -250,9 +250,9 @@ static int omap_hsmmc_1_set_power(struct device *dev, int slot, int power_on, | |||
250 | mmc_slot(host).before_set_reg(dev, slot, power_on, vdd); | 250 | mmc_slot(host).before_set_reg(dev, slot, power_on, vdd); |
251 | 251 | ||
252 | if (power_on) | 252 | if (power_on) |
253 | ret = mmc_regulator_set_ocr(host->vcc, vdd); | 253 | ret = mmc_regulator_set_ocr(host->mmc, host->vcc, vdd); |
254 | else | 254 | else |
255 | ret = mmc_regulator_set_ocr(host->vcc, 0); | 255 | ret = mmc_regulator_set_ocr(host->mmc, host->vcc, 0); |
256 | 256 | ||
257 | if (mmc_slot(host).after_set_reg) | 257 | if (mmc_slot(host).after_set_reg) |
258 | mmc_slot(host).after_set_reg(dev, slot, power_on, vdd); | 258 | 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 | * chips/cards need an interface voltage rail too. | 291 | * chips/cards need an interface voltage rail too. |
292 | */ | 292 | */ |
293 | if (power_on) { | 293 | if (power_on) { |
294 | ret = mmc_regulator_set_ocr(host->vcc, vdd); | 294 | ret = mmc_regulator_set_ocr(host->mmc, host->vcc, vdd); |
295 | /* Enable interface voltage rail, if needed */ | 295 | /* Enable interface voltage rail, if needed */ |
296 | if (ret == 0 && host->vcc_aux) { | 296 | if (ret == 0 && host->vcc_aux) { |
297 | ret = regulator_enable(host->vcc_aux); | 297 | ret = regulator_enable(host->vcc_aux); |
298 | if (ret < 0) | 298 | if (ret < 0) |
299 | ret = mmc_regulator_set_ocr(host->vcc, 0); | 299 | ret = mmc_regulator_set_ocr(host->mmc, |
300 | host->vcc, 0); | ||
300 | } | 301 | } |
301 | } else { | 302 | } else { |
303 | /* Shut down the rail */ | ||
302 | if (host->vcc_aux) | 304 | if (host->vcc_aux) |
303 | ret = regulator_disable(host->vcc_aux); | 305 | ret = regulator_disable(host->vcc_aux); |
304 | if (ret == 0) | 306 | if (!ret) { |
305 | ret = mmc_regulator_set_ocr(host->vcc, 0); | 307 | /* Then proceed to shut down the local regulator */ |
308 | ret = mmc_regulator_set_ocr(host->mmc, | ||
309 | host->vcc, 0); | ||
310 | } | ||
306 | } | 311 | } |
307 | 312 | ||
308 | if (mmc_slot(host).after_set_reg) | 313 | 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 | if (cardsleep) { | 348 | if (cardsleep) { |
344 | /* VCC can be turned off if card is asleep */ | 349 | /* VCC can be turned off if card is asleep */ |
345 | if (sleep) | 350 | if (sleep) |
346 | err = mmc_regulator_set_ocr(host->vcc, 0); | 351 | err = mmc_regulator_set_ocr(host->mmc, host->vcc, 0); |
347 | else | 352 | else |
348 | err = mmc_regulator_set_ocr(host->vcc, vdd); | 353 | err = mmc_regulator_set_ocr(host->mmc, host->vcc, vdd); |
349 | } else | 354 | } else |
350 | err = regulator_set_mode(host->vcc, mode); | 355 | err = regulator_set_mode(host->vcc, mode); |
351 | if (err) | 356 | if (err) |
diff --git a/drivers/mmc/host/pxamci.c b/drivers/mmc/host/pxamci.c index b7dfcac31e8a..7257738fd7da 100644 --- a/drivers/mmc/host/pxamci.c +++ b/drivers/mmc/host/pxamci.c | |||
@@ -99,14 +99,25 @@ static inline void pxamci_init_ocr(struct pxamci_host *host) | |||
99 | } | 99 | } |
100 | } | 100 | } |
101 | 101 | ||
102 | static inline void pxamci_set_power(struct pxamci_host *host, unsigned int vdd) | 102 | static inline int pxamci_set_power(struct pxamci_host *host, |
103 | unsigned char power_mode, | ||
104 | unsigned int vdd) | ||
103 | { | 105 | { |
104 | int on; | 106 | int on; |
105 | 107 | ||
106 | #ifdef CONFIG_REGULATOR | 108 | if (host->vcc) { |
107 | if (host->vcc) | 109 | int ret; |
108 | mmc_regulator_set_ocr(host->vcc, vdd); | 110 | |
109 | #endif | 111 | if (power_mode == MMC_POWER_UP) { |
112 | ret = mmc_regulator_set_ocr(host->mmc, host->vcc, vdd); | ||
113 | if (ret) | ||
114 | return ret; | ||
115 | } else if (power_mode == MMC_POWER_OFF) { | ||
116 | ret = mmc_regulator_set_ocr(host->mmc, host->vcc, 0); | ||
117 | if (ret) | ||
118 | return ret; | ||
119 | } | ||
120 | } | ||
110 | if (!host->vcc && host->pdata && | 121 | if (!host->vcc && host->pdata && |
111 | gpio_is_valid(host->pdata->gpio_power)) { | 122 | gpio_is_valid(host->pdata->gpio_power)) { |
112 | on = ((1 << vdd) & host->pdata->ocr_mask); | 123 | 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 | } | 126 | } |
116 | if (!host->vcc && host->pdata && host->pdata->setpower) | 127 | if (!host->vcc && host->pdata && host->pdata->setpower) |
117 | host->pdata->setpower(mmc_dev(host->mmc), vdd); | 128 | host->pdata->setpower(mmc_dev(host->mmc), vdd); |
129 | |||
130 | return 0; | ||
118 | } | 131 | } |
119 | 132 | ||
120 | static void pxamci_stop_clock(struct pxamci_host *host) | 133 | 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 | } | 503 | } |
491 | 504 | ||
492 | if (host->power_mode != ios->power_mode) { | 505 | if (host->power_mode != ios->power_mode) { |
506 | int ret; | ||
507 | |||
493 | host->power_mode = ios->power_mode; | 508 | host->power_mode = ios->power_mode; |
494 | 509 | ||
495 | pxamci_set_power(host, ios->vdd); | 510 | ret = pxamci_set_power(host, ios->power_mode, ios->vdd); |
511 | if (ret) { | ||
512 | dev_err(mmc_dev(mmc), "unable to set power\n"); | ||
513 | /* | ||
514 | * The .set_ios() function in the mmc_host_ops | ||
515 | * struct return void, and failing to set the | ||
516 | * power should be rare so we print an error and | ||
517 | * return here. | ||
518 | */ | ||
519 | return; | ||
520 | } | ||
496 | 521 | ||
497 | if (ios->power_mode == MMC_POWER_ON) | 522 | if (ios->power_mode == MMC_POWER_ON) |
498 | host->cmdat |= CMDAT_INIT; | 523 | host->cmdat |= CMDAT_INIT; |
@@ -503,8 +528,8 @@ static void pxamci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) | |||
503 | else | 528 | else |
504 | host->cmdat &= ~CMDAT_SD_4DAT; | 529 | host->cmdat &= ~CMDAT_SD_4DAT; |
505 | 530 | ||
506 | pr_debug("PXAMCI: clkrt = %x cmdat = %x\n", | 531 | dev_dbg(mmc_dev(mmc), "PXAMCI: clkrt = %x cmdat = %x\n", |
507 | host->clkrt, host->cmdat); | 532 | host->clkrt, host->cmdat); |
508 | } | 533 | } |
509 | 534 | ||
510 | static void pxamci_enable_sdio_irq(struct mmc_host *host, int enable) | 535 | static void pxamci_enable_sdio_irq(struct mmc_host *host, int enable) |