diff --git a/board/sunxi/board.c b/board/sunxi/board.c
index 838e89f..810fbd4 100644
--- a/board/sunxi/board.c
+++ b/board/sunxi/board.c
@@ -237,14 +237,14 @@ static void nand_clock_setup(void)
 void board_nand_init(void)
 {
 	nand_pinmux_setup();
 	nand_clock_setup();
 #ifndef CONFIG_SPL_BUILD
 	sunxi_nand_init();
 #endif
 }
 #endif
 
-#ifdef CONFIG_GENERIC_MMC
+#if defined(CONFIG_GENERIC_MMC) && !(defined(CONFIG_DM_MMC) && defined(CONFIG_PINCTRL))
 static void mmc_pinmux_setup(int sdc)
 {
 	unsigned int pin;
@@ -422,75 +422,76 @@ static void mmc_pinmux_setup(int sdc)
 
 int board_mmc_init(bd_t *bis)
 {
+#if !(defined(CONFIG_DM_MMC) && defined(CONFIG_PINCTRL))
 	__maybe_unused struct mmc *mmc0, *mmc1;
 	__maybe_unused char buf[512];
 	__maybe_unused u32 val;
 
 	mmc_pinmux_setup(CONFIG_MMC_SUNXI_SLOT);
 	mmc0 = sunxi_mmc_init(CONFIG_MMC_SUNXI_SLOT);
 	if (!mmc0)
 		return -1;
 
 #if CONFIG_MMC_SUNXI_SLOT_EXTRA != -1
 	mmc_pinmux_setup(CONFIG_MMC_SUNXI_SLOT_EXTRA);
 	mmc1 = sunxi_mmc_init(CONFIG_MMC_SUNXI_SLOT_EXTRA);
 	if (!mmc1)
 		return -1;
 #endif
 
 #if !defined(CONFIG_SPL_BUILD) && CONFIG_MMC_SUNXI_SLOT_EXTRA == 2
 #if CONFIG_MACH_SUN6I
 	/*
 	 * the bootdevice is shown in VER_REG in the system controller
 	 * if U_boot is set then the ROM trys to boot from mmc0 regardless
 	 * of the BOOT_SEL pins.
 	 */
 
 #if 0
 	val = readl(0x1c00024);
 	if ((val & (1<<10))) /* check UBOOT_SEL */
 	{
 		switch((val & (3<<8))>>8) /* check BOOT_SEL pins */
 		{
 			case 0x0: /* SPI0 boot */
 				break;
 			case 0x1: /* eMMC2 boot */
 				/* Check if there is a boot loader on eMMC2
 				 * If not we want to fall back to SD card */
 				if (mmc_init(mmc1) == 0 &&
 				    mmc1->block_dev.block_read(&mmc1->block_dev, 16, 1, buf) == 1) {
 					buf[12] = 0;
 					if (strcmp(&buf[4], "eGON.BT0") == 0) {
 						/* Boot loader found, swap to make eMMC the first device */
 						mmc0->block_dev.devnum = 1;
 						mmc1->block_dev.devnum = 0;
 					}
 				}
 				break;
 			case 0x2: /* SDC2 boot */
 				break;
 			case 0x3: /* NAND Flash boot */
 				break;
 		}
 	}
 #endif
 
 #else
 	/*
 	 * On systems with an emmc (mmc2), figure out if we are booting from
 	 * the emmc and if we are make it "mmc dev 0" so that boot.scr, etc.
 	 * are searched there first. Note we only do this for u-boot proper,
 	 * not for the SPL, see spl_boot_device().
 	 */
 	if (readb(SPL_ADDR + 0x28) == SUNXI_BOOTED_FROM_MMC2) {
 		/* Booting from emmc / mmc2, swap */
 		mmc0->block_dev.devnum = 1;
 		mmc1->block_dev.devnum = 0;
 	}
 #endif
 #endif
-
+#endif
 	return 0;
 }
 #endif
 
@@ -854,43 +855,45 @@ static void setup_environment(const void *fdt)
  */
 static void setup_environment(const void *fdt)
 {
+#if !defined(CONFIG_DM_MMC)
 	uint8_t mac_addr[6];
 	char serial_string[17] = { 0 };
 	struct mmc *mmc0;
 	struct sunxi_mmc_host {
 		unsigned mmc_no;
 		uint32_t *mclkreg;
 		unsigned fatal_err;
 		struct sunxi_mmc *reg;
 		struct mmc_config cfg;
 	};
 
 	mmc0 = find_mmc_device(1);
 
 	/* lookup the real device number to get the eMMC */
 	if(((struct sunxi_mmc_host*)mmc0->priv)->mmc_no != 2)
 		mmc0 = find_mmc_device(0);
 
 	if(mmc0->has_init == 0)
 		mmc_init(mmc0);
 
 	if (!getenv("ethaddr")) {
 		/* Non OUI / registered MAC address */
 		mac_addr[0] = 0x02;
 		mac_addr[1] = (mmc0->cid[0] >> 24) & 0xff;
 		mac_addr[2] = (mmc0->cid[2] >> 0) & 0xff;
 		mac_addr[3] = (mmc0->cid[3] >> 24) & 0xff;
 		mac_addr[4] = (mmc0->cid[3] >> 16) & 0xff;
 		mac_addr[5] = (mmc0->cid[3] >> 8) & 0xff;
 
 		eth_setenv_enetaddr("ethaddr", mac_addr);
 	}
 
 	if (!getenv("serial#")) {
 		snprintf(serial_string, sizeof(serial_string), "%08x%02x%06x",
 			 mmc0->cid[0]>>24,mmc0->cid[2]&0xff,mmc0->cid[3]>>8);
 		setenv("serial#", serial_string);
 	}
+#endif
 }
 #endif
 
diff --git a/drivers/mmc/sunxi_mmc.c b/drivers/mmc/sunxi_mmc.c
index 46abe4a..8075b9c 100644
--- a/drivers/mmc/sunxi_mmc.c
+++ b/drivers/mmc/sunxi_mmc.c
@@ -1,89 +1,165 @@
 /*
  * (C) Copyright 2007-2011
  * Allwinner Technology Co., Ltd. <www.allwinnertech.com>
  * Aaron <leafy.myeh@allwinnertech.com>
  *
+ * (C) 2017 Theobroma Systems Design und Consulting GmbH
+ *
  * MMC driver for allwinner sunxi platform.
  *
  * SPDX-License-Identifier:	GPL-2.0+
  */
 
 #include <common.h>
-#include <errno.h>
-#include <malloc.h>
-#include <mmc.h>
-#include <asm/io.h>
+#include <asm-generic/gpio.h>
 #include <asm/arch/clock.h>
 #include <asm/arch/cpu.h>
 #include <asm/arch/gpio.h>
 #include <asm/arch/mmc.h>
-#include <asm-generic/gpio.h>
+#include <asm/io.h>
+#include <clk.h>
+#include <dm/device.h>
+#include <dt-structs.h>
+#include <errno.h>
+#include <linux/iopoll.h>
+#include <malloc.h>
+#include <mmc.h>
+#include <reset.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+struct sunxi_mmc_plat {
+	struct mmc mmc;
+};
 
 struct sunxi_mmc_host {
 	unsigned mmc_no;
+#if !defined(CONFIG_DM_MMC)
 	uint32_t *mclkreg;
+#endif
 	unsigned fatal_err;
 	struct sunxi_mmc *reg;
 	struct mmc_config cfg;
+	bool cd_inverted;
+#if defined(CONFIG_DM_MMC)
+	struct mmc *mmc;
+	struct gpio_desc cd_gpio;	/* card-detect (optional) */
+	struct gpio_desc pwr_gpio;	/* power-enabled (optional) */
+	struct gpio_desc wp_gpio;	/* write-protect (optional) */
+	bool wp_inverted;
+	struct reset_ctl reset;
+	struct clk ahb_clk_gate;
+	struct clk mmc_clk;
+#else
+	int cd_pin;
+#endif
 };
 
