Merge git://git.denx.de/u-boot-mmc
diff --git a/cmd/mmc.c b/cmd/mmc.c
index 5def4ea..58fdc36 100644
--- a/cmd/mmc.c
+++ b/cmd/mmc.c
@@ -23,7 +23,12 @@
 			(mmc->cid[1] >> 24), (mmc->cid[1] >> 16) & 0xff,
 			(mmc->cid[1] >> 8) & 0xff, mmc->cid[1] & 0xff);
 
-	printf("Tran Speed: %d\n", mmc->tran_speed);
+	printf("Bus Speed: %d\n", mmc->clock);
+#if CONFIG_IS_ENABLED(MMC_VERBOSE)
+	printf("Mode : %s\n", mmc_mode_name(mmc->selected_mode));
+	mmc_dump_capabilities("card capabilities", mmc->card_caps);
+	mmc_dump_capabilities("host capabilities", mmc->host_caps);
+#endif
 	printf("Rd Block Len: %d\n", mmc->read_bl_len);
 
 	printf("%s version %d.%d", IS_SD(mmc) ? "SD" : "MMC",
@@ -40,15 +45,19 @@
 	printf("Bus Width: %d-bit%s\n", mmc->bus_width,
 			mmc->ddr_mode ? " DDR" : "");
 
+#if CONFIG_IS_ENABLED(MMC_WRITE)
 	puts("Erase Group Size: ");
 	print_size(((u64)mmc->erase_grp_size) << 9, "\n");
+#endif
 
 	if (!IS_SD(mmc) && mmc->version >= MMC_VERSION_4_41) {
 		bool has_enh = (mmc->part_support & ENHNCD_SUPPORT) != 0;
 		bool usr_enh = has_enh && (mmc->part_attr & EXT_CSD_ENH_USR);
 
+#if CONFIG_IS_ENABLED(MMC_HW_PARTITIONING)
 		puts("HC WP Group Size: ");
 		print_size(((u64)mmc->hc_wp_grp_size) << 9, "\n");
+#endif
 
 		puts("User Capacity: ");
 		print_size(mmc->capacity_user, usr_enh ? " ENH" : "");
@@ -297,6 +306,8 @@
 
 	return (n == cnt) ? CMD_RET_SUCCESS : CMD_RET_FAILURE;
 }
+
+#if CONFIG_IS_ENABLED(MMC_WRITE)
 static int do_mmc_write(cmd_tbl_t *cmdtp, int flag,
 			int argc, char * const argv[])
 {
@@ -355,6 +366,8 @@
 
 	return (n == cnt) ? CMD_RET_SUCCESS : CMD_RET_FAILURE;
 }
+#endif
+
 static int do_mmc_rescan(cmd_tbl_t *cmdtp, int flag,
 			 int argc, char * const argv[])
 {
@@ -433,6 +446,7 @@
 	return CMD_RET_SUCCESS;
 }
 
+#if CONFIG_IS_ENABLED(MMC_HW_PARTITIONING)
 static int parse_hwpart_user(struct mmc_hwpart_conf *pconf,
 			     int argc, char * const argv[])
 {
@@ -582,6 +596,7 @@
 		return CMD_RET_FAILURE;
 	}
 }
+#endif
 
 #ifdef CONFIG_SUPPORT_EMMC_BOOT
 static int do_mmc_bootbus(cmd_tbl_t *cmdtp, int flag,
@@ -785,13 +800,17 @@
 static cmd_tbl_t cmd_mmc[] = {
 	U_BOOT_CMD_MKENT(info, 1, 0, do_mmcinfo, "", ""),
 	U_BOOT_CMD_MKENT(read, 4, 1, do_mmc_read, "", ""),
+#if CONFIG_IS_ENABLED(MMC_WRITE)
 	U_BOOT_CMD_MKENT(write, 4, 0, do_mmc_write, "", ""),
 	U_BOOT_CMD_MKENT(erase, 3, 0, do_mmc_erase, "", ""),
+#endif
 	U_BOOT_CMD_MKENT(rescan, 1, 1, do_mmc_rescan, "", ""),
 	U_BOOT_CMD_MKENT(part, 1, 1, do_mmc_part, "", ""),
 	U_BOOT_CMD_MKENT(dev, 3, 0, do_mmc_dev, "", ""),
 	U_BOOT_CMD_MKENT(list, 1, 1, do_mmc_list, "", ""),
+#if CONFIG_IS_ENABLED(MMC_HW_PARTITIONING)
 	U_BOOT_CMD_MKENT(hwpartition, 28, 0, do_mmc_hwpartition, "", ""),
+#endif
 #ifdef CONFIG_SUPPORT_EMMC_BOOT
 	U_BOOT_CMD_MKENT(bootbus, 5, 0, do_mmc_bootbus, "", ""),
 	U_BOOT_CMD_MKENT(bootpart-resize, 4, 0, do_mmc_boot_resize, "", ""),
diff --git a/cmd/mvebu/bubt.c b/cmd/mvebu/bubt.c
index a1997ac..23fb8cd 100644
--- a/cmd/mvebu/bubt.c
+++ b/cmd/mvebu/bubt.c
@@ -110,7 +110,7 @@
 /********************************************************************
  *     eMMC services
  ********************************************************************/
-#ifdef CONFIG_DM_MMC
+#if CONFIG_IS_ENABLED(DM_MMC) && CONFIG_IS_ENABLED(MMC_WRITE)
 static int mmc_burn_image(size_t image_size)
 {
 	struct mmc	*mmc;
diff --git a/common/Makefile b/common/Makefile
index 1416620..c7bde23 100644
--- a/common/Makefile
+++ b/common/Makefile
@@ -109,6 +109,7 @@
 obj-y += memsize.o
 obj-y += stdio.o
 
+ifndef CONFIG_SPL_BUILD
 # This option is not just y/n - it can have a numeric value
 ifdef CONFIG_FASTBOOT_FLASH
 obj-y += image-sparse.o
@@ -119,6 +120,7 @@
 obj-y += fb_nand.o
 endif
 endif
+endif
 
 ifdef CONFIG_CMD_EEPROM_LAYOUT
 obj-y += eeprom/eeprom_field.o eeprom/eeprom_layout.o
diff --git a/common/spl/Kconfig b/common/spl/Kconfig
index 9d35f41..d686b1e 100644
--- a/common/spl/Kconfig
+++ b/common/spl/Kconfig
@@ -301,6 +301,7 @@
 config SPL_SAVEENV
 	bool "Support save environment"
 	depends on SPL_ENV_SUPPORT
+	select SPL_MMC_WRITE if ENV_IS_IN_MMC
 	help
 	  Enable save environment support in SPL after setenv. By default
 	  the saveenv option is not provided in SPL, but some boards need
@@ -415,6 +416,14 @@
 	  this option to build the drivers in drivers/mmc as part of an SPL
 	  build.
 
+config SPL_MMC_WRITE
+	bool "MMC/SD/SDIO card support for write operations in SPL"
+	depends on SPL_MMC_SUPPORT
+	default n
+	help
+	  Enable write access to MMC and SD Cards in SPL
+
+
 config SPL_MPC8XXX_INIT_DDR_SUPPORT
 	bool "Support MPC8XXX DDR init"
 	help
diff --git a/configs/am335x_hs_evm_defconfig b/configs/am335x_hs_evm_defconfig
index 78cf48d..63e7a07 100644
--- a/configs/am335x_hs_evm_defconfig
+++ b/configs/am335x_hs_evm_defconfig
@@ -13,10 +13,12 @@
 CONFIG_FIT_IMAGE_POST_PROCESS=y
 CONFIG_SPL_LOAD_FIT=y
 CONFIG_SPL_FIT_IMAGE_POST_PROCESS=y
+CONFIG_LOGLEVEL=3
 CONFIG_SYS_CONSOLE_INFO_QUIET=y
 CONFIG_VERSION_VARIABLE=y
 CONFIG_ARCH_MISC_INIT=y
 CONFIG_SPL=y
+CONFIG_SPL_FIT_IMAGE_TINY=y
 # CONFIG_SPL_ENV_SUPPORT is not set
 # CONFIG_SPL_EXT_SUPPORT is not set
 CONFIG_SPL_MTD_SUPPORT=y
@@ -37,6 +39,7 @@
 CONFIG_DM_I2C=y
 CONFIG_MISC=y
 CONFIG_DM_MMC=y
+# CONFIG_MMC_HW_PARTITIONING is not set
 CONFIG_MMC_OMAP_HS=y
 CONFIG_NAND=y
 CONFIG_SPI_FLASH=y
@@ -60,5 +63,6 @@
 CONFIG_USB_GADGET_PRODUCT_NUM=0xd022
 CONFIG_USB_GADGET_DOWNLOAD=y
 CONFIG_USB_ETHER=y
+CONFIG_SPL_TINY_MEMSET=y
 CONFIG_RSA=y
 CONFIG_LZO=y
diff --git a/configs/omapl138_lcdk_defconfig b/configs/omapl138_lcdk_defconfig
index 8d6b12f..0d4506e 100644
--- a/configs/omapl138_lcdk_defconfig
+++ b/configs/omapl138_lcdk_defconfig
@@ -8,6 +8,7 @@
 CONFIG_SPL_SERIAL_SUPPORT=y
 CONFIG_SPL_NAND_SUPPORT=y
 CONFIG_BOOTDELAY=3
+CONFIG_LOGLEVEL=3
 CONFIG_VERSION_VARIABLE=y
 # CONFIG_DISPLAY_CPUINFO is not set
 # CONFIG_DISPLAY_BOARDINFO is not set
diff --git a/configs/openrd_base_defconfig b/configs/openrd_base_defconfig
index 1a829b7..f91e0f9 100644
--- a/configs/openrd_base_defconfig
+++ b/configs/openrd_base_defconfig
@@ -25,6 +25,7 @@
 CONFIG_ISO_PARTITION=y
 CONFIG_ENV_IS_IN_NAND=y
 CONFIG_MVSATA_IDE=y
+# CONFIG_MMC_HW_PARTITIONING is not set
 CONFIG_SYS_NS16550=y
 CONFIG_USB=y
 CONFIG_USB_EHCI_HCD=y
diff --git a/configs/openrd_client_defconfig b/configs/openrd_client_defconfig
index 7a95b5b..e65c437 100644
--- a/configs/openrd_client_defconfig
+++ b/configs/openrd_client_defconfig
@@ -25,6 +25,7 @@
 CONFIG_ISO_PARTITION=y
 CONFIG_ENV_IS_IN_NAND=y
 CONFIG_MVSATA_IDE=y
+# CONFIG_MMC_HW_PARTITIONING is not set
 CONFIG_SYS_NS16550=y
 CONFIG_USB=y
 CONFIG_USB_EHCI_HCD=y
diff --git a/configs/openrd_ultimate_defconfig b/configs/openrd_ultimate_defconfig
index 757be16..dfd809d 100644
--- a/configs/openrd_ultimate_defconfig
+++ b/configs/openrd_ultimate_defconfig
@@ -25,6 +25,7 @@
 CONFIG_ISO_PARTITION=y
 CONFIG_ENV_IS_IN_NAND=y
 CONFIG_MVSATA_IDE=y
+# CONFIG_MMC_HW_PARTITIONING is not set
 CONFIG_SYS_NS16550=y
 CONFIG_USB=y
 CONFIG_USB_EHCI_HCD=y
diff --git a/drivers/mmc/Kconfig b/drivers/mmc/Kconfig
index 8fbeaa7..ab0627a 100644
--- a/drivers/mmc/Kconfig
+++ b/drivers/mmc/Kconfig
@@ -10,6 +10,13 @@
 	  If you want MMC/SD/SDIO support, you should say Y here and
 	  also to your specific host controller driver.
 
+config MMC_WRITE
+	bool "support for MMC/SD write operations"
+	depends on MMC
+	default y
+	help
+	  Enable write access to MMC and SD Cards
+
 config DM_MMC
 	bool "Enable MMC controllers using Driver Model"
 	depends on DM
@@ -42,6 +49,75 @@
 	  If you have an ARM(R) platform with a Multimedia Card slot,
 	  say Y or M here.
 
+config MMC_QUIRKS
+	bool "Enable quirks"
+	default y
+	help
+	  Some cards and hosts may sometimes behave unexpectedly (quirks).
+	  This option enable workarounds to handle those quirks. Some of them
+	  are enabled by default, other may require additionnal flags or are
+	  enabled by the host driver.
+
+config MMC_HW_PARTITIONING
+	bool "Support for HW partitioning command(eMMC)"
+	default y
+	help
+	  This adds a command and an API to do hardware partitioning on eMMC
+	  devices.
+
+config MMC_IO_VOLTAGE
+	bool "Support IO voltage configuration"
+	help
+	  IO voltage configuration allows selecting the voltage level of the IO
+	  lines (not the level of main supply). This is required for UHS
+	  support. For eMMC this not mandatory, but not enabling this option may
+	  prevent the driver of using the faster modes.
+
+config SPL_MMC_IO_VOLTAGE
+	bool "Support IO voltage configuration in SPL"
+	default n
+	help
+	  IO voltage configuration allows selecting the voltage level of the IO
+	  lines (not the level of main supply). This is required for UHS
+	  support. For eMMC this not mandatory, but not enabling this option may
+	  prevent the driver of using the faster modes.
+
+config MMC_UHS_SUPPORT
+	bool "enable UHS support"
+	depends on MMC_IO_VOLTAGE
+	help
+	  The Ultra High Speed (UHS) bus is available on some SDHC and SDXC
+	  cards. The IO voltage must be switchable from 3.3v to 1.8v. The bus
+	  frequency can go up to 208MHz (SDR104)
+
+config SPL_MMC_UHS_SUPPORT
+	bool "enable UHS support in SPL"
+	depends on SPL_MMC_IO_VOLTAGE
+	help
+	  The Ultra High Speed (UHS) bus is available on some SDHC and SDXC
+	  cards. The IO voltage must be switchable from 3.3v to 1.8v. The bus
+	  frequency can go up to 208MHz (SDR104)
+
+config MMC_HS200_SUPPORT
+	bool "enable HS200 support"
+	help
+	  The HS200 mode is support by some eMMC. The bus frequency is up to
+	  200MHz. This mode requires tuning the IO.
+
+
+config SPL_MMC_HS200_SUPPORT
+	bool "enable HS200 support in SPL"
+	help
+	  The HS200 mode is support by some eMMC. The bus frequency is up to
+	  200MHz. This mode requires tuning the IO.
+
+config MMC_VERBOSE
+	bool "Output more information about the MMC"
+	default y
+	help
+	  Enable the output of more information about the card such as the
+	  operating mode.
+
 config SPL_MMC_TINY
 	bool "Tiny MMC framework in SPL"
 	help
diff --git a/drivers/mmc/Makefile b/drivers/mmc/Makefile
index 9af375b..64b6f21 100644
--- a/drivers/mmc/Makefile
+++ b/drivers/mmc/Makefile
@@ -7,6 +7,7 @@
 
 obj-y += mmc.o
 obj-$(CONFIG_$(SPL_)DM_MMC) += mmc-uclass.o
+obj-$(CONFIG_$(SPL_)MMC_WRITE) += mmc_write.o
 
 ifndef CONFIG_$(SPL_)BLK
 obj-y += mmc_legacy.o
@@ -16,9 +17,6 @@
 
 ifdef CONFIG_SPL_BUILD
 obj-$(CONFIG_SPL_MMC_BOOT) += fsl_esdhc_spl.o
-obj-$(CONFIG_SPL_SAVEENV) += mmc_write.o
-else
-obj-y += mmc_write.o
 endif
 
 obj-$(CONFIG_ARM_PL180_MMCI) += arm_pl180_mmci.o
diff --git a/drivers/mmc/exynos_dw_mmc.c b/drivers/mmc/exynos_dw_mmc.c
index 5edd383..e40575e 100644
--- a/drivers/mmc/exynos_dw_mmc.c
+++ b/drivers/mmc/exynos_dw_mmc.c
@@ -168,6 +168,7 @@
 
 	if (host->dev_index > 4) {
 		printf("DWMMC%d: Can't get the dev index\n", host->dev_index);
+		free(priv);
 		return -EINVAL;
 	}
 
@@ -178,6 +179,7 @@
 	base = fdtdec_get_addr(blob, node, "reg");
 	if (!base) {
 		printf("DWMMC%d: Can't get base address\n", host->dev_index);
+		free(priv);
 		return -EINVAL;
 	}
 	host->ioaddr = (void *)base;
@@ -187,6 +189,7 @@
 	if (err) {
 		printf("DWMMC%d: Can't get sdr-timings for devider\n",
 				host->dev_index);
+		free(priv);
 		return -EINVAL;
 	}
 
diff --git a/drivers/mmc/fsl_esdhc.c b/drivers/mmc/fsl_esdhc.c
index 499d622..71c62f4 100644
--- a/drivers/mmc/fsl_esdhc.c
+++ b/drivers/mmc/fsl_esdhc.c
@@ -647,7 +647,11 @@
 	esdhc_write32(&regs->clktunectrlstatus, 0x0);
 
 	/* Put VEND_SPEC to default value */
-	esdhc_write32(&regs->vendorspec, VENDORSPEC_INIT);
+	if (priv->vs18_enable)
+		esdhc_write32(&regs->vendorspec, (VENDORSPEC_INIT |
+			      ESDHC_VENDORSPEC_VSELECT));
+	else
+		esdhc_write32(&regs->vendorspec, VENDORSPEC_INIT);
 
 	/* Disable DLL_CTRL delay line */
 	esdhc_write32(&regs->dllctrl, 0x0);
@@ -665,7 +669,7 @@
 #endif
 
 	/* Set the initial clock speed */
-	mmc_set_clock(mmc, 400000);
+	mmc_set_clock(mmc, 400000, false);
 
 	/* Disable the BRR and BWR bits in IRQSTAT */
 	esdhc_clrbits32(&regs->irqstaten, IRQSTATEN_BRR | IRQSTATEN_BWR);
@@ -676,9 +680,6 @@
 	/* Set timout to the maximum value */
 	esdhc_clrsetbits32(&regs->sysctl, SYSCTL_TIMEOUT_MASK, 14 << 16);
 
-	if (priv->vs18_enable)
-		esdhc_setbits32(&regs->vendorspec, ESDHC_VENDORSPEC_VSELECT);
-
 	return 0;
 }
 
diff --git a/drivers/mmc/gen_atmel_mci.c b/drivers/mmc/gen_atmel_mci.c
index 22154d1..1c108b5 100644
--- a/drivers/mmc/gen_atmel_mci.c
+++ b/drivers/mmc/gen_atmel_mci.c
@@ -74,6 +74,20 @@
 	      cmdr, cmdr & 0x3F, arg, status, msg);
 }
 
