summaryrefslogtreecommitdiff
path: root/drivers/mmc/mmc.c
diff options
context:
space:
mode:
authorJean-Jacques Hiblot <jjhiblot@ti.com>2017-05-12 20:16:38 +0200
committerJason Liu <jason.hui.liu@nxp.com>2017-11-03 02:37:11 +0800
commit6f7c748336f759acabd3675d0998be4d26b05c4d (patch)
tree16c7cebe44d192984e32d5e5c073beed6b56c8e2 /drivers/mmc/mmc.c
parenta70300217ccab60672574f133985518fff1a84fc (diff)
mmc: Add support for UHS modes
Add UHS modes to the list of supported modes, get the UHS capabilites of the SDcard and implement the procedure to switch the voltage (UHS modes use 1v8 IO lines) During the voltage switch procedure, DAT0 is used by the card to signal when it's ready. The optional card_busy() callback can be used to get this information from the host driver. Signed-off-by: Jean-Jacques Hiblot <jjhiblot@ti.com> Reviewed-by: Simon Glass <sjg@chromium.org>
Diffstat (limited to 'drivers/mmc/mmc.c')
-rw-r--r--drivers/mmc/mmc.c169
1 files changed, 162 insertions, 7 deletions
diff --git a/drivers/mmc/mmc.c b/drivers/mmc/mmc.c
index f6509f1398..074d286c8f 100644
--- a/drivers/mmc/mmc.c
+++ b/drivers/mmc/mmc.c
@@ -31,6 +31,7 @@ static const unsigned int sd_au_size[] = {
};
static int mmc_set_signal_voltage(struct mmc *mmc, uint signal_voltage);
static void mmc_power_cycle(struct mmc *mmc);
+static int mmc_card_busy(struct mmc *mmc);
#if CONFIG_IS_ENABLED(MMC_TINY)
static struct mmc mmc_static;
@@ -403,7 +404,68 @@ static int mmc_go_idle(struct mmc *mmc)
return 0;
}
-static int sd_send_op_cond(struct mmc *mmc)
+static int mmc_switch_voltage(struct mmc *mmc, int signal_voltage)
+{
+ struct mmc_cmd cmd;
+ int err = 0;
+
+ /*
+ * Send CMD11 only if the request is to switch the card to
+ * 1.8V signalling.
+ */
+ if (signal_voltage == MMC_SIGNAL_VOLTAGE_330)
+ return mmc_set_signal_voltage(mmc, signal_voltage);
+
+ cmd.cmdidx = SD_CMD_SWITCH_UHS18V;
+ cmd.cmdarg = 0;
+ cmd.resp_type = MMC_RSP_R1;
+
+ err = mmc_send_cmd(mmc, &cmd, NULL);
+ if (err)
+ goto fail;
+
+ if (!mmc_host_is_spi(host) && (cmd.response[0] & MMC_STATUS_ERROR))
+ goto fail;
+
+ /*
+ * The card should drive cmd and dat[0:3] low immediately
+ * after the response of cmd11, but wait 1 ms to be sure
+ */
+ udelay(1000);
+ if (mmc_card_busy(mmc))
+ goto fail;
+
+ /*
+ * During a signal voltage level switch, the clock must be gated
+ * for 5 ms according to the SD spec
+ */
+ mmc_set_clock(mmc, mmc->clock, true);
+
+ err = mmc_set_signal_voltage(mmc, signal_voltage);
+ if (err)
+ goto fail;
+
+ /* Keep clock gated for at least 10 ms, though spec only says 5 ms */
+ udelay(10000);
+ mmc_set_clock(mmc, mmc->clock, false);
+
+ /* Wait for at least 1 ms according to spec */
+ udelay(1000);
+
+ /*
+ * Failure to switch is indicated by the card holding
+ * dat[0:3] low
+ */
+ if (mmc_card_busy(mmc))
+ goto fail;
+
+ return 0;
+
+fail:
+ return -EIO;
+}
+
+static int sd_send_op_cond(struct mmc *mmc, bool uhs_en)
{
int timeout = 1000;
int err;
@@ -435,6 +497,9 @@ static int sd_send_op_cond(struct mmc *mmc)
if (mmc->version == SD_VERSION_2)
cmd.cmdarg |= OCR_HCS;
+ if (uhs_en)
+ cmd.cmdarg |= OCR_S18R;
+
err = mmc_send_cmd(mmc, &cmd, NULL);
if (err)
@@ -465,6 +530,13 @@ static int sd_send_op_cond(struct mmc *mmc)
mmc->ocr = cmd.response[0];
+ if (!(mmc_host_is_spi(mmc)) && (cmd.response[0] & 0x41000000)
+ == 0x41000000) {
+ err = mmc_switch_voltage(mmc, MMC_SIGNAL_VOLTAGE_180);
+ if (err)
+ return err;
+ }
+
mmc->high_capacity = ((mmc->ocr & OCR_HCS) == OCR_HCS);
mmc->rca = 0;
@@ -977,6 +1049,7 @@ static int sd_get_capabilities(struct mmc *mmc)
ALLOC_CACHE_ALIGN_BUFFER(uint, switch_status, 16);
struct mmc_data data;
int timeout;
+ u32 sd3_bus_mode;
mmc->card_caps = MMC_MODE_1BIT;
@@ -1058,6 +1131,22 @@ retry_scr:
if (__be32_to_cpu(switch_status[3]) & SD_HIGHSPEED_SUPPORTED)
mmc->card_caps |= MMC_CAP(SD_HS);
+ /* Version before 3.0 don't support UHS modes */
+ if (mmc->version < SD_VERSION_3)
+ return 0;
+
+ sd3_bus_mode = __be32_to_cpu(switch_status[3]) >> 16 & 0x1f;
+ if (sd3_bus_mode & SD_MODE_UHS_SDR104)
+ mmc->card_caps |= MMC_CAP(UHS_SDR104);
+ if (sd3_bus_mode & SD_MODE_UHS_SDR50)
+ mmc->card_caps |= MMC_CAP(UHS_SDR50);
+ if (sd3_bus_mode & SD_MODE_UHS_SDR25)
+ mmc->card_caps |= MMC_CAP(UHS_SDR25);
+ if (sd3_bus_mode & SD_MODE_UHS_SDR12)
+ mmc->card_caps |= MMC_CAP(UHS_SDR12);
+ if (sd3_bus_mode & SD_MODE_UHS_DDR50)
+ mmc->card_caps |= MMC_CAP(UHS_DDR50);
+
return 0;
}
@@ -1065,13 +1154,36 @@ static int sd_set_card_speed(struct mmc *mmc, enum bus_mode mode)
{
int err;
ALLOC_CACHE_ALIGN_BUFFER(uint, switch_status, 16);
+ int speed;
+
+ switch (mode) {
+ case SD_LEGACY:
+ case UHS_SDR12:
+ speed = UHS_SDR12_BUS_SPEED;
+ break;
+ case SD_HS:
+ case UHS_SDR25:
+ speed = UHS_SDR25_BUS_SPEED;
+ break;
+ case UHS_SDR50:
+ speed = UHS_SDR50_BUS_SPEED;
+ break;
+ case UHS_DDR50:
+ speed = UHS_DDR50_BUS_SPEED;
+ break;
+ case UHS_SDR104:
+ speed = UHS_SDR104_BUS_SPEED;
+ break;
+ default:
+ return -EINVAL;
+ }
- err = sd_switch(mmc, SD_SWITCH_SWITCH, 0, 1, (u8 *)switch_status);
+ err = sd_switch(mmc, SD_SWITCH_SWITCH, 0, speed, (u8 *)switch_status);
if (err)
return err;
- if ((__be32_to_cpu(switch_status[4]) & 0x0f000000) != 0x01000000)
+ if ((__be32_to_cpu(switch_status[4]) >> 24) != speed)
return -ENOTSUPP;
return 0;
@@ -1291,10 +1403,31 @@ static int mmc_set_signal_voltage(struct mmc *mmc, uint signal_voltage)
static const struct mode_width_tuning sd_modes_by_pref[] = {
{
+ .mode = UHS_SDR104,
+ .widths = MMC_MODE_4BIT | MMC_MODE_1BIT,
+ .tuning = MMC_SEND_TUNING_BLOCK
+ },
+ {
+ .mode = UHS_SDR50,
+ .widths = MMC_MODE_4BIT | MMC_MODE_1BIT,
+ },
+ {
+ .mode = UHS_DDR50,
+ .widths = MMC_MODE_4BIT | MMC_MODE_1BIT,
+ },
+ {
+ .mode = UHS_SDR25,
+ .widths = MMC_MODE_4BIT | MMC_MODE_1BIT,
+ },
+ {
.mode = SD_HS,
.widths = MMC_MODE_4BIT | MMC_MODE_1BIT,
},
{
+ .mode = UHS_SDR12,
+ .widths = MMC_MODE_4BIT | MMC_MODE_1BIT,
+ },
+ {
.mode = SD_LEGACY,
.widths = MMC_MODE_4BIT | MMC_MODE_1BIT,
}
@@ -1310,18 +1443,24 @@ static int sd_select_mode_and_width(struct mmc *mmc)
int err;
uint widths[] = {MMC_MODE_4BIT, MMC_MODE_1BIT};
const struct mode_width_tuning *mwt;
+ bool uhs_en = (mmc->ocr & OCR_S18R) ? true : false;
+ uint caps;
+
err = sd_get_capabilities(mmc);
if (err)
return err;
/* Restrict card's capabilities by what the host can do */
- mmc->card_caps &= (mmc->cfg->host_caps | MMC_MODE_1BIT);
+ caps = mmc->card_caps & (mmc->cfg->host_caps | MMC_MODE_1BIT);
- for_each_sd_mode_by_pref(mmc->card_caps, mwt) {
+ if (!uhs_en)
+ caps &= ~UHS_CAPS;
+
+ for_each_sd_mode_by_pref(caps, mwt) {
uint *w;
for (w = widths; w < widths + ARRAY_SIZE(widths); w++) {
- if (*w & mmc->card_caps & mwt->widths) {
+ if (*w & caps & mwt->widths) {
debug("trying mode %s width %d (at %d MHz)\n",
mmc_mode_name(mwt->mode),
bus_width(*w),
@@ -1342,6 +1481,16 @@ static int sd_select_mode_and_width(struct mmc *mmc)
mmc_select_mode(mmc, mwt->mode);
mmc_set_clock(mmc, mmc->tran_speed, false);
+ /* execute tuning if needed */
+ if (mwt->tuning && !mmc_host_is_spi(mmc)) {
+ err = mmc_execute_tuning(mmc,
+ mwt->tuning);
+ if (err) {
+ debug("tuning failed\n");
+ goto error;
+ }
+ }
+
err = sd_read_ssr(mmc);
if (!err)
return 0;
@@ -1993,6 +2142,7 @@ static void mmc_power_cycle(struct mmc *mmc)
int mmc_start_init(struct mmc *mmc)
{
bool no_card;
+ bool uhs_en = supports_uhs(mmc->cfg->host_caps);
int err;
/* we pretend there's no card when init is NULL */
@@ -2028,6 +2178,7 @@ int mmc_start_init(struct mmc *mmc)
#endif
mmc->ddr_mode = 0;
+retry:
mmc_power_cycle(mmc);
/* Reset the Card */
@@ -2043,7 +2194,11 @@ int mmc_start_init(struct mmc *mmc)
err = mmc_send_if_cond(mmc);
/* Now try to get the SD card's operating condition */
- err = sd_send_op_cond(mmc);
+ err = sd_send_op_cond(mmc, uhs_en);
+ if (err && uhs_en) {
+ uhs_en = false;
+ goto retry;
+ }
/* If the command timed out, we check for an MMC card */
if (err == -ETIMEDOUT) {