+#if defined(CONFIG_DM_MMC) && defined(CONFIG_DM_MMC_OPS)
+static const struct dm_mmc_ops sunxi_mmc_ops;
+#else
+static const struct mmc_ops sunxi_mmc_ops;
+#endif
+
+#if !defined(CONFIG_DM_MMC)
 /* support 4 mmc hosts */
 struct sunxi_mmc_host mmc_host[4];
 
 static int sunxi_mmc_getcd_gpio(int sdc_no)
 {
 	switch (sdc_no) {
 	case 0: return sunxi_name_to_gpio(CONFIG_MMC0_CD_PIN);
 	case 1: return sunxi_name_to_gpio(CONFIG_MMC1_CD_PIN);
 	case 2: return sunxi_name_to_gpio(CONFIG_MMC2_CD_PIN);
+#if !defined(CONFIG_ARCH_SUN50I)  /* only 3 MMC controllers on the A64 */
 	case 3: return sunxi_name_to_gpio(CONFIG_MMC3_CD_PIN);
+#endif
 	}
 	return -EINVAL;
 }
 
 static int mmc_resource_init(int sdc_no)
 {
 	struct sunxi_mmc_host *mmchost = &mmc_host[sdc_no];
 	struct sunxi_ccm_reg *ccm = (struct sunxi_ccm_reg *)SUNXI_CCM_BASE;
 	int cd_pin, ret = 0;
 
 	debug("init mmc %d resource\n", sdc_no);
 
 	switch (sdc_no) {
 	case 0:
 		mmchost->reg = (struct sunxi_mmc *)SUNXI_MMC0_BASE;
 		mmchost->mclkreg = &ccm->sd0_clk_cfg;
 		break;
 	case 1:
 		mmchost->reg = (struct sunxi_mmc *)SUNXI_MMC1_BASE;
 		mmchost->mclkreg = &ccm->sd1_clk_cfg;
 		break;
 	case 2:
 		mmchost->reg = (struct sunxi_mmc *)SUNXI_MMC2_BASE;
 		mmchost->mclkreg = &ccm->sd2_clk_cfg;
 		break;
+#if !defined(CONFIG_ARCH_SUN50I)  /* only 3 MMC controllers on the A64 */
 	case 3:
 		mmchost->reg = (struct sunxi_mmc *)SUNXI_MMC3_BASE;
 		mmchost->mclkreg = &ccm->sd3_clk_cfg;
 		break;
+#endif
 	default:
 		printf("Wrong mmc number %d\n", sdc_no);
 		return -1;
 	}
 	mmchost->mmc_no = sdc_no;
 
 	cd_pin = sunxi_mmc_getcd_gpio(sdc_no);
 	if (cd_pin >= 0) {
 		ret = gpio_request(cd_pin, "mmc_cd");
 		if (!ret) {
 			sunxi_gpio_set_pull(cd_pin, SUNXI_GPIO_PULL_UP);
 			ret = gpio_direction_input(cd_pin);
 		}
 	}
+	mmchost->cd_pin = cd_pin;
+
+	return ret;
+}
+#endif
+
+#if defined(CONFIG_DM_MMC)
+static int mmc_resource_init_from_udev(struct udevice *dev)
+{
+	struct sunxi_mmc_host *mmchost = dev_get_priv(dev);
+	int ret = 0;
+
+	debug("%s: %s\n", dev->name, __func__);
+
+	switch ((uintptr_t)mmchost->reg) {
+	case SUNXI_MMC0_BASE:
+		mmchost->mmc_no = 0;
+		break;
+	case SUNXI_MMC1_BASE:
+		mmchost->mmc_no = 1;
+		break;
+	case SUNXI_MMC2_BASE:
+		mmchost->mmc_no = 2;
+		break;
+#if !defined(CONFIG_ARCH_SUN50I)  /* only 3 MMC controllers on the A64 */
+	case SUNXI_MMC3_BASE:
+		mmchost->mmc_no = 3;
+		break;
+#endif
+	default:
+		debug("%s: unknown base address %p\n", __func__, mmchost->reg);
+		return -1;
+	}
+
+	debug("%s: mmc_no %d\n", dev->name, mmchost->mmc_no);
 
 	return ret;
 }
