diff mbox

[U-Boot,v4,27/33] rockchip: Add an MMC driver

Message ID 1440429171-2555-28-git-send-email-sjg@chromium.org
State Superseded
Delegated to: Tom Rini
Headers show

Commit Message

Simon Glass Aug. 24, 2015, 3:12 p.m. UTC
Add an MMC driver which supports RK3288, but may also support other SoCs.
It uses the Designware MMC device.

Signed-off-by: Simon Glass <sjg@chromium.org>
---

Changes in v4: None
Changes in v3: None
Changes in v2: None

 drivers/mmc/Kconfig        |  9 +++++
 drivers/mmc/Makefile       |  1 +
 drivers/mmc/rockchip_mmc.c | 98 ++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 108 insertions(+)
 create mode 100644 drivers/mmc/rockchip_mmc.c

Comments

Jaehoon Chung Aug. 25, 2015, 2:31 a.m. UTC | #1
Hi, Simon.


On 08/25/2015 12:12 AM, Simon Glass wrote:
> Add an MMC driver which supports RK3288, but may also support other SoCs.
> It uses the Designware MMC device.

It's based on designware controller. then how about using "_dw_mmc" as postfix?

> 
> Signed-off-by: Simon Glass <sjg@chromium.org>
> ---
> 
> Changes in v4: None
> Changes in v3: None
> Changes in v2: None
> 
>  drivers/mmc/Kconfig        |  9 +++++
>  drivers/mmc/Makefile       |  1 +
>  drivers/mmc/rockchip_mmc.c | 98 ++++++++++++++++++++++++++++++++++++++++++++++
>  3 files changed, 108 insertions(+)
>  create mode 100644 drivers/mmc/rockchip_mmc.c
> 
> diff --git a/drivers/mmc/Kconfig b/drivers/mmc/Kconfig
> index 3e835f7..cd5f53c 100644
> --- a/drivers/mmc/Kconfig
> +++ b/drivers/mmc/Kconfig
> @@ -10,6 +10,15 @@ config DM_MMC
>  	  appear as block devices in U-Boot and can support filesystems such
>  	  as EXT4 and FAT.
>  
> +config ROCKCHIP_MMC
> +	bool "Rockchip SD/MMC controller support"
> +	depends on DM && OF_CONTROL
> +	help
> +	  This enables support for the Rockchip SD/MMM controller, which is
> +	  based on Designware IP. The device is compatible with SD 3.0,
> +	  SDIO 3.0 and MMC 4.5 and supports common eMMC chips as well as
> +	  removeable SD and micro-SD cards.

I think it's not good that described spec version.
In future, Rockchip can support upper version than SD3.0, SDIO3.0 and MMC4.5.

> +
>  config SH_SDHI
>  	bool "SuperH/Renesas ARM SoCs on-chip SDHI host controller support"
>  	depends on RMOBILE
> diff --git a/drivers/mmc/Makefile b/drivers/mmc/Makefile
> index cae207c..7fd63de 100644
> --- a/drivers/mmc/Makefile
> +++ b/drivers/mmc/Makefile
> @@ -29,6 +29,7 @@ obj-$(CONFIG_MXS_MMC) += mxsmmc.o
>  obj-$(CONFIG_OMAP_HSMMC) += omap_hsmmc.o
>  obj-$(CONFIG_X86) += pci_mmc.o
>  obj-$(CONFIG_PXA_MMC_GENERIC) += pxa_mmc_gen.o
> +obj-$(CONFIG_ROCKCHIP_MMC) += rockchip_mmc.o
>  obj-$(CONFIG_SUPPORT_EMMC_RPMB) += rpmb.o
>  obj-$(CONFIG_S3C_SDI) += s3c_sdi.o
>  obj-$(CONFIG_S5P_SDHCI) += s5p_sdhci.o
> diff --git a/drivers/mmc/rockchip_mmc.c b/drivers/mmc/rockchip_mmc.c
> new file mode 100644
> index 0000000..430e7e5
> --- /dev/null
> +++ b/drivers/mmc/rockchip_mmc.c
> @@ -0,0 +1,98 @@
> +/*
> + * Copyright (c) 2013 Google, Inc
> + *
> + * SPDX-License-Identifier:	GPL-2.0+
> + */
> +
> +#include <common.h>
> +#include <clk.h>
> +#include <dm.h>
> +#include <dwmmc.h>
> +#include <errno.h>
> +#include <syscon.h>
> +#include <asm/arch/clock.h>
> +#include <asm/arch/periph.h>
> +#include <linux/err.h>
> +
> +DECLARE_GLOBAL_DATA_PTR;
> +
> +struct rockchip_mmc_priv {
> +	struct udevice *clk;
> +	struct rk3288_grf *grf;
> +	struct dwmci_host host;
> +};
> +
> +static uint rockchip_mmc_get_mmc_clk(struct dwmci_host *host, uint freq)

