summaryrefslogtreecommitdiffstats
path: root/drivers/mmc/core/sd.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/mmc/core/sd.c')
-rw-r--r--drivers/mmc/core/sd.c65
1 files changed, 65 insertions, 0 deletions
diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c
index 5b7c99855635..6970b82171f7 100644
--- a/drivers/mmc/core/sd.c
+++ b/drivers/mmc/core/sd.c
@@ -452,6 +452,66 @@ static int sd_select_driver_type(struct mmc_card *card, u8 *status)
return 0;
}
+static int sd_set_bus_speed_mode(struct mmc_card *card, u8 *status)
+{
+ unsigned int bus_speed = 0, timing = 0;
+ int err;
+
+ /*
+ * If the host doesn't support any of the UHS-I modes, fallback on
+ * default speed.
+ */
+ if (!(card->host->caps & (MMC_CAP_UHS_SDR12 | MMC_CAP_UHS_SDR25 |
+ MMC_CAP_UHS_SDR50 | MMC_CAP_UHS_SDR104 | MMC_CAP_UHS_DDR50)))
+ return 0;
+
+ if ((card->host->caps & MMC_CAP_UHS_SDR104) &&
+ (card->sw_caps.sd3_bus_mode & SD_MODE_UHS_SDR104)) {
+ bus_speed = UHS_SDR104_BUS_SPEED;
+ timing = MMC_TIMING_UHS_SDR104;
+ card->sw_caps.uhs_max_dtr = UHS_SDR104_MAX_DTR;
+ } else if ((card->host->caps & MMC_CAP_UHS_DDR50) &&
+ (card->sw_caps.sd3_bus_mode & SD_MODE_UHS_DDR50)) {
+ bus_speed = UHS_DDR50_BUS_SPEED;
+ timing = MMC_TIMING_UHS_DDR50;
+ card->sw_caps.uhs_max_dtr = UHS_DDR50_MAX_DTR;
+ } else if ((card->host->caps & (MMC_CAP_UHS_SDR104 |
+ MMC_CAP_UHS_SDR50)) && (card->sw_caps.sd3_bus_mode &
+ SD_MODE_UHS_SDR50)) {
+ bus_speed = UHS_SDR50_BUS_SPEED;
+ timing = MMC_TIMING_UHS_SDR50;
+ card->sw_caps.uhs_max_dtr = UHS_SDR50_MAX_DTR;
+ } else if ((card->host->caps & (MMC_CAP_UHS_SDR104 |
+ MMC_CAP_UHS_SDR50 | MMC_CAP_UHS_SDR25)) &&
+ (card->sw_caps.sd3_bus_mode & SD_MODE_UHS_SDR25)) {
+ bus_speed = UHS_SDR25_BUS_SPEED;
+ timing = MMC_TIMING_UHS_SDR25;
+ card->sw_caps.uhs_max_dtr = UHS_SDR25_MAX_DTR;
+ } else if ((card->host->caps & (MMC_CAP_UHS_SDR104 |
+ MMC_CAP_UHS_SDR50 | MMC_CAP_UHS_SDR25 |
+ MMC_CAP_UHS_SDR12)) && (card->sw_caps.sd3_bus_mode &
+ SD_MODE_UHS_SDR12)) {
+ bus_speed = UHS_SDR12_BUS_SPEED;
+ timing = MMC_TIMING_UHS_SDR12;
+ card->sw_caps.uhs_max_dtr = UHS_SDR12_MAX_DTR;
+ }
+
+ card->sd_bus_speed = bus_speed;
+ err = mmc_sd_switch(card, 1, 0, bus_speed, status);
+ if (err)
+ return err;
+
+ if ((status[16] & 0xF) != bus_speed)
+ printk(KERN_WARNING "%s: Problem setting bus speed mode!\n",
+ mmc_hostname(card->host));
+ else {
+ mmc_set_timing(card->host, timing);
+ mmc_set_clock(card->host, card->sw_caps.uhs_max_dtr);
+ }
+
+ return 0;
+}
+
/*
* UHS-I specific initialization procedure
*/
@@ -485,6 +545,11 @@ static int mmc_sd_init_uhs_card(struct mmc_card *card)
/* Set the driver strength for the card */
err = sd_select_driver_type(card, status);
+ if (err)
+ goto out;
+
+ /* Set bus speed mode of the card */
+ err = sd_set_bus_speed_mode(card, status);
out:
kfree(status);
OpenPOWER on IntegriCloud