+#endif
 
+#if !defined(CONFIG_DM_MMC)
 static int mmc_set_mod_clk(struct sunxi_mmc_host *mmchost, unsigned int hz)
 {
 	unsigned int pll, pll_hz, div, n, oclk_dly, sclk_dly;
@@ -154,112 +230,156 @@ static int mmc_set_mod_clk(struct sunxi_mmc_host *mmchost, unsigned int hz)
 	return 0;
 }
 
-static int mmc_clk_io_on(int sdc_no)
+static int mmc_clk_io_on(struct sunxi_mmc_host *mmchost)
 {
-	struct sunxi_mmc_host *mmchost = &mmc_host[sdc_no];
 	struct sunxi_ccm_reg *ccm = (struct sunxi_ccm_reg *)SUNXI_CCM_BASE;
+	int sdc_no = mmchost->mmc_no;
 
 	debug("init mmc %d clock and io\n", sdc_no);
 
 	/* config ahb clock */
 	setbits_le32(&ccm->ahb_gate0, 1 << AHB_GATE_OFFSET_MMC(sdc_no));
 
 #ifdef CONFIG_SUNXI_GEN_SUN6I
 	/* unassert reset */
 	setbits_le32(&ccm->ahb_reset0_cfg, 1 << AHB_RESET_OFFSET_MMC(sdc_no));
 #endif
 #if defined(CONFIG_MACH_SUN9I)
 	/* sun9i has a mmc-common module, also set the gate and reset there */
 	writel(SUNXI_MMC_COMMON_CLK_GATE | SUNXI_MMC_COMMON_RESET,
 	       SUNXI_MMC_COMMON_BASE + 4 * sdc_no);
 #endif
 
 	return mmc_set_mod_clk(mmchost, 24000000);
 }