Well, how about using "dwmci" instead of "mmc". it's just my preference. :)
(Since it has used function name as "dwmci" in other dwmmc file.)

> +{
> +	struct udevice *dev = host->priv;
> +	struct rockchip_mmc_priv *priv = dev_get_priv(dev);
> +	int ret;
> +
> +	ret = clk_set_periph_rate(priv->clk, PERIPH_ID_SDMMC0 + host->dev_index,
> +				  freq);
> +	if (ret < 0) {
> +		debug("%s: err=%d\n", __func__, ret);
> +		return ret;
> +	}
> +
> +	return freq;
> +}
> +
> +static int rockchip_mmc_ofdata_to_platdata(struct udevice *dev)
> +{
> +	struct rockchip_mmc_priv *priv = dev_get_priv(dev);
> +	struct dwmci_host *host = &priv->host;
> +
> +	host->name = dev->name;
> +	host->ioaddr = (void *)dev_get_addr(dev);
> +	host->buswidth = fdtdec_get_int(gd->fdt_blob, dev->of_offset,
> +					"bus-width", 4);
> +	host->get_mmc_clk = rockchip_mmc_get_mmc_clk;
> +	host->priv = dev;
> +
> +	/* TODO(sjg@chromium.org): Remove the need for this hack */
> +	host->dev_index = (ulong)host->ioaddr == 0xff0f0000 ? 0 : 1;
> +
> +	return 0;
> +}
> +
> +int rockchip_mmc_probe(struct udevice *dev)
> +{
> +	struct mmc_uclass_priv *upriv = dev_get_uclass_priv(dev);
> +	struct rockchip_mmc_priv *priv = dev_get_priv(dev);
> +	struct dwmci_host *host = &priv->host;
> +	u32 minmax[2];
> +	int ret;
> +
> +	priv->grf = syscon_get_first_range(ROCKCHIP_SYSCON_GRF);
> +	if (IS_ERR(priv->grf))
> +		return PTR_ERR(priv->grf);
> +	ret = uclass_get_device(UCLASS_CLK, CLK_GENERAL, &priv->clk);
> +	if (ret)
> +		return ret;
> +
> +	ret = fdtdec_get_int_array(gd->fdt_blob, dev->of_offset,
> +				   "clock-freq-min-max", minmax, 2);
> +	if (!ret)
> +		ret = add_dwmci(host, minmax[1], minmax[0]);
> +	if (ret)
> +		return ret;
> +
> +	upriv->mmc = host->mmc;
> +
> +	return 0;
> +}
> +
> +static const struct udevice_id rockchip_mmc_ids[] = {
> +	{ .compatible = "rockchip,rk3288-dw-mshc" },
> +	{ }
> +};
> +
> +U_BOOT_DRIVER(rockchip_mmc_drv) = {
> +	.name		= "rockchip_mmc",
> +	.id		= UCLASS_MMC,
> +	.of_match	= rockchip_mmc_ids,
> +	.ofdata_to_platdata = rockchip_mmc_ofdata_to_platdata,
> +	.probe		= rockchip_mmc_probe,
> +	.priv_auto_alloc_size = sizeof(struct dwmci_host),
> +};
>
Andre Przywara Aug. 25, 2015, 6:26 p.m. UTC | #2
Hi Simon,

