aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/mmc
diff options
context:
space:
mode:
authorGuennadi Liakhovetski <g.liakhovetski@gmx.de>2013-02-15 10:14:00 -0500
committerChris Ball <cjb@laptop.org>2013-02-24 14:37:24 -0500
commit619b08d45a40229040d6db8c9fb1f40b7e58b71f (patch)
tree8615eb31a5c4f751c7fc87cd7fbbaffaa7b4fbeb /drivers/mmc
parent27902c14aa2376d53755b6c02e3be671fd890e30 (diff)
mmc: tmio: add support for the VccQ regulator
Some SD/MMC interfaces use 2 power regulators: one to power the card itself (Vcc) and another one to pull signal lines up (VccQ). In case of eMMC and UHS SD cards the regulators also have to be configured to supply different voltages. The preferred order of turning supply power on and off is to turn Vcc first on and last off. Signed-off-by: Guennadi Liakhovetski <g.liakhovetski@gmx.de> Signed-off-by: Chris Ball <cjb@laptop.org>
Diffstat (limited to 'drivers/mmc')
-rw-r--r--drivers/mmc/host/tmio_mmc_pio.c56
1 files changed, 46 insertions, 10 deletions
diff --git a/drivers/mmc/host/tmio_mmc_pio.c b/drivers/mmc/host/tmio_mmc_pio.c
index e5b3fcf55c4c..f508ecb5b8a7 100644
--- a/drivers/mmc/host/tmio_mmc_pio.c
+++ b/drivers/mmc/host/tmio_mmc_pio.c
@@ -43,6 +43,7 @@
43#include <linux/platform_device.h> 43#include <linux/platform_device.h>
44#include <linux/pm_qos.h> 44#include <linux/pm_qos.h>
45#include <linux/pm_runtime.h> 45#include <linux/pm_runtime.h>
46#include <linux/regulator/consumer.h>
46#include <linux/scatterlist.h> 47#include <linux/scatterlist.h>
47#include <linux/spinlock.h> 48#include <linux/spinlock.h>
48#include <linux/workqueue.h> 49#include <linux/workqueue.h>
@@ -155,6 +156,7 @@ static void tmio_mmc_set_clock(struct tmio_mmc_host *host, int new_clock)
155 host->set_clk_div(host->pdev, (clk>>22) & 1); 156 host->set_clk_div(host->pdev, (clk>>22) & 1);
156 157
157 sd_ctrl_write16(host, CTL_SD_CARD_CLK_CTL, clk & 0x1ff); 158 sd_ctrl_write16(host, CTL_SD_CARD_CLK_CTL, clk & 0x1ff);
159 msleep(10);
158} 160}
159 161
160static void tmio_mmc_clk_stop(struct tmio_mmc_host *host) 162static void tmio_mmc_clk_stop(struct tmio_mmc_host *host)
@@ -768,16 +770,48 @@ static int tmio_mmc_clk_update(struct mmc_host *mmc)
768 return ret; 770 return ret;
769} 771}
770 772
771static void tmio_mmc_set_power(struct tmio_mmc_host *host, struct mmc_ios *ios) 773static void tmio_mmc_power_on(struct tmio_mmc_host *host, unsigned short vdd)
772{ 774{
773 struct mmc_host *mmc = host->mmc; 775 struct mmc_host *mmc = host->mmc;
776 int ret = 0;
777
778 /* .set_ios() is returning void, so, no chance to report an error */
774 779
775 if (host->set_pwr) 780 if (host->set_pwr)
776 host->set_pwr(host->pdev, ios->power_mode != MMC_POWER_OFF); 781 host->set_pwr(host->pdev, 1);
782
783 if (!IS_ERR(mmc->supply.vmmc)) {
784 ret = mmc_regulator_set_ocr(mmc, mmc->supply.vmmc, vdd);
785 /*
786 * Attention: empiric value. With a b43 WiFi SDIO card this
787 * delay proved necessary for reliable card-insertion probing.
788 * 100us were not enough. Is this the same 140us delay, as in
789 * tmio_mmc_set_ios()?
790 */
791 udelay(200);
792 }
793 /*
794 * It seems, VccQ should be switched on after Vcc, this is also what the
795 * omap_hsmmc.c driver does.
796 */
797 if (!IS_ERR(mmc->supply.vqmmc) && !ret) {
798 regulator_enable(mmc->supply.vqmmc);
799 udelay(200);
800 }
801}
802
803static void tmio_mmc_power_off(struct tmio_mmc_host *host)
804{
805 struct mmc_host *mmc = host->mmc;
806
807 if (!IS_ERR(mmc->supply.vqmmc))
808 regulator_disable(mmc->supply.vqmmc);
809
777 if (!IS_ERR(mmc->supply.vmmc)) 810 if (!IS_ERR(mmc->supply.vmmc))
778 /* Errors ignored... */ 811 mmc_regulator_set_ocr(mmc, mmc->supply.vmmc, 0);
779 mmc_regulator_set_ocr(mmc, mmc->supply.vmmc, 812
780 ios->power_mode ? ios->vdd : 0); 813 if (host->set_pwr)
814 host->set_pwr(host->pdev, 0);
781} 815}
782 816
783/* Set MMC clock / power. 817/* Set MMC clock / power.
@@ -828,18 +862,20 @@ static void tmio_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
828 if (!host->power) { 862 if (!host->power) {
829 tmio_mmc_clk_update(mmc); 863 tmio_mmc_clk_update(mmc);
830 pm_runtime_get_sync(dev); 864 pm_runtime_get_sync(dev);
831 host->power = true;
832 } 865 }
833 tmio_mmc_set_clock(host, ios->clock); 866 tmio_mmc_set_clock(host, ios->clock);
834 /* power up SD bus */ 867 if (!host->power) {
835 tmio_mmc_set_power(host, ios); 868 /* power up SD card and the bus */
869 tmio_mmc_power_on(host, ios->vdd);
870 host->power = true;
871 }
836 /* start bus clock */ 872 /* start bus clock */
837 tmio_mmc_clk_start(host); 873 tmio_mmc_clk_start(host);
838 } else if (ios->power_mode != MMC_POWER_UP) { 874 } else if (ios->power_mode != MMC_POWER_UP) {
839 if (ios->power_mode == MMC_POWER_OFF)
840 tmio_mmc_set_power(host, ios);
841 if (host->power) { 875 if (host->power) {
842 struct tmio_mmc_data *pdata = host->pdata; 876 struct tmio_mmc_data *pdata = host->pdata;
877 if (ios->power_mode == MMC_POWER_OFF)
878 tmio_mmc_power_off(host);
843 tmio_mmc_clk_stop(host); 879 tmio_mmc_clk_stop(host);
844 host->power = false; 880 host->power = false;
845 pm_runtime_put(dev); 881 pm_runtime_put(dev);