diff mbox

[U-Boot,4/9] drivers:mmc:sdhci: enable support for DT

Message ID 1390832143-372-5-git-send-email-p.wilczek@samsung.com
State Changes Requested
Delegated to: Minkyu Kang
Headers show

Commit Message

Piotr Wilczek Jan. 27, 2014, 2:15 p.m. UTC
This patch enables support for device tree for sdhci driver.
Non DT case is still supported.

Signed-off-by: Piotr Wilczek <p.wilczek@samsung.com>
Signed-off-by: Kyungmin Park <kyungmin.park@samsung.com>
Cc: Minkyu Kang <mk7.kang@samsung.com>
---
 arch/arm/include/asm/arch-exynos/mmc.h |    7 ++
 drivers/mmc/s5p_sdhci.c                |  130 +++++++++++++++++++++++++++++++-
 include/fdtdec.h                       |    1 +
 include/sdhci.h                        |    5 ++
 lib/fdtdec.c                           |    1 +
 5 files changed, 143 insertions(+), 1 deletion(-)

Comments

Jaehoon Chung Jan. 28, 2014, 9:42 a.m. UTC | #1
Hi, Piotr.

On 01/27/2014 11:15 PM, Piotr Wilczek wrote:
> This patch enables support for device tree for sdhci driver.
> Non DT case is still supported.
> 
> Signed-off-by: Piotr Wilczek <p.wilczek@samsung.com>
> Signed-off-by: Kyungmin Park <kyungmin.park@samsung.com>
> Cc: Minkyu Kang <mk7.kang@samsung.com>
> ---
>  arch/arm/include/asm/arch-exynos/mmc.h |    7 ++
>  drivers/mmc/s5p_sdhci.c                |  130 +++++++++++++++++++++++++++++++-
>  include/fdtdec.h                       |    1 +
>  include/sdhci.h                        |    5 ++
>  lib/fdtdec.c                           |    1 +
>  5 files changed, 143 insertions(+), 1 deletion(-)
> 
> diff --git a/arch/arm/include/asm/arch-exynos/mmc.h b/arch/arm/include/asm/arch-exynos/mmc.h
> index 98d6530..0fb6461 100644
> --- a/arch/arm/include/asm/arch-exynos/mmc.h
> +++ b/arch/arm/include/asm/arch-exynos/mmc.h
> @@ -53,6 +53,8 @@
>  #define SDHCI_CTRL4_DRIVE_MASK(_x)	((_x) << 16)
>  #define SDHCI_CTRL4_DRIVE_SHIFT		(16)
>  
> +#define SDHCI_MAX_HOSTS 4
> +
>  int s5p_sdhci_init(u32 regbase, int index, int bus_width);
>  
>  static inline int s5p_mmc_init(int index, int bus_width)
> @@ -62,4 +64,9 @@ static inline int s5p_mmc_init(int index, int bus_width)
>  
>  	return s5p_sdhci_init(base, index, bus_width);
>  }
> +
> +#ifdef CONFIG_OF_CONTROL
> +int exynos_mmc_init(const void *blob);
> +#endif
> +
>  #endif
> diff --git a/drivers/mmc/s5p_sdhci.c b/drivers/mmc/s5p_sdhci.c
> index 40ff873..7456ee0 100644
> --- a/drivers/mmc/s5p_sdhci.c
> +++ b/drivers/mmc/s5p_sdhci.c
> @@ -8,9 +8,15 @@
>  #include <common.h>
>  #include <malloc.h>
>  #include <sdhci.h>
> +#include <fdtdec.h>
> +#include <libfdt.h>
> +#include <asm/gpio.h>
>  #include <asm/arch/mmc.h>
>  #include <asm/arch/clk.h>
> -
> +#include <errno.h>
> +#ifdef CONFIG_OF_CONTROL
> +#include <asm/arch/pinmux.h>
> +#endif
>  static char *S5P_NAME = "SAMSUNG SDHCI";
>  static void s5p_sdhci_set_control_reg(struct sdhci_host *host)
>  {
> @@ -86,3 +92,125 @@ int s5p_sdhci_init(u32 regbase, int index, int bus_width)
>  
>  	return add_sdhci(host, 52000000, 400000);
>  }
> +
> +#ifdef CONFIG_OF_CONTROL
> +struct sdhci_host sdhci_host[SDHCI_MAX_HOSTS];
> +
> +static int do_sdhci_init(struct sdhci_host *host)
> +{
> +	int dev_id, flag;
> +	int err = 0;
> +
> +	flag = host->bus_width == 8 ? PINMUX_FLAG_8BIT_MODE : PINMUX_FLAG_NONE;
> +	dev_id = host->index + PERIPH_ID_SDMMC0;
> +
> +	if (fdt_gpio_isvalid(&host->pwr_gpio)) {
> +		gpio_direction_output(host->pwr_gpio.gpio, 1);

what's pwr_gpio? Is it used for the both(eMMC and SD card)?

> +		err = exynos_pinmux_config(dev_id, flag);
> +		if (err) {
> +			debug("MMC not configured\n");
> +			return err;
> +		}
> +	}
> +
> +	if (fdt_gpio_isvalid(&host->cd_gpio)) {
> +		gpio_direction_output(host->cd_gpio.gpio, 0xf);
> +		if (gpio_get_value(host->cd_gpio.gpio))
> +			return -ENODEV;
> +
> +		err = exynos_pinmux_config(dev_id, flag);
> +		if (err) {
> +			printf("external SD not configured\n");
> +			return err;
> +		}
> +	}
> +
> +	host->name = S5P_NAME;
> +
> +	host->quirks = SDHCI_QUIRK_NO_HISPD_BIT | SDHCI_QUIRK_BROKEN_VOLTAGE |
> +		SDHCI_QUIRK_BROKEN_R1B | SDHCI_QUIRK_32BIT_DMA_ADDR |
> +		SDHCI_QUIRK_WAIT_SEND_CMD;
> +	host->voltages = MMC_VDD_32_33 | MMC_VDD_33_34 | MMC_VDD_165_195;
> +	host->version = sdhci_readw(host, SDHCI_HOST_VERSION);
> +
> +	host->set_control_reg = &s5p_sdhci_set_control_reg;
> +	host->set_clock = set_mmc_clk;
> +
> +	host->host_caps = MMC_MODE_HC;
> +
> +	return add_sdhci(host, 52000000, 400000);
> +}
> +
> +static int sdhci_get_config(const void *blob, int node, struct sdhci_host *host)
> +{
> +	int bus_width, dev_id;
> +	unsigned int base;
> +
> +	/* Get device id */
> +	dev_id = pinmux_decode_periph_id(blob, node);
> +	if (dev_id < PERIPH_ID_SDMMC0) {

Didn't need to check other boundary?

> +		debug("MMC: Can't get device id\n");
> +		return -1;
> +	}
> +	host->index = dev_id - PERIPH_ID_SDMMC0;
> +
> +	/* Get bus width */
> +	bus_width = fdtdec_get_int(blob, node, "samsung,bus-width", 0);
> +	if (bus_width <= 0) {
> +		debug("MMC: Can't get bus-width\n");
> +		return -1;
> +	}
> +	host->bus_width = bus_width;
> +
> +	/* Get the base address from the device node */
> +	base = fdtdec_get_addr(blob, node, "reg");
> +	if (!base) {
> +		debug("DWMMC: Can't get base address\n");

DWMMC?

> +		return -1;
> +	}
> +	host->ioaddr = (void *)base;
> +
> +	fdtdec_decode_gpio(blob, node, "pwr-gpios", &host->pwr_gpio);
> +	fdtdec_decode_gpio(blob, node, "cd-gpios", &host->cd_gpio);
> +
> +	return 0;
> +}
> +
> +static int process_nodes(const void *blob, int node_list[], int count)
> +{
> +	struct sdhci_host *host;
> +	int i, node;
> +
> +	debug("%s: count = %d\n", __func__, count);
> +
> +	/* build sdhci_host[] for each controller */
> +	for (i = 0; i < count; i++) {
> +		node = node_list[i];
> +		if (node <= 0)
> +			continue;
> +
> +		host = &sdhci_host[i];
> +
> +		if (sdhci_get_config(blob, node, host)) {
> +			printf("%s: failed to decode dev %d\n",	__func__, i);
> +			return -1;
> +		}
> +		do_sdhci_init(host);
> +	}
> +	return 0;
> +}
> +
> +int exynos_mmc_init(const void *blob)
> +{
> +	int count;
> +	int node_list[SDHCI_MAX_HOSTS];
> +
> +	count = fdtdec_find_aliases_for_id(blob, "mmc",
> +			COMPAT_SAMSUNG_EXYNOS_MMC, node_list,
> +			SDHCI_MAX_HOSTS);
> +
> +	process_nodes(blob, node_list, count);
> +
> +	return 1;
> +}
> +#endif
> diff --git a/include/fdtdec.h b/include/fdtdec.h
> index f12b4aa..d637f88 100644
> --- a/include/fdtdec.h
> +++ b/include/fdtdec.h
> @@ -81,6 +81,7 @@ enum fdt_compat_id {
>  	COMPAT_SAMSUNG_EXYNOS_MIPI_DSI,	/* Exynos mipi dsi */
>  	COMPAT_SAMSUNG_EXYNOS5_DP,	/* Exynos Display port controller */
>  	COMPAT_SAMSUNG_EXYNOS5_DWMMC,	/* Exynos5 DWMMC controller */
> +	COMPAT_SAMSUNG_EXYNOS_MMC,	/* Exynos MMC controller */
>  	COMPAT_SAMSUNG_EXYNOS_SERIAL,	/* Exynos UART */
>  	COMPAT_MAXIM_MAX77686_PMIC,	/* MAX77686 PMIC */
>  	COMPAT_GENERIC_SPI_FLASH,	/* Generic SPI Flash chip */
> diff --git a/include/sdhci.h b/include/sdhci.h
> index 74d06ae..0cba703 100644
> --- a/include/sdhci.h
> +++ b/include/sdhci.h
> @@ -12,6 +12,7 @@
>  
>  #include <asm/io.h>
>  #include <mmc.h>
> +#include <fdtdec.h>
>  
>  /*
>   * Controller registers
> @@ -244,6 +245,10 @@ struct sdhci_host {
>  	const struct sdhci_ops *ops;
>  	int index;
>  
> +	int bus_width;
> +	struct fdt_gpio_state pwr_gpio;	/* Change Detect GPIO */

pwr_gpio is Change Detect GPIO? I think that Comment is wrong.

> +	struct fdt_gpio_state cd_gpio;		/* Change Detect GPIO */

Maybe Card detect GPIO is right.
> +
>  	void (*set_control_reg)(struct sdhci_host *host);
>  	void (*set_clock)(int dev_index, unsigned int div);
>  	uint	voltages;
> diff --git a/lib/fdtdec.c b/lib/fdtdec.c
> index 46e67ff..a88f648 100644
> --- a/lib/fdtdec.c
> +++ b/lib/fdtdec.c
> @@ -54,6 +54,7 @@ static const char * const compat_names[COMPAT_COUNT] = {
>  	COMPAT(SAMSUNG_EXYNOS_MIPI_DSI, "samsung,exynos-mipi-dsi"),
>  	COMPAT(SAMSUNG_EXYNOS5_DP, "samsung,exynos5-dp"),
>  	COMPAT(SAMSUNG_EXYNOS5_DWMMC, "samsung,exynos5250-dwmmc"),
> +	COMPAT(SAMSUNG_EXYNOS_MMC, "samsung,exynos-mmc"),

how about using "exynos4-mmc" instead of "exynos-mmc"?

Best Regards,
Jaehoon Chung

>  	COMPAT(SAMSUNG_EXYNOS_SERIAL, "samsung,exynos4210-uart"),
>  	COMPAT(MAXIM_MAX77686_PMIC, "maxim,max77686_pmic"),
>  	COMPAT(GENERIC_SPI_FLASH, "spi-flash"),
>
Piotr Wilczek Jan. 28, 2014, 12:31 p.m. UTC | #2
Hi Jaehoon,

> Hi, Piotr.
> 
> On 01/27/2014 11:15 PM, Piotr Wilczek wrote:
> > This patch enables support for device tree for sdhci driver.
> > Non DT case is still supported.
> >
> > Signed-off-by: Piotr Wilczek <p.wilczek@samsung.com>
> > Signed-off-by: Kyungmin Park <kyungmin.park@samsung.com>
> > Cc: Minkyu Kang <mk7.kang@samsung.com>
> > ---
> >  arch/arm/include/asm/arch-exynos/mmc.h |    7 ++
> >  drivers/mmc/s5p_sdhci.c                |  130
> +++++++++++++++++++++++++++++++-
> >  include/fdtdec.h                       |    1 +
> >  include/sdhci.h                        |    5 ++
> >  lib/fdtdec.c                           |    1 +
> >  5 files changed, 143 insertions(+), 1 deletion(-)
> >
> > diff --git a/arch/arm/include/asm/arch-exynos/mmc.h
> > b/arch/arm/include/asm/arch-exynos/mmc.h
> > index 98d6530..0fb6461 100644
> > --- a/arch/arm/include/asm/arch-exynos/mmc.h
> > +++ b/arch/arm/include/asm/arch-exynos/mmc.h
> > @@ -53,6 +53,8 @@
> >  #define SDHCI_CTRL4_DRIVE_MASK(_x)	((_x) << 16)
> >  #define SDHCI_CTRL4_DRIVE_SHIFT		(16)
> >
> > +#define SDHCI_MAX_HOSTS 4
> > +
> >  int s5p_sdhci_init(u32 regbase, int index, int bus_width);
> >
> >  static inline int s5p_mmc_init(int index, int bus_width) @@ -62,4
> > +64,9 @@ static inline int s5p_mmc_init(int index, int bus_width)
> >
> >  	return s5p_sdhci_init(base, index, bus_width);  }
> > +
> > +#ifdef CONFIG_OF_CONTROL
> > +int exynos_mmc_init(const void *blob); #endif
> > +
> >  #endif
> > diff --git a/drivers/mmc/s5p_sdhci.c b/drivers/mmc/s5p_sdhci.c index
> > 40ff873..7456ee0 100644
> > --- a/drivers/mmc/s5p_sdhci.c
> > +++ b/drivers/mmc/s5p_sdhci.c
> > @@ -8,9 +8,15 @@
> >  #include <common.h>
> >  #include <malloc.h>
> >  #include <sdhci.h>
> > +#include <fdtdec.h>
> > +#include <libfdt.h>
> > +#include <asm/gpio.h>
> >  #include <asm/arch/mmc.h>
> >  #include <asm/arch/clk.h>
> > -
> > +#include <errno.h>
> > +#ifdef CONFIG_OF_CONTROL
> > +#include <asm/arch/pinmux.h>
> > +#endif
> >  static char *S5P_NAME = "SAMSUNG SDHCI";  static void
> > s5p_sdhci_set_control_reg(struct sdhci_host *host)  { @@ -86,3
> +92,125
> > @@ int s5p_sdhci_init(u32 regbase, int index, int bus_width)
> >
> >  	return add_sdhci(host, 52000000, 400000);  }
> > +
> > +#ifdef CONFIG_OF_CONTROL
> > +struct sdhci_host sdhci_host[SDHCI_MAX_HOSTS];
> > +
> > +static int do_sdhci_init(struct sdhci_host *host) {
> > +	int dev_id, flag;
> > +	int err = 0;
> > +
> > +	flag = host->bus_width == 8 ? PINMUX_FLAG_8BIT_MODE :
> PINMUX_FLAG_NONE;
> > +	dev_id = host->index + PERIPH_ID_SDMMC0;
> > +
> > +	if (fdt_gpio_isvalid(&host->pwr_gpio)) {
> > +		gpio_direction_output(host->pwr_gpio.gpio, 1);
> 
> what's pwr_gpio? Is it used for the both(eMMC and SD card)?
pwr_gpio is used only for eMMC.
cd_gpio is used for SD Card.

> 
> > +		err = exynos_pinmux_config(dev_id, flag);
> > +		if (err) {
> > +			debug("MMC not configured\n");
> > +			return err;
> > +		}
> > +	}
> > +
> > +	if (fdt_gpio_isvalid(&host->cd_gpio)) {
> > +		gpio_direction_output(host->cd_gpio.gpio, 0xf);
> > +		if (gpio_get_value(host->cd_gpio.gpio))
> > +			return -ENODEV;
> > +
> > +		err = exynos_pinmux_config(dev_id, flag);
> > +		if (err) {
> > +			printf("external SD not configured\n");
> > +			return err;
> > +		}
> > +	}
> > +
> > +	host->name = S5P_NAME;
> > +
> > +	host->quirks = SDHCI_QUIRK_NO_HISPD_BIT |
> SDHCI_QUIRK_BROKEN_VOLTAGE |
> > +		SDHCI_QUIRK_BROKEN_R1B | SDHCI_QUIRK_32BIT_DMA_ADDR |
> > +		SDHCI_QUIRK_WAIT_SEND_CMD;
> > +	host->voltages = MMC_VDD_32_33 | MMC_VDD_33_34 | MMC_VDD_165_195;
> > +	host->version = sdhci_readw(host, SDHCI_HOST_VERSION);
> > +
> > +	host->set_control_reg = &s5p_sdhci_set_control_reg;
> > +	host->set_clock = set_mmc_clk;
> > +
> > +	host->host_caps = MMC_MODE_HC;
> > +
> > +	return add_sdhci(host, 52000000, 400000); }
> > +
> > +static int sdhci_get_config(const void *blob, int node, struct
> > +sdhci_host *host) {
> > +	int bus_width, dev_id;
> > +	unsigned int base;
> > +
> > +	/* Get device id */
> > +	dev_id = pinmux_decode_periph_id(blob, node);
> > +	if (dev_id < PERIPH_ID_SDMMC0) {
> 
> Didn't need to check other boundary?
Yes, I should check for the PERIPH_ID_SDMMC3

> 
> > +		debug("MMC: Can't get device id\n");
> > +		return -1;
> > +	}
> > +	host->index = dev_id - PERIPH_ID_SDMMC0;
> > +
> > +	/* Get bus width */
> > +	bus_width = fdtdec_get_int(blob, node, "samsung,bus-width", 0);
> > +	if (bus_width <= 0) {
> > +		debug("MMC: Can't get bus-width\n");
> > +		return -1;
> > +	}
> > +	host->bus_width = bus_width;
> > +
> > +	/* Get the base address from the device node */
> > +	base = fdtdec_get_addr(blob, node, "reg");
> > +	if (!base) {
> > +		debug("DWMMC: Can't get base address\n");
> 
> DWMMC?
MMC

> 
> > +		return -1;
> > +	}
> > +	host->ioaddr = (void *)base;
> > +
> > +	fdtdec_decode_gpio(blob, node, "pwr-gpios", &host->pwr_gpio);
> > +	fdtdec_decode_gpio(blob, node, "cd-gpios", &host->cd_gpio);
> > +
> > +	return 0;
> > +}
> > +
> > +static int process_nodes(const void *blob, int node_list[], int
> > +count) {
> > +	struct sdhci_host *host;
> > +	int i, node;
> > +
> > +	debug("%s: count = %d\n", __func__, count);
> > +
> > +	/* build sdhci_host[] for each controller */
> > +	for (i = 0; i < count; i++) {
> > +		node = node_list[i];
> > +		if (node <= 0)
> > +			continue;
> > +
> > +		host = &sdhci_host[i];
> > +
> > +		if (sdhci_get_config(blob, node, host)) {
> > +			printf("%s: failed to decode dev %d\n",	__func__,
> i);
> > +			return -1;
> > +		}
> > +		do_sdhci_init(host);
> > +	}
> > +	return 0;
> > +}
> > +
> > +int exynos_mmc_init(const void *blob) {
> > +	int count;
> > +	int node_list[SDHCI_MAX_HOSTS];
> > +
> > +	count = fdtdec_find_aliases_for_id(blob, "mmc",
> > +			COMPAT_SAMSUNG_EXYNOS_MMC, node_list,
> > +			SDHCI_MAX_HOSTS);
> > +
> > +	process_nodes(blob, node_list, count);
> > +
> > +	return 1;
> > +}
> > +#endif
> > diff --git a/include/fdtdec.h b/include/fdtdec.h index
> > f12b4aa..d637f88 100644
> > --- a/include/fdtdec.h
> > +++ b/include/fdtdec.h
> > @@ -81,6 +81,7 @@ enum fdt_compat_id {
> >  	COMPAT_SAMSUNG_EXYNOS_MIPI_DSI,	/* Exynos mipi dsi */
> >  	COMPAT_SAMSUNG_EXYNOS5_DP,	/* Exynos Display port controller */
> >  	COMPAT_SAMSUNG_EXYNOS5_DWMMC,	/* Exynos5 DWMMC controller */
> > +	COMPAT_SAMSUNG_EXYNOS_MMC,	/* Exynos MMC controller */
> >  	COMPAT_SAMSUNG_EXYNOS_SERIAL,	/* Exynos UART */
> >  	COMPAT_MAXIM_MAX77686_PMIC,	/* MAX77686 PMIC */
> >  	COMPAT_GENERIC_SPI_FLASH,	/* Generic SPI Flash chip */
> > diff --git a/include/sdhci.h b/include/sdhci.h index 74d06ae..0cba703
> > 100644
> > --- a/include/sdhci.h
> > +++ b/include/sdhci.h
> > @@ -12,6 +12,7 @@
> >
> >  #include <asm/io.h>
> >  #include <mmc.h>
> > +#include <fdtdec.h>
> >
> >  /*
> >   * Controller registers
> > @@ -244,6 +245,10 @@ struct sdhci_host {
> >  	const struct sdhci_ops *ops;
> >  	int index;
> >
> > +	int bus_width;
> > +	struct fdt_gpio_state pwr_gpio;	/* Change Detect GPIO */
> 
> pwr_gpio is Change Detect GPIO? I think that Comment is wrong.
Yes, the comment is wrong.

> 
> > +	struct fdt_gpio_state cd_gpio;		/* Change Detect GPIO */
> 
> Maybe Card detect GPIO is right.
Yes

> > +
> >  	void (*set_control_reg)(struct sdhci_host *host);
> >  	void (*set_clock)(int dev_index, unsigned int div);
> >  	uint	voltages;
> > diff --git a/lib/fdtdec.c b/lib/fdtdec.c index 46e67ff..a88f648
> 100644
> > --- a/lib/fdtdec.c
> > +++ b/lib/fdtdec.c
> > @@ -54,6 +54,7 @@ static const char * const
> compat_names[COMPAT_COUNT] = {
> >  	COMPAT(SAMSUNG_EXYNOS_MIPI_DSI, "samsung,exynos-mipi-dsi"),
> >  	COMPAT(SAMSUNG_EXYNOS5_DP, "samsung,exynos5-dp"),
> >  	COMPAT(SAMSUNG_EXYNOS5_DWMMC, "samsung,exynos5250-dwmmc"),
> > +	COMPAT(SAMSUNG_EXYNOS_MMC, "samsung,exynos-mmc"),
> 
> how about using "exynos4-mmc" instead of "exynos-mmc"?
Is this driver only for Exynos4?

> 
> Best Regards,
> Jaehoon Chung
> 
> >  	COMPAT(SAMSUNG_EXYNOS_SERIAL, "samsung,exynos4210-uart"),
> >  	COMPAT(MAXIM_MAX77686_PMIC, "maxim,max77686_pmic"),
> >  	COMPAT(GENERIC_SPI_FLASH, "spi-flash"),
> >
Best regards,
Piotr Wilczek
diff mbox

Patch

diff --git a/arch/arm/include/asm/arch-exynos/mmc.h b/arch/arm/include/asm/arch-exynos/mmc.h
index 98d6530..0fb6461 100644
--- a/arch/arm/include/asm/arch-exynos/mmc.h
+++ b/arch/arm/include/asm/arch-exynos/mmc.h
@@ -53,6 +53,8 @@ 
 #define SDHCI_CTRL4_DRIVE_MASK(_x)	((_x) << 16)
 #define SDHCI_CTRL4_DRIVE_SHIFT		(16)
 
+#define SDHCI_MAX_HOSTS 4
+
 int s5p_sdhci_init(u32 regbase, int index, int bus_width);
 
 static inline int s5p_mmc_init(int index, int bus_width)
@@ -62,4 +64,9 @@  static inline int s5p_mmc_init(int index, int bus_width)
 
 	return s5p_sdhci_init(base, index, bus_width);
 }
+
+#ifdef CONFIG_OF_CONTROL
+int exynos_mmc_init(const void *blob);
+#endif
+
 #endif
diff --git a/drivers/mmc/s5p_sdhci.c b/drivers/mmc/s5p_sdhci.c
index 40ff873..7456ee0 100644
--- a/drivers/mmc/s5p_sdhci.c
+++ b/drivers/mmc/s5p_sdhci.c
@@ -8,9 +8,15 @@ 
 #include <common.h>
 #include <malloc.h>
 #include <sdhci.h>
+#include <fdtdec.h>
+#include <libfdt.h>
+#include <asm/gpio.h>
 #include <asm/arch/mmc.h>
 #include <asm/arch/clk.h>
-
+#include <errno.h>
+#ifdef CONFIG_OF_CONTROL
+#include <asm/arch/pinmux.h>
+#endif
 static char *S5P_NAME = "SAMSUNG SDHCI";
 static void s5p_sdhci_set_control_reg(struct sdhci_host *host)
 {
@@ -86,3 +92,125 @@  int s5p_sdhci_init(u32 regbase, int index, int bus_width)
 
 	return add_sdhci(host, 52000000, 400000);
 }
+
+#ifdef CONFIG_OF_CONTROL
+struct sdhci_host sdhci_host[SDHCI_MAX_HOSTS];
+
+static int do_sdhci_init(struct sdhci_host *host)
+{
+	int dev_id, flag;
+	int err = 0;
+
+	flag = host->bus_width == 8 ? PINMUX_FLAG_8BIT_MODE : PINMUX_FLAG_NONE;
+	dev_id = host->index + PERIPH_ID_SDMMC0;
+
+	if (fdt_gpio_isvalid(&host->pwr_gpio)) {
+		gpio_direction_output(host->pwr_gpio.gpio, 1);
+		err = exynos_pinmux_config(dev_id, flag);
+		if (err) {
+			debug("MMC not configured\n");
+			return err;
+		}
+	}
+
+	if (fdt_gpio_isvalid(&host->cd_gpio)) {
+		gpio_direction_output(host->cd_gpio.gpio, 0xf);
+		if (gpio_get_value(host->cd_gpio.gpio))
+			return -ENODEV;
+
+		err = exynos_pinmux_config(dev_id, flag);
+		if (err) {
+			printf("external SD not configured\n");
+			return err;
+		}
+	}
+
+	host->name = S5P_NAME;
+
+	host->quirks = SDHCI_QUIRK_NO_HISPD_BIT | SDHCI_QUIRK_BROKEN_VOLTAGE |
+		SDHCI_QUIRK_BROKEN_R1B | SDHCI_QUIRK_32BIT_DMA_ADDR |
+		SDHCI_QUIRK_WAIT_SEND_CMD;
+	host->voltages = MMC_VDD_32_33 | MMC_VDD_33_34 | MMC_VDD_165_195;
+	host->version = sdhci_readw(host, SDHCI_HOST_VERSION);
+
+	host->set_control_reg = &s5p_sdhci_set_control_reg;
+	host->set_clock = set_mmc_clk;
+
+	host->host_caps = MMC_MODE_HC;
+
+	return add_sdhci(host, 52000000, 400000);
+}
+
+static int sdhci_get_config(const void *blob, int node, struct sdhci_host *host)
+{
+	int bus_width, dev_id;
+	unsigned int base;
+
+	/* Get device id */
+	dev_id = pinmux_decode_periph_id(blob, node);
+	if (dev_id < PERIPH_ID_SDMMC0) {
+		debug("MMC: Can't get device id\n");
+		return -1;
+	}
+	host->index = dev_id - PERIPH_ID_SDMMC0;
+
+	/* Get bus width */
+	bus_width = fdtdec_get_int(blob, node, "samsung,bus-width", 0);
+	if (bus_width <= 0) {
+		debug("MMC: Can't get bus-width\n");
+		return -1;
+	}
+	host->bus_width = bus_width;
+
+	/* Get the base address from the device node */
+	base = fdtdec_get_addr(blob, node, "reg");
+	if (!base) {
+		debug("DWMMC: Can't get base address\n");
+		return -1;
+	}
+	host->ioaddr = (void *)base;
+
+	fdtdec_decode_gpio(blob, node, "pwr-gpios", &host->pwr_gpio);
+	fdtdec_decode_gpio(blob, node, "cd-gpios", &host->cd_gpio);
+
+	return 0;
+}
+
+static int process_nodes(const void *blob, int node_list[], int count)
+{
+	struct sdhci_host *host;
+	int i, node;
+
+	debug("%s: count = %d\n", __func__, count);
+
+	/* build sdhci_host[] for each controller */
+	for (i = 0; i < count; i++) {
+		node = node_list[i];
+		if (node <= 0)
+			continue;
+
+		host = &sdhci_host[i];
+
+		if (sdhci_get_config(blob, node, host)) {
+			printf("%s: failed to decode dev %d\n",	__func__, i);
+			return -1;
+		}
+		do_sdhci_init(host);
+	}
+	return 0;
+}
+
+int exynos_mmc_init(const void *blob)
+{
+	int count;
+	int node_list[SDHCI_MAX_HOSTS];
+
+	count = fdtdec_find_aliases_for_id(blob, "mmc",
+			COMPAT_SAMSUNG_EXYNOS_MMC, node_list,
+			SDHCI_MAX_HOSTS);
+
+	process_nodes(blob, node_list, count);
+
+	return 1;
+}
+#endif
diff --git a/include/fdtdec.h b/include/fdtdec.h
index f12b4aa..d637f88 100644
--- a/include/fdtdec.h
+++ b/include/fdtdec.h
@@ -81,6 +81,7 @@  enum fdt_compat_id {
 	COMPAT_SAMSUNG_EXYNOS_MIPI_DSI,	/* Exynos mipi dsi */
 	COMPAT_SAMSUNG_EXYNOS5_DP,	/* Exynos Display port controller */
 	COMPAT_SAMSUNG_EXYNOS5_DWMMC,	/* Exynos5 DWMMC controller */
+	COMPAT_SAMSUNG_EXYNOS_MMC,	/* Exynos MMC controller */
 	COMPAT_SAMSUNG_EXYNOS_SERIAL,	/* Exynos UART */
 	COMPAT_MAXIM_MAX77686_PMIC,	/* MAX77686 PMIC */
 	COMPAT_GENERIC_SPI_FLASH,	/* Generic SPI Flash chip */
diff --git a/include/sdhci.h b/include/sdhci.h
index 74d06ae..0cba703 100644
--- a/include/sdhci.h
+++ b/include/sdhci.h
@@ -12,6 +12,7 @@ 
 
 #include <asm/io.h>
 #include <mmc.h>
+#include <fdtdec.h>
 
 /*
  * Controller registers
@@ -244,6 +245,10 @@  struct sdhci_host {
 	const struct sdhci_ops *ops;
 	int index;
 
+	int bus_width;
+	struct fdt_gpio_state pwr_gpio;	/* Change Detect GPIO */
+	struct fdt_gpio_state cd_gpio;		/* Change Detect GPIO */
+
 	void (*set_control_reg)(struct sdhci_host *host);
 	void (*set_clock)(int dev_index, unsigned int div);
 	uint	voltages;
diff --git a/lib/fdtdec.c b/lib/fdtdec.c
index 46e67ff..a88f648 100644
--- a/lib/fdtdec.c
+++ b/lib/fdtdec.c
@@ -54,6 +54,7 @@  static const char * const compat_names[COMPAT_COUNT] = {
 	COMPAT(SAMSUNG_EXYNOS_MIPI_DSI, "samsung,exynos-mipi-dsi"),
 	COMPAT(SAMSUNG_EXYNOS5_DP, "samsung,exynos5-dp"),
 	COMPAT(SAMSUNG_EXYNOS5_DWMMC, "samsung,exynos5250-dwmmc"),
+	COMPAT(SAMSUNG_EXYNOS_MMC, "samsung,exynos-mmc"),
 	COMPAT(SAMSUNG_EXYNOS_SERIAL, "samsung,exynos4210-uart"),
 	COMPAT(MAXIM_MAX77686_PMIC, "maxim,max77686_pmic"),
 	COMPAT(GENERIC_SPI_FLASH, "spi-flash"),