{
return dm_mmc_get_cd(mmc->dev);
}
+
+int dm_mmc_set_voltage(struct udevice *dev)
+{
+ struct dm_mmc_ops *ops = mmc_get_ops(dev);
+
+ if (!ops->set_voltage)
+ return -ENOSYS;
+
+ return ops->set_voltage(dev);
+}
+
+int mmc_set_voltage(struct mmc *mmc)
+{
+ return dm_mmc_set_voltage(mmc->dev);
+}
+
+int dm_mmc_set_uhs(struct udevice *dev)
+{
+ struct dm_mmc_ops *ops = mmc_get_ops(dev);
+
+ if (!ops->set_uhs)
+ return -ENOSYS;
+
+ return ops->set_uhs(dev);
+}
+
+int mmc_switch_uhs(struct mmc *mmc)
+{
+ return dm_mmc_set_uhs(mmc->dev);
+}
+
+int dm_mmc_execute_tuning(struct udevice *dev)
+{
+ struct mmc *mmc = mmc_get_mmc_dev(dev);
+ struct dm_mmc_ops *ops = mmc_get_ops(dev);
+ u8 opcode;
+
+ if (!ops->execute_tuning)
+ return -ENOSYS;
+
+ if (IS_SD(mmc))
+ opcode = MMC_CMD_SEND_TUNING_BLOCK;
+ else
+ opcode = MMC_CMD_SEND_TUNING_BLOCK_HS200;
+
+ return ops->execute_tuning(dev, opcode);
+}
+
+int mmc_execute_tuning(struct mmc *mmc)
+{
+ return dm_mmc_execute_tuning(mmc->dev);
+}
#endif
struct mmc *mmc_get_mmc_dev(struct udevice *dev)
return 0;
}
+#ifndef CONFIG_DM_MMC_OPS
+static int mmc_set_voltage(struct mmc *mmc)
+{
+ int err = 0;
+
+ if (mmc->cfg->ops->set_voltage) {
+ err = mmc->cfg->ops->set_voltage(mmc);
+ if (err)
+ return err;
+ }
+
+ return err;
+}
+#endif
+
+static int mmc_switch_voltage(struct mmc *mmc)
+{
+ struct mmc_cmd cmd;
+ int err = 0;
+
+ cmd.cmdidx = SD_CMD_SWITCH_UHS18V;
+ cmd.cmdarg = 0;
+ cmd.resp_type = MMC_RSP_NONE;
+
+ err = mmc_send_cmd(mmc, &cmd, NULL);
+ if (err)
+ return err;
+
+ err = mmc_set_voltage(mmc);
+
+ return err;
+}
+
+static int mmc_host_uhs(struct mmc *mmc)
+{
+ return mmc->cfg->host_caps &
+ (MMC_MODE_UHS_SDR12 | MMC_MODE_UHS_SDR25 |
+ MMC_MODE_UHS_SDR50 | MMC_MODE_UHS_SDR104 |
+ MMC_MODE_UHS_DDR50);
+}
+
static int sd_send_op_cond(struct mmc *mmc)
{
int timeout = 1000;
if (mmc->version == SD_VERSION_2)
cmd.cmdarg |= OCR_HCS;
+ if (mmc_host_uhs(mmc))
+ cmd.cmdarg |= SD_OCR_S18R;
+
err = mmc_send_cmd(mmc, &cmd, NULL);
if (err)
mmc->ocr = cmd.response[0];
+ if (mmc->ocr & SD_OCR_S18R) {
+ err = mmc_switch_voltage(mmc);
+ if (err)
+ return err;
+ mmc->is_uhs = 1;
+ }
+
mmc->high_capacity = ((mmc->ocr & OCR_HCS) == OCR_HCS);
mmc->rca = 0;
ALLOC_CACHE_ALIGN_BUFFER(uint, switch_status, 16);
struct mmc_data data;
int timeout;
+ u8 mode;
mmc->card_caps = 0;
break;
}
+ mode = MMC_TIMING_HS;
+
+ if (mmc->is_uhs && mmc->version >= SD_VERSION_3) {
+ if (!(mmc_host_uhs(mmc)))
+ return 0;
+
+ if (__be32_to_cpu(switch_status[3]) &
+ SD_UHS_SPEED_SDR104) {
+ mode = MMC_TIMING_UHS_SDR104;
+ mmc->card_caps |= MMC_MODE_UHS_SDR104;
+ mmc->tran_speed = 208000000;
+ } else if (__be32_to_cpu(switch_status[3]) &
+ SD_UHS_SPEED_SDR50) {
+ mode = MMC_TIMING_UHS_SDR50;
+ mmc->card_caps |= MMC_MODE_UHS_SDR50;
+ mmc->tran_speed = 100000000;
+ } else if (__be32_to_cpu(switch_status[3]) &
+ SD_UHS_SPEED_DDR50) {
+ mode = MMC_TIMING_UHS_DDR50;
+ mmc->card_caps |= MMC_MODE_UHS_DDR50;
+ mmc->tran_speed = 50000000;
+ } else if (__be32_to_cpu(switch_status[3]) &
+ SD_UHS_SPEED_SDR25) {
+ mode = MMC_TIMING_UHS_SDR25;
+ mmc->card_caps |= MMC_MODE_UHS_SDR25;
+ mmc->tran_speed = 50000000;
+ } else {
+ mode = MMC_TIMING_UHS_SDR12;
+ mmc->card_caps |= MMC_MODE_UHS_SDR12;
+ mmc->tran_speed = 25000000;
+ }
+ mmc->uhsmode = mode;
+ }
+
/* If high-speed isn't supported, we return */
if (!(__be32_to_cpu(switch_status[3]) & SD_HIGHSPEED_SUPPORTED))
return 0;
(mmc->cfg->host_caps & MMC_MODE_HS)))
return 0;
- err = sd_switch(mmc, SD_SWITCH_SWITCH, 0, 1, (u8 *)switch_status);
+ err = sd_switch(mmc, SD_SWITCH_SWITCH, 0, mode, (u8 *)switch_status);
if (err)
return err;
mmc_set_ios(mmc);
}
+#ifndef CONFIG_DM_MMC_OPS
+static int mmc_switch_uhs(struct mmc *mmc)
+{
+ int err = 0;
+
+ if (mmc->cfg->ops->set_uhs)
+ err = mmc->cfg->ops->set_uhs(mmc);
+
+ return err;
+}
+
+static int mmc_execute_tuning(struct mmc *mmc)
+{
+ int err = 0;
+ u8 cmd;
+
+ if (mmc->cfg->ops->execute_tuning) {
+ if (IS_SD(mmc))
+ cmd = MMC_CMD_SEND_TUNING_BLOCK;
+ else
+ cmd = MMC_CMD_SEND_TUNING_BLOCK_HS200;
+ err = mmc->cfg->ops->execute_tuning(mmc, cmd);
+ }
+
+ return err;
+}
+#endif
static int mmc_startup(struct mmc *mmc)
{
err = sd_read_ssr(mmc);
if (err)
return err;
-
- if (mmc->card_caps & MMC_MODE_HS)
- mmc->tran_speed = 50000000;
- else
- mmc->tran_speed = 25000000;
+ if (mmc->card_caps & MMC_MODE_UHS) {
+ err = mmc_switch_uhs(mmc);
+ if (err)
+ return err;
+ } else {
+ if (mmc->card_caps & MMC_MODE_HS)
+ mmc->tran_speed = 50000000;
+ else
+ mmc->tran_speed = 25000000;
+ }
} else if (mmc->version >= MMC_VERSION_4) {
/* Only version 4 of MMC supports wider bus widths */
int idx;
mmc_set_clock(mmc, mmc->tran_speed);
+ if ((mmc->card_caps & (MMC_MODE_UHS_SDR50 |
+ MMC_MODE_UHS_SDR104)) &&
+ (mmc->cfg->host_caps & MMC_MODE_NEEDS_TUNING)) {
+ err = mmc_execute_tuning(mmc);
+ if (err)
+ return err;
+ }
+
/* Fix the block length for DDR mode */
if (mmc->ddr_mode) {
mmc->read_bl_len = MMC_MAX_BLOCK_LEN;
#define MMC_MODE_NEEDS_TUNING (1 << 11)
#define MMC_MODE_HS200 (1 << 12)
+#define MMC_MODE_UHS (MMC_MODE_UHS_SDR12 | MMC_MODE_UHS_SDR25 | \
+ MMC_MODE_UHS_SDR50 | MMC_MODE_UHS_SDR104 | \
+ MMC_MODE_UHS_DDR50)
#define SD_DATA_4BIT 0x00040000
#define IS_SD(x) ((x)->version & SD_VERSION_SD)
#define MMC_CMD_SET_BLOCKLEN 16
#define MMC_CMD_READ_SINGLE_BLOCK 17
#define MMC_CMD_READ_MULTIPLE_BLOCK 18
+#define MMC_CMD_SEND_TUNING_BLOCK 19
+#define MMC_CMD_SEND_TUNING_BLOCK_HS200 21
#define MMC_CMD_SET_BLOCK_COUNT 23
#define MMC_CMD_WRITE_SINGLE_BLOCK 24
#define MMC_CMD_WRITE_MULTIPLE_BLOCK 25
/* SCR definitions in different words */
#define SD_HIGHSPEED_BUSY 0x00020000
#define SD_HIGHSPEED_SUPPORTED 0x00020000
+#define SD_UHS_SPEED_SDR104 0x00080000
+#define SD_UHS_SPEED_SDR50 0x00040000
+#define SD_UHS_SPEED_DDR50 0x00100000
+#define SD_UHS_SPEED_SDR25 0x00020000
#define OCR_BUSY 0x80000000
#define OCR_HCS 0x40000000
#define OCR_VOLTAGE_MASK 0x007FFF80
#define OCR_ACCESS_MODE 0x60000000
+#define SD_OCR_S18R (1 << 24)
+
#define MMC_ERASE_ARG 0x00000000
#define MMC_SECURE_ERASE_ARG 0x80000000
#define MMC_TRIM_ARG 0x00000001
#define MMC_NUM_BOOT_PARTITION 2
#define MMC_PART_RPMB 3 /* RPMB partition number */
+#define MMC_TIMING_UHS_SDR12 0
+#define MMC_TIMING_UHS_SDR25 1
+#define MMC_TIMING_UHS_SDR50 2
+#define MMC_TIMING_UHS_SDR104 3
+#define MMC_TIMING_UHS_DDR50 4
+#define MMC_TIMING_HS 1
+
/* Driver model support */
/**
* @return 0 if write-enabled, 1 if write-protected, -ve on error
*/
int (*get_wp)(struct udevice *dev);
+
+ /**
+ * set_volatge() - Switches host for new voltage
+ * @dev:>-------Device to check
+ *
+ * @return 0 on success otherwise error value
+ */
+ int (*set_voltage)(struct udevice *dev);
+
+ /**
+ * set_uhs() - Sets the controller for specific uhs mode
+ * @dev:>-------Device to check
+ *
+ * @return 0 after setting uhs mode
+ */
+ int (*set_uhs)(struct udevice *dev);
+
+ /**
+ * execute_tuning() - Executes the required tuning sequence
+ * @dev:>-------Device to check
+ * @opcode:>----Command to be sent for tuning
+ *
+ * @return 0 on success otherwise error value
+ */
+ int (*execute_tuning)(struct udevice *dev, u8 opcode);
};
#define mmc_get_ops(dev) ((struct dm_mmc_ops *)(dev)->driver->ops)
int dm_mmc_set_ios(struct udevice *dev);
int dm_mmc_get_cd(struct udevice *dev);
int dm_mmc_get_wp(struct udevice *dev);
+int dm_mmc_set_voltage(struct udevice *dev);
+int dm_mmc_set_uhs(struct udevice *dev);
+int dm_mmc_execute_tuning(struct udevice *dev);
/* Transition functions for compatibility */
int mmc_set_ios(struct mmc *mmc);
int mmc_getcd(struct mmc *mmc);
int mmc_getwp(struct mmc *mmc);
-
+int mmc_set_voltage(struct mmc *mmc);
+int mmc_switch_uhs(struct mmc *mmc);
+int mmc_execute_tuning(struct mmc *mmc);
#else
struct mmc_ops {
int (*send_cmd)(struct mmc *mmc,
int (*init)(struct mmc *mmc);
int (*getcd)(struct mmc *mmc);
int (*getwp)(struct mmc *mmc);
+ int (*set_voltage)(struct mmc *mmc);
+ int (*set_uhs)(struct mmc *mmc);
+ int (*execute_tuning)(struct mmc *mmc, u8 opcode);
};
#endif
#ifdef CONFIG_DM_MMC
struct udevice *dev; /* Device for this MMC controller */
#endif
+ u8 is_uhs;
+ u8 uhsmode;
};
struct mmc_hwpart_conf {