+static inline void mci_set_blklen(atmel_mci_t *mci, int blklen)
+{
+	unsigned int version = atmel_mci_get_version(mci);
+
+	blklen &= 0xfffc;
+
+	/* MCI IP version >= 0x200 has blkr */
+	if (version >= 0x200)
+		writel(MMCI_BFINS(BLKLEN, blklen, readl(&mci->blkr)),
+		       &mci->blkr);
+	else
+		writel(MMCI_BFINS(BLKLEN, blklen, readl(&mci->mr)), &mci->mr);
+}
+
 /* Setup for MCI Clock and Block Size */
 #ifdef CONFIG_DM_MMC
 static void mci_set_mode(struct udevice *dev, u32 hz, u32 blklen)
@@ -124,7 +138,6 @@
 		priv->curr_clk = bus_hz / (clkdiv * 2 + clkodd + 2);
 	else
 		priv->curr_clk = (bus_hz / (clkdiv + 1)) / 2;
-	blklen &= 0xfffc;
 
 	mr = MMCI_BF(CLKDIV, clkdiv);
 
@@ -138,14 +151,10 @@
 	 */
 	if (version >= 0x500)
 		mr |= MMCI_BF(CLKODD, clkodd);
-	else
-		mr |= MMCI_BF(BLKLEN, blklen);
 
 	writel(mr, &mci->mr);
 
-	/* MCI IP version >= 0x200 has blkr */
-	if (version >= 0x200)
-		writel(MMCI_BF(BLKLEN, blklen), &mci->blkr);
+	mci_set_blklen(mci, blklen);
 
 	if (mmc->card_caps & mmc->cfg->host_caps & MMC_MODE_HS)
 		writel(MMCI_BIT(HSMODE), &mci->cfg);
@@ -236,7 +245,6 @@
 {
 	struct atmel_mci_plat *plat = dev_get_platdata(dev);
 	struct atmel_mci_priv *priv = dev_get_priv(dev);
-	struct mmc *mmc = mmc_get_mmc_dev(dev);
 	atmel_mci_t *mci = plat->mci;
 #else
 static int
@@ -257,11 +265,13 @@
 	/* Figure out the transfer arguments */
 	cmdr = mci_encode_cmd(cmd, data, &error_flags);
 
+	mci_set_blklen(mci, data->blocksize);
+
 	/* For multi blocks read/write, set the block register */
 	if ((cmd->cmdidx == MMC_CMD_READ_MULTIPLE_BLOCK)
 			|| (cmd->cmdidx == MMC_CMD_WRITE_MULTIPLE_BLOCK))
-		writel(data->blocks | MMCI_BF(BLKLEN, mmc->read_bl_len),
-			&mci->blkr);
+		writel(data->blocks | MMCI_BF(BLKLEN, data->blocksize),
+		       &mci->blkr);
 
 	/* Send the command */
 	writel(cmd->cmdarg, &mci->argr);
@@ -295,17 +305,15 @@
 	if (data) {
 		u32 word_count, block_count;
 		u32* ioptr;
-		u32 sys_blocksize, dummy, i;
+		u32 i;
 		u32 (*mci_data_op)
 			(atmel_mci_t *mci, u32* data, u32 error_flags);
 
 		if (data->flags & MMC_DATA_READ) {
 			mci_data_op = mci_data_read;
-			sys_blocksize = mmc->read_bl_len;
 			ioptr = (u32*)data->dest;
 		} else {
 			mci_data_op = mci_data_write;
-			sys_blocksize = mmc->write_bl_len;
 			ioptr = (u32*)data->src;
 		}
 
@@ -328,16 +336,6 @@
 					     1, cnt, 0);
 			}
 #endif
-#ifdef DEBUG
-			if (!status && word_count < (sys_blocksize / 4))
-				printf("filling rest of block...\n");
-#endif
-			/* fill the rest of a full block */
-			while (!status && word_count < (sys_blocksize / 4)) {
-				status = mci_data_op(mci, &dummy,
-					error_flags);
-				word_count++;
-			}
 			if (status) {
 				dump_cmd(cmdr, cmd->cmdarg, status,
 					"Data Transfer Failed");
diff --git a/drivers/mmc/meson_gx_mmc.c b/drivers/mmc/meson_gx_mmc.c
index 4652fab..a2cd5d3 100644
--- a/drivers/mmc/meson_gx_mmc.c
+++ b/drivers/mmc/meson_gx_mmc.c
@@ -250,7 +250,7 @@
 	mmc->priv = pdata;
 	upriv->mmc = mmc;
 
-	mmc_set_clock(mmc, cfg->f_min);
+	mmc_set_clock(mmc, cfg->f_min, false);
 
 	/* reset all status bits */
 	meson_write(mmc, STATUS_MASK, MESON_SD_EMMC_STATUS);
diff --git a/drivers/mmc/mmc-uclass.c b/drivers/mmc/mmc-uclass.c
index 5dda20c..26c6ab7 100644
--- a/drivers/mmc/mmc-uclass.c
+++ b/drivers/mmc/mmc-uclass.c
@@ -10,7 +10,6 @@
 #include <dm.h>
 #include <dm/device-internal.h>
 #include <dm/lists.h>
-#include <dm/root.h>
 #include "mmc_private.h"
 
 DECLARE_GLOBAL_DATA_PTR;
@@ -51,6 +50,35 @@
 	return dm_mmc_set_ios(mmc->dev);
 }
 
+void dm_mmc_send_init_stream(struct udevice *dev)
+{
+	struct dm_mmc_ops *ops = mmc_get_ops(dev);
+
+	if (ops->send_init_stream)
+		ops->send_init_stream(dev);
+}
+
+void mmc_send_init_stream(struct mmc *mmc)
+{
+	dm_mmc_send_init_stream(mmc->dev);
+}
+
+#if CONFIG_IS_ENABLED(MMC_UHS_SUPPORT)
+int dm_mmc_wait_dat0(struct udevice *dev, int state, int timeout)
+{
+	struct dm_mmc_ops *ops = mmc_get_ops(dev);
+
+	if (!ops->wait_dat0)
+		return -ENOSYS;
+	return ops->wait_dat0(dev, state, timeout);
+}
+
+int mmc_wait_dat0(struct mmc *mmc, int state, int timeout)
+{
+	return dm_mmc_wait_dat0(mmc->dev, state, timeout);
+}
+#endif
+
 int dm_mmc_get_wp(struct udevice *dev)
 {
 	struct dm_mmc_ops *ops = mmc_get_ops(dev);
@@ -79,6 +107,73 @@
 	return dm_mmc_get_cd(mmc->dev);
 }
 
+#ifdef MMC_SUPPORTS_TUNING
+int dm_mmc_execute_tuning(struct udevice *dev, uint opcode)
+{
+	struct dm_mmc_ops *ops = mmc_get_ops(dev);
+
+	if (!ops->execute_tuning)
+		return -ENOSYS;
+	return ops->execute_tuning(dev, opcode);
+}
+
+int mmc_execute_tuning(struct mmc *mmc, uint opcode)
+{
+	return dm_mmc_execute_tuning(mmc->dev, opcode);
+}
+#endif
+
+int mmc_of_parse(struct udevice *dev, struct mmc_config *cfg)
+{
+	int val;
+
+	val = dev_read_u32_default(dev, "bus-width", 1);
+
+	switch (val) {
+	case 0x8:
+		cfg->host_caps |= MMC_MODE_8BIT;
+		/* fall through */
+	case 0x4:
+		cfg->host_caps |= MMC_MODE_4BIT;
+		/* fall through */
+	case 0x1:
+		cfg->host_caps |= MMC_MODE_1BIT;
+		break;
+	default:
+		debug("warning: %s invalid bus-width property. using 1-bit\n",
+		      dev_read_name(dev));
+		cfg->host_caps |= MMC_MODE_1BIT;
+		break;
+	}
+
+	cfg->f_max = dev_read_u32_default(dev, "max-frequency", 52000000);
+
+	if (dev_read_bool(dev, "cap-sd-highspeed"))
+		cfg->host_caps |= MMC_CAP(SD_HS);
+	if (dev_read_bool(dev, "cap-mmc-highspeed"))
+		cfg->host_caps |= MMC_CAP(MMC_HS);
+	if (dev_read_bool(dev, "sd-uhs-sdr12"))
+		cfg->host_caps |= MMC_CAP(UHS_SDR12);
+	if (dev_read_bool(dev, "sd-uhs-sdr25"))
+		cfg->host_caps |= MMC_CAP(UHS_SDR25);
+	if (dev_read_bool(dev, "sd-uhs-sdr50"))
+		cfg->host_caps |= MMC_CAP(UHS_SDR50);
+	if (dev_read_bool(dev, "sd-uhs-sdr104"))
+		cfg->host_caps |= MMC_CAP(UHS_SDR104);
+	if (dev_read_bool(dev, "sd-uhs-ddr50"))
+		cfg->host_caps |= MMC_CAP(UHS_DDR50);
+	if (dev_read_bool(dev, "mmc-ddr-1_8v"))
+		cfg->host_caps |= MMC_CAP(MMC_DDR_52);
+	if (dev_read_bool(dev, "mmc-ddr-1_2v"))
+		cfg->host_caps |= MMC_CAP(MMC_DDR_52);
+	if (dev_read_bool(dev, "mmc-hs200-1_8v"))
+		cfg->host_caps |= MMC_CAP(MMC_HS_200);
+	if (dev_read_bool(dev, "mmc-hs200-1_2v"))
+		cfg->host_caps |= MMC_CAP(MMC_HS_200);
+
+	return 0;
+}
+
 struct mmc *mmc_get_mmc_dev(struct udevice *dev)
 {
 	struct mmc_uclass_priv *upriv;
@@ -275,7 +370,7 @@
 
 static const struct blk_ops mmc_blk_ops = {
 	.read	= mmc_bread,
-#ifndef CONFIG_SPL_BUILD
+#if CONFIG_IS_ENABLED(MMC_WRITE)
 	.write	= mmc_bwrite,
 	.erase	= mmc_berase,
 #endif
diff --git a/drivers/mmc/mmc.c b/drivers/mmc/mmc.c
index 38d2e07..53c8191 100644
--- a/drivers/mmc/mmc.c
+++ b/drivers/mmc/mmc.c
@@ -22,13 +22,9 @@
 #include <div64.h>
 #include "mmc_private.h"
 
-static const unsigned int sd_au_size[] = {
-	0,		SZ_16K / 512,		SZ_32K / 512,
-	SZ_64K / 512,	SZ_128K / 512,		SZ_256K / 512,
-	SZ_512K / 512,	SZ_1M / 512,		SZ_2M / 512,
-	SZ_4M / 512,	SZ_8M / 512,		(SZ_8M + SZ_4M) / 512,
-	SZ_16M / 512,	(SZ_16M + SZ_8M) / 512,	SZ_32M / 512,	SZ_64M / 512,
-};
+static int mmc_set_signal_voltage(struct mmc *mmc, uint signal_voltage);
+static int mmc_power_cycle(struct mmc *mmc);
+static int mmc_select_mode_and_width(struct mmc *mmc, uint card_caps);
 
 #if CONFIG_IS_ENABLED(MMC_TINY)
 static struct mmc mmc_static;
@@ -54,6 +50,14 @@
 #endif
 
 #if !CONFIG_IS_ENABLED(DM_MMC)
+
+#if CONFIG_IS_ENABLED(MMC_UHS_SUPPORT)
+static int mmc_wait_dat0(struct mmc *mmc, int state, int timeout)
+{
+	return -ENOSYS;
+}
+#endif
+
 __weak int board_mmc_getwp(struct mmc *mmc)
 {
 	return -1;
@@ -148,6 +152,71 @@
 	printf("CURR STATE:%d\n", status);
 }
 #endif
+
+#if CONFIG_IS_ENABLED(MMC_VERBOSE) || defined(DEBUG)
+const char *mmc_mode_name(enum bus_mode mode)
+{
+	static const char *const names[] = {
+	      [MMC_LEGACY]	= "MMC legacy",
+	      [SD_LEGACY]	= "SD Legacy",
+	      [MMC_HS]		= "MMC High Speed (26MHz)",
+	      [SD_HS]		= "SD High Speed (50MHz)",
+	      [UHS_SDR12]	= "UHS SDR12 (25MHz)",
+	      [UHS_SDR25]	= "UHS SDR25 (50MHz)",
+	      [UHS_SDR50]	= "UHS SDR50 (100MHz)",
+	      [UHS_SDR104]	= "UHS SDR104 (208MHz)",
+	      [UHS_DDR50]	= "UHS DDR50 (50MHz)",
+	      [MMC_HS_52]	= "MMC High Speed (52MHz)",
+	      [MMC_DDR_52]	= "MMC DDR52 (52MHz)",
+	      [MMC_HS_200]	= "HS200 (200MHz)",
+	};
+
+	if (mode >= MMC_MODES_END)
+		return "Unknown mode";
+	else
+		return names[mode];
+}
+#endif
+
+static uint mmc_mode2freq(struct mmc *mmc, enum bus_mode mode)
+{
+	static const int freqs[] = {
+	      [SD_LEGACY]	= 25000000,
+	      [MMC_HS]		= 26000000,
+	      [SD_HS]		= 50000000,
+#if CONFIG_IS_ENABLED(MMC_UHS_SUPPORT)
+	      [UHS_SDR12]	= 25000000,
+	      [UHS_SDR25]	= 50000000,
+	      [UHS_SDR50]	= 100000000,
+	      [UHS_DDR50]	= 50000000,
+#ifdef MMC_SUPPORTS_TUNING
+	      [UHS_SDR104]	= 208000000,
+#endif
+#endif
+	      [MMC_HS_52]	= 52000000,
+	      [MMC_DDR_52]	= 52000000,
+#if CONFIG_IS_ENABLED(MMC_HS200_SUPPORT)
+	      [MMC_HS_200]	= 200000000,
+#endif
+	};
+
+	if (mode == MMC_LEGACY)
+		return mmc->legacy_speed;
+	else if (mode >= MMC_MODES_END)
+		return 0;
+	else
+		return freqs[mode];
+}
+
+static int mmc_select_mode(struct mmc *mmc, enum bus_mode mode)
+{
+	mmc->selected_mode = mode;
+	mmc->tran_speed = mmc_mode2freq(mmc, mode);
+	mmc->ddr_mode = mmc_is_mode_ddr(mode);
+	debug("selecting mode %s (freq : %d MHz)\n", mmc_mode_name(mode),
+	      mmc->tran_speed / 1000000);
+	return 0;
+}
 
 #if !CONFIG_IS_ENABLED(DM_MMC)
 int mmc_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd, struct mmc_data *data)
@@ -179,10 +248,11 @@
 			    (cmd.response[0] & MMC_STATUS_CURR_STATE) !=
 			     MMC_STATE_PRG)
 				break;