+#endif
+
+#if defined(CONFIG_DM_MMC)
+static int mmc_clk_io_on(struct sunxi_mmc_host *mmchost)
+{
+	/* Enable the AHB clock gate */
+	clk_enable(&mmchost->ahb_clk_gate);
+
+	/* Deassert the AHB module reset */
+	reset_deassert(&mmchost->reset);
+
+#if defined(CONFIG_MACH_SUN9I)
+	/* TODO --- covert this to DM */
+	/* sun9i has a mmc-common module, also set the gate and reset there */
+	writel(SUNXI_MMC_COMMON_CLK_GATE | SUNXI_MMC_COMMON_RESET,
+	       SUNXI_MMC_COMMON_BASE + 4 * sdc_no);
+#endif
+
+	clk_set_rate(&mmchost->mmc_clk, 24000000);
+	clk_enable(&mmchost->mmc_clk);
+
+	return 0;
+}
+#endif
 
 static int mmc_update_clk(struct mmc *mmc)
 {
 	struct sunxi_mmc_host *mmchost = mmc->priv;
 	unsigned int cmd;
 	unsigned timeout_msecs = 2000;
 	unsigned long start = get_timer(0);
 
+	debug("%s: base %p\n", __func__, mmchost->reg);
+
 	cmd = SUNXI_MMC_CMD_START |
 	      SUNXI_MMC_CMD_UPCLK_ONLY |
 	      SUNXI_MMC_CMD_WAIT_PRE_OVER;
 	writel(cmd, &mmchost->reg->cmd);
 	while (readl(&mmchost->reg->cmd) & SUNXI_MMC_CMD_START) {
 		if (get_timer(start) > timeout_msecs)
 			return -1;
 	}
 
 	/* clock update sets various irq status bits, clear these */
 	writel(readl(&mmchost->reg->rint), &mmchost->reg->rint);
 
 	return 0;
 }
 
 static int mmc_config_clock(struct mmc *mmc)
 {
 	struct sunxi_mmc_host *mmchost = mmc->priv;
 	unsigned rval = readl(&mmchost->reg->clkcr);
 
 	/* Disable Clock */
 	rval &= ~SUNXI_MMC_CLK_ENABLE;
 	writel(rval, &mmchost->reg->clkcr);
 	if (mmc_update_clk(mmc))
 		return -1;
 
+#if !defined(CONFIG_DM_MMC)
 	/* Set mod_clk to new rate */
 	if (mmc_set_mod_clk(mmchost, mmc->clock))
 		return -1;
+#else
+	if (clk_set_rate(&mmchost->mmc_clk, mmc->clock) == 0)
+		return -1;
+#endif
 
 	/* Clear internal divider */
 	rval &= ~SUNXI_MMC_CLK_DIVIDER_MASK;
 	writel(rval, &mmchost->reg->clkcr);
 
 	/* Re-enable Clock */
 	rval |= SUNXI_MMC_CLK_ENABLE;
 	writel(rval, &mmchost->reg->clkcr);
 	if (mmc_update_clk(mmc))
 		return -1;
 
 	return 0;
 }
 