On 24/08/15 16:12, Simon Glass wrote:
> Add an MMC driver which supports RK3288, but may also support other SoCs.
> It uses the Designware MMC device.
> 
> Signed-off-by: Simon Glass <sjg@chromium.org>
> ---
> 
> Changes in v4: None
> Changes in v3: None
> Changes in v2: None
> 
>  drivers/mmc/Kconfig        |  9 +++++
>  drivers/mmc/Makefile       |  1 +
>  drivers/mmc/rockchip_mmc.c | 98 ++++++++++++++++++++++++++++++++++++++++++++++
>  3 files changed, 108 insertions(+)
>  create mode 100644 drivers/mmc/rockchip_mmc.c
> 
> diff --git a/drivers/mmc/Kconfig b/drivers/mmc/Kconfig
> index 3e835f7..cd5f53c 100644
> --- a/drivers/mmc/Kconfig
> +++ b/drivers/mmc/Kconfig
> @@ -10,6 +10,15 @@ config DM_MMC
>  	  appear as block devices in U-Boot and can support filesystems such
>  	  as EXT4 and FAT.
>  
> +config ROCKCHIP_MMC
> +	bool "Rockchip SD/MMC controller support"
> +	depends on DM && OF_CONTROL
> +	help
> +	  This enables support for the Rockchip SD/MMM controller, which is
> +	  based on Designware IP. The device is compatible with SD 3.0,
> +	  SDIO 3.0 and MMC 4.5 and supports common eMMC chips as well as
> +	  removeable SD and micro-SD cards.
> +
>  config SH_SDHI
>  	bool "SuperH/Renesas ARM SoCs on-chip SDHI host controller support"
>  	depends on RMOBILE
> diff --git a/drivers/mmc/Makefile b/drivers/mmc/Makefile
> index cae207c..7fd63de 100644
> --- a/drivers/mmc/Makefile
> +++ b/drivers/mmc/Makefile
> @@ -29,6 +29,7 @@ obj-$(CONFIG_MXS_MMC) += mxsmmc.o
>  obj-$(CONFIG_OMAP_HSMMC) += omap_hsmmc.o
>  obj-$(CONFIG_X86) += pci_mmc.o
>  obj-$(CONFIG_PXA_MMC_GENERIC) += pxa_mmc_gen.o
> +obj-$(CONFIG_ROCKCHIP_MMC) += rockchip_mmc.o
>  obj-$(CONFIG_SUPPORT_EMMC_RPMB) += rpmb.o
>  obj-$(CONFIG_S3C_SDI) += s3c_sdi.o
>  obj-$(CONFIG_S5P_SDHCI) += s5p_sdhci.o
> diff --git a/drivers/mmc/rockchip_mmc.c b/drivers/mmc/rockchip_mmc.c
> new file mode 100644
> index 0000000..430e7e5
> --- /dev/null
> +++ b/drivers/mmc/rockchip_mmc.c
> @@ -0,0 +1,98 @@
> +/*
> + * Copyright (c) 2013 Google, Inc
> + *
> + * SPDX-License-Identifier:	GPL-2.0+
> + */
> +
> +#include <common.h>
> +#include <clk.h>
> +#include <dm.h>
> +#include <dwmmc.h>
> +#include <errno.h>
> +#include <syscon.h>
> +#include <asm/arch/clock.h>
> +#include <asm/arch/periph.h>
> +#include <linux/err.h>
> +
> +DECLARE_GLOBAL_DATA_PTR;
> +
> +struct rockchip_mmc_priv {
> +	struct udevice *clk;
> +	struct rk3288_grf *grf;
> +	struct dwmci_host host;
> +};
> +
> +static uint rockchip_mmc_get_mmc_clk(struct dwmci_host *host, uint freq)
> +{
> +	struct udevice *dev = host->priv;
> +	struct rockchip_mmc_priv *priv = dev_get_priv(dev);
> +	int ret;
> +
> +	ret = clk_set_periph_rate(priv->clk, PERIPH_ID_SDMMC0 + host->dev_index,
> +				  freq);
> +	if (ret < 0) {
> +		debug("%s: err=%d\n", __func__, ret);
> +		return ret;
> +	}
> +
> +	return freq;
> +}
> +
> +static int rockchip_mmc_ofdata_to_platdata(struct udevice *dev)
> +{
> +	struct rockchip_mmc_priv *priv = dev_get_priv(dev);
> +	struct dwmci_host *host = &priv->host;
> +
> +	host->name = dev->name;
> +	host->ioaddr = (void *)dev_get_addr(dev);
> +	host->buswidth = fdtdec_get_int(gd->fdt_blob, dev->of_offset,
> +					"bus-width", 4);
> +	host->get_mmc_clk = rockchip_mmc_get_mmc_clk;
> +	host->priv = dev;
> +
> +	/* TODO(sjg@chromium.org): Remove the need for this hack */
> +	host->dev_index = (ulong)host->ioaddr == 0xff0f0000 ? 0 : 1;
> +
> +	return 0;
> +}
> +
> +int rockchip_mmc_probe(struct udevice *dev)
> +{
> +	struct mmc_uclass_priv *upriv = dev_get_uclass_priv(dev);
> +	struct rockchip_mmc_priv *priv = dev_get_priv(dev);
> +	struct dwmci_host *host = &priv->host;
> +	u32 minmax[2];
> +	int ret;
> +
> +	priv->grf = syscon_get_first_range(ROCKCHIP_SYSCON_GRF);
> +	if (IS_ERR(priv->grf))
> +		return PTR_ERR(priv->grf);
> +	ret = uclass_get_device(UCLASS_CLK, CLK_GENERAL, &priv->clk);
> +	if (ret)
> +		return ret;
> +
> +	ret = fdtdec_get_int_array(gd->fdt_blob, dev->of_offset,
> +				   "clock-freq-min-max", minmax, 2);
> +	if (!ret)
> +		ret = add_dwmci(host, minmax[1], minmax[0]);
> +	if (ret)
> +		return ret;
> +
> +	upriv->mmc = host->mmc;
> +
> +	return 0;
> +}
> +
> +static const struct udevice_id rockchip_mmc_ids[] = {
> +	{ .compatible = "rockchip,rk3288-dw-mshc" },
> +	{ }
> +};
> +
> +U_BOOT_DRIVER(rockchip_mmc_drv) = {
> +	.name		= "rockchip_mmc",
> +	.id		= UCLASS_MMC,
> +	.of_match	= rockchip_mmc_ids,
> +	.ofdata_to_platdata = rockchip_mmc_ofdata_to_platdata,
> +	.probe		= rockchip_mmc_probe,
> +	.priv_auto_alloc_size = sizeof(struct dwmci_host),

Shouldn't that be "sizeof(struct rockchip_mmc_priv)" here instead? (I
mentioned that already before, just not sure whether that mail got lost
or I was just talking nonsense back then).

