Skip to content

Commit

Permalink
mmc: sdhci: update signal voltage switch code
Browse files Browse the repository at this point in the history
The protocol related code is moved to core stack. So update the host
driver accordingly.

Signed-off-by: Kevin Liu <kliu5@marvell.com>
Tested-by: Tim Wang <wangtt@marvell.com>
Signed-off-by: Chris Ball <cjb@laptop.org>
  • Loading branch information
Kevin Liu authored and cjb committed Feb 24, 2013
1 parent 0797e5f commit 20b92a3
Showing 1 changed file with 78 additions and 114 deletions.
192 changes: 78 additions & 114 deletions drivers/mmc/host/sdhci.c
Original file line number Diff line number Diff line change
Expand Up @@ -1615,157 +1615,120 @@ static void sdhci_enable_sdio_irq(struct mmc_host *mmc, int enable)
spin_unlock_irqrestore(&host->lock, flags);
}

static int sdhci_do_3_3v_signal_voltage_switch(struct sdhci_host *host,
u16 ctrl)
static int sdhci_do_start_signal_voltage_switch(struct sdhci_host *host,
int signal_voltage)
{
u16 ctrl;
int ret;

/* Set 1.8V Signal Enable in the Host Control2 register to 0 */
ctrl &= ~SDHCI_CTRL_VDD_180;
sdhci_writew(host, ctrl, SDHCI_HOST_CONTROL2);

if (host->vqmmc) {
ret = regulator_set_voltage(host->vqmmc, 2700000, 3600000);
if (ret) {
pr_warning("%s: Switching to 3.3V signalling voltage "
" failed\n", mmc_hostname(host->mmc));
return -EIO;
}
}
/* Wait for 5ms */
usleep_range(5000, 5500);
/*
* Signal Voltage Switching is only applicable for Host Controllers
* v3.00 and above.
*/
if (host->version < SDHCI_SPEC_300)
return 0;

/* 3.3V regulator output should be stable within 5 ms */
ctrl = sdhci_readw(host, SDHCI_HOST_CONTROL2);
if (!(ctrl & SDHCI_CTRL_VDD_180))
return 0;

pr_warning("%s: 3.3V regulator output did not became stable\n",
mmc_hostname(host->mmc));
switch (signal_voltage) {
case MMC_SIGNAL_VOLTAGE_330:
/* Set 1.8V Signal Enable in the Host Control2 register to 0 */
ctrl &= ~SDHCI_CTRL_VDD_180;
sdhci_writew(host, ctrl, SDHCI_HOST_CONTROL2);

return -EIO;
}
if (host->vqmmc) {
ret = regulator_set_voltage(host->vqmmc, 2700000, 3600000);
if (ret) {
pr_warning("%s: Switching to 3.3V signalling voltage "
" failed\n", mmc_hostname(host->mmc));
return -EIO;
}
}
/* Wait for 5ms */
usleep_range(5000, 5500);

static int sdhci_do_1_8v_signal_voltage_switch(struct sdhci_host *host,
u16 ctrl)
{
u8 pwr;
u16 clk;
u32 present_state;
int ret;
/* 3.3V regulator output should be stable within 5 ms */
ctrl = sdhci_readw(host, SDHCI_HOST_CONTROL2);
if (!(ctrl & SDHCI_CTRL_VDD_180))
return 0;

/* Stop SDCLK */
clk = sdhci_readw(host, SDHCI_CLOCK_CONTROL);
clk &= ~SDHCI_CLOCK_CARD_EN;
sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL);
pr_warning("%s: 3.3V regulator output did not became stable\n",
mmc_hostname(host->mmc));

return -EAGAIN;
case MMC_SIGNAL_VOLTAGE_180:
if (host->vqmmc) {
ret = regulator_set_voltage(host->vqmmc,
1700000, 1950000);
if (ret) {
pr_warning("%s: Switching to 1.8V signalling voltage "
" failed\n", mmc_hostname(host->mmc));
return -EIO;
}
}

/* Check whether DAT[3:0] is 0000 */
present_state = sdhci_readl(host, SDHCI_PRESENT_STATE);
if (!((present_state & SDHCI_DATA_LVL_MASK) >>
SDHCI_DATA_LVL_SHIFT)) {
/*
* Enable 1.8V Signal Enable in the Host Control2
* register
*/
if (host->vqmmc)
ret = regulator_set_voltage(host->vqmmc,
1700000, 1950000);
else
ret = 0;
ctrl |= SDHCI_CTRL_VDD_180;
sdhci_writew(host, ctrl, SDHCI_HOST_CONTROL2);

if (!ret) {
ctrl |= SDHCI_CTRL_VDD_180;
sdhci_writew(host, ctrl, SDHCI_HOST_CONTROL2);
/* Wait for 5ms */
usleep_range(5000, 5500);

/* Wait for 5ms */
usleep_range(5000, 5500);
/* 1.8V regulator output should be stable within 5 ms */
ctrl = sdhci_readw(host, SDHCI_HOST_CONTROL2);
if (ctrl & SDHCI_CTRL_VDD_180)
return 0;

ctrl = sdhci_readw(host, SDHCI_HOST_CONTROL2);
if (ctrl & SDHCI_CTRL_VDD_180) {
/* Provide SDCLK again and wait for 1ms */
clk = sdhci_readw(host, SDHCI_CLOCK_CONTROL);
clk |= SDHCI_CLOCK_CARD_EN;
sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL);
usleep_range(1000, 1500);
pr_warning("%s: 1.8V regulator output did not became stable\n",
mmc_hostname(host->mmc));

/*
* If DAT[3:0] level is 1111b, then the card
* was successfully switched to 1.8V signaling.
*/
present_state = sdhci_readl(host,
SDHCI_PRESENT_STATE);
if ((present_state & SDHCI_DATA_LVL_MASK) ==
SDHCI_DATA_LVL_MASK)
return 0;
return -EAGAIN;
case MMC_SIGNAL_VOLTAGE_120:
if (host->vqmmc) {
ret = regulator_set_voltage(host->vqmmc, 1100000, 1300000);
if (ret) {
pr_warning("%s: Switching to 1.2V signalling voltage "
" failed\n", mmc_hostname(host->mmc));
return -EIO;
}
}
}

/*
* If we are here, that means the switch to 1.8V signaling
* failed. We power cycle the card, and retry initialization
* sequence by setting S18R to 0.
*/
pwr = sdhci_readb(host, SDHCI_POWER_CONTROL);
pwr &= ~SDHCI_POWER_ON;
sdhci_writeb(host, pwr, SDHCI_POWER_CONTROL);
if (host->vmmc)
regulator_disable(host->vmmc);

/* Wait for 1ms as per the spec */
usleep_range(1000, 1500);
pwr |= SDHCI_POWER_ON;
sdhci_writeb(host, pwr, SDHCI_POWER_CONTROL);
if (host->vmmc)
regulator_enable(host->vmmc);

pr_warning("%s: Switching to 1.8V signalling voltage failed, "
"retrying with S18R set to 0\n", mmc_hostname(host->mmc));

return -EAGAIN;
}

static int sdhci_do_start_signal_voltage_switch(struct sdhci_host *host,
struct mmc_ios *ios)
{
u16 ctrl;

/*
* Signal Voltage Switching is only applicable for Host Controllers
* v3.00 and above.
*/
if (host->version < SDHCI_SPEC_300)
return 0;

/*
* We first check whether the request is to set signalling voltage
* to 3.3V. If so, we change the voltage to 3.3V and return quickly.
*/
ctrl = sdhci_readw(host, SDHCI_HOST_CONTROL2);
if (ios->signal_voltage == MMC_SIGNAL_VOLTAGE_330)
return sdhci_do_3_3v_signal_voltage_switch(host, ctrl);
else if (!(ctrl & SDHCI_CTRL_VDD_180) &&
(ios->signal_voltage == MMC_SIGNAL_VOLTAGE_180))
return sdhci_do_1_8v_signal_voltage_switch(host, ctrl);
else
default:
/* No signal voltage switch required */
return 0;
}
}