-			else if (cmd.response[0] & MMC_STATUS_MASK) {
+
+			if (cmd.response[0] & MMC_STATUS_MASK) {
 #if !defined(CONFIG_SPL_BUILD) || defined(CONFIG_SPL_LIBCOMMON_SUPPORT)
-				printf("Status Error: 0x%08X\n",
-					cmd.response[0]);
+				pr_err("Status Error: 0x%08X\n",
+				       cmd.response[0]);
 #endif
 				return -ECOMM;
 			}
@@ -198,7 +268,7 @@
 	mmc_trace_state(mmc, &cmd);
 	if (timeout <= 0) {
 #if !defined(CONFIG_SPL_BUILD) || defined(CONFIG_SPL_LIBCOMMON_SUPPORT)
-		printf("Timeout waiting card ready\n");
+		pr_err("Timeout waiting card ready\n");
 #endif
 		return -ETIMEDOUT;
 	}
@@ -209,6 +279,7 @@
 int mmc_set_blocklen(struct mmc *mmc, int len)
 {
 	struct mmc_cmd cmd;
+	int err;
 
 	if (mmc->ddr_mode)
 		return 0;
@@ -217,8 +288,95 @@
 	cmd.resp_type = MMC_RSP_R1;
 	cmd.cmdarg = len;
 
+	err = mmc_send_cmd(mmc, &cmd, NULL);
+
+#ifdef CONFIG_MMC_QUIRKS
+	if (err && (mmc->quirks & MMC_QUIRK_RETRY_SET_BLOCKLEN)) {
+		int retries = 4;
+		/*
+		 * It has been seen that SET_BLOCKLEN may fail on the first
+		 * attempt, let's try a few more time
+		 */
+		do {
+			err = mmc_send_cmd(mmc, &cmd, NULL);
+			if (!err)
+				break;
+		} while (retries--);
+	}
+#endif
+
+	return err;
+}
+
+#ifdef MMC_SUPPORTS_TUNING
+static const u8 tuning_blk_pattern_4bit[] = {
+	0xff, 0x0f, 0xff, 0x00, 0xff, 0xcc, 0xc3, 0xcc,
+	0xc3, 0x3c, 0xcc, 0xff, 0xfe, 0xff, 0xfe, 0xef,
+	0xff, 0xdf, 0xff, 0xdd, 0xff, 0xfb, 0xff, 0xfb,
+	0xbf, 0xff, 0x7f, 0xff, 0x77, 0xf7, 0xbd, 0xef,
+	0xff, 0xf0, 0xff, 0xf0, 0x0f, 0xfc, 0xcc, 0x3c,
+	0xcc, 0x33, 0xcc, 0xcf, 0xff, 0xef, 0xff, 0xee,
+	0xff, 0xfd, 0xff, 0xfd, 0xdf, 0xff, 0xbf, 0xff,
+	0xbb, 0xff, 0xf7, 0xff, 0xf7, 0x7f, 0x7b, 0xde,
+};
+
+static const u8 tuning_blk_pattern_8bit[] = {
+	0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00,
+	0xff, 0xff, 0xcc, 0xcc, 0xcc, 0x33, 0xcc, 0xcc,
+	0xcc, 0x33, 0x33, 0xcc, 0xcc, 0xcc, 0xff, 0xff,
+	0xff, 0xee, 0xff, 0xff, 0xff, 0xee, 0xee, 0xff,
+	0xff, 0xff, 0xdd, 0xff, 0xff, 0xff, 0xdd, 0xdd,
+	0xff, 0xff, 0xff, 0xbb, 0xff, 0xff, 0xff, 0xbb,
+	0xbb, 0xff, 0xff, 0xff, 0x77, 0xff, 0xff, 0xff,
+	0x77, 0x77, 0xff, 0x77, 0xbb, 0xdd, 0xee, 0xff,
+	0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00,
+	0x00, 0xff, 0xff, 0xcc, 0xcc, 0xcc, 0x33, 0xcc,
+	0xcc, 0xcc, 0x33, 0x33, 0xcc, 0xcc, 0xcc, 0xff,
+	0xff, 0xff, 0xee, 0xff, 0xff, 0xff, 0xee, 0xee,
+	0xff, 0xff, 0xff, 0xdd, 0xff, 0xff, 0xff, 0xdd,
+	0xdd, 0xff, 0xff, 0xff, 0xbb, 0xff, 0xff, 0xff,
+	0xbb, 0xbb, 0xff, 0xff, 0xff, 0x77, 0xff, 0xff,
+	0xff, 0x77, 0x77, 0xff, 0x77, 0xbb, 0xdd, 0xee,
+};
+
+int mmc_send_tuning(struct mmc *mmc, u32 opcode, int *cmd_error)
+{
+	struct mmc_cmd cmd;
+	struct mmc_data data;
+	const u8 *tuning_block_pattern;
+	int size, err;
+
+	if (mmc->bus_width == 8) {
+		tuning_block_pattern = tuning_blk_pattern_8bit;
+		size = sizeof(tuning_blk_pattern_8bit);
+	} else if (mmc->bus_width == 4) {
+		tuning_block_pattern = tuning_blk_pattern_4bit;
+		size = sizeof(tuning_blk_pattern_4bit);
+	} else {
+		return -EINVAL;
+	}
+
+	ALLOC_CACHE_ALIGN_BUFFER(u8, data_buf, size);
+
+	cmd.cmdidx = opcode;
+	cmd.cmdarg = 0;
+	cmd.resp_type = MMC_RSP_R1;
+
+	data.dest = (void *)data_buf;
+	data.blocks = 1;
+	data.blocksize = size;
+	data.flags = MMC_DATA_READ;
+
+	err = mmc_send_cmd(mmc, &cmd, &data);
+	if (err)
+		return err;
+
-	return mmc_send_cmd(mmc, &cmd, NULL);
+	if (memcmp(data_buf, tuning_block_pattern, size))
+		return -EIO;
+
+	return 0;
 }
+#endif
 
 static int mmc_read_blocks(struct mmc *mmc, void *dst, lbaint_t start,
 			   lbaint_t blkcnt)
@@ -252,7 +410,7 @@
 		cmd.resp_type = MMC_RSP_R1b;
 		if (mmc_send_cmd(mmc, &cmd, NULL)) {
 #if !defined(CONFIG_SPL_BUILD) || defined(CONFIG_SPL_LIBCOMMON_SUPPORT)
-			printf("mmc fail to send stop cmd\n");
+			pr_err("mmc fail to send stop cmd\n");
 #endif
 			return 0;
 		}
@@ -292,8 +450,8 @@
 
 	if ((start + blkcnt) > block_dev->lba) {
 #if !defined(CONFIG_SPL_BUILD) || defined(CONFIG_SPL_LIBCOMMON_SUPPORT)
-		printf("MMC: block number 0x" LBAF " exceeds max(0x" LBAF ")\n",
-			start + blkcnt, block_dev->lba);
+		pr_err("MMC: block number 0x" LBAF " exceeds max(0x" LBAF ")\n",
+		       start + blkcnt, block_dev->lba);
 #endif
 		return 0;
 	}
@@ -338,8 +496,70 @@
 
 	return 0;
 }
+
+#if CONFIG_IS_ENABLED(MMC_UHS_SUPPORT)
+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)
+		return err;
+
+	if (!mmc_host_is_spi(mmc) && (cmd.response[0] & MMC_STATUS_ERROR))
+		return -EIO;
+
+	/*
+	 * The card should drive cmd and dat[0:3] low immediately
+	 * after the response of cmd11, but wait 100 us to be sure
+	 */
+	err = mmc_wait_dat0(mmc, 0, 100);
+	if (err == -ENOSYS)
+		udelay(100);
+	else if (err)
+		return -ETIMEDOUT;
+
+	/*
+	 * 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)
+		return err;
+
+	/* Keep clock gated for at least 10 ms, though spec only says 5 ms */
+	mdelay(10);
+	mmc_set_clock(mmc, mmc->clock, false);
+
+	/*
+	 * Failure to switch is indicated by the card holding
+	 * dat[0:3] low. Wait for at least 1 ms according to spec
+	 */
+	err = mmc_wait_dat0(mmc, 1, 1000);
+	if (err == -ENOSYS)
+		udelay(1000);
+	else if (err)
+		return -ETIMEDOUT;
+
+	return 0;
+}
+#endif
 
-static int sd_send_op_cond(struct mmc *mmc)
+static int sd_send_op_cond(struct mmc *mmc, bool uhs_en)
 {
 	int timeout = 1000;
 	int err;
@@ -371,6 +591,9 @@
 		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)
@@ -401,6 +624,15 @@
 
 	mmc->ocr = cmd.response[0];
 
+#if CONFIG_IS_ENABLED(MMC_UHS_SUPPORT)
+	if (uhs_en && !(mmc_host_is_spi(mmc)) && (cmd.response[0] & 0x41000000)
+	    == 0x41000000) {
+		err = mmc_switch_voltage(mmc, MMC_SIGNAL_VOLTAGE_180);
+		if (err)
+			return err;
+	}
+#endif
+
 	mmc->high_capacity = ((mmc->ocr & OCR_HCS) == OCR_HCS);
 	mmc->rca = 0;
 
@@ -546,53 +778,86 @@
 
 }
 
-static int mmc_change_freq(struct mmc *mmc)
+static int mmc_set_card_speed(struct mmc *mmc, enum bus_mode mode)
 {
-	ALLOC_CACHE_ALIGN_BUFFER(u8, ext_csd, MMC_MAX_BLOCK_LEN);
-	char cardtype;
 	int err;
+	int speed_bits;
 
-	mmc->card_caps = 0;
+	ALLOC_CACHE_ALIGN_BUFFER(u8, test_csd, MMC_MAX_BLOCK_LEN);
 
-	if (mmc_host_is_spi(mmc))
-		return 0;
+	switch (mode) {
+	case MMC_HS:
+	case MMC_HS_52:
+	case MMC_DDR_52:
+		speed_bits = EXT_CSD_TIMING_HS;
+		break;
+#if CONFIG_IS_ENABLED(MMC_HS200_SUPPORT)
+	case MMC_HS_200:
+		speed_bits = EXT_CSD_TIMING_HS200;
+		break;
+#endif
+	case MMC_LEGACY:
+		speed_bits = EXT_CSD_TIMING_LEGACY;
+		break;
+	default:
+		return -EINVAL;
+	}
+	err = mmc_switch(mmc, EXT_CSD_CMD_SET_NORMAL, EXT_CSD_HS_TIMING,
+			 speed_bits);
+	if (err)
+		return err;
 
-	/* Only version 4 supports high-speed */
-	if (mmc->version < MMC_VERSION_4)
-		return 0;
+	if ((mode == MMC_HS) || (mode == MMC_HS_52)) {
+		/* Now check to see that it worked */
+		err = mmc_send_ext_csd(mmc, test_csd);
+		if (err)
+			return err;
 
-	mmc->card_caps |= MMC_MODE_4BIT | MMC_MODE_8BIT;
+		/* No high-speed support */
+		if (!test_csd[EXT_CSD_HS_TIMING])
+			return -ENOTSUPP;
+	}
 
-	err = mmc_send_ext_csd(mmc, ext_csd);
+	return 0;
+}
 
-	if (err)
-		return err;
+static int mmc_get_capabilities(struct mmc *mmc)
+{
+	u8 *ext_csd = mmc->ext_csd;
+	char cardtype;
 
-	cardtype = ext_csd[EXT_CSD_CARD_TYPE] & 0xf;
+	mmc->card_caps = MMC_MODE_1BIT | MMC_CAP(MMC_LEGACY);
 
-	err = mmc_switch(mmc, EXT_CSD_CMD_SET_NORMAL, EXT_CSD_HS_TIMING, 1);
+	if (mmc_host_is_spi(mmc))
+		return 0;
 
-	if (err)
-		return err;
+	/* Only version 4 supports high-speed */
+	if (mmc->version < MMC_VERSION_4)
+		return 0;
 
-	/* Now check to see that it worked */
-	err = mmc_send_ext_csd(mmc, ext_csd);
+	if (!ext_csd) {
+		pr_err("No ext_csd found!\n"); /* this should enver happen */
+		return -ENOTSUPP;
+	}
 
-	if (err)
-		return err;
+	mmc->card_caps |= MMC_MODE_4BIT | MMC_MODE_8BIT;
 
-	/* No high-speed support */
-	if (!ext_csd[EXT_CSD_HS_TIMING])
-		return 0;
+	cardtype = ext_csd[EXT_CSD_CARD_TYPE] & 0x3f;
+	mmc->cardtype = cardtype;
 
-	/* High Speed is set, there are two types: 52MHz and 26MHz */
+#if CONFIG_IS_ENABLED(MMC_HS200_SUPPORT)
+	if (cardtype & (EXT_CSD_CARD_TYPE_HS200_1_2V |
+			EXT_CSD_CARD_TYPE_HS200_1_8V)) {
+		mmc->card_caps |= MMC_MODE_HS200;
+	}
+#endif
 	if (cardtype & EXT_CSD_CARD_TYPE_52) {
-		if (cardtype & EXT_CSD_CARD_TYPE_DDR_1_8V)
+		if (cardtype & EXT_CSD_CARD_TYPE_DDR_52)
 			mmc->card_caps |= MMC_MODE_DDR_52MHz;
-		mmc->card_caps |= MMC_MODE_HS_52MHz | MMC_MODE_HS;
-	} else {
-		mmc->card_caps |= MMC_MODE_HS;
+		mmc->card_caps |= MMC_MODE_HS_52MHz;
 	}
+	if (cardtype & EXT_CSD_CARD_TYPE_26)
+		mmc->card_caps |= MMC_MODE_HS;
 
 	return 0;
 }
@@ -622,13 +887,49 @@
 
 	mmc_get_blk_desc(mmc)->lba = lldiv(mmc->capacity, mmc->read_bl_len);
 