+#if defined(CONFIG_DM_MMC_OPS)
+static int sunxi_mmc_set_ios(struct udevice *dev)
+{
+	struct mmc *mmc = mmc_get_mmc_dev(dev);
+#else
 static int sunxi_mmc_set_ios(struct mmc *mmc)
 {
+#endif
 	struct sunxi_mmc_host *mmchost = mmc->priv;
 
 	debug("set ios: bus_width: %x, clock: %d\n",
 	      mmc->bus_width, mmc->clock);
 
 	/* Change clock first */
 	if (mmc->clock && mmc_config_clock(mmc) != 0) {
 		mmchost->fatal_err = 1;
 		return -EINVAL;
 	}
 
 	/* Change bus width */
 	if (mmc->bus_width == 8)
 		writel(0x2, &mmchost->reg->width);
 	else if (mmc->bus_width == 4)
 		writel(0x1, &mmchost->reg->width);
 	else
 		writel(0x0, &mmchost->reg->width);
 
 	return 0;
 }
 
 static int sunxi_mmc_core_init(struct mmc *mmc)
 {
 	struct sunxi_mmc_host *mmchost = mmc->priv;
+	uint32_t regval;
+	int ret = 0;
+
+	debug("%s: base %p", __func__, mmchost->reg);
 
 	/* Reset controller */
 	writel(SUNXI_MMC_GCTRL_RESET, &mmchost->reg->gctrl);
-	udelay(1000);
 
-	return 0;
+	/* Wait for the reset bit (auto-clearing) to deassert */
+	ret = readl_poll_timeout(&mmchost->reg->gctrl, regval,
+				 !(regval & SUNXI_MMC_GCTRL_RESET), 1000000);
+
+	return ret;
 }
 
 static int mmc_trans_data_by_cpu(struct mmc *mmc, struct mmc_data *data)
@@ -317,113 +437,120 @@ static int mmc_rint_wait(struct mmc *mmc, unsigned int timeout_msecs,
 	return 0;
 }
 
+#if defined(CONFIG_DM_MMC_OPS)
+static int sunxi_mmc_send_cmd(struct udevice *dev, struct mmc_cmd *cmd,
+			      struct mmc_data *data)
+{
+	struct mmc *mmc = mmc_get_mmc_dev(dev);
+#else
 static int sunxi_mmc_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd,
 			      struct mmc_data *data)
 {
+#endif
 	struct sunxi_mmc_host *mmchost = mmc->priv;
 	unsigned int cmdval = SUNXI_MMC_CMD_START;
 	unsigned int timeout_msecs;
 	int error = 0;
 	unsigned int status = 0;
 	unsigned int bytecnt = 0;
 
 	if (mmchost->fatal_err)
 		return -1;
 	if (cmd->resp_type & MMC_RSP_BUSY)
 		debug("mmc cmd %d check rsp busy\n", cmd->cmdidx);
 	if (cmd->cmdidx == 12)
 		return 0;
 
 	if (!cmd->cmdidx)
 		cmdval |= SUNXI_MMC_CMD_SEND_INIT_SEQ;
 	if (cmd->resp_type & MMC_RSP_PRESENT)
 		cmdval |= SUNXI_MMC_CMD_RESP_EXPIRE;
 	if (cmd->resp_type & MMC_RSP_136)
 		cmdval |= SUNXI_MMC_CMD_LONG_RESPONSE;
 	if (cmd->resp_type & MMC_RSP_CRC)
 		cmdval |= SUNXI_MMC_CMD_CHK_RESPONSE_CRC;
 
 	if (data) {
 		if ((u32)(long)data->dest & 0x3) {
 			error = -1;
 			goto out;
 		}
 
 		cmdval |= SUNXI_MMC_CMD_DATA_EXPIRE|SUNXI_MMC_CMD_WAIT_PRE_OVER;
 		if (data->flags & MMC_DATA_WRITE)
 			cmdval |= SUNXI_MMC_CMD_WRITE;
 		if (data->blocks > 1)
 			cmdval |= SUNXI_MMC_CMD_AUTO_STOP;
 		writel(data->blocksize, &mmchost->reg->blksz);
 		writel(data->blocks * data->blocksize, &mmchost->reg->bytecnt);
 	}
 
-	debug("mmc %d, cmd %d(0x%08x), arg 0x%08x\n", mmchost->mmc_no,
+	debug("mmc %p, cmd %d(0x%08x), arg 0x%08x\n", mmchost,
 	      cmd->cmdidx, cmdval | cmd->cmdidx, cmd->cmdarg);
 	writel(cmd->cmdarg, &mmchost->reg->arg);
 
 	if (!data)
 		writel(cmdval | cmd->cmdidx, &mmchost->reg->cmd);
 
 	/*
 	 * transfer data and check status
 	 * STATREG[2] : FIFO empty
 	 * STATREG[3] : FIFO full
 	 */
 	if (data) {
 		int ret = 0;
 
 		bytecnt = data->blocksize * data->blocks;
 		debug("trans data %d bytes\n", bytecnt);
 		writel(cmdval | cmd->cmdidx, &mmchost->reg->cmd);
 		ret = mmc_trans_data_by_cpu(mmc, data);
 		if (ret) {
 			error = readl(&mmchost->reg->rint) & \
 				SUNXI_MMC_RINT_INTERRUPT_ERROR_BIT;
 			error = -ETIMEDOUT;
 			goto out;
 		}
 	}
 
 	error = mmc_rint_wait(mmc, 1000, SUNXI_MMC_RINT_COMMAND_DONE, "cmd");
 	if (error)
 		goto out;
 
 	if (data) {
 		timeout_msecs = 120;
 		debug("cacl timeout %x msec\n", timeout_msecs);
 		error = mmc_rint_wait(mmc, timeout_msecs,
 				      data->blocks > 1 ?
 				      SUNXI_MMC_RINT_AUTO_COMMAND_DONE :
 				      SUNXI_MMC_RINT_DATA_OVER,
 				      "data");
 		if (error)
 			goto out;
 	}
 
 	if (cmd->resp_type & MMC_RSP_BUSY) {
 		unsigned long start = get_timer(0);
 		timeout_msecs = 2000;
 
 		do {
 			status = readl(&mmchost->reg->status);
 			if (get_timer(start) > timeout_msecs) {
 				debug("busy timeout\n");
 				error = -ETIMEDOUT;
 				goto out;
 			}
 		} while (status & SUNXI_MMC_STATUS_CARD_DATA_BUSY);
 	}
 
 	if (cmd->resp_type & MMC_RSP_136) {
 		cmd->response[0] = readl(&mmchost->reg->resp3);
 		cmd->response[1] = readl(&mmchost->reg->resp2);
 		cmd->response[2] = readl(&mmchost->reg->resp1);
 		cmd->response[3] = readl(&mmchost->reg->resp0);
 		debug("mmc resp 0x%08x 0x%08x 0x%08x 0x%08x\n",
 		      cmd->response[3], cmd->response[2],
 		      cmd->response[1], cmd->response[0]);
 	} else {
 		cmd->response[0] = readl(&mmchost->reg->resp0);
 		debug("mmc resp 0x%08x\n", cmd->response[0]);
 	}
@@ -439,50 +566,225 @@ out:
 	return error;
 }
 
+static inline int cdpin_is_valid(struct sunxi_mmc_host *priv)
+{
+#if !defined(CONFIG_DM_MMC)
+	return priv->cd_pin >= 0;
+#else
+	return dm_gpio_is_valid(&priv->cd_gpio);
+#endif
+}
+
+static inline int cdpin_get_value(struct sunxi_mmc_host *priv)
+{
+#if !defined(CONFIG_DM_MMC)
+	return gpio_get_value(priv->cd_pin);
+#else
+	return dm_gpio_get_value(&priv->cd_gpio);
+#endif
+}
+
+#if defined(CONFIG_DM_MMC_OPS)
+static int sunxi_mmc_getcd(struct udevice *dev)
+{
+	struct mmc *mmc = mmc_get_mmc_dev(dev);
+#else
 static int sunxi_mmc_getcd(struct mmc *mmc)
 {
-	struct sunxi_mmc_host *mmchost = mmc->priv;
-	int cd_pin;
+#endif
+	struct sunxi_mmc_host *priv = mmc->priv;
+	int value = 1;
 
-	cd_pin = sunxi_mmc_getcd_gpio(mmchost->mmc_no);
-	if (cd_pin < 0)
-		return 1;
+	if (cdpin_is_valid(priv)) {
+		value = cdpin_get_value(priv);
 
-	return !gpio_get_value(cd_pin);
-}
+		if (priv->cd_inverted)
+			return !value;
+	}
 
-static const struct mmc_ops sunxi_mmc_ops = {
-	.send_cmd	= sunxi_mmc_send_cmd,
-	.set_ios	= sunxi_mmc_set_ios,
-	.init		= sunxi_mmc_core_init,
-	.getcd		= sunxi_mmc_getcd,
-};
+	return value;
+}
 
+#if !defined(CONFIG_DM_MMC)
 struct mmc *sunxi_mmc_init(int sdc_no)
 {
 	struct mmc_config *cfg = &mmc_host[sdc_no].cfg;
 
 	memset(&mmc_host[sdc_no], 0, sizeof(struct sunxi_mmc_host));
+	mmc_host[sdc_no].cd_inverted = true;
 
 	cfg->name = "SUNXI SD/MMC";
 	cfg->ops  = &sunxi_mmc_ops;
 
 	cfg->voltages = MMC_VDD_32_33 | MMC_VDD_33_34;
 	cfg->host_caps = MMC_MODE_4BIT;
 #if defined(CONFIG_MACH_SUN50I) || defined(CONFIG_MACH_SUN8I)
 	if (sdc_no == 2)
 		cfg->host_caps = MMC_MODE_8BIT;
 #endif
 	cfg->host_caps |= MMC_MODE_HS_52MHz | MMC_MODE_HS;
 	cfg->b_max = CONFIG_SYS_MMC_MAX_BLK_COUNT;
 
 	cfg->f_min = 400000;
 	cfg->f_max = 52000000;
 
 	if (mmc_resource_init(sdc_no) != 0)
 		return NULL;
 
-	mmc_clk_io_on(sdc_no);
+	mmc_clk_io_on(&mmc_host[sdc_no]);
 
 	return mmc_create(cfg, &mmc_host[sdc_no]);
 }
+#endif
+
+#if defined(CONFIG_DM_MMC) && defined(CONFIG_DM_MMC_OPS)
+static const struct dm_mmc_ops sunxi_mmc_ops = {
+	.send_cmd	= sunxi_mmc_send_cmd,
+	.set_ios	= sunxi_mmc_set_ios,
+	.get_cd		= sunxi_mmc_getcd,
+};
+#else
+static const struct mmc_ops sunxi_mmc_ops = {
+	.send_cmd	= sunxi_mmc_send_cmd,
+	.set_ios	= sunxi_mmc_set_ios,
+	.init		= sunxi_mmc_core_init,
+	.getcd		= sunxi_mmc_getcd,
+};
+#endif
+
+#if defined(CONFIG_DM_MMC)
+static int sunxi_mmc_ofdata_to_platdata(struct udevice *dev)
+{
+	return 0;
+}
+
+static int sunxi_mmc_probe(struct udevice *dev)
+{
+	struct mmc_uclass_priv *upriv = dev_get_uclass_priv(dev);
+#if defined(CONFIG_BLK)
+	struct sunxi_mmc_plat *plat = dev_get_platdata(dev);
+#endif
+	struct sunxi_mmc_host *priv = dev_get_priv(dev);
+	struct mmc_config *cfg = &priv->cfg;
+	int bus_width;
+	u32 f_minmax[2];
+
+	priv->reg = (void *)dev_get_addr(dev);
+	cfg->name = "SUNXI SD/MMC";
+#if !(defined(CONFIG_DM_MMC) && defined(CONFIG_DM_MMC_OPS))
+	cfg->ops  = &sunxi_mmc_ops;
+#endif
+	cfg->voltages = MMC_VDD_32_33 | MMC_VDD_33_34;
+	cfg->b_max = CONFIG_SYS_MMC_MAX_BLK_COUNT;
+	cfg->host_caps = MMC_MODE_HS_52MHz | MMC_MODE_HS;
+
+	bus_width = fdtdec_get_int(gd->fdt_blob, dev->of_offset,
+				   "bus-width", 4);
+	if (bus_width == 8)
+		cfg->host_caps |= MMC_MODE_8BIT;
+	else if (bus_width == 4)
+		cfg->host_caps |= MMC_MODE_4BIT;
+
+	debug("%s: reg %p bus_width %d\n", dev->name, priv->reg, bus_width);
+
+	if (!fdtdec_get_int_array(gd->fdt_blob, dev->of_offset,
+				  "clock-freq-min-max", f_minmax, 2)) {
+		cfg->f_min = f_minmax[0];
+		cfg->f_max = f_minmax[1];
+	} else {
+		/* use the defaults */
+		cfg->f_min = 400000;
+		cfg->f_max = 52000000;
+	}
+
+	/* Some legacy functionality in our tree still depends on the
+	 * mmchost->mmc_no... until we can get rid of this, initialise
+	 * it based on the base address of the device.
+	 */
+	if (mmc_resource_init_from_udev(dev) != 0)
+		return -EINVAL;
+
+	/* All GPIOs are optional */
+	gpio_request_by_name(dev, "cd-gpios", 0,
+			     &priv->cd_gpio, GPIOD_IS_IN);
+	priv->cd_inverted = fdtdec_get_bool(gd->fdt_blob, dev->of_offset,
+					    "cd-inverted");
+	gpio_request_by_name(dev, "wp-gpios", 0,
+			     &priv->wp_gpio, GPIOD_IS_IN);
+	priv->wp_inverted = fdtdec_get_bool(gd->fdt_blob, dev->of_offset,
+					    "wp-inverted");
+	gpio_request_by_name(dev, "power-gpios", 0,
+			     &priv->pwr_gpio, GPIOD_IS_OUT);
+	if (dm_gpio_is_valid(&priv->pwr_gpio))
+		dm_gpio_set_value(&priv->pwr_gpio, 1);
+
+	if (reset_get_by_name(dev, "ahb", &priv->reset)) {
+		error("%s: failed to get 'ahb' reset\n", dev->name);
+		return -EINVAL;
+	}
+
+	if (clk_get_by_name(dev, "ahb", &priv->ahb_clk_gate) ||
+	    clk_get_by_name(dev, "mmc", &priv->mmc_clk)) {
+		error("%s: failed to get all required clocks ('ahb', 'mmc')\n",
+		      dev->name);
+		return -EINVAL;
+	}
+
+	mmc_clk_io_on(priv);
+
+#if defined(CONFIG_BLK)
+	priv->mmc = &plat->mmc;
+#else
+	priv->mmc = mmc_create(cfg, priv);
+	if (priv->mmc == NULL)
+		return -1;
+#endif
+	priv->mmc->priv = priv;
+	priv->mmc->dev = dev;
+	priv->mmc->cfg = cfg;
+	priv->mmc->has_init = 0;
+	upriv->mmc = priv->mmc;
+
+#if defined(CONFIG_DM_MMC) && defined(CONFIG_DM_MMC_OPS)
+	sunxi_mmc_core_init(priv->mmc);
+#endif
+	return 0;
+}
+
+#if defined(CONFIG_BLK)
+static int sunxi_mmc_bind(struct udevice *dev)
+{
+	struct sunxi_mmc_plat *plat = dev_get_platdata(dev);
+	struct sunxi_mmc_host *priv = dev_get_priv(dev);
+
+	debug("%s: %s\n", dev->name, __func__);
+
+	/* TODO: To move cfg into plat, we need to change the legacy
+	   code, which references through the arrays... */
+
+	return mmc_bind(dev, &plat->mmc, &priv->cfg);
+}
+#endif
+
+static const struct udevice_id sunxi_mmc_ids[] = {
+	{ .compatible = "allwinner,sun50i-a64-mmc" },
+	{ }
+};
+
+U_BOOT_DRIVER(sunxi_mmc_drv) = {
+	.name		= "sunxi_mmc",
+	.id		= UCLASS_MMC,
+	.of_match	= sunxi_mmc_ids,
+	.ofdata_to_platdata = sunxi_mmc_ofdata_to_platdata,
+	.probe		= sunxi_mmc_probe,
+	.priv_auto_alloc_size = sizeof(struct sunxi_mmc_host),
+	.platdata_auto_alloc_size = sizeof(struct sunxi_mmc_plat),
+#if defined(CONFIG_DM_MMC_OPS)
+	.ops		= &sunxi_mmc_ops,
+#endif
+#if defined(CONFIG_BLK)
+	.bind           = sunxi_mmc_bind,
+#endif
+};
+
+#endif