static int sdhci_start_signal_voltage_switch(struct mmc_host *mmc,
struct mmc_ios *ios)
int signal_voltage)
{
struct sdhci_host *host = mmc_priv(mmc);
int err;

if (host->version < SDHCI_SPEC_300)
return 0;
sdhci_runtime_pm_get(host);
err = sdhci_do_start_signal_voltage_switch(host, ios);
err = sdhci_do_start_signal_voltage_switch(host, signal_voltage);
sdhci_runtime_pm_put(host);
return err;
}

static int sdhci_card_busy(struct mmc_host *mmc)
{
struct sdhci_host *host = mmc_priv(mmc);
u32 present_state;

sdhci_runtime_pm_get(host);
/* Check whether DAT[3:0] is 0000 */
present_state = sdhci_readl(host, SDHCI_PRESENT_STATE);
sdhci_runtime_pm_put(host);

return !(present_state & SDHCI_DATA_LVL_MASK);
}

static int sdhci_execute_tuning(struct mmc_host *mmc, u32 opcode)
{
struct sdhci_host *host;
Expand Down Expand Up @@ -2036,6 +1999,7 @@ static const struct mmc_host_ops sdhci_ops = {
.execute_tuning = sdhci_execute_tuning,
.enable_preset_value = sdhci_enable_preset_value,
.card_event = sdhci_card_event,
.card_busy = sdhci_card_busy,
};

/*****************************************************************************\
Expand Down

0 comments on commit 20b92a3

Please sign in to comment.