+	return 0;
+}
+
+#if CONFIG_IS_ENABLED(MMC_HS200_SUPPORT)
+static int mmc_boot_part_access_chk(struct mmc *mmc, unsigned int part_num)
+{
+	int forbidden = 0;
+	bool change = false;
+
+	if (part_num & PART_ACCESS_MASK)
+		forbidden = MMC_CAP(MMC_HS_200);
+
+	if (MMC_CAP(mmc->selected_mode) & forbidden) {
+		debug("selected mode (%s) is forbidden for part %d\n",
+		      mmc_mode_name(mmc->selected_mode), part_num);
+		change = true;
+	} else if (mmc->selected_mode != mmc->best_mode) {
+		debug("selected mode is not optimal\n");
+		change = true;
+	}
+
+	if (change)
+		return mmc_select_mode_and_width(mmc,
+						 mmc->card_caps & ~forbidden);
+
+	return 0;
+}
+#else
+static inline int mmc_boot_part_access_chk(struct mmc *mmc,
+					   unsigned int part_num)
+{
 	return 0;
 }
+#endif
 
 int mmc_switch_part(struct mmc *mmc, unsigned int part_num)
 {
 	int ret;
 
+	ret = mmc_boot_part_access_chk(mmc, part_num);
+	if (ret)
+		return ret;
+
 	ret = mmc_switch(mmc, EXT_CSD_CMD_SET_NORMAL, EXT_CSD_PART_CONF,
 			 (mmc->part_config & ~PART_ACCESS_MASK)
 			 | (part_num & PART_ACCESS_MASK));
@@ -645,6 +946,7 @@
 	return ret;
 }
 
+#if CONFIG_IS_ENABLED(MMC_HW_PARTITIONING)
 int mmc_hwpart_config(struct mmc *mmc,
 		      const struct mmc_hwpart_conf *conf,
 		      enum mmc_hwpart_conf_mode mode)
@@ -663,17 +965,17 @@
 		return -EINVAL;
 
 	if (IS_SD(mmc) || (mmc->version < MMC_VERSION_4_41)) {
-		printf("eMMC >= 4.4 required for enhanced user data area\n");
+		pr_err("eMMC >= 4.4 required for enhanced user data area\n");
 		return -EMEDIUMTYPE;
 	}
 
 	if (!(mmc->part_support & PART_SUPPORT)) {
-		printf("Card does not support partitioning\n");
+		pr_err("Card does not support partitioning\n");
 		return -EMEDIUMTYPE;
 	}
 
 	if (!mmc->hc_wp_grp_size) {
-		printf("Card does not define HC WP group size\n");
+		pr_err("Card does not define HC WP group size\n");
 		return -EMEDIUMTYPE;
 	}
 