Cheers,
Andre.
Simon Glass Aug. 30, 2015, 10:45 p.m. UTC | #3
Hi Jaehoon,

On 24 August 2015 at 20:31, Jaehoon Chung <jh80.chung@samsung.com> wrote:
> Hi, Simon.
>
>
> On 08/25/2015 12:12 AM, Simon Glass wrote:
>> Add an MMC driver which supports RK3288, but may also support other SoCs.
>> It uses the Designware MMC device.
>
> It's based on designware controller. then how about using "_dw_mmc" as postfix?

OK done

>
>>
>> Signed-off-by: Simon Glass <sjg@chromium.org>
>> ---
>>
>> Changes in v4: None
>> Changes in v3: None
>> Changes in v2: None
>>
>>  drivers/mmc/Kconfig        |  9 +++++
>>  drivers/mmc/Makefile       |  1 +
>>  drivers/mmc/rockchip_mmc.c | 98 ++++++++++++++++++++++++++++++++++++++++++++++
>>  3 files changed, 108 insertions(+)
>>  create mode 100644 drivers/mmc/rockchip_mmc.c
>>
>> diff --git a/drivers/mmc/Kconfig b/drivers/mmc/Kconfig
>> index 3e835f7..cd5f53c 100644
>> --- a/drivers/mmc/Kconfig
>> +++ b/drivers/mmc/Kconfig
>> @@ -10,6 +10,15 @@ config DM_MMC
>>         appear as block devices in U-Boot and can support filesystems such
>>         as EXT4 and FAT.
>>
>> +config ROCKCHIP_MMC
>> +     bool "Rockchip SD/MMC controller support"
>> +     depends on DM && OF_CONTROL
>> +     help
>> +       This enables support for the Rockchip SD/MMM controller, which is
>> +       based on Designware IP. The device is compatible with SD 3.0,
>> +       SDIO 3.0 and MMC 4.5 and supports common eMMC chips as well as
>> +       removeable SD and micro-SD cards.
>
> I think it's not good that described spec version.
> In future, Rockchip can support upper version than SD3.0, SDIO3.0 and MMC4.5.

Do you mean that Rockchip can support MMC 5, for example? I tweaked
the help a little based on your comments.

