diff options
Diffstat (limited to 'drivers/mmc/core')
-rw-r--r-- | drivers/mmc/core/core.c | 32 | ||||
-rw-r--r-- | drivers/mmc/core/core.h | 1 | ||||
-rw-r--r-- | drivers/mmc/core/sd.c | 36 |
3 files changed, 67 insertions, 2 deletions
diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c index 1dbc18576219..5005a6323165 100644 --- a/drivers/mmc/core/core.c +++ b/drivers/mmc/core/core.c @@ -942,6 +942,38 @@ u32 mmc_select_voltage(struct mmc_host *host, u32 ocr) return ocr; } +int mmc_set_signal_voltage(struct mmc_host *host, int signal_voltage) +{ + struct mmc_command cmd = {0}; + int err = 0; + + BUG_ON(!host); + + /* + * Send CMD11 only if the request is to switch the card to + * 1.8V signalling. + */ + if (signal_voltage == MMC_SIGNAL_VOLTAGE_180) { + cmd.opcode = SD_SWITCH_VOLTAGE; + cmd.arg = 0; + cmd.flags = MMC_RSP_R1 | MMC_CMD_AC; + + err = mmc_wait_for_cmd(host, &cmd, 0); + if (err) + return err; + + if (!mmc_host_is_spi(host) && (cmd.resp[0] & R1_ERROR)) + return -EIO; + } + + host->ios.signal_voltage = signal_voltage; + + if (host->ops->start_signal_voltage_switch) + err = host->ops->start_signal_voltage_switch(host, &host->ios); + + return err; +} + /* * Select timing parameters for host. */ diff --git a/drivers/mmc/core/core.h b/drivers/mmc/core/core.h index ca1fdde29df6..7745dea430b8 100644 --- a/drivers/mmc/core/core.h +++ b/drivers/mmc/core/core.h @@ -41,6 +41,7 @@ void mmc_set_bus_width(struct mmc_host *host, unsigned int width); void mmc_set_bus_width_ddr(struct mmc_host *host, unsigned int width, unsigned int ddr); u32 mmc_select_voltage(struct mmc_host *host, u32 ocr); +int mmc_set_signal_voltage(struct mmc_host *host, int signal_voltage); void mmc_set_timing(struct mmc_host *host, unsigned int timing); static inline void mmc_delay(unsigned int ms) diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c index 6dac89fe0535..b0cd285d272a 100644 --- a/drivers/mmc/core/sd.c +++ b/drivers/mmc/core/sd.c @@ -403,6 +403,7 @@ struct device_type sd_type = { int mmc_sd_get_cid(struct mmc_host *host, u32 ocr, u32 *cid) { int err; + u32 rocr; /* * Since we're changing the OCR value, we seem to @@ -420,12 +421,38 @@ int mmc_sd_get_cid(struct mmc_host *host, u32 ocr, u32 *cid) */ err = mmc_send_if_cond(host, ocr); if (!err) - ocr |= 1 << 30; + ocr |= SD_OCR_CCS; - err = mmc_send_app_op_cond(host, ocr, NULL); + /* + * If the host supports one of UHS-I modes, request the card + * to switch to 1.8V signaling level. + */ + if (host->caps & (MMC_CAP_UHS_SDR12 | MMC_CAP_UHS_SDR25 | + MMC_CAP_UHS_SDR50 | MMC_CAP_UHS_SDR104 | MMC_CAP_UHS_DDR50)) + ocr |= SD_OCR_S18R; + + /* If the host can supply more than 150mA, XPC should be set to 1. */ + if (host->caps & (MMC_CAP_SET_XPC_330 | MMC_CAP_SET_XPC_300 | + MMC_CAP_SET_XPC_180)) + ocr |= SD_OCR_XPC; + +try_again: + err = mmc_send_app_op_cond(host, ocr, &rocr); if (err) return err; + /* + * In case CCS and S18A in the response is set, start Signal Voltage + * Switch procedure. SPI mode doesn't support CMD11. + */ + if (!mmc_host_is_spi(host) && ((rocr & 0x41000000) == 0x41000000)) { + err = mmc_set_signal_voltage(host, MMC_SIGNAL_VOLTAGE_180); + if (err) { + ocr &= ~SD_OCR_S18R; + goto try_again; + } + } + if (mmc_host_is_spi(host)) err = mmc_send_cid(host, cid); else @@ -773,6 +800,11 @@ int mmc_attach_sd(struct mmc_host *host) BUG_ON(!host); WARN_ON(!host->claimed); + /* Make sure we are at 3.3V signalling voltage */ + err = mmc_set_signal_voltage(host, MMC_SIGNAL_VOLTAGE_330); + if (err) + return err; + err = mmc_send_app_op_cond(host, 0, &ocr); if (err) return err; |