@@ -681,7 +983,7 @@
 	if (conf->user.enh_size) {
 		if (conf->user.enh_size % mmc->hc_wp_grp_size ||
 		    conf->user.enh_start % mmc->hc_wp_grp_size) {
-			printf("User data enhanced area not HC WP group "
+			pr_err("User data enhanced area not HC WP group "
 			       "size aligned\n");
 			return -EINVAL;
 		}
@@ -700,7 +1002,7 @@
 
 	for (pidx = 0; pidx < 4; pidx++) {
 		if (conf->gp_part[pidx].size % mmc->hc_wp_grp_size) {
-			printf("GP%i partition not HC WP group size "
+			pr_err("GP%i partition not HC WP group size "
 			       "aligned\n", pidx+1);
 			return -EINVAL;
 		}
@@ -712,7 +1014,7 @@
 	}
 
 	if (part_attrs && ! (mmc->part_support & ENHNCD_SUPPORT)) {
-		printf("Card does not support enhanced attribute\n");
+		pr_err("Card does not support enhanced attribute\n");
 		return -EMEDIUMTYPE;
 	}
 
@@ -725,7 +1027,7 @@
 		(ext_csd[EXT_CSD_MAX_ENH_SIZE_MULT+1] << 8) +
 		ext_csd[EXT_CSD_MAX_ENH_SIZE_MULT];
 	if (tot_enh_size_mult > max_enh_size_mult) {
-		printf("Total enhanced size exceeds maximum (%u > %u)\n",
+		pr_err("Total enhanced size exceeds maximum (%u > %u)\n",
 		       tot_enh_size_mult, max_enh_size_mult);
 		return -EMEDIUMTYPE;
 	}
@@ -759,7 +1061,7 @@
 
 	if (ext_csd[EXT_CSD_PARTITION_SETTING] &
 	    EXT_CSD_PARTITION_SETTING_COMPLETED) {
-		printf("Card already partitioned\n");
+		pr_err("Card already partitioned\n");
 		return -EPERM;
 	}
 
@@ -838,6 +1140,7 @@
 
 	return 0;
 }
+#endif
 
 #if !CONFIG_IS_ENABLED(DM_MMC)
 int mmc_getcd(struct mmc *mmc)
@@ -878,16 +1181,19 @@
 }
 
 
-static int sd_change_freq(struct mmc *mmc)
+static int sd_get_capabilities(struct mmc *mmc)
 {
 	int err;
 	struct mmc_cmd cmd;
-	ALLOC_CACHE_ALIGN_BUFFER(uint, scr, 2);
-	ALLOC_CACHE_ALIGN_BUFFER(uint, switch_status, 16);
+	ALLOC_CACHE_ALIGN_BUFFER(__be32, scr, 2);
+	ALLOC_CACHE_ALIGN_BUFFER(__be32, switch_status, 16);
 	struct mmc_data data;
 	int timeout;
+#if CONFIG_IS_ENABLED(MMC_UHS_SUPPORT)
+	u32 sd3_bus_mode;
+#endif
 
-	mmc->card_caps = 0;
+	mmc->card_caps = MMC_MODE_1BIT | MMC_CAP(SD_LEGACY);
 
 	if (mmc_host_is_spi(mmc))
 		return 0;
@@ -964,32 +1270,115 @@
 	}
 
 	/* If high-speed isn't supported, we return */
-	if (!(__be32_to_cpu(switch_status[3]) & SD_HIGHSPEED_SUPPORTED))
-		return 0;
+	if (__be32_to_cpu(switch_status[3]) & SD_HIGHSPEED_SUPPORTED)
+		mmc->card_caps |= MMC_CAP(SD_HS);
 
-	/*
-	 * If the host doesn't support SD_HIGHSPEED, do not switch card to
-	 * HIGHSPEED mode even if the card support SD_HIGHSPPED.
-	 * This can avoid furthur problem when the card runs in different
-	 * mode between the host.
-	 */
-	if (!((mmc->cfg->host_caps & MMC_MODE_HS_52MHz) &&
-		(mmc->cfg->host_caps & MMC_MODE_HS)))
+#if CONFIG_IS_ENABLED(MMC_UHS_SUPPORT)
+	/* 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);
+#endif
+
+	return 0;
+}
+
+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:
+		speed = UHS_SDR12_BUS_SPEED;
+		break;
+	case SD_HS:
+		speed = HIGH_SPEED_BUS_SPEED;
+		break;
+#if CONFIG_IS_ENABLED(MMC_UHS_SUPPORT)
+	case UHS_SDR12:
+		speed = UHS_SDR12_BUS_SPEED;
+		break;
+	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;
+#endif
+	default:
+		return -EINVAL;
+	}
+
+	err = sd_switch(mmc, SD_SWITCH_SWITCH, 0, speed, (u8 *)switch_status);
+	if (err)
+		return err;
+
+	if ((__be32_to_cpu(switch_status[4]) >> 24) != speed)
+		return -ENOTSUPP;
+
+	return 0;
+}
+
+int sd_select_bus_width(struct mmc *mmc, int w)
+{
+	int err;
+	struct mmc_cmd cmd;
+
-	err = sd_switch(mmc, SD_SWITCH_SWITCH, 0, 1, (u8 *)switch_status);
+	if ((w != 4) && (w != 1))
+		return -EINVAL;
+
+	cmd.cmdidx = MMC_CMD_APP_CMD;
+	cmd.resp_type = MMC_RSP_R1;
+	cmd.cmdarg = mmc->rca << 16;
 
+	err = mmc_send_cmd(mmc, &cmd, NULL);
 	if (err)
 		return err;
 
-	if ((__be32_to_cpu(switch_status[4]) & 0x0f000000) == 0x01000000)
-		mmc->card_caps |= MMC_MODE_HS;
+	cmd.cmdidx = SD_CMD_APP_SET_BUS_WIDTH;
+	cmd.resp_type = MMC_RSP_R1;
+	if (w == 4)
+		cmd.cmdarg = 2;
+	else if (w == 1)
+		cmd.cmdarg = 0;
+	err = mmc_send_cmd(mmc, &cmd, NULL);
+	if (err)
+		return err;
 
 	return 0;
 }
 
+#if CONFIG_IS_ENABLED(MMC_WRITE)
 static int sd_read_ssr(struct mmc *mmc)
 {
+	static const unsigned int sd_au_size[] = {
+		0,		SZ_16K / 512,		SZ_32K / 512,
+		SZ_64K / 512,	SZ_128K / 512,		SZ_256K / 512,
+		SZ_512K / 512,	SZ_1M / 512,		SZ_2M / 512,
+		SZ_4M / 512,	SZ_8M / 512,		(SZ_8M + SZ_4M) / 512,
+		SZ_16M / 512,	(SZ_16M + SZ_8M) / 512,	SZ_32M / 512,
+		SZ_64M / 512,
+	};
 	int err, i;
 	struct mmc_cmd cmd;
 	ALLOC_CACHE_ALIGN_BUFFER(uint, ssr, 16);
@@ -1043,7 +1432,7 @@
 
 	return 0;
 }
-
+#endif
 /* frequency bases */
 /* divided by 10 to be nice to platforms without floating point */
 static const int fbase[] = {
@@ -1075,15 +1464,42 @@
 	80,
 };
 
+static inline int bus_width(uint cap)
+{
+	if (cap == MMC_MODE_8BIT)
+		return 8;
+	if (cap == MMC_MODE_4BIT)
+		return 4;
+	if (cap == MMC_MODE_1BIT)
+		return 1;
+	pr_warn("invalid bus witdh capability 0x%x\n", cap);
+	return 0;
+}
+
 #if !CONFIG_IS_ENABLED(DM_MMC)
-static void mmc_set_ios(struct mmc *mmc)
+#ifdef MMC_SUPPORTS_TUNING
+static int mmc_execute_tuning(struct mmc *mmc, uint opcode)
+{
+	return -ENOTSUPP;
+}
+#endif
+
+static void mmc_send_init_stream(struct mmc *mmc)
+{
+}
+
+static int mmc_set_ios(struct mmc *mmc)
 {
+	int ret = 0;
+
 	if (mmc->cfg->ops->set_ios)
-		mmc->cfg->ops->set_ios(mmc);
+		ret = mmc->cfg->ops->set_ios(mmc);
+
+	return ret;
 }
 #endif
 
-void mmc_set_clock(struct mmc *mmc, uint clock)
+int mmc_set_clock(struct mmc *mmc, uint clock, bool disable)
 {
 	if (clock > mmc->cfg->f_max)
 		clock = mmc->cfg->f_max;
@@ -1092,38 +1508,629 @@
 		clock = mmc->cfg->f_min;
 
 	mmc->clock = clock;
+	mmc->clk_disable = disable;
 
-	mmc_set_ios(mmc);
+	return mmc_set_ios(mmc);
 }
 
-static void mmc_set_bus_width(struct mmc *mmc, uint width)
+static int mmc_set_bus_width(struct mmc *mmc, uint width)
 {
 	mmc->bus_width = width;
 
-	mmc_set_ios(mmc);
+	return mmc_set_ios(mmc);
 }
 
-static int mmc_startup(struct mmc *mmc)
+#if CONFIG_IS_ENABLED(MMC_VERBOSE) || defined(DEBUG)
+/*
+ * helper function to display the capabilities in a human
+ * friendly manner. The capabilities include bus width and
+ * supported modes.
+ */
+void mmc_dump_capabilities(const char *text, uint caps)
 {
-	int err, i;
-	uint mult, freq;
-	u64 cmult, csize, capacity;
-	struct mmc_cmd cmd;
-	ALLOC_CACHE_ALIGN_BUFFER(u8, ext_csd, MMC_MAX_BLOCK_LEN);
-	ALLOC_CACHE_ALIGN_BUFFER(u8, test_csd, MMC_MAX_BLOCK_LEN);
-	bool has_parts = false;
-	bool part_completed;
-	struct blk_desc *bdesc;
+	enum bus_mode mode;
 
-#ifdef CONFIG_MMC_SPI_CRC_ON
-	if (mmc_host_is_spi(mmc)) { /* enable CRC check for spi */
-		cmd.cmdidx = MMC_CMD_SPI_CRC_ON_OFF;
-		cmd.resp_type = MMC_RSP_R1;
-		cmd.cmdarg = 1;
-		err = mmc_send_cmd(mmc, &cmd, NULL);
+	printf("%s: widths [", text);
+	if (caps & MMC_MODE_8BIT)
+		printf("8, ");
+	if (caps & MMC_MODE_4BIT)
+		printf("4, ");
+	if (caps & MMC_MODE_1BIT)
+		printf("1, ");
+	printf("\b\b] modes [");
+	for (mode = MMC_LEGACY; mode < MMC_MODES_END; mode++)
+		if (MMC_CAP(mode) & caps)
+			printf("%s, ", mmc_mode_name(mode));
+	printf("\b\b]\n");
+}
+#endif
 
-		if (err)
-			return err;
+struct mode_width_tuning {
+	enum bus_mode mode;
+	uint widths;
+#ifdef MMC_SUPPORTS_TUNING
+	uint tuning;
+#endif
+};
+
+#if CONFIG_IS_ENABLED(MMC_IO_VOLTAGE)
+int mmc_voltage_to_mv(enum mmc_voltage voltage)
+{
+	switch (voltage) {
+	case MMC_SIGNAL_VOLTAGE_000: return 0;
+	case MMC_SIGNAL_VOLTAGE_330: return 3300;
+	case MMC_SIGNAL_VOLTAGE_180: return 1800;
+	case MMC_SIGNAL_VOLTAGE_120: return 1200;
+	}
+	return -EINVAL;
+}
+
+static int mmc_set_signal_voltage(struct mmc *mmc, uint signal_voltage)
+{
+	int err;
+
+	if (mmc->signal_voltage == signal_voltage)
+		return 0;
+
+	mmc->signal_voltage = signal_voltage;
+	err = mmc_set_ios(mmc);
+	if (err)
+		debug("unable to set voltage (err %d)\n", err);
+
+	return err;
+}
+#else
+static inline int mmc_set_signal_voltage(struct mmc *mmc, uint signal_voltage)
+{
+	return 0;
+}
+#endif
+
+static const struct mode_width_tuning sd_modes_by_pref[] = {
+#if CONFIG_IS_ENABLED(MMC_UHS_SUPPORT)
+#ifdef MMC_SUPPORTS_TUNING
+	{
+		.mode = UHS_SDR104,
+		.widths = MMC_MODE_4BIT | MMC_MODE_1BIT,
+		.tuning = MMC_CMD_SEND_TUNING_BLOCK
+	},
+#endif
+	{
+		.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,
+	},
+#endif
+	{
+		.mode = SD_HS,
+		.widths = MMC_MODE_4BIT | MMC_MODE_1BIT,
+	},
+#if CONFIG_IS_ENABLED(MMC_UHS_SUPPORT)
+	{
+		.mode = UHS_SDR12,
+		.widths = MMC_MODE_4BIT | MMC_MODE_1BIT,
+	},
+#endif
+	{
+		.mode = SD_LEGACY,
+		.widths = MMC_MODE_4BIT | MMC_MODE_1BIT,
+	}
+};
+
+#define for_each_sd_mode_by_pref(caps, mwt) \
+	for (mwt = sd_modes_by_pref;\
+	     mwt < sd_modes_by_pref + ARRAY_SIZE(sd_modes_by_pref);\
+	     mwt++) \
+		if (caps & MMC_CAP(mwt->mode))
+
+static int sd_select_mode_and_width(struct mmc *mmc, uint card_caps)
+{
+	int err;
+	uint widths[] = {MMC_MODE_4BIT, MMC_MODE_1BIT};
+	const struct mode_width_tuning *mwt;
+#if CONFIG_IS_ENABLED(MMC_UHS_SUPPORT)
+	bool uhs_en = (mmc->ocr & OCR_S18R) ? true : false;
+#else
+	bool uhs_en = false;
+#endif
+	uint caps;
+
+#ifdef DEBUG
+	mmc_dump_capabilities("sd card", card_caps);
+	mmc_dump_capabilities("host", mmc->host_caps);
+#endif
+
+	/* Restrict card's capabilities by what the host can do */
+	caps = card_caps & mmc->host_caps;
+
+	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 & caps & mwt->widths) {
+				debug("trying mode %s width %d (at %d MHz)\n",
+				      mmc_mode_name(mwt->mode),
+				      bus_width(*w),
+				      mmc_mode2freq(mmc, mwt->mode) / 1000000);
+
+				/* configure the bus width (card + host) */
+				err = sd_select_bus_width(mmc, bus_width(*w));
+				if (err)
+					goto error;
+				mmc_set_bus_width(mmc, bus_width(*w));
+
+				/* configure the bus mode (card) */
+				err = sd_set_card_speed(mmc, mwt->mode);
+				if (err)
+					goto error;
+
+				/* configure the bus mode (host) */
+				mmc_select_mode(mmc, mwt->mode);
+				mmc_set_clock(mmc, mmc->tran_speed, false);
+
+#ifdef MMC_SUPPORTS_TUNING
+				/* 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;
+					}
+				}
+#endif
+
+#if CONFIG_IS_ENABLED(MMC_WRITE)
+				err = sd_read_ssr(mmc);
+				if (!err)
+					pr_warn("unable to read ssr\n");
+#endif
+				if (!err)
+					return 0;
+
+error:
+				/* revert to a safer bus speed */
+				mmc_select_mode(mmc, SD_LEGACY);
+				mmc_set_clock(mmc, mmc->tran_speed, false);
+			}
+		}
+	}
+
+	printf("unable to select a mode\n");
+	return -ENOTSUPP;
+}
+
+/*
+ * read the compare the part of ext csd that is constant.
+ * This can be used to check that the transfer is working
+ * as expected.
+ */
+static int mmc_read_and_compare_ext_csd(struct mmc *mmc)
+{
+	int err;
+	const u8 *ext_csd = mmc->ext_csd;
+	ALLOC_CACHE_ALIGN_BUFFER(u8, test_csd, MMC_MAX_BLOCK_LEN);
+
+	if (mmc->version < MMC_VERSION_4)
+		return 0;
+
+	err = mmc_send_ext_csd(mmc, test_csd);
+	if (err)
+		return err;
+
+	/* Only compare read only fields */
+	if (ext_csd[EXT_CSD_PARTITIONING_SUPPORT]
+		== test_csd[EXT_CSD_PARTITIONING_SUPPORT] &&
+	    ext_csd[EXT_CSD_HC_WP_GRP_SIZE]
+		== test_csd[EXT_CSD_HC_WP_GRP_SIZE] &&
+	    ext_csd[EXT_CSD_REV]
+		== test_csd[EXT_CSD_REV] &&
+	    ext_csd[EXT_CSD_HC_ERASE_GRP_SIZE]
+		== test_csd[EXT_CSD_HC_ERASE_GRP_SIZE] &&
+	    memcmp(&ext_csd[EXT_CSD_SEC_CNT],
+		   &test_csd[EXT_CSD_SEC_CNT], 4) == 0)
+		return 0;
+
+	return -EBADMSG;
+}
+
+#if CONFIG_IS_ENABLED(MMC_IO_VOLTAGE)
+static int mmc_set_lowest_voltage(struct mmc *mmc, enum bus_mode mode,
+				  uint32_t allowed_mask)
+{
+	u32 card_mask = 0;
+
+	switch (mode) {
+	case MMC_HS_200:
+		if (mmc->cardtype & EXT_CSD_CARD_TYPE_HS200_1_8V)
+			card_mask |= MMC_SIGNAL_VOLTAGE_180;
+		if (mmc->cardtype & EXT_CSD_CARD_TYPE_HS200_1_2V)
+			card_mask |= MMC_SIGNAL_VOLTAGE_120;
+		break;
+	case MMC_DDR_52:
+		if (mmc->cardtype & EXT_CSD_CARD_TYPE_DDR_1_8V)
+			card_mask |= MMC_SIGNAL_VOLTAGE_330 |
+				     MMC_SIGNAL_VOLTAGE_180;
+		if (mmc->cardtype & EXT_CSD_CARD_TYPE_DDR_1_2V)
+			card_mask |= MMC_SIGNAL_VOLTAGE_120;
+		break;
+	default:
+		card_mask |= MMC_SIGNAL_VOLTAGE_330;
+		break;
+	}
+
+	while (card_mask & allowed_mask) {
+		enum mmc_voltage best_match;
+
+		best_match = 1 << (ffs(card_mask & allowed_mask) - 1);
+		if (!mmc_set_signal_voltage(mmc,  best_match))
+			return 0;
+
+		allowed_mask &= ~best_match;
+	}
+
+	return -ENOTSUPP;
+}
+#else
+static inline int mmc_set_lowest_voltage(struct mmc *mmc, enum bus_mode mode,
+					 uint32_t allowed_mask)
+{
+	return 0;
+}
+#endif
+
+static const struct mode_width_tuning mmc_modes_by_pref[] = {
+#if CONFIG_IS_ENABLED(MMC_HS200_SUPPORT)
+	{
+		.mode = MMC_HS_200,
+		.widths = MMC_MODE_8BIT | MMC_MODE_4BIT,
+		.tuning = MMC_CMD_SEND_TUNING_BLOCK_HS200
+	},
+#endif
+	{
+		.mode = MMC_DDR_52,
+		.widths = MMC_MODE_8BIT | MMC_MODE_4BIT,
+	},
+	{
+		.mode = MMC_HS_52,
+		.widths = MMC_MODE_8BIT | MMC_MODE_4BIT | MMC_MODE_1BIT,
+	},
+	{
+		.mode = MMC_HS,
+		.widths = MMC_MODE_8BIT | MMC_MODE_4BIT | MMC_MODE_1BIT,
+	},
+	{
+		.mode = MMC_LEGACY,
+		.widths = MMC_MODE_8BIT | MMC_MODE_4BIT | MMC_MODE_1BIT,
+	}
+};
+
+#define for_each_mmc_mode_by_pref(caps, mwt) \
+	for (mwt = mmc_modes_by_pref;\
+	    mwt < mmc_modes_by_pref + ARRAY_SIZE(mmc_modes_by_pref);\
+	    mwt++) \
+		if (caps & MMC_CAP(mwt->mode))
+
+static const struct ext_csd_bus_width {
+	uint cap;
+	bool is_ddr;
+	uint ext_csd_bits;
+} ext_csd_bus_width[] = {
+	{MMC_MODE_8BIT, true, EXT_CSD_DDR_BUS_WIDTH_8},
+	{MMC_MODE_4BIT, true, EXT_CSD_DDR_BUS_WIDTH_4},
+	{MMC_MODE_8BIT, false, EXT_CSD_BUS_WIDTH_8},
+	{MMC_MODE_4BIT, false, EXT_CSD_BUS_WIDTH_4},
+	{MMC_MODE_1BIT, false, EXT_CSD_BUS_WIDTH_1},
+};
+
+#define for_each_supported_width(caps, ddr, ecbv) \
+	for (ecbv = ext_csd_bus_width;\
+	    ecbv < ext_csd_bus_width + ARRAY_SIZE(ext_csd_bus_width);\
+	    ecbv++) \
+		if ((ddr == ecbv->is_ddr) && (caps & ecbv->cap))
+
+static int mmc_select_mode_and_width(struct mmc *mmc, uint card_caps)
+{
+	int err;
+	const struct mode_width_tuning *mwt;
+	const struct ext_csd_bus_width *ecbw;
+
+#ifdef DEBUG
+	mmc_dump_capabilities("mmc", card_caps);
+	mmc_dump_capabilities("host", mmc->host_caps);
+#endif
+
+	/* Restrict card's capabilities by what the host can do */
+	card_caps &= mmc->host_caps;
+
+	/* Only version 4 of MMC supports wider bus widths */
+	if (mmc->version < MMC_VERSION_4)
+		return 0;
+
+	if (!mmc->ext_csd) {
+		debug("No ext_csd found!\n"); /* this should enver happen */
+		return -ENOTSUPP;
+	}
+
+	mmc_set_clock(mmc, mmc->legacy_speed, false);
+
+	for_each_mmc_mode_by_pref(card_caps, mwt) {
+		for_each_supported_width(card_caps & mwt->widths,
+					 mmc_is_mode_ddr(mwt->mode), ecbw) {
+			enum mmc_voltage old_voltage;
+			debug("trying mode %s width %d (at %d MHz)\n",
+			      mmc_mode_name(mwt->mode),
+			      bus_width(ecbw->cap),
+			      mmc_mode2freq(mmc, mwt->mode) / 1000000);
+			old_voltage = mmc->signal_voltage;
+			err = mmc_set_lowest_voltage(mmc, mwt->mode,
+						     MMC_ALL_SIGNAL_VOLTAGE);
+			if (err)
+				continue;
+
+			/* configure the bus width (card + host) */
+			err = mmc_switch(mmc, EXT_CSD_CMD_SET_NORMAL,
+				    EXT_CSD_BUS_WIDTH,
+				    ecbw->ext_csd_bits & ~EXT_CSD_DDR_FLAG);
+			if (err)
+				goto error;
+			mmc_set_bus_width(mmc, bus_width(ecbw->cap));
+
+			/* configure the bus speed (card) */
+			err = mmc_set_card_speed(mmc, mwt->mode);
+			if (err)
+				goto error;
+
+			/*
+			 * configure the bus width AND the ddr mode (card)
+			 * The host side will be taken care of in the next step
+			 */
+			if (ecbw->ext_csd_bits & EXT_CSD_DDR_FLAG) {
+				err = mmc_switch(mmc, EXT_CSD_CMD_SET_NORMAL,
+						 EXT_CSD_BUS_WIDTH,
+						 ecbw->ext_csd_bits);
+				if (err)
+					goto error;
+			}
+
+			/* configure the bus mode (host) */
+			mmc_select_mode(mmc, mwt->mode);
+			mmc_set_clock(mmc, mmc->tran_speed, false);
+#ifdef MMC_SUPPORTS_TUNING
+
+			/* execute tuning if needed */
+			if (mwt->tuning) {
+				err = mmc_execute_tuning(mmc, mwt->tuning);
+				if (err) {
+					debug("tuning failed\n");
+					goto error;
+				}
+			}
+#endif
+
+			/* do a transfer to check the configuration */
+			err = mmc_read_and_compare_ext_csd(mmc);
+			if (!err)
+				return 0;
+error:
+			mmc_set_signal_voltage(mmc, old_voltage);
+			/* if an error occured, revert to a safer bus mode */
+			mmc_switch(mmc, EXT_CSD_CMD_SET_NORMAL,
+				   EXT_CSD_BUS_WIDTH, EXT_CSD_BUS_WIDTH_1);
+			mmc_select_mode(mmc, MMC_LEGACY);
+			mmc_set_bus_width(mmc, 1);
+		}
+	}
+
+	pr_err("unable to select a mode\n");
+
+	return -ENOTSUPP;
+}
+
+static int mmc_startup_v4(struct mmc *mmc)
+{
+	int err, i;
+	u64 capacity;
+	bool has_parts = false;
+	bool part_completed;
+	static const u32 mmc_versions[] = {
+		MMC_VERSION_4,
+		MMC_VERSION_4_1,
+		MMC_VERSION_4_2,
+		MMC_VERSION_4_3,
+		MMC_VERSION_4_41,
+		MMC_VERSION_4_5,
+		MMC_VERSION_5_0,
+		MMC_VERSION_5_1
+	};
+
+	ALLOC_CACHE_ALIGN_BUFFER(u8, ext_csd, MMC_MAX_BLOCK_LEN);
+
+	if (IS_SD(mmc) || (mmc->version < MMC_VERSION_4))
+		return 0;
+
+	/* check  ext_csd version and capacity */
+	err = mmc_send_ext_csd(mmc, ext_csd);
+	if (err)
+		goto error;
+
+	/* store the ext csd for future reference */
+	if (!mmc->ext_csd)
+		mmc->ext_csd = malloc(MMC_MAX_BLOCK_LEN);
+	if (!mmc->ext_csd)
+		return -ENOMEM;
+	memcpy(mmc->ext_csd, ext_csd, MMC_MAX_BLOCK_LEN);
+
+	if (ext_csd[EXT_CSD_REV] > ARRAY_SIZE(mmc_versions))
+		return -EINVAL;
+
+	mmc->version = mmc_versions[ext_csd[EXT_CSD_REV]];
+
+	if (mmc->version >= MMC_VERSION_4_2) {
+		/*
+		 * According to the JEDEC Standard, the value of
+		 * ext_csd's capacity is valid if the value is more
+		 * than 2GB
+		 */
+		capacity = ext_csd[EXT_CSD_SEC_CNT] << 0
+				| ext_csd[EXT_CSD_SEC_CNT + 1] << 8
+				| ext_csd[EXT_CSD_SEC_CNT + 2] << 16
+				| ext_csd[EXT_CSD_SEC_CNT + 3] << 24;
+		capacity *= MMC_MAX_BLOCK_LEN;
+		if ((capacity >> 20) > 2 * 1024)
+			mmc->capacity_user = capacity;
+	}
+
+	/* The partition data may be non-zero but it is only
+	 * effective if PARTITION_SETTING_COMPLETED is set in
+	 * EXT_CSD, so ignore any data if this bit is not set,
+	 * except for enabling the high-capacity group size
+	 * definition (see below).
+	 */
+	part_completed = !!(ext_csd[EXT_CSD_PARTITION_SETTING] &
+			    EXT_CSD_PARTITION_SETTING_COMPLETED);
+
+	/* store the partition info of emmc */
+	mmc->part_support = ext_csd[EXT_CSD_PARTITIONING_SUPPORT];
+	if ((ext_csd[EXT_CSD_PARTITIONING_SUPPORT] & PART_SUPPORT) ||
+	    ext_csd[EXT_CSD_BOOT_MULT])
+		mmc->part_config = ext_csd[EXT_CSD_PART_CONF];
+	if (part_completed &&
+	    (ext_csd[EXT_CSD_PARTITIONING_SUPPORT] & ENHNCD_SUPPORT))
+		mmc->part_attr = ext_csd[EXT_CSD_PARTITIONS_ATTRIBUTE];
+
+	mmc->capacity_boot = ext_csd[EXT_CSD_BOOT_MULT] << 17;
+
+	mmc->capacity_rpmb = ext_csd[EXT_CSD_RPMB_MULT] << 17;
+
+	for (i = 0; i < 4; i++) {
+		int idx = EXT_CSD_GP_SIZE_MULT + i * 3;
+		uint mult = (ext_csd[idx + 2] << 16) +
+			(ext_csd[idx + 1] << 8) + ext_csd[idx];
+		if (mult)
+			has_parts = true;
+		if (!part_completed)
+			continue;
+		mmc->capacity_gp[i] = mult;
+		mmc->capacity_gp[i] *=
+			ext_csd[EXT_CSD_HC_ERASE_GRP_SIZE];
+		mmc->capacity_gp[i] *= ext_csd[EXT_CSD_HC_WP_GRP_SIZE];
+		mmc->capacity_gp[i] <<= 19;
+	}
+
+#ifndef CONFIG_SPL_BUILD
+	if (part_completed) {
+		mmc->enh_user_size =
+			(ext_csd[EXT_CSD_ENH_SIZE_MULT + 2] << 16) +
+			(ext_csd[EXT_CSD_ENH_SIZE_MULT + 1] << 8) +
+			ext_csd[EXT_CSD_ENH_SIZE_MULT];
+		mmc->enh_user_size *= ext_csd[EXT_CSD_HC_ERASE_GRP_SIZE];
+		mmc->enh_user_size *= ext_csd[EXT_CSD_HC_WP_GRP_SIZE];
+		mmc->enh_user_size <<= 19;
+		mmc->enh_user_start =
+			(ext_csd[EXT_CSD_ENH_START_ADDR + 3] << 24) +
+			(ext_csd[EXT_CSD_ENH_START_ADDR + 2] << 16) +
+			(ext_csd[EXT_CSD_ENH_START_ADDR + 1] << 8) +
+			ext_csd[EXT_CSD_ENH_START_ADDR];
+		if (mmc->high_capacity)
+			mmc->enh_user_start <<= 9;
+	}
+#endif
+
+	/*
+	 * Host needs to enable ERASE_GRP_DEF bit if device is
+	 * partitioned. This bit will be lost every time after a reset
+	 * or power off. This will affect erase size.
+	 */
+	if (part_completed)
+		has_parts = true;
+	if ((ext_csd[EXT_CSD_PARTITIONING_SUPPORT] & PART_SUPPORT) &&
+	    (ext_csd[EXT_CSD_PARTITIONS_ATTRIBUTE] & PART_ENH_ATTRIB))
+		has_parts = true;
+	if (has_parts) {
+		err = mmc_switch(mmc, EXT_CSD_CMD_SET_NORMAL,
+				 EXT_CSD_ERASE_GROUP_DEF, 1);
+
+		if (err)
+			goto error;
+
+		ext_csd[EXT_CSD_ERASE_GROUP_DEF] = 1;
+	}
+
+	if (ext_csd[EXT_CSD_ERASE_GROUP_DEF] & 0x01) {
+#if CONFIG_IS_ENABLED(MMC_WRITE)
+		/* Read out group size from ext_csd */
+		mmc->erase_grp_size =
+			ext_csd[EXT_CSD_HC_ERASE_GRP_SIZE] * 1024;
+#endif
+		/*
+		 * if high capacity and partition setting completed
+		 * SEC_COUNT is valid even if it is smaller than 2 GiB
+		 * JEDEC Standard JESD84-B45, 6.2.4
+		 */
+		if (mmc->high_capacity && part_completed) {
+			capacity = (ext_csd[EXT_CSD_SEC_CNT]) |
+				(ext_csd[EXT_CSD_SEC_CNT + 1] << 8) |
+				(ext_csd[EXT_CSD_SEC_CNT + 2] << 16) |
+				(ext_csd[EXT_CSD_SEC_CNT + 3] << 24);
+			capacity *= MMC_MAX_BLOCK_LEN;
+			mmc->capacity_user = capacity;
+		}
+	}
+#if CONFIG_IS_ENABLED(MMC_WRITE)
+	else {
+		/* Calculate the group size from the csd value. */
+		int erase_gsz, erase_gmul;
+
+		erase_gsz = (mmc->csd[2] & 0x00007c00) >> 10;
+		erase_gmul = (mmc->csd[2] & 0x000003e0) >> 5;
+		mmc->erase_grp_size = (erase_gsz + 1)
+			* (erase_gmul + 1);
+	}
+#endif
+#if CONFIG_IS_ENABLED(MMC_HW_PARTITIONING)
+	mmc->hc_wp_grp_size = 1024
+		* ext_csd[EXT_CSD_HC_ERASE_GRP_SIZE]
+		* ext_csd[EXT_CSD_HC_WP_GRP_SIZE];
+#endif
+
+	mmc->wr_rel_set = ext_csd[EXT_CSD_WR_REL_SET];
+
+	return 0;
+error:
+	if (mmc->ext_csd) {
+		free(mmc->ext_csd);
+		mmc->ext_csd = NULL;
+	}
+	return err;
+}
+
+static int mmc_startup(struct mmc *mmc)
+{
+	int err, i;
+	uint mult, freq;
+	u64 cmult, csize;
+	struct mmc_cmd cmd;
+	struct blk_desc *bdesc;
+
+#ifdef CONFIG_MMC_SPI_CRC_ON
+	if (mmc_host_is_spi(mmc)) { /* enable CRC check for spi */
+		cmd.cmdidx = MMC_CMD_SPI_CRC_ON_OFF;
+		cmd.resp_type = MMC_RSP_R1;
+		cmd.cmdarg = 1;
+		err = mmc_send_cmd(mmc, &cmd, NULL);
+		if (err)
+			return err;
 	}
 #endif
 
@@ -1135,6 +2142,21 @@
 
 	err = mmc_send_cmd(mmc, &cmd, NULL);
 
+#ifdef CONFIG_MMC_QUIRKS
+	if (err && (mmc->quirks & MMC_QUIRK_RETRY_SEND_CID)) {
+		int retries = 4;
+		/*
+		 * It has been seen that SEND_CID may fail on the first
+		 * attempt, let's try a few more time
+		 */
+		do {
+			err = mmc_send_cmd(mmc, &cmd, NULL);
+			if (!err)
+				break;
+		} while (retries--);
+	}
+#endif
+
 	if (err)
 		return err;
 
@@ -1203,15 +2225,18 @@
 	freq = fbase[(cmd.response[0] & 0x7)];
 	mult = multipliers[((cmd.response[0] >> 3) & 0xf)];
 
-	mmc->tran_speed = freq * mult;
+	mmc->legacy_speed = freq * mult;
+	mmc_select_mode(mmc, MMC_LEGACY);
 
 	mmc->dsr_imp = ((cmd.response[1] >> 12) & 0x1);
 	mmc->read_bl_len = 1 << ((cmd.response[1] >> 16) & 0xf);
+#if CONFIG_IS_ENABLED(MMC_WRITE)
 
 	if (IS_SD(mmc))
 		mmc->write_bl_len = mmc->read_bl_len;
 	else
 		mmc->write_bl_len = 1 << ((cmd.response[3] >> 22) & 0xf);
+#endif
 
 	if (mmc->high_capacity) {
 		csize = (mmc->csd[1] & 0x3f) << 16
@@ -1233,15 +2258,17 @@
 	if (mmc->read_bl_len > MMC_MAX_BLOCK_LEN)
 		mmc->read_bl_len = MMC_MAX_BLOCK_LEN;
 
+#if CONFIG_IS_ENABLED(MMC_WRITE)
 	if (mmc->write_bl_len > MMC_MAX_BLOCK_LEN)
 		mmc->write_bl_len = MMC_MAX_BLOCK_LEN;
+#endif
 
 	if ((mmc->dsr_imp) && (0xffffffff != mmc->dsr)) {
 		cmd.cmdidx = MMC_CMD_SET_DSR;
 		cmd.cmdarg = (mmc->dsr & 0xffff) << 16;
 		cmd.resp_type = MMC_RSP_NONE;
 		if (mmc_send_cmd(mmc, &cmd, NULL))
-			printf("MMC: SET_DSR failed\n");
+			pr_warn("MMC: SET_DSR failed\n");
 	}
 
 	/* Select the card, and put it into Transfer Mode */
@@ -1258,299 +2285,42 @@
 	/*
 	 * For SD, its erase group is always one sector
 	 */
+#if CONFIG_IS_ENABLED(MMC_WRITE)
 	mmc->erase_grp_size = 1;
+#endif
 	mmc->part_config = MMCPART_NOAVAILABLE;
-	if (!IS_SD(mmc) && (mmc->version >= MMC_VERSION_4)) {
-		/* check  ext_csd version and capacity */
-		err = mmc_send_ext_csd(mmc, ext_csd);
-		if (err)
-			return err;
-		if (ext_csd[EXT_CSD_REV] >= 2) {
-			/*
-			 * According to the JEDEC Standard, the value of
-			 * ext_csd's capacity is valid if the value is more
-			 * than 2GB
-			 */
-			capacity = ext_csd[EXT_CSD_SEC_CNT] << 0
-					| ext_csd[EXT_CSD_SEC_CNT + 1] << 8
-					| ext_csd[EXT_CSD_SEC_CNT + 2] << 16
-					| ext_csd[EXT_CSD_SEC_CNT + 3] << 24;
-			capacity *= MMC_MAX_BLOCK_LEN;
-			if ((capacity >> 20) > 2 * 1024)
-				mmc->capacity_user = capacity;
-		}
-
-		switch (ext_csd[EXT_CSD_REV]) {
-		case 1:
-			mmc->version = MMC_VERSION_4_1;
-			break;
-		case 2:
-			mmc->version = MMC_VERSION_4_2;
-			break;
-		case 3:
-			mmc->version = MMC_VERSION_4_3;
-			break;
-		case 5:
-			mmc->version = MMC_VERSION_4_41;
-			break;
-		case 6:
-			mmc->version = MMC_VERSION_4_5;
-			break;
-		case 7:
-			mmc->version = MMC_VERSION_5_0;
-			break;
-		case 8:
-			mmc->version = MMC_VERSION_5_1;
-			break;
-		}
-
-		/* The partition data may be non-zero but it is only
-		 * effective if PARTITION_SETTING_COMPLETED is set in
-		 * EXT_CSD, so ignore any data if this bit is not set,
-		 * except for enabling the high-capacity group size
-		 * definition (see below). */
-		part_completed = !!(ext_csd[EXT_CSD_PARTITION_SETTING] &
-				    EXT_CSD_PARTITION_SETTING_COMPLETED);
-
-		/* store the partition info of emmc */
-		mmc->part_support = ext_csd[EXT_CSD_PARTITIONING_SUPPORT];
-		if ((ext_csd[EXT_CSD_PARTITIONING_SUPPORT] & PART_SUPPORT) ||
-		    ext_csd[EXT_CSD_BOOT_MULT])
-			mmc->part_config = ext_csd[EXT_CSD_PART_CONF];
-		if (part_completed &&
-		    (ext_csd[EXT_CSD_PARTITIONING_SUPPORT] & ENHNCD_SUPPORT))
-			mmc->part_attr = ext_csd[EXT_CSD_PARTITIONS_ATTRIBUTE];
 
-		mmc->capacity_boot = ext_csd[EXT_CSD_BOOT_MULT] << 17;
-
-		mmc->capacity_rpmb = ext_csd[EXT_CSD_RPMB_MULT] << 17;
-
-		for (i = 0; i < 4; i++) {
-			int idx = EXT_CSD_GP_SIZE_MULT + i * 3;
-			uint mult = (ext_csd[idx + 2] << 16) +
-				(ext_csd[idx + 1] << 8) + ext_csd[idx];
-			if (mult)
-				has_parts = true;
-			if (!part_completed)
-				continue;
-			mmc->capacity_gp[i] = mult;
-			mmc->capacity_gp[i] *=
-				ext_csd[EXT_CSD_HC_ERASE_GRP_SIZE];
-			mmc->capacity_gp[i] *= ext_csd[EXT_CSD_HC_WP_GRP_SIZE];
-			mmc->capacity_gp[i] <<= 19;
-		}
-
-		if (part_completed) {
-			mmc->enh_user_size =
-				(ext_csd[EXT_CSD_ENH_SIZE_MULT+2] << 16) +
-				(ext_csd[EXT_CSD_ENH_SIZE_MULT+1] << 8) +
-				ext_csd[EXT_CSD_ENH_SIZE_MULT];
-			mmc->enh_user_size *= ext_csd[EXT_CSD_HC_ERASE_GRP_SIZE];
-			mmc->enh_user_size *= ext_csd[EXT_CSD_HC_WP_GRP_SIZE];
-			mmc->enh_user_size <<= 19;
-			mmc->enh_user_start =
-				(ext_csd[EXT_CSD_ENH_START_ADDR+3] << 24) +
-				(ext_csd[EXT_CSD_ENH_START_ADDR+2] << 16) +
-				(ext_csd[EXT_CSD_ENH_START_ADDR+1] << 8) +
-				ext_csd[EXT_CSD_ENH_START_ADDR];
-			if (mmc->high_capacity)
-				mmc->enh_user_start <<= 9;
-		}
-
-		/*
-		 * Host needs to enable ERASE_GRP_DEF bit if device is
-		 * partitioned. This bit will be lost every time after a reset
-		 * or power off. This will affect erase size.
-		 */
-		if (part_completed)
-			has_parts = true;
-		if ((ext_csd[EXT_CSD_PARTITIONING_SUPPORT] & PART_SUPPORT) &&
-		    (ext_csd[EXT_CSD_PARTITIONS_ATTRIBUTE] & PART_ENH_ATTRIB))
-			has_parts = true;
-		if (has_parts) {
-			err = mmc_switch(mmc, EXT_CSD_CMD_SET_NORMAL,
-				EXT_CSD_ERASE_GROUP_DEF, 1);
-
-			if (err)
-				return err;
-			else
-				ext_csd[EXT_CSD_ERASE_GROUP_DEF] = 1;
-		}
-
-		if (ext_csd[EXT_CSD_ERASE_GROUP_DEF] & 0x01) {
-			/* Read out group size from ext_csd */
-			mmc->erase_grp_size =
-				ext_csd[EXT_CSD_HC_ERASE_GRP_SIZE] * 1024;
-			/*
-			 * if high capacity and partition setting completed
-			 * SEC_COUNT is valid even if it is smaller than 2 GiB
-			 * JEDEC Standard JESD84-B45, 6.2.4
-			 */
-			if (mmc->high_capacity && part_completed) {
-				capacity = (ext_csd[EXT_CSD_SEC_CNT]) |
-					(ext_csd[EXT_CSD_SEC_CNT + 1] << 8) |
-					(ext_csd[EXT_CSD_SEC_CNT + 2] << 16) |
-					(ext_csd[EXT_CSD_SEC_CNT + 3] << 24);
-				capacity *= MMC_MAX_BLOCK_LEN;
-				mmc->capacity_user = capacity;
-			}
-		} else {
-			/* Calculate the group size from the csd value. */
-			int erase_gsz, erase_gmul;
-			erase_gsz = (mmc->csd[2] & 0x00007c00) >> 10;
-			erase_gmul = (mmc->csd[2] & 0x000003e0) >> 5;
-			mmc->erase_grp_size = (erase_gsz + 1)
-				* (erase_gmul + 1);
-		}
-
-		mmc->hc_wp_grp_size = 1024
-			* ext_csd[EXT_CSD_HC_ERASE_GRP_SIZE]
-			* ext_csd[EXT_CSD_HC_WP_GRP_SIZE];
-
-		mmc->wr_rel_set = ext_csd[EXT_CSD_WR_REL_SET];
-	}
-
-	err = mmc_set_capacity(mmc, mmc_get_blk_desc(mmc)->hwpart);
+	err = mmc_startup_v4(mmc);
 	if (err)
 		return err;
 
-	if (IS_SD(mmc))
-		err = sd_change_freq(mmc);
-	else
-		err = mmc_change_freq(mmc);
-
+	err = mmc_set_capacity(mmc, mmc_get_blk_desc(mmc)->hwpart);
 	if (err)
 		return err;
 
-	/* Restrict card's capabilities by what the host can do */
-	mmc->card_caps &= mmc->cfg->host_caps;
-
 	if (IS_SD(mmc)) {
-		if (mmc->card_caps & MMC_MODE_4BIT) {
-			cmd.cmdidx = MMC_CMD_APP_CMD;
-			cmd.resp_type = MMC_RSP_R1;
-			cmd.cmdarg = mmc->rca << 16;
-
-			err = mmc_send_cmd(mmc, &cmd, NULL);
-			if (err)
-				return err;
-
-			cmd.cmdidx = SD_CMD_APP_SET_BUS_WIDTH;
-			cmd.resp_type = MMC_RSP_R1;
-			cmd.cmdarg = 2;
-			err = mmc_send_cmd(mmc, &cmd, NULL);
-			if (err)
-				return err;
-
-			mmc_set_bus_width(mmc, 4);
-		}
-
-		err = sd_read_ssr(mmc);
+		err = sd_get_capabilities(mmc);
 		if (err)
 			return err;
-
-		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;
-
-		/* An array of possible bus widths in order of preference */
-		static unsigned ext_csd_bits[] = {
-			EXT_CSD_DDR_BUS_WIDTH_8,
-			EXT_CSD_DDR_BUS_WIDTH_4,
-			EXT_CSD_BUS_WIDTH_8,
-			EXT_CSD_BUS_WIDTH_4,
-			EXT_CSD_BUS_WIDTH_1,
-		};
-
-		/* An array to map CSD bus widths to host cap bits */
-		static unsigned ext_to_hostcaps[] = {
-			[EXT_CSD_DDR_BUS_WIDTH_4] =
-				MMC_MODE_DDR_52MHz | MMC_MODE_4BIT,
-			[EXT_CSD_DDR_BUS_WIDTH_8] =
-				MMC_MODE_DDR_52MHz | MMC_MODE_8BIT,
-			[EXT_CSD_BUS_WIDTH_4] = MMC_MODE_4BIT,
-			[EXT_CSD_BUS_WIDTH_8] = MMC_MODE_8BIT,
-		};
-
-		/* An array to map chosen bus width to an integer */
-		static unsigned widths[] = {
-			8, 4, 8, 4, 1,
-		};
-
-		for (idx=0; idx < ARRAY_SIZE(ext_csd_bits); idx++) {
-			unsigned int extw = ext_csd_bits[idx];
-			unsigned int caps = ext_to_hostcaps[extw];
-
-			/*
-			 * If the bus width is still not changed,
-			 * don't try to set the default again.
-			 * Otherwise, recover from switch attempts
-			 * by switching to 1-bit bus width.
-			 */
-			if (extw == EXT_CSD_BUS_WIDTH_1 &&
-					mmc->bus_width == 1) {
-				err = 0;
-				break;
-			}
-
-			/*
-			 * Check to make sure the card and controller support
-			 * these capabilities
-			 */
-			if ((mmc->card_caps & caps) != caps)
-				continue;
-
-			err = mmc_switch(mmc, EXT_CSD_CMD_SET_NORMAL,
-					EXT_CSD_BUS_WIDTH, extw);
-
-			if (err)
-				continue;
-
-			mmc->ddr_mode = (caps & MMC_MODE_DDR_52MHz) ? 1 : 0;
-			mmc_set_bus_width(mmc, widths[idx]);
-
-			err = mmc_send_ext_csd(mmc, test_csd);
-
-			if (err)
-				continue;
-
-			/* Only compare read only fields */
-			if (ext_csd[EXT_CSD_PARTITIONING_SUPPORT]
-				== test_csd[EXT_CSD_PARTITIONING_SUPPORT] &&
-			    ext_csd[EXT_CSD_HC_WP_GRP_SIZE]
-				== test_csd[EXT_CSD_HC_WP_GRP_SIZE] &&
-			    ext_csd[EXT_CSD_REV]
-				== test_csd[EXT_CSD_REV] &&
-			    ext_csd[EXT_CSD_HC_ERASE_GRP_SIZE]
-				== test_csd[EXT_CSD_HC_ERASE_GRP_SIZE] &&
-			    memcmp(&ext_csd[EXT_CSD_SEC_CNT],
-				   &test_csd[EXT_CSD_SEC_CNT], 4) == 0)
-				break;
-			else
-				err = -EBADMSG;
-		}
-
+		err = sd_select_mode_and_width(mmc, mmc->card_caps);
+	} else {
+		err = mmc_get_capabilities(mmc);
 		if (err)
 			return err;
-
-		if (mmc->card_caps & MMC_MODE_HS) {
-			if (mmc->card_caps & MMC_MODE_HS_52MHz)
-				mmc->tran_speed = 52000000;
-			else
-				mmc->tran_speed = 26000000;
-		}
+		mmc_select_mode_and_width(mmc, mmc->card_caps);
 	}
 
+	if (err)
+		return err;
+
-	mmc_set_clock(mmc, mmc->tran_speed);
+	mmc->best_mode = mmc->selected_mode;
 
 	/* Fix the block length for DDR mode */
 	if (mmc->ddr_mode) {
 		mmc->read_bl_len = MMC_MAX_BLOCK_LEN;
+#if CONFIG_IS_ENABLED(MMC_WRITE)
 		mmc->write_bl_len = MMC_MAX_BLOCK_LEN;
+#endif
 	}
 
 	/* fill in device description */
@@ -1618,22 +2388,18 @@
 static int mmc_power_init(struct mmc *mmc)
 {
 #if CONFIG_IS_ENABLED(DM_MMC)
-#if defined(CONFIG_DM_REGULATOR) && !defined(CONFIG_SPL_BUILD)
-	struct udevice *vmmc_supply;
+#if CONFIG_IS_ENABLED(DM_REGULATOR)
 	int ret;
 
 	ret = device_get_supply_regulator(mmc->dev, "vmmc-supply",
-					  &vmmc_supply);
-	if (ret) {
+					  &mmc->vmmc_supply);
+	if (ret)
 		debug("%s: No vmmc supply\n", mmc->dev->name);
-		return 0;
-	}
 
-	ret = regulator_set_enable(vmmc_supply, true);
-	if (ret) {
-		puts("Error enabling VMMC supply\n");
-		return ret;
-	}
+	ret = device_get_supply_regulator(mmc->dev, "vqmmc-supply",
+					  &mmc->vqmmc_supply);
+	if (ret)
+		debug("%s: No vqmmc supply\n", mmc->dev->name);
 #endif
 #else /* !CONFIG_DM_MMC */
 	/*
@@ -1645,11 +2411,86 @@
 	return 0;
 }
 
+/*
+ * put the host in the initial state:
+ * - turn on Vdd (card power supply)
+ * - configure the bus width and clock to minimal values
+ */
+static void mmc_set_initial_state(struct mmc *mmc)
+{
+	int err;
+
+	/* First try to set 3.3V. If it fails set to 1.8V */
+	err = mmc_set_signal_voltage(mmc, MMC_SIGNAL_VOLTAGE_330);
+	if (err != 0)
+		err = mmc_set_signal_voltage(mmc, MMC_SIGNAL_VOLTAGE_180);
+	if (err != 0)
+		pr_warn("mmc: failed to set signal voltage\n");
+
+	mmc_select_mode(mmc, MMC_LEGACY);
+	mmc_set_bus_width(mmc, 1);
+	mmc_set_clock(mmc, 0, false);
+}
+
+static int mmc_power_on(struct mmc *mmc)
+{
+#if CONFIG_IS_ENABLED(DM_MMC) && CONFIG_IS_ENABLED(DM_REGULATOR)
+	if (mmc->vmmc_supply) {
+		int ret = regulator_set_enable(mmc->vmmc_supply, true);
+
+		if (ret) {
+			puts("Error enabling VMMC supply\n");
+			return ret;
+		}
+	}
+#endif
+	return 0;
+}
+
+static int mmc_power_off(struct mmc *mmc)
+{
+	mmc_set_clock(mmc, 1, true);
+#if CONFIG_IS_ENABLED(DM_MMC) && CONFIG_IS_ENABLED(DM_REGULATOR)
+	if (mmc->vmmc_supply) {
+		int ret = regulator_set_enable(mmc->vmmc_supply, false);
+
+		if (ret) {
+			debug("Error disabling VMMC supply\n");
+			return ret;
+		}
+	}
+#endif
+	return 0;
+}
+
+static int mmc_power_cycle(struct mmc *mmc)
+{
+	int ret;
+
+	ret = mmc_power_off(mmc);
+	if (ret)
+		return ret;
+	/*
+	 * SD spec recommends at least 1ms of delay. Let's wait for 2ms
+	 * to be on the safer side.
+	 */
+	udelay(2000);
+	return mmc_power_on(mmc);
+}
+
 int mmc_start_init(struct mmc *mmc)
 {
 	bool no_card;
+	bool uhs_en = supports_uhs(mmc->cfg->host_caps);
 	int err;
 
+	/*
+	 * all hosts are capable of 1 bit bus-width and able to use the legacy
+	 * timings.
+	 */
+	mmc->host_caps = mmc->cfg->host_caps | MMC_CAP(SD_LEGACY) |
+			 MMC_CAP(MMC_LEGACY) | MMC_MODE_1BIT;
+
 	/* we pretend there's no card when init is NULL */
 	no_card = mmc_getcd(mmc) == 0;
 #if !CONFIG_IS_ENABLED(DM_MMC)
@@ -1673,6 +2514,26 @@
 	if (err)
 		return err;
 
+#ifdef CONFIG_MMC_QUIRKS
+	mmc->quirks = MMC_QUIRK_RETRY_SET_BLOCKLEN |
+		      MMC_QUIRK_RETRY_SEND_CID;
+#endif
+
+	err = mmc_power_cycle(mmc);
+	if (err) {
+		/*
+		 * if power cycling is not supported, we should not try
+		 * to use the UHS modes, because we wouldn't be able to
+		 * recover from an error during the UHS initialization.
+		 */
+		debug("Unable to do a full power cycle. Disabling the UHS modes for safety\n");
+		uhs_en = false;
+		mmc->host_caps &= ~UHS_CAPS;
+		err = mmc_power_on(mmc);
+	}
+	if (err)
+		return err;
+
 #if CONFIG_IS_ENABLED(DM_MMC)
 	/* The device has already been probed ready for use */
 #else
@@ -1682,8 +2543,10 @@
 		return err;
 #endif
 	mmc->ddr_mode = 0;
-	mmc_set_bus_width(mmc, 1);
-	mmc_set_clock(mmc, 1);
+
+retry:
+	mmc_set_initial_state(mmc);
+	mmc_send_init_stream(mmc);
 
 	/* Reset the Card */
 	err = mmc_go_idle(mmc);
@@ -1698,7 +2561,12 @@
 	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;
+		mmc_power_cycle(mmc);
+		goto retry;
+	}
 
 	/* If the command timed out, we check for an MMC card */
 	if (err == -ETIMEDOUT) {
@@ -1706,7 +2574,7 @@
 
 		if (err) {
 #if !defined(CONFIG_SPL_BUILD) || defined(CONFIG_SPL_LIBCOMMON_SUPPORT)
-			printf("Card did not respond to voltage select!\n");
+			pr_err("Card did not respond to voltage select!\n");
 #endif
 			return -EOPNOTSUPP;
 		}
@@ -1812,7 +2680,7 @@
 	uclass_foreach_dev(dev, uc) {
 		ret = device_probe(dev);
 		if (ret)
-			printf("%s - probe failed: %d\n", dev->name, ret);
+			pr_err("%s - probe failed: %d\n", dev->name, ret);
 	}
 
 	return 0;
diff --git a/drivers/mmc/mmc_private.h b/drivers/mmc/mmc_private.h
index 1290eed..a9be4b0 100644
--- a/drivers/mmc/mmc_private.h
+++ b/drivers/mmc/mmc_private.h
@@ -28,7 +28,7 @@
 		void *dst);
 #endif
 
-#if !(defined(CONFIG_SPL_BUILD) && !defined(CONFIG_SPL_SAVEENV))
+#if CONFIG_IS_ENABLED(MMC_WRITE)
 
 #if CONFIG_IS_ENABLED(BLK)
 ulong mmc_bwrite(struct udevice *dev, lbaint_t start, lbaint_t blkcnt,
@@ -40,7 +40,7 @@
 ulong mmc_berase(struct blk_desc *block_dev, lbaint_t start, lbaint_t blkcnt);
 #endif
 
-#else /* CONFIG_SPL_BUILD and CONFIG_SPL_SAVEENV is not defined */
+#else /* CONFIG_SPL_MMC_WRITE is not defined */
 
 /* declare dummies to reduce code size. */
 
diff --git a/drivers/mmc/omap_hsmmc.c b/drivers/mmc/omap_hsmmc.c
index efa4389..30443d1 100644
--- a/drivers/mmc/omap_hsmmc.c
+++ b/drivers/mmc/omap_hsmmc.c
@@ -28,9 +28,9 @@
 #include <mmc.h>
 #include <part.h>
 #include <i2c.h>
-#include <twl4030.h>
-#include <twl6030.h>
+#if defined(CONFIG_OMAP54XX) || defined(CONFIG_OMAP44XX)
 #include <palmas.h>
+#endif
 #include <asm/io.h>
 #include <asm/arch/mmc_host_def.h>
 #if !defined(CONFIG_SOC_KEYSTONE)
diff --git a/drivers/mmc/sandbox_mmc.c b/drivers/mmc/sandbox_mmc.c
index fdb29a5..8a5d256 100644
--- a/drivers/mmc/sandbox_mmc.c
+++ b/drivers/mmc/sandbox_mmc.c
@@ -48,9 +48,12 @@
 		cmd->response[1] = 10 << 16;	/* 1 << block_len */
 		break;
 	case SD_CMD_SWITCH_FUNC: {
+		if (!data)
+			break;
 		u32 *resp = (u32 *)data->dest;
-
 		resp[7] = cpu_to_be32(SD_HIGHSPEED_BUSY);
+		if ((cmd->cmdarg & 0xF) == UHS_SDR12_BUS_SPEED)
+			resp[4] = (cmd->cmdarg & 0xF) << 24;
 		break;
 	}
 	case MMC_CMD_READ_SINGLE_BLOCK:
diff --git a/drivers/mmc/sdhci.c b/drivers/mmc/sdhci.c
index 11d1f0c..e2ddf5d 100644
--- a/drivers/mmc/sdhci.c
+++ b/drivers/mmc/sdhci.c
@@ -157,7 +157,6 @@
 	/* Timeout unit - ms */
 	static unsigned int cmd_timeout = SDHCI_CMD_DEFAULT_TIMEOUT;
 
-	sdhci_writel(host, SDHCI_INT_ALL_MASK, SDHCI_INT_STATUS);
 	mask = SDHCI_CMD_INHIBIT | SDHCI_DATA_INHIBIT;
 
 	/* We shouldn't wait for data inihibit for stop commands, even
@@ -181,6 +180,8 @@
 		udelay(1000);
 	}
 
+	sdhci_writel(host, SDHCI_INT_ALL_MASK, SDHCI_INT_STATUS);
+
 	mask = SDHCI_INT_RESPONSE;
 	if (!(cmd->resp_type & MMC_RSP_PRESENT))
 		flags = SDHCI_CMD_RESP_NONE;
@@ -201,7 +202,7 @@
 		flags |= SDHCI_CMD_DATA;
 
 	/* Set Transfer mode regarding to data flag */
-	if (data != 0) {
+	if (data) {
 		sdhci_writeb(host, 0xe, SDHCI_TIMEOUT_CONTROL);
 		mode = SDHCI_TRNS_BLK_CNT_EN;
 		trans_bytes = data->blocks * data->blocksize;
@@ -249,7 +250,7 @@
 
 	sdhci_writel(host, cmd->cmdarg, SDHCI_ARGUMENT);
 #ifdef CONFIG_MMC_SDHCI_SDMA
-	if (data != 0) {
+	if (data) {
 		trans_bytes = ALIGN(trans_bytes, CONFIG_SYS_CACHELINE_SIZE);
 		flush_cache(start_addr, trans_bytes);
 	}
diff --git a/drivers/power/power_core.c b/drivers/power/power_core.c
index b72286d..46840a3 100644
--- a/drivers/power/power_core.c
+++ b/drivers/power/power_core.c
@@ -47,36 +47,6 @@
 	return 0;
 }
 
-static void pmic_show_info(struct pmic *p)
-{
-	printf("PMIC: %s\n", p->name);
-}
-
-static int pmic_dump(struct pmic *p)
-{
-	int i, ret;
-	u32 val;
-
-	if (!p) {
-		puts("Wrong PMIC name!\n");
-		return -ENODEV;
-	}
-
-	pmic_show_info(p);
-	for (i = 0; i < p->number_of_regs; i++) {
-		ret = pmic_reg_read(p, i, &val);
-		if (ret)
-			puts("PMIC: Registers dump failed\n");
-
-		if (!(i % 8))
-			printf("\n0x%02x: ", i);
-
-		printf("%08x ", val);
-	}
-	puts("\n");
-	return 0;
-}
-
 struct pmic *pmic_alloc(void)
 {
 	struct pmic *p;
@@ -108,7 +78,33 @@
 	return NULL;
 }
 
+#ifndef CONFIG_SPL_BUILD
+static int pmic_dump(struct pmic *p)
+{
+	int i, ret;
+	u32 val;
+
+	if (!p) {
+		puts("Wrong PMIC name!\n");
+		return -ENODEV;
+	}
+
+	printf("PMIC: %s\n", p->name);
+	for (i = 0; i < p->number_of_regs; i++) {
+		ret = pmic_reg_read(p, i, &val);
+		if (ret)
+			puts("PMIC: Registers dump failed\n");
+
+		if (!(i % 8))
+			printf("\n0x%02x: ", i);
+
+		printf("%08x ", val);
+	}
+	puts("\n");
+	return 0;
+}
+
-const char *power_get_interface(int interface)
+static const char *power_get_interface(int interface)
 {
 	const char *power_interface[] = {"I2C", "SPI", "|+|-|"};
 	return power_interface[interface];
@@ -125,7 +121,7 @@
 	}
 }
 
-int do_pmic(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
+static int do_pmic(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
 {
 	u32 ret, reg, val;
 	char *cmd, *name;
@@ -221,3 +217,4 @@
 	"pmic name bat state - write register\n"
 	"pmic name bat charge - write register\n"
 );
+#endif
diff --git a/include/mmc.h b/include/mmc.h
index 010ebe0..a46eaed 100644
--- a/include/mmc.h
+++ b/include/mmc.h
@@ -15,6 +15,13 @@
 #include <linux/compiler.h>
 #include <part.h>
 
+#if CONFIG_IS_ENABLED(MMC_HS200_SUPPORT)
+#define MMC_SUPPORTS_TUNING
+#endif
+#if CONFIG_IS_ENABLED(MMC_UHS_SUPPORT)
+#define MMC_SUPPORTS_TUNING
+#endif
+
 /* SD/MMC version bits; 8 flags, 8 major, 8 minor, 8 change */
 #define SD_VERSION_SD	(1U << 31)
 #define MMC_VERSION_MMC	(1U << 30)
@@ -52,12 +59,17 @@
 #define MMC_VERSION_5_0		MAKE_MMC_VERSION(5, 0, 0)
 #define MMC_VERSION_5_1		MAKE_MMC_VERSION(5, 1, 0)
 
+#define MMC_CAP(mode)		(1 << mode)
+#define MMC_MODE_HS		(MMC_CAP(MMC_HS) | MMC_CAP(SD_HS))
+#define MMC_MODE_HS_52MHz	MMC_CAP(MMC_HS_52)
+#define MMC_MODE_DDR_52MHz	MMC_CAP(MMC_DDR_52)
+#define MMC_MODE_HS200		MMC_CAP(MMC_HS_200)
+
-#define MMC_MODE_HS		(1 << 0)
-#define MMC_MODE_HS_52MHz	(1 << 1)
-#define MMC_MODE_4BIT		(1 << 2)
-#define MMC_MODE_8BIT		(1 << 3)
-#define MMC_MODE_SPI		(1 << 4)
-#define MMC_MODE_DDR_52MHz	(1 << 5)
+#define MMC_MODE_8BIT		BIT(30)
+#define MMC_MODE_4BIT		BIT(29)
+#define MMC_MODE_1BIT		BIT(28)
+#define MMC_MODE_SPI		BIT(27)
+
 
 #define SD_DATA_4BIT	0x00040000
 
@@ -82,6 +94,8 @@
 #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
@@ -109,12 +123,34 @@
 #define SD_CMD_APP_SEND_OP_COND		41
 #define SD_CMD_APP_SEND_SCR		51
 
+static inline bool mmc_is_tuning_cmd(uint cmdidx)
+{
+	if ((cmdidx == MMC_CMD_SEND_TUNING_BLOCK_HS200) ||
+	    (cmdidx == MMC_CMD_SEND_TUNING_BLOCK))
+		return true;
+	return false;
+}
+
 /* SCR definitions in different words */
 #define SD_HIGHSPEED_BUSY	0x00020000
 #define SD_HIGHSPEED_SUPPORTED	0x00020000
 
+#define UHS_SDR12_BUS_SPEED	0
+#define HIGH_SPEED_BUS_SPEED	1
+#define UHS_SDR25_BUS_SPEED	1
+#define UHS_SDR50_BUS_SPEED	2
+#define UHS_SDR104_BUS_SPEED	3
+#define UHS_DDR50_BUS_SPEED	4
+
+#define SD_MODE_UHS_SDR12	BIT(UHS_SDR12_BUS_SPEED)
+#define SD_MODE_UHS_SDR25	BIT(UHS_SDR25_BUS_SPEED)
+#define SD_MODE_UHS_SDR50	BIT(UHS_SDR50_BUS_SPEED)
+#define SD_MODE_UHS_SDR104	BIT(UHS_SDR104_BUS_SPEED)
+#define SD_MODE_UHS_DDR50	BIT(UHS_DDR50_BUS_SPEED)
+
 #define OCR_BUSY		0x80000000
 #define OCR_HCS			0x40000000
+#define OCR_S18R		0x1000000
 #define OCR_VOLTAGE_MASK	0x007FFF80
 #define OCR_ACCESS_MODE		0x60000000
 
@@ -206,11 +242,23 @@
 #define EXT_CSD_CARD_TYPE_DDR_52	(EXT_CSD_CARD_TYPE_DDR_1_8V \
 					| EXT_CSD_CARD_TYPE_DDR_1_2V)
 
+#define EXT_CSD_CARD_TYPE_HS200_1_8V	BIT(4)	/* Card can run at 200MHz */
+						/* SDR mode @1.8V I/O */
+#define EXT_CSD_CARD_TYPE_HS200_1_2V	BIT(5)	/* Card can run at 200MHz */
+						/* SDR mode @1.2V I/O */
+#define EXT_CSD_CARD_TYPE_HS200		(EXT_CSD_CARD_TYPE_HS200_1_8V | \
+					 EXT_CSD_CARD_TYPE_HS200_1_2V)
+
 #define EXT_CSD_BUS_WIDTH_1	0	/* Card is in 1 bit mode */
 #define EXT_CSD_BUS_WIDTH_4	1	/* Card is in 4 bit mode */
 #define EXT_CSD_BUS_WIDTH_8	2	/* Card is in 8 bit mode */
 #define EXT_CSD_DDR_BUS_WIDTH_4	5	/* Card is in 4 bit DDR mode */
 #define EXT_CSD_DDR_BUS_WIDTH_8	6	/* Card is in 8 bit DDR mode */
+#define EXT_CSD_DDR_FLAG	BIT(2)	/* Flag for DDR mode */
+
+#define EXT_CSD_TIMING_LEGACY	0	/* no high speed */
+#define EXT_CSD_TIMING_HS	1	/* HS */
+#define EXT_CSD_TIMING_HS200	2	/* HS200 */
 
 #define EXT_CSD_BOOT_ACK_ENABLE			(1 << 6)
 #define EXT_CSD_BOOT_PARTITION_ENABLE		(1 << 3)
@@ -265,6 +313,20 @@
 #define ENHNCD_SUPPORT		(0x2)
 #define PART_ENH_ATTRIB		(0x1f)
 
+#define MMC_QUIRK_RETRY_SEND_CID	BIT(0)
+#define MMC_QUIRK_RETRY_SET_BLOCKLEN	BIT(1)
+
+enum mmc_voltage {
+	MMC_SIGNAL_VOLTAGE_000 = 0,
+	MMC_SIGNAL_VOLTAGE_120 = 1,
+	MMC_SIGNAL_VOLTAGE_180 = 2,
+	MMC_SIGNAL_VOLTAGE_330 = 4,
+};
+
+#define MMC_ALL_SIGNAL_VOLTAGE (MMC_SIGNAL_VOLTAGE_120 |\
+				MMC_SIGNAL_VOLTAGE_180 |\
+				MMC_SIGNAL_VOLTAGE_330)
+
 /* Maximum block size for MMC */
 #define MMC_MAX_BLOCK_LEN	512
 
@@ -347,6 +409,14 @@
 	int (*set_ios)(struct udevice *dev);
 
 	/**
+	 * send_init_stream() - send the initialization stream: 74 clock cycles
+	 * This is used after power up before sending the first command
+	 *
+	 * @dev:	Device to update
+	 */
+	void (*send_init_stream)(struct udevice *dev);
+
+	/**
 	 * get_cd() - See whether a card is present
 	 *
 	 * @dev:	Device to check
@@ -361,6 +431,30 @@
 	 * @return 0 if write-enabled, 1 if write-protected, -ve on error
 	 */
 	int (*get_wp)(struct udevice *dev);
+
+#ifdef MMC_SUPPORTS_TUNING
+	/**
+	 * execute_tuning() - Start the tuning process
+	 *
+	 * @dev:	Device to start the tuning
+	 * @opcode:	Command opcode to send
+	 * @return 0 if OK, -ve on error
+	 */
+	int (*execute_tuning)(struct udevice *dev, uint opcode);
+#endif
+
+#if CONFIG_IS_ENABLED(MMC_UHS_SUPPORT)
+	/**
+	 * wait_dat0() - wait until dat0 is in the target state
+	 *		(CLK must be running during the wait)
+	 *
+	 * @dev:	Device to check
+	 * @state:	target state
+	 * @timeout:	timeout in us
+	 * @return 0 if dat0 is in the target state, -ve on error
+	 */
+	int (*wait_dat0)(struct udevice *dev, int state, int timeout);
+#endif
 };
 
 #define mmc_get_ops(dev)        ((struct dm_mmc_ops *)(dev)->driver->ops)
@@ -368,13 +462,19 @@
 int dm_mmc_send_cmd(struct udevice *dev, struct mmc_cmd *cmd,
 		    struct mmc_data *data);
 int dm_mmc_set_ios(struct udevice *dev);
+void dm_mmc_send_init_stream(struct udevice *dev);
 int dm_mmc_get_cd(struct udevice *dev);
 int dm_mmc_get_wp(struct udevice *dev);
+int dm_mmc_execute_tuning(struct udevice *dev, uint opcode);
+int dm_mmc_wait_dat0(struct udevice *dev, int state, int timeout);
 
 /* Transition functions for compatibility */
 int mmc_set_ios(struct mmc *mmc);
+void mmc_send_init_stream(struct mmc *mmc);
 int mmc_getcd(struct mmc *mmc);
 int mmc_getwp(struct mmc *mmc);
+int mmc_execute_tuning(struct mmc *mmc, uint opcode);
+int mmc_wait_dat0(struct mmc *mmc, int state, int timeout);
 
 #else
 struct mmc_ops {
@@ -406,6 +506,50 @@
 	unsigned int erase_offset;	/* In milliseconds */
 };
 
+enum bus_mode {
+	MMC_LEGACY,
+	SD_LEGACY,
+	MMC_HS,
+	SD_HS,
+	MMC_HS_52,
+	MMC_DDR_52,
+	UHS_SDR12,
+	UHS_SDR25,
+	UHS_SDR50,
+	UHS_DDR50,
+	UHS_SDR104,
+	MMC_HS_200,
+	MMC_MODES_END
+};
+
+const char *mmc_mode_name(enum bus_mode mode);
+void mmc_dump_capabilities(const char *text, uint caps);
+
+static inline bool mmc_is_mode_ddr(enum bus_mode mode)
+{
+	if (mode == MMC_DDR_52)
+		return true;
+#if CONFIG_IS_ENABLED(MMC_UHS_SUPPORT)
+	else if (mode == UHS_DDR50)
+		return true;
+#endif
+	else
+		return false;
+}
+
+#define UHS_CAPS (MMC_CAP(UHS_SDR12) | MMC_CAP(UHS_SDR25) | \
+		  MMC_CAP(UHS_SDR50) | MMC_CAP(UHS_SDR104) | \
+		  MMC_CAP(UHS_DDR50))
+
+static inline bool supports_uhs(uint caps)
+{
+#if CONFIG_IS_ENABLED(MMC_UHS_SUPPORT)
+	return (caps & UHS_CAPS) ? true : false;
+#else
+	return false;
+#endif
+}
+
 /*
  * With CONFIG_DM_MMC enabled, struct mmc can be accessed from the MMC device
  * with mmc_get_mmc_dev().
@@ -421,9 +565,12 @@
 	void *priv;
 	uint has_init;
 	int high_capacity;
+	bool clk_disable; /* true if the clock can be turned off */
 	uint bus_width;
 	uint clock;
+	enum mmc_voltage signal_voltage;
 	uint card_caps;
+	uint host_caps;
 	uint ocr;
 	uint dsr;
 	uint dsr_imp;
@@ -436,18 +583,27 @@
 	u8 wr_rel_set;
 	u8 part_config;
 	uint tran_speed;
+	uint legacy_speed; /* speed for the legacy mode provided by the card */
 	uint read_bl_len;
+#if CONFIG_IS_ENABLED(MMC_WRITE)
 	uint write_bl_len;
 	uint erase_grp_size;	/* in 512-byte sectors */
+#endif
+#if CONFIG_IS_ENABLED(MMC_HW_PARTITIONING)
 	uint hc_wp_grp_size;	/* in 512-byte sectors */
+#endif
+#if CONFIG_IS_ENABLED(MMC_WRITE)
 	struct sd_ssr	ssr;	/* SD status register */
+#endif
 	u64 capacity;
 	u64 capacity_user;
 	u64 capacity_boot;
 	u64 capacity_rpmb;
 	u64 capacity_gp[4];
+#ifndef CONFIG_SPL_BUILD
 	u64 enh_user_start;
 	u64 enh_user_size;
+#endif
 #if !CONFIG_IS_ENABLED(BLK)
 	struct blk_desc block_dev;
 #endif
@@ -457,7 +613,21 @@
 	int ddr_mode;
 #if CONFIG_IS_ENABLED(DM_MMC)
 	struct udevice *dev;	/* Device for this MMC controller */
+#if CONFIG_IS_ENABLED(DM_REGULATOR)
+	struct udevice *vmmc_supply;	/* Main voltage regulator (Vcc)*/
+	struct udevice *vqmmc_supply;	/* IO voltage regulator (Vccq)*/
+#endif
 #endif
+	u8 *ext_csd;
+	u32 cardtype;		/* cardtype read from the MMC */
+	enum mmc_voltage current_voltage;
+	enum bus_mode selected_mode; /* mode currently used */
+	enum bus_mode best_mode; /* best mode is the supported mode with the
+				  * highest bandwidth. It may not always be the
+				  * operating mode due to limitations when
+				  * accessing the boot partitions
+				  */
+	u32 quirks;
 };
 
 struct mmc_hwpart_conf {
@@ -507,8 +677,36 @@
 int mmc_unbind(struct udevice *dev);
 int mmc_initialize(bd_t *bis);
 int mmc_init(struct mmc *mmc);
+int mmc_send_tuning(struct mmc *mmc, u32 opcode, int *cmd_error);
+
+/**
+ * mmc_of_parse() - Parse the device tree to get the capabilities of the host
+ *
+ * @dev:	MMC device
+ * @cfg:	MMC configuration
+ * @return 0 if OK, -ve on error
+ */
+int mmc_of_parse(struct udevice *dev, struct mmc_config *cfg);
+
 int mmc_read(struct mmc *mmc, u64 src, uchar *dst, int size);
-void mmc_set_clock(struct mmc *mmc, uint clock);
+
+/**
+ * mmc_voltage_to_mv() - Convert a mmc_voltage in mV
+ *
+ * @voltage:	The mmc_voltage to convert
+ * @return the value in mV if OK, -EINVAL on error (invalid mmc_voltage value)
+ */
+int mmc_voltage_to_mv(enum mmc_voltage voltage);
+
+/**
+ * mmc_set_clock() - change the bus clock
+ * @mmc:	MMC struct
+ * @clock:	bus frequency in Hz
+ * @disable:	flag indicating if the clock must on or off
+ * @return 0 if OK, -ve on error
+ */
+int mmc_set_clock(struct mmc *mmc, uint clock, bool disable);
+
 struct mmc *find_mmc_device(int dev_num);
 int mmc_set_dev(int dev_num);
 void print_mmc_devices(char separator);