>
>> +
>>  config SH_SDHI
>>       bool "SuperH/Renesas ARM SoCs on-chip SDHI host controller support"
>>       depends on RMOBILE
>> diff --git a/drivers/mmc/Makefile b/drivers/mmc/Makefile
>> index cae207c..7fd63de 100644
>> --- a/drivers/mmc/Makefile
>> +++ b/drivers/mmc/Makefile
>> @@ -29,6 +29,7 @@ obj-$(CONFIG_MXS_MMC) += mxsmmc.o
>>  obj-$(CONFIG_OMAP_HSMMC) += omap_hsmmc.o
>>  obj-$(CONFIG_X86) += pci_mmc.o
>>  obj-$(CONFIG_PXA_MMC_GENERIC) += pxa_mmc_gen.o
>> +obj-$(CONFIG_ROCKCHIP_MMC) += rockchip_mmc.o
>>  obj-$(CONFIG_SUPPORT_EMMC_RPMB) += rpmb.o
>>  obj-$(CONFIG_S3C_SDI) += s3c_sdi.o
>>  obj-$(CONFIG_S5P_SDHCI) += s5p_sdhci.o
>> diff --git a/drivers/mmc/rockchip_mmc.c b/drivers/mmc/rockchip_mmc.c
>> new file mode 100644
>> index 0000000..430e7e5
>> --- /dev/null
>> +++ b/drivers/mmc/rockchip_mmc.c
>> @@ -0,0 +1,98 @@
>> +/*
>> + * Copyright (c) 2013 Google, Inc
>> + *
>> + * SPDX-License-Identifier:  GPL-2.0+
>> + */
>> +
>> +#include <common.h>
>> +#include <clk.h>
>> +#include <dm.h>
>> +#include <dwmmc.h>
>> +#include <errno.h>
>> +#include <syscon.h>
>> +#include <asm/arch/clock.h>
>> +#include <asm/arch/periph.h>
>> +#include <linux/err.h>
>> +
>> +DECLARE_GLOBAL_DATA_PTR;
>> +
>> +struct rockchip_mmc_priv {
>> +     struct udevice *clk;
>> +     struct rk3288_grf *grf;
>> +     struct dwmci_host host;
>> +};
>> +
>> +static uint rockchip_mmc_get_mmc_clk(struct dwmci_host *host, uint freq)
>
> Well, how about using "dwmci" instead of "mmc". it's just my preference. :)
> (Since it has used function name as "dwmci" in other dwmmc file.)

OK done

>
>> +{
>> +     struct udevice *dev = host->priv;
>> +     struct rockchip_mmc_priv *priv = dev_get_priv(dev);
>> +     int ret;
>> +
>> +     ret = clk_set_periph_rate(priv->clk, PERIPH_ID_SDMMC0 + host->dev_index,
>> +                               freq);
>> +     if (ret < 0) {
>> +             debug("%s: err=%d\n", __func__, ret);
>> +             return ret;
>> +     }
>> +
>> +     return freq;
>> +}
>> +
>> +static int rockchip_mmc_ofdata_to_platdata(struct udevice *dev)
>> +{
>> +     struct rockchip_mmc_priv *priv = dev_get_priv(dev);
>> +     struct dwmci_host *host = &priv->host;
>> +
>> +     host->name = dev->name;
>> +     host->ioaddr = (void *)dev_get_addr(dev);
>> +     host->buswidth = fdtdec_get_int(gd->fdt_blob, dev->of_offset,
>> +                                     "bus-width", 4);
>> +     host->get_mmc_clk = rockchip_mmc_get_mmc_clk;
>> +     host->priv = dev;
>> +
>> +     /* TODO(sjg@chromium.org): Remove the need for this hack */
>> +     host->dev_index = (ulong)host->ioaddr == 0xff0f0000 ? 0 : 1;
>> +
>> +     return 0;
>> +}
>> +
>> +int rockchip_mmc_probe(struct udevice *dev)
>> +{
>> +     struct mmc_uclass_priv *upriv = dev_get_uclass_priv(dev);
>> +     struct rockchip_mmc_priv *priv = dev_get_priv(dev);
>> +     struct dwmci_host *host = &priv->host;
>> +     u32 minmax[2];
>> +     int ret;
>> +
>> +     priv->grf = syscon_get_first_range(ROCKCHIP_SYSCON_GRF);
>> +     if (IS_ERR(priv->grf))
>> +             return PTR_ERR(priv->grf);
>> +     ret = uclass_get_device(UCLASS_CLK, CLK_GENERAL, &priv->clk);
>> +     if (ret)
>> +             return ret;
>> +
>> +     ret = fdtdec_get_int_array(gd->fdt_blob, dev->of_offset,
>> +                                "clock-freq-min-max", minmax, 2);
>> +     if (!ret)
>> +             ret = add_dwmci(host, minmax[1], minmax[0]);
>> +     if (ret)
>> +             return ret;
>> +
>> +     upriv->mmc = host->mmc;
>> +
>> +     return 0;
>> +}
>> +
>> +static const struct udevice_id rockchip_mmc_ids[] = {
>> +     { .compatible = "rockchip,rk3288-dw-mshc" },
>> +     { }
>> +};
>> +
>> +U_BOOT_DRIVER(rockchip_mmc_drv) = {
>> +     .name           = "rockchip_mmc",
>> +     .id             = UCLASS_MMC,
>> +     .of_match       = rockchip_mmc_ids,
>> +     .ofdata_to_platdata = rockchip_mmc_ofdata_to_platdata,
>> +     .probe          = rockchip_mmc_probe,
>> +     .priv_auto_alloc_size = sizeof(struct dwmci_host),
>> +};
>>
>

