summaryrefslogtreecommitdiff
path: root/drivers/mmc
diff options
context:
space:
mode:
authorPavan Kunapuli <pkunapuli@nvidia.com>2011-11-30 18:49:23 +0530
committerVarun Wadekar <vwadekar@nvidia.com>2011-12-23 03:27:40 -0800
commit9cfa02e6af2d69681b369a584e311237184eaa6b (patch)
tree2a3e088c4705b0bbf80afbf627e3700f269f65f2 /drivers/mmc
parent60efdbb211ef9295511125e5c058545316c24acb (diff)
mmc: tegra: Add support for voltage switching
Enable SDHCI_QUIRK_NON_STD_VOLTAGE_SWITCHING. Implement switch_signal_voltage callback for tegra sdmmc controller to switch the voltage using regulator calls. Bug 906650 Reviewed-on: http://git-master/r/67138 Change-Id: I3237fde03fff1bd112db4f12ad66c5d68ffada09 Signed-off-by: Pavan Kunapuli <pkunapuli@nvidia.com> Signed-off-by: Varun Wadekar <vwadekar@nvidia.com> Reviewed-on: http://git-master/r/69700
Diffstat (limited to 'drivers/mmc')
-rw-r--r--drivers/mmc/host/sdhci-tegra.c90
1 files changed, 84 insertions, 6 deletions
diff --git a/drivers/mmc/host/sdhci-tegra.c b/drivers/mmc/host/sdhci-tegra.c
index 3cdaf24b6db4..423eb6267550 100644
--- a/drivers/mmc/host/sdhci-tegra.c
+++ b/drivers/mmc/host/sdhci-tegra.c
@@ -29,7 +29,7 @@
#include "sdhci-pltfm.h"
-#define SDHCI_VENDOR_CLOCK_CNTRL 0x100
+#define SDHCI_VENDOR_CLOCK_CNTRL 0x100
#define SDHCI_VENDOR_CLOCK_CNTRL_SDMMC_CLK 0x1
#define SDHCI_VENDOR_CLOCK_CNTRL_PADPIPE_CLKEN_OVERRIDE 0x8
#define SDHCI_VENDOR_CLOCK_CNTRL_BASE_CLK_FREQ_SHIFT 8
@@ -38,7 +38,17 @@
#define SDHCI_VENDOR_MISC_CNTRL 0x120
#define SDHCI_VENDOR_MISC_CNTRL_SDMMC_SPARE0_ENABLE_SD_3_0 0x20
-#define SDHOST_1V8_OCR_MASK 0x8
+#define SDMMC_AUTO_CAL_CONFIG 0x1E4
+#define SDMMC_AUTO_CAL_CONFIG_AUTO_CAL_ENABLE 0x20000000
+#define SDMMC_AUTO_CAL_CONFIG_AUTO_CAL_PD_OFFSET_SHIFT 0x8
+#define SDMMC_AUTO_CAL_CONFIG_AUTO_CAL_PD_OFFSET 0x70
+#define SDMMC_AUTO_CAL_CONFIG_AUTO_CAL_PU_OFFSET 0x62
+
+#define SDHOST_1V8_OCR_MASK 0x8
+#define SDHOST_HIGH_VOLT_MIN 2700000
+#define SDHOST_HIGH_VOLT_MAX 3600000
+#define SDHOST_LOW_VOLT_MIN 1800000
+#define SDHOST_LOW_VOLT_MAX 1800000
#define TEGRA_SDHOST_MIN_FREQ 50000000
#define TEGRA2_SDHOST_STD_FREQ 50000000
@@ -434,6 +444,72 @@ static void tegra_sdhci_set_clock(struct sdhci_host *sdhci, unsigned int clock)
}
}
+static int tegra_sdhci_signal_voltage_switch(struct sdhci_host *sdhci,
+ unsigned int signal_voltage)
+{
+ struct sdhci_pltfm_host *pltfm_host = sdhci_priv(sdhci);
+ struct tegra_sdhci_host *tegra_host = pltfm_host->priv;
+ unsigned int min_uV = SDHOST_HIGH_VOLT_MIN;
+ unsigned int max_uV = SDHOST_HIGH_VOLT_MAX;
+ unsigned int rc;
+ u16 clk, ctrl;
+ unsigned int val;
+
+ /* Switch OFF the card clock to prevent glitches on the clock line */
+ clk = sdhci_readw(sdhci, SDHCI_CLOCK_CONTROL);
+ clk &= ~SDHCI_CLOCK_CARD_EN;
+ sdhci_writew(sdhci, clk, SDHCI_CLOCK_CONTROL);
+
+ ctrl = sdhci_readw(sdhci, SDHCI_HOST_CONTROL2);
+ if (signal_voltage == MMC_SIGNAL_VOLTAGE_180) {
+ ctrl |= SDHCI_CTRL_VDD_180;
+ min_uV = SDHOST_LOW_VOLT_MIN;
+ max_uV = SDHOST_LOW_VOLT_MAX;
+ } else if (signal_voltage == MMC_SIGNAL_VOLTAGE_330) {
+ if (ctrl & SDHCI_CTRL_VDD_180)
+ ctrl &= ~SDHCI_CTRL_VDD_180;
+ }
+ sdhci_writew(sdhci, ctrl, SDHCI_HOST_CONTROL2);
+
+ /* Switch the I/O rail voltage */
+ if (tegra_host->vdd_io_reg) {
+ rc = regulator_set_voltage(tegra_host->vdd_io_reg,
+ min_uV, max_uV);
+ if (rc) {
+ dev_err(mmc_dev(sdhci->mmc), "switching to 1.8V"
+ "failed . Switching back to 3.3V\n");
+ regulator_set_voltage(tegra_host->vdd_io_reg,
+ SDHOST_HIGH_VOLT_MIN,
+ SDHOST_HIGH_VOLT_MAX);
+ return rc;
+ }
+ }
+
+ /* Wait for 10 msec for the voltage to be switched */
+ mdelay(10);
+
+ /* Enable the card clock */
+ clk |= SDHCI_CLOCK_CARD_EN;
+ sdhci_writew(sdhci, clk, SDHCI_CLOCK_CONTROL);
+
+ if (signal_voltage == MMC_SIGNAL_VOLTAGE_180) {
+ /* Do Auto Calibration for 1.8V signal voltage */
+ val = sdhci_readl(sdhci, SDMMC_AUTO_CAL_CONFIG);
+ val |= SDMMC_AUTO_CAL_CONFIG_AUTO_CAL_ENABLE;
+ /* Program Auto cal PD offset(bits 8:14) */
+ val &= ~(0x7F <<
+ SDMMC_AUTO_CAL_CONFIG_AUTO_CAL_PD_OFFSET_SHIFT);
+ val |= (SDMMC_AUTO_CAL_CONFIG_AUTO_CAL_PD_OFFSET <<
+ SDMMC_AUTO_CAL_CONFIG_AUTO_CAL_PD_OFFSET_SHIFT);
+ /* Program Auto cal PU offset(bits 0:6) */
+ val &= ~0x7F;
+ val |= SDMMC_AUTO_CAL_CONFIG_AUTO_CAL_PU_OFFSET;
+ sdhci_writel(sdhci, val, SDMMC_AUTO_CAL_CONFIG);
+ }
+
+ return 0;
+}
+
static int tegra_sdhci_suspend(struct sdhci_host *sdhci, pm_message_t state)
{
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(sdhci);
@@ -500,12 +576,14 @@ static struct sdhci_ops tegra_sdhci_ops = {
.resume = tegra_sdhci_resume,
.platform_reset_exit = tegra_sdhci_reset_exit,
.set_uhs_signaling = tegra_sdhci_set_uhs_signaling,
+ .switch_signal_voltage = tegra_sdhci_signal_voltage_switch,
};
static struct sdhci_pltfm_data sdhci_tegra_pdata = {
.quirks = SDHCI_QUIRK_BROKEN_TIMEOUT_VAL |
#ifndef CONFIG_ARCH_TEGRA_2x_SOC
SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK |
+ SDHCI_QUIRK_NON_STD_VOLTAGE_SWITCHING |
#endif
#ifdef CONFIG_ARCH_TEGRA_3x_SOC
SDHCI_QUIRK_NONSTANDARD_CLOCK |
@@ -612,15 +690,15 @@ static int __devinit sdhci_tegra_probe(struct platform_device *pdev)
if (!plat->mmc_data.built_in) {
if (plat->mmc_data.ocr_mask & SDHOST_1V8_OCR_MASK) {
- tegra_host->vddio_min_uv = 1800000;
- tegra_host->vddio_max_uv = 1800000;
+ tegra_host->vddio_min_uv = SDHOST_LOW_VOLT_MIN;
+ tegra_host->vddio_max_uv = SDHOST_LOW_VOLT_MAX;
} else {
/*
* Set the minV and maxV to default
* voltage range of 2.7V - 3.6V
*/
- tegra_host->vddio_min_uv = 2700000;
- tegra_host->vddio_max_uv = 3600000;
+ tegra_host->vddio_min_uv = SDHOST_HIGH_VOLT_MIN;
+ tegra_host->vddio_max_uv = SDHOST_HIGH_VOLT_MAX;
}
tegra_host->vdd_io_reg = regulator_get(mmc_dev(host->mmc), "vddio_sdmmc");
if (IS_ERR_OR_NULL(tegra_host->vdd_io_reg)) {