Commit 01730558 authored by Doug Anderson's avatar Doug Anderson Committed by Ulf Hansson

mmc: dw_mmc: Support voltage changes

For UHS cards we need the ability to switch voltages from 3.3V to
1.8V.  Add support to the dw_mmc driver to handle this.  Note that
dw_mmc needs a little bit of extra code since the interface needs a
special bit programmed to the CMD register while CMD11 is progressing.
This means adding a few extra states to the state machine to track.
Signed-off-by: default avatarDoug Anderson <dianders@chromium.org>
Signed-off-by: default avatarYuvaraj Kumar C D <yuvaraj.cd@samsung.com>
Signed-off-by: default avatarUlf Hansson <ulf.hansson@linaro.org>
parent 51da2240
...@@ -29,6 +29,7 @@ ...@@ -29,6 +29,7 @@
#include <linux/irq.h> #include <linux/irq.h>
#include <linux/mmc/host.h> #include <linux/mmc/host.h>
#include <linux/mmc/mmc.h> #include <linux/mmc/mmc.h>
#include <linux/mmc/sd.h>
#include <linux/mmc/sdio.h> #include <linux/mmc/sdio.h>
#include <linux/mmc/dw_mmc.h> #include <linux/mmc/dw_mmc.h>
#include <linux/bitops.h> #include <linux/bitops.h>
...@@ -234,10 +235,13 @@ static void dw_mci_init_debugfs(struct dw_mci_slot *slot) ...@@ -234,10 +235,13 @@ static void dw_mci_init_debugfs(struct dw_mci_slot *slot)
} }
#endif /* defined(CONFIG_DEBUG_FS) */ #endif /* defined(CONFIG_DEBUG_FS) */
static void mci_send_cmd(struct dw_mci_slot *slot, u32 cmd, u32 arg);
static u32 dw_mci_prepare_command(struct mmc_host *mmc, struct mmc_command *cmd) static u32 dw_mci_prepare_command(struct mmc_host *mmc, struct mmc_command *cmd)
{ {
struct mmc_data *data; struct mmc_data *data;
struct dw_mci_slot *slot = mmc_priv(mmc); struct dw_mci_slot *slot = mmc_priv(mmc);
struct dw_mci *host = slot->host;
const struct dw_mci_drv_data *drv_data = slot->host->drv_data; const struct dw_mci_drv_data *drv_data = slot->host->drv_data;
u32 cmdr; u32 cmdr;
cmd->error = -EINPROGRESS; cmd->error = -EINPROGRESS;
...@@ -253,6 +257,34 @@ static u32 dw_mci_prepare_command(struct mmc_host *mmc, struct mmc_command *cmd) ...@@ -253,6 +257,34 @@ static u32 dw_mci_prepare_command(struct mmc_host *mmc, struct mmc_command *cmd)
else if (cmd->opcode != MMC_SEND_STATUS && cmd->data) else if (cmd->opcode != MMC_SEND_STATUS && cmd->data)
cmdr |= SDMMC_CMD_PRV_DAT_WAIT; cmdr |= SDMMC_CMD_PRV_DAT_WAIT;
if (cmd->opcode == SD_SWITCH_VOLTAGE) {
u32 clk_en_a;
/* Special bit makes CMD11 not die */
cmdr |= SDMMC_CMD_VOLT_SWITCH;
/* Change state to continue to handle CMD11 weirdness */
WARN_ON(slot->host->state != STATE_SENDING_CMD);
slot->host->state = STATE_SENDING_CMD11;
/*
* We need to disable low power mode (automatic clock stop)
* while doing voltage switch so we don't confuse the card,
* since stopping the clock is a specific part of the UHS
* voltage change dance.
*
* Note that low power mode (SDMMC_CLKEN_LOW_PWR) will be
* unconditionally turned back on in dw_mci_setup_bus() if it's
* ever called with a non-zero clock. That shouldn't happen
* until the voltage change is all done.
*/
clk_en_a = mci_readl(host, CLKENA);
clk_en_a &= ~(SDMMC_CLKEN_LOW_PWR << slot->id);
mci_writel(host, CLKENA, clk_en_a);
mci_send_cmd(slot, SDMMC_CMD_UPD_CLK |
SDMMC_CMD_PRV_DAT_WAIT, 0);
}
if (cmd->flags & MMC_RSP_PRESENT) { if (cmd->flags & MMC_RSP_PRESENT) {
/* We expect a response, so set this bit */ /* We expect a response, so set this bit */
cmdr |= SDMMC_CMD_RESP_EXP; cmdr |= SDMMC_CMD_RESP_EXP;
...@@ -775,11 +807,15 @@ static void dw_mci_setup_bus(struct dw_mci_slot *slot, bool force_clkinit) ...@@ -775,11 +807,15 @@ static void dw_mci_setup_bus(struct dw_mci_slot *slot, bool force_clkinit)
unsigned int clock = slot->clock; unsigned int clock = slot->clock;
u32 div; u32 div;
u32 clk_en_a; u32 clk_en_a;
u32 sdmmc_cmd_bits = SDMMC_CMD_UPD_CLK | SDMMC_CMD_PRV_DAT_WAIT;
/* We must continue to set bit 28 in CMD until the change is complete */
if (host->state == STATE_WAITING_CMD11_DONE)
sdmmc_cmd_bits |= SDMMC_CMD_VOLT_SWITCH;
if (!clock) { if (!clock) {
mci_writel(host, CLKENA, 0); mci_writel(host, CLKENA, 0);
mci_send_cmd(slot, mci_send_cmd(slot, sdmmc_cmd_bits, 0);
SDMMC_CMD_UPD_CLK | SDMMC_CMD_PRV_DAT_WAIT, 0);
} else if (clock != host->current_speed || force_clkinit) { } else if (clock != host->current_speed || force_clkinit) {
div = host->bus_hz / clock; div = host->bus_hz / clock;
if (host->bus_hz % clock && host->bus_hz > clock) if (host->bus_hz % clock && host->bus_hz > clock)
...@@ -803,15 +839,13 @@ static void dw_mci_setup_bus(struct dw_mci_slot *slot, bool force_clkinit) ...@@ -803,15 +839,13 @@ static void dw_mci_setup_bus(struct dw_mci_slot *slot, bool force_clkinit)
mci_writel(host, CLKSRC, 0); mci_writel(host, CLKSRC, 0);
/* inform CIU */ /* inform CIU */
mci_send_cmd(slot, mci_send_cmd(slot, sdmmc_cmd_bits, 0);
SDMMC_CMD_UPD_CLK | SDMMC_CMD_PRV_DAT_WAIT, 0);
/* set clock to desired speed */ /* set clock to desired speed */
mci_writel(host, CLKDIV, div); mci_writel(host, CLKDIV, div);
/* inform CIU */ /* inform CIU */
mci_send_cmd(slot, mci_send_cmd(slot, sdmmc_cmd_bits, 0);
SDMMC_CMD_UPD_CLK | SDMMC_CMD_PRV_DAT_WAIT, 0);
/* enable clock; only low power if no SDIO */ /* enable clock; only low power if no SDIO */
clk_en_a = SDMMC_CLKEN_ENABLE << slot->id; clk_en_a = SDMMC_CLKEN_ENABLE << slot->id;
...@@ -820,8 +854,7 @@ static void dw_mci_setup_bus(struct dw_mci_slot *slot, bool force_clkinit) ...@@ -820,8 +854,7 @@ static void dw_mci_setup_bus(struct dw_mci_slot *slot, bool force_clkinit)
mci_writel(host, CLKENA, clk_en_a); mci_writel(host, CLKENA, clk_en_a);
/* inform CIU */ /* inform CIU */
mci_send_cmd(slot, mci_send_cmd(slot, sdmmc_cmd_bits, 0);
SDMMC_CMD_UPD_CLK | SDMMC_CMD_PRV_DAT_WAIT, 0);
/* keep the clock with reflecting clock dividor */ /* keep the clock with reflecting clock dividor */
slot->__clk_old = clock << div; slot->__clk_old = clock << div;
...@@ -897,6 +930,17 @@ static void dw_mci_queue_request(struct dw_mci *host, struct dw_mci_slot *slot, ...@@ -897,6 +930,17 @@ static void dw_mci_queue_request(struct dw_mci *host, struct dw_mci_slot *slot,
slot->mrq = mrq; slot->mrq = mrq;
if (host->state == STATE_WAITING_CMD11_DONE) {
dev_warn(&slot->mmc->class_dev,
"Voltage change didn't complete\n");
/*
* this case isn't expected to happen, so we can
* either crash here or just try to continue on
* in the closest possible state
*/
host->state = STATE_IDLE;
}
if (host->state == STATE_IDLE) { if (host->state == STATE_IDLE) {
host->state = STATE_SENDING_CMD; host->state = STATE_SENDING_CMD;
dw_mci_start_request(host, slot); dw_mci_start_request(host, slot);
...@@ -973,6 +1017,9 @@ static void dw_mci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) ...@@ -973,6 +1017,9 @@ static void dw_mci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
/* Slot specific timing and width adjustment */ /* Slot specific timing and width adjustment */
dw_mci_setup_bus(slot, false); dw_mci_setup_bus(slot, false);
if (slot->host->state == STATE_WAITING_CMD11_DONE && ios->clock != 0)
slot->host->state = STATE_IDLE;
switch (ios->power_mode) { switch (ios->power_mode) {
case MMC_POWER_UP: case MMC_POWER_UP:
if (!IS_ERR(mmc->supply.vmmc)) { if (!IS_ERR(mmc->supply.vmmc)) {
...@@ -1016,6 +1063,59 @@ static void dw_mci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) ...@@ -1016,6 +1063,59 @@ static void dw_mci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
} }
} }
static int dw_mci_card_busy(struct mmc_host *mmc)
{
struct dw_mci_slot *slot = mmc_priv(mmc);
u32 status;
/*
* Check the busy bit which is low when DAT[3:0]
* (the data lines) are 0000
*/
status = mci_readl(slot->host, STATUS);
return !!(status & SDMMC_STATUS_BUSY);
}
static int dw_mci_switch_voltage(struct mmc_host *mmc, struct mmc_ios *ios)
{
struct dw_mci_slot *slot = mmc_priv(mmc);
struct dw_mci *host = slot->host;
u32 uhs;
u32 v18 = SDMMC_UHS_18V << slot->id;
int min_uv, max_uv;
int ret;
/*
* Program the voltage. Note that some instances of dw_mmc may use
* the UHS_REG for this. For other instances (like exynos) the UHS_REG
* does no harm but you need to set the regulator directly. Try both.
*/
uhs = mci_readl(host, UHS_REG);
if (ios->signal_voltage == MMC_SIGNAL_VOLTAGE_330) {
min_uv = 2700000;
max_uv = 3600000;
uhs &= ~v18;
} else {
min_uv = 1700000;
max_uv = 1950000;
uhs |= v18;
}
if (!IS_ERR(mmc->supply.vqmmc)) {
ret = regulator_set_voltage(mmc->supply.vqmmc, min_uv, max_uv);
if (ret) {
dev_err(&mmc->class_dev,
"Regulator set error %d: %d - %d\n",
ret, min_uv, max_uv);
return ret;
}
}
mci_writel(host, UHS_REG, uhs);
return 0;
}
static int dw_mci_get_ro(struct mmc_host *mmc) static int dw_mci_get_ro(struct mmc_host *mmc)
{ {
int read_only; int read_only;
...@@ -1158,6 +1258,9 @@ static const struct mmc_host_ops dw_mci_ops = { ...@@ -1158,6 +1258,9 @@ static const struct mmc_host_ops dw_mci_ops = {
.get_cd = dw_mci_get_cd, .get_cd = dw_mci_get_cd,
.enable_sdio_irq = dw_mci_enable_sdio_irq, .enable_sdio_irq = dw_mci_enable_sdio_irq,
.execute_tuning = dw_mci_execute_tuning, .execute_tuning = dw_mci_execute_tuning,
.card_busy = dw_mci_card_busy,
.start_signal_voltage_switch = dw_mci_switch_voltage,
}; };
static void dw_mci_request_end(struct dw_mci *host, struct mmc_request *mrq) static void dw_mci_request_end(struct dw_mci *host, struct mmc_request *mrq)
...@@ -1181,7 +1284,11 @@ static void dw_mci_request_end(struct dw_mci *host, struct mmc_request *mrq) ...@@ -1181,7 +1284,11 @@ static void dw_mci_request_end(struct dw_mci *host, struct mmc_request *mrq)
dw_mci_start_request(host, slot); dw_mci_start_request(host, slot);
} else { } else {
dev_vdbg(host->dev, "list empty\n"); dev_vdbg(host->dev, "list empty\n");
host->state = STATE_IDLE;
if (host->state == STATE_SENDING_CMD11)
host->state = STATE_WAITING_CMD11_DONE;
else
host->state = STATE_IDLE;
} }
spin_unlock(&host->lock); spin_unlock(&host->lock);
...@@ -1292,8 +1399,10 @@ static void dw_mci_tasklet_func(unsigned long priv) ...@@ -1292,8 +1399,10 @@ static void dw_mci_tasklet_func(unsigned long priv)
switch (state) { switch (state) {
case STATE_IDLE: case STATE_IDLE:
case STATE_WAITING_CMD11_DONE:
break; break;
case STATE_SENDING_CMD11:
case STATE_SENDING_CMD: case STATE_SENDING_CMD:
if (!test_and_clear_bit(EVENT_CMD_COMPLETE, if (!test_and_clear_bit(EVENT_CMD_COMPLETE,
&host->pending_events)) &host->pending_events))
...@@ -1894,6 +2003,14 @@ static irqreturn_t dw_mci_interrupt(int irq, void *dev_id) ...@@ -1894,6 +2003,14 @@ static irqreturn_t dw_mci_interrupt(int irq, void *dev_id)
} }
if (pending) { if (pending) {
/* Check volt switch first, since it can look like an error */
if ((host->state == STATE_SENDING_CMD11) &&
(pending & SDMMC_INT_VOLT_SWITCH)) {
mci_writel(host, RINTSTS, SDMMC_INT_VOLT_SWITCH);
pending &= ~SDMMC_INT_VOLT_SWITCH;
dw_mci_cmd_interrupt(host, pending);
}
if (pending & DW_MCI_CMD_ERROR_FLAGS) { if (pending & DW_MCI_CMD_ERROR_FLAGS) {
mci_writel(host, RINTSTS, DW_MCI_CMD_ERROR_FLAGS); mci_writel(host, RINTSTS, DW_MCI_CMD_ERROR_FLAGS);
host->cmd_status = pending; host->cmd_status = pending;
...@@ -1999,7 +2116,9 @@ static void dw_mci_work_routine_card(struct work_struct *work) ...@@ -1999,7 +2116,9 @@ static void dw_mci_work_routine_card(struct work_struct *work)
switch (host->state) { switch (host->state) {
case STATE_IDLE: case STATE_IDLE:
case STATE_WAITING_CMD11_DONE:
break; break;
case STATE_SENDING_CMD11:
case STATE_SENDING_CMD: case STATE_SENDING_CMD:
mrq->cmd->error = -ENOMEDIUM; mrq->cmd->error = -ENOMEDIUM;
if (!mrq->data) if (!mrq->data)
......
...@@ -99,6 +99,7 @@ ...@@ -99,6 +99,7 @@
#define SDMMC_INT_HLE BIT(12) #define SDMMC_INT_HLE BIT(12)
#define SDMMC_INT_FRUN BIT(11) #define SDMMC_INT_FRUN BIT(11)
#define SDMMC_INT_HTO BIT(10) #define SDMMC_INT_HTO BIT(10)
#define SDMMC_INT_VOLT_SWITCH BIT(10) /* overloads bit 10! */
#define SDMMC_INT_DRTO BIT(9) #define SDMMC_INT_DRTO BIT(9)
#define SDMMC_INT_RTO BIT(8) #define SDMMC_INT_RTO BIT(8)
#define SDMMC_INT_DCRC BIT(7) #define SDMMC_INT_DCRC BIT(7)
...@@ -113,6 +114,7 @@ ...@@ -113,6 +114,7 @@
/* Command register defines */ /* Command register defines */
#define SDMMC_CMD_START BIT(31) #define SDMMC_CMD_START BIT(31)
#define SDMMC_CMD_USE_HOLD_REG BIT(29) #define SDMMC_CMD_USE_HOLD_REG BIT(29)
#define SDMMC_CMD_VOLT_SWITCH BIT(28)
#define SDMMC_CMD_CCS_EXP BIT(23) #define SDMMC_CMD_CCS_EXP BIT(23)
#define SDMMC_CMD_CEATA_RD BIT(22) #define SDMMC_CMD_CEATA_RD BIT(22)
#define SDMMC_CMD_UPD_CLK BIT(21) #define SDMMC_CMD_UPD_CLK BIT(21)
...@@ -130,6 +132,7 @@ ...@@ -130,6 +132,7 @@
/* Status register defines */ /* Status register defines */
#define SDMMC_GET_FCNT(x) (((x)>>17) & 0x1FFF) #define SDMMC_GET_FCNT(x) (((x)>>17) & 0x1FFF)
#define SDMMC_STATUS_DMA_REQ BIT(31) #define SDMMC_STATUS_DMA_REQ BIT(31)
#define SDMMC_STATUS_BUSY BIT(9)
/* FIFOTH register defines */ /* FIFOTH register defines */
#define SDMMC_SET_FIFOTH(m, r, t) (((m) & 0x7) << 28 | \ #define SDMMC_SET_FIFOTH(m, r, t) (((m) & 0x7) << 28 | \
((r) & 0xFFF) << 16 | \ ((r) & 0xFFF) << 16 | \
...@@ -150,7 +153,7 @@ ...@@ -150,7 +153,7 @@
#define SDMMC_GET_VERID(x) ((x) & 0xFFFF) #define SDMMC_GET_VERID(x) ((x) & 0xFFFF)
/* Card read threshold */ /* Card read threshold */
#define SDMMC_SET_RD_THLD(v, x) (((v) & 0x1FFF) << 16 | (x)) #define SDMMC_SET_RD_THLD(v, x) (((v) & 0x1FFF) << 16 | (x))
#define SDMMC_UHS_18V BIT(0)
/* All ctrl reset bits */ /* All ctrl reset bits */
#define SDMMC_CTRL_ALL_RESET_FLAGS \ #define SDMMC_CTRL_ALL_RESET_FLAGS \
(SDMMC_CTRL_RESET | SDMMC_CTRL_FIFO_RESET | SDMMC_CTRL_DMA_RESET) (SDMMC_CTRL_RESET | SDMMC_CTRL_FIFO_RESET | SDMMC_CTRL_DMA_RESET)
......
...@@ -26,6 +26,8 @@ enum dw_mci_state { ...@@ -26,6 +26,8 @@ enum dw_mci_state {
STATE_DATA_BUSY, STATE_DATA_BUSY,
STATE_SENDING_STOP, STATE_SENDING_STOP,
STATE_DATA_ERROR, STATE_DATA_ERROR,
STATE_SENDING_CMD11,
STATE_WAITING_CMD11_DONE,
}; };
enum { enum {
......
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