Regards,
Simon
Simon Glass Aug. 30, 2015, 10:45 p.m. UTC | #4
Hi Andre,

On 25 August 2015 at 12:26, Andre Przywara <andre.przywara@arm.com> wrote:
> Hi Simon,
>
> On 24/08/15 16:12, Simon Glass wrote:
>> Add an MMC driver which supports RK3288, but may also support other SoCs.
>> It uses the Designware MMC device.
>>
>> Signed-off-by: Simon Glass <sjg@chromium.org>
>> ---
>>
>> Changes in v4: None
>> Changes in v3: None
>> Changes in v2: None
>>
>>  drivers/mmc/Kconfig        |  9 +++++
>>  drivers/mmc/Makefile       |  1 +
>>  drivers/mmc/rockchip_mmc.c | 98 ++++++++++++++++++++++++++++++++++++++++++++++
>>  3 files changed, 108 insertions(+)
>>  create mode 100644 drivers/mmc/rockchip_mmc.c

[snip]

>> +U_BOOT_DRIVER(rockchip_mmc_drv) = {
>> +     .name           = "rockchip_mmc",
>> +     .id             = UCLASS_MMC,
>> +     .of_match       = rockchip_mmc_ids,
>> +     .ofdata_to_platdata = rockchip_mmc_ofdata_to_platdata,
>> +     .probe          = rockchip_mmc_probe,
>> +     .priv_auto_alloc_size = sizeof(struct dwmci_host),
>
> Shouldn't that be "sizeof(struct rockchip_mmc_priv)" here instead? (I
> mentioned that already before, just not sure whether that mail got lost
> or I was just talking nonsense back then).

Yes it should, thank you for catching that!

Regards,
Simon
diff mbox

Patch

diff --git a/drivers/mmc/Kconfig b/drivers/mmc/Kconfig
index 3e835f7..cd5f53c 100644
--- a/drivers/mmc/Kconfig
+++ b/drivers/mmc/Kconfig
@@ -10,6 +10,15 @@  config DM_MMC
 	  appear as block devices in U-Boot and can support filesystems such
 	  as EXT4 and FAT.
 
+config ROCKCHIP_MMC
+	bool "Rockchip SD/MMC controller support"
+	depends on DM && OF_CONTROL
+	help
+	  This enables support for the Rockchip SD/MMM controller, which is
+	  based on Designware IP. The device is compatible with SD 3.0,
+	  SDIO 3.0 and MMC 4.5 and supports common eMMC chips as well as
+	  removeable SD and micro-SD cards.
+
 config SH_SDHI
 	bool "SuperH/Renesas ARM SoCs on-chip SDHI host controller support"
 	depends on RMOBILE
diff --git a/drivers/mmc/Makefile b/drivers/mmc/Makefile
index cae207c..7fd63de 100644
--- a/drivers/mmc/Makefile
+++ b/drivers/mmc/Makefile
@@ -29,6 +29,7 @@  obj-$(CONFIG_MXS_MMC) += mxsmmc.o
 obj-$(CONFIG_OMAP_HSMMC) += omap_hsmmc.o
 obj-$(CONFIG_X86) += pci_mmc.o
 obj-$(CONFIG_PXA_MMC_GENERIC) += pxa_mmc_gen.o
+obj-$(CONFIG_ROCKCHIP_MMC) += rockchip_mmc.o
 obj-$(CONFIG_SUPPORT_EMMC_RPMB) += rpmb.o
 obj-$(CONFIG_S3C_SDI) += s3c_sdi.o
 obj-$(CONFIG_S5P_SDHCI) += s5p_sdhci.o
diff --git a/drivers/mmc/rockchip_mmc.c b/drivers/mmc/rockchip_mmc.c
new file mode 100644
index 0000000..430e7e5
--- /dev/null
+++ b/drivers/mmc/rockchip_mmc.c
@@ -0,0 +1,98 @@ 
+/*
+ * Copyright (c) 2013 Google, Inc
+ *
+ * SPDX-License-Identifier:	GPL-2.0+
+ */
+
+#include <common.h>
+#include <clk.h>
+#include <dm.h>
+#include <dwmmc.h>
+#include <errno.h>
+#include <syscon.h>
+#include <asm/arch/clock.h>
+#include <asm/arch/periph.h>
+#include <linux/err.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+struct rockchip_mmc_priv {
+	struct udevice *clk;
+	struct rk3288_grf *grf;
+	struct dwmci_host host;
+};
+
+static uint rockchip_mmc_get_mmc_clk(struct dwmci_host *host, uint freq)
+{
+	struct udevice *dev = host->priv;
+	struct rockchip_mmc_priv *priv = dev_get_priv(dev);
+	int ret;
+
+	ret = clk_set_periph_rate(priv->clk, PERIPH_ID_SDMMC0 + host->dev_index,
+				  freq);
+	if (ret < 0) {
+		debug("%s: err=%d\n", __func__, ret);
+		return ret;
+	}
+
+	return freq;
+}
+
+static int rockchip_mmc_ofdata_to_platdata(struct udevice *dev)
+{
+	struct rockchip_mmc_priv *priv = dev_get_priv(dev);
+	struct dwmci_host *host = &priv->host;
+
+	host->name = dev->name;
+	host->ioaddr = (void *)dev_get_addr(dev);
+	host->buswidth = fdtdec_get_int(gd->fdt_blob, dev->of_offset,
+					"bus-width", 4);
+	host->get_mmc_clk = rockchip_mmc_get_mmc_clk;
+	host->priv = dev;
+
+	/* TODO(sjg@chromium.org): Remove the need for this hack */
+	host->dev_index = (ulong)host->ioaddr == 0xff0f0000 ? 0 : 1;
+
+	return 0;
+}
+
+int rockchip_mmc_probe(struct udevice *dev)
+{
+	struct mmc_uclass_priv *upriv = dev_get_uclass_priv(dev);
+	struct rockchip_mmc_priv *priv = dev_get_priv(dev);
+	struct dwmci_host *host = &priv->host;
+	u32 minmax[2];
+	int ret;
+
+	priv->grf = syscon_get_first_range(ROCKCHIP_SYSCON_GRF);
+	if (IS_ERR(priv->grf))
+		return PTR_ERR(priv->grf);
+	ret = uclass_get_device(UCLASS_CLK, CLK_GENERAL, &priv->clk);
+	if (ret)
+		return ret;
+
+	ret = fdtdec_get_int_array(gd->fdt_blob, dev->of_offset,
+				   "clock-freq-min-max", minmax, 2);
+	if (!ret)
+		ret = add_dwmci(host, minmax[1], minmax[0]);
+	if (ret)
+		return ret;
+
+	upriv->mmc = host->mmc;
+
+	return 0;
+}
+
+static const struct udevice_id rockchip_mmc_ids[] = {
+	{ .compatible = "rockchip,rk3288-dw-mshc" },
+	{ }
+};
+
+U_BOOT_DRIVER(rockchip_mmc_drv) = {
+	.name		= "rockchip_mmc",
+	.id		= UCLASS_MMC,
+	.of_match	= rockchip_mmc_ids,
+	.ofdata_to_platdata = rockchip_mmc_ofdata_to_platdata,
+	.probe		= rockchip_mmc_probe,
+	.priv_auto_alloc_size = sizeof(struct dwmci_host),
+};