diff mbox

[U-Boot,V3] fsl: esdhc: support driver model

Message ID 1458886616-20499-1-git-send-email-van.freenix@gmail.com
State Accepted
Commit 96f0407b00f9b28e101e6fb3c7a8bf6349d226ee
Delegated to: York Sun
Headers show

Commit Message

Peng Fan March 25, 2016, 6:16 a.m. UTC
Support Driver Model for fsl esdhc driver.

1. Introduce a new structure struct fsl_esdhc_priv
2. Refactor fsl_esdhc_initialize which is originally used by board code.
   - Introduce fsl_esdhc_init to be common usage for DM and non-DM
   - Introduce fsl_esdhc_cfg_to_priv to build the bridge for non-DM part.
   - The original API for board code is still there, but we use
     'fsl_esdhc_cfg_to_priv' and 'fsl_esdhc_init' to serve it.
3. All the functions are changed to use 'struct fsl_esdhc_priv', except
   fsl_esdhc_initialize.
4. Since clk driver is not implemented, use mxc_get_clock to geth
   the clk and fill 'priv->sdhc_clk'.

Has been tested on i.MX6UL 14X14 EVK board:
"
=>dm tree
....
 simple_bus  [ + ]    |   `-- aips-bus@02100000
  mmc        [ + ]    |       |-- usdhc@02190000
  mmc        [ + ]    |       |-- usdhc@02194000
....
=> mmc list
FSL_SDHC: 0 (SD)
FSL_SDHC: 1 (SD)
"

Signed-off-by: Peng Fan <van.freenix@gmail.com>
Cc: York Sun <york.sun@nxp.com>
Cc: Yangbo Lu <yangbo.lu@nxp.com>
Cc: Hector Palacios <hector.palacios@digi.com>
Cc: Eric Nelson <eric@nelint.com>
Cc: Stefano Babic <sbabic@denx.de>
Cc: Fabio Estevam <fabio.estevam@nxp.com>
Cc: Pantelis Antoniou <panto@antoniou-consulting.com>
Cc: Simon Glass <sjg@chromium.org>
---

V3:
 Fix build error reported by York for PPC.

V2:
 restructure the V1 patch.
 Introduce fsl_esdhc_priv structure.
 Introduce code to handle cd-gpios and non-removable.

 drivers/mmc/fsl_esdhc.c | 253 ++++++++++++++++++++++++++++++++++++++++--------
 1 file changed, 213 insertions(+), 40 deletions(-)

Comments

Eric Nelson March 25, 2016, 8:12 p.m. UTC | #1
Hi Peng,

On 03/24/2016 11:16 PM, Peng Fan wrote:
> Support Driver Model for fsl esdhc driver.
> 
> 1. Introduce a new structure struct fsl_esdhc_priv
> 2. Refactor fsl_esdhc_initialize which is originally used by board code.
>    - Introduce fsl_esdhc_init to be common usage for DM and non-DM
>    - Introduce fsl_esdhc_cfg_to_priv to build the bridge for non-DM part.
>    - The original API for board code is still there, but we use
>      'fsl_esdhc_cfg_to_priv' and 'fsl_esdhc_init' to serve it.
> 3. All the functions are changed to use 'struct fsl_esdhc_priv', except
>    fsl_esdhc_initialize.
> 4. Since clk driver is not implemented, use mxc_get_clock to geth
>    the clk and fill 'priv->sdhc_clk'.
> 
> Has been tested on i.MX6UL 14X14 EVK board:
> "
> =>dm tree
> ....
>  simple_bus  [ + ]    |   `-- aips-bus@02100000
>   mmc        [ + ]    |       |-- usdhc@02190000
>   mmc        [ + ]    |       |-- usdhc@02194000
> ....
> => mmc list
> FSL_SDHC: 0 (SD) 
> FSL_SDHC: 1 (SD)
> "
> 

After pulling in device tree files from the mainline kernel, I was able
to test this on a SABRE Lite, so you can add my:

Tested-By: Eric Nelson <eric@nelint.com>

Is somebody prepping patches to pull in support for i.MX6DQ?

Please advise,


Eric
Peng Fan March 27, 2016, 2:57 p.m. UTC | #2
Hi Eric,

On Fri, Mar 25, 2016 at 01:12:39PM -0700, Eric Nelson wrote:
>Hi Peng,
>
>On 03/24/2016 11:16 PM, Peng Fan wrote:
>> Support Driver Model for fsl esdhc driver.
>> 
>> 1. Introduce a new structure struct fsl_esdhc_priv
>> 2. Refactor fsl_esdhc_initialize which is originally used by board code.
>>    - Introduce fsl_esdhc_init to be common usage for DM and non-DM
>>    - Introduce fsl_esdhc_cfg_to_priv to build the bridge for non-DM part.
>>    - The original API for board code is still there, but we use
>>      'fsl_esdhc_cfg_to_priv' and 'fsl_esdhc_init' to serve it.
>> 3. All the functions are changed to use 'struct fsl_esdhc_priv', except
>>    fsl_esdhc_initialize.
>> 4. Since clk driver is not implemented, use mxc_get_clock to geth
>>    the clk and fill 'priv->sdhc_clk'.
>> 
>> Has been tested on i.MX6UL 14X14 EVK board:
>> "
>> =>dm tree
>> ....
>>  simple_bus  [ + ]    |   `-- aips-bus@02100000
>>   mmc        [ + ]    |       |-- usdhc@02190000
>>   mmc        [ + ]    |       |-- usdhc@02194000
>> ....
>> => mmc list
>> FSL_SDHC: 0 (SD) 
>> FSL_SDHC: 1 (SD)
>> "
>> 
>
>After pulling in device tree files from the mainline kernel, I was able
>to test this on a SABRE Lite, so you can add my:
>
>Tested-By: Eric Nelson <eric@nelint.com>

Thanks, Eric.

>
>Is somebody prepping patches to pull in support for i.MX6DQ?

I can not follow you on this? you mean adding dts files for i.MX6DQ or else?

Regards,
Peng

>
>Please advise,
>
>
>Eric
Peng Fan March 27, 2016, 2:59 p.m. UTC | #3
Hi York,

Could you test this patch for PPC and layerscape platform? Since I
only test this on i.MX6 platform, I do not want to break PPC or else.

Thanks,
Peng.

On Fri, Mar 25, 2016 at 02:16:56PM +0800, Peng Fan wrote:
>Support Driver Model for fsl esdhc driver.
>
>1. Introduce a new structure struct fsl_esdhc_priv
>2. Refactor fsl_esdhc_initialize which is originally used by board code.
>   - Introduce fsl_esdhc_init to be common usage for DM and non-DM
>   - Introduce fsl_esdhc_cfg_to_priv to build the bridge for non-DM part.
>   - The original API for board code is still there, but we use
>     'fsl_esdhc_cfg_to_priv' and 'fsl_esdhc_init' to serve it.
>3. All the functions are changed to use 'struct fsl_esdhc_priv', except
>   fsl_esdhc_initialize.
>4. Since clk driver is not implemented, use mxc_get_clock to geth
>   the clk and fill 'priv->sdhc_clk'.
>
>Has been tested on i.MX6UL 14X14 EVK board:
>"
>=>dm tree
>....
> simple_bus  [ + ]    |   `-- aips-bus@02100000
>  mmc        [ + ]    |       |-- usdhc@02190000
>  mmc        [ + ]    |       |-- usdhc@02194000
>....
>=> mmc list
>FSL_SDHC: 0 (SD)
>FSL_SDHC: 1 (SD)
>"
>
>Signed-off-by: Peng Fan <van.freenix@gmail.com>
>Cc: York Sun <york.sun@nxp.com>
>Cc: Yangbo Lu <yangbo.lu@nxp.com>
>Cc: Hector Palacios <hector.palacios@digi.com>
>Cc: Eric Nelson <eric@nelint.com>
>Cc: Stefano Babic <sbabic@denx.de>
>Cc: Fabio Estevam <fabio.estevam@nxp.com>
>Cc: Pantelis Antoniou <panto@antoniou-consulting.com>
>Cc: Simon Glass <sjg@chromium.org>
>---
>
>V3:
> Fix build error reported by York for PPC.
>
>V2:
> restructure the V1 patch.
> Introduce fsl_esdhc_priv structure.
> Introduce code to handle cd-gpios and non-removable.
>
> drivers/mmc/fsl_esdhc.c | 253 ++++++++++++++++++++++++++++++++++++++++--------
> 1 file changed, 213 insertions(+), 40 deletions(-)
>
>diff --git a/drivers/mmc/fsl_esdhc.c b/drivers/mmc/fsl_esdhc.c
>index ea5f4bf..3acf9e8 100644
>--- a/drivers/mmc/fsl_esdhc.c
>+++ b/drivers/mmc/fsl_esdhc.c
>@@ -20,6 +20,8 @@
> #include <fsl_esdhc.h>
> #include <fdt_support.h>
> #include <asm/io.h>
>+#include <dm.h>
>+#include <asm-generic/gpio.h>
> 
> DECLARE_GLOBAL_DATA_PTR;
> 
>@@ -72,6 +74,30 @@ struct fsl_esdhc {
> 	uint    scr;		/* eSDHC control register */
> };
> 
>+/**
>+ * struct fsl_esdhc_priv
>+ *
>+ * @esdhc_regs: registers of the sdhc controller
>+ * @sdhc_clk: Current clk of the sdhc controller
>+ * @bus_width: bus width, 1bit, 4bit or 8bit
>+ * @cfg: mmc config
>+ * @mmc: mmc
>+ * Following is used when Driver Model is enabled for MMC
>+ * @dev: pointer for the device
>+ * @non_removable: 0: removable; 1: non-removable
>+ * @cd_gpio: gpio for card detection
>+ */
>+struct fsl_esdhc_priv {
>+	struct fsl_esdhc *esdhc_regs;
>+	unsigned int sdhc_clk;
>+	unsigned int bus_width;
>+	struct mmc_config cfg;
>+	struct mmc *mmc;
>+	struct udevice *dev;
>+	int non_removable;
>+	struct gpio_desc cd_gpio;
>+};
>+
> /* Return the XFERTYP flags for a given command and data packet */
> static uint esdhc_xfertyp(struct mmc_cmd *cmd, struct mmc_data *data)
> {
>@@ -118,8 +144,8 @@ static uint esdhc_xfertyp(struct mmc_cmd *cmd, struct mmc_data *data)
> static void
> esdhc_pio_read_write(struct mmc *mmc, struct mmc_data *data)
> {
>-	struct fsl_esdhc_cfg *cfg = mmc->priv;
>-	struct fsl_esdhc *regs = (struct fsl_esdhc *)cfg->esdhc_base;
>+	struct fsl_esdhc_priv *priv = mmc->priv;
>+	struct fsl_esdhc *regs = priv->esdhc_regs;
> 	uint blocks;
> 	char *buffer;
> 	uint databuf;
>@@ -180,8 +206,8 @@ esdhc_pio_read_write(struct mmc *mmc, struct mmc_data *data)
> static int esdhc_setup_data(struct mmc *mmc, struct mmc_data *data)
> {
> 	int timeout;
>-	struct fsl_esdhc_cfg *cfg = mmc->priv;
>-	struct fsl_esdhc *regs = (struct fsl_esdhc *)cfg->esdhc_base;
>+	struct fsl_esdhc_priv *priv = mmc->priv;
>+	struct fsl_esdhc *regs = priv->esdhc_regs;
> #ifdef CONFIG_FSL_LAYERSCAPE
> 	dma_addr_t addr;
> #endif
>@@ -312,8 +338,8 @@ esdhc_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd, struct mmc_data *data)
> 	int	err = 0;
> 	uint	xfertyp;
> 	uint	irqstat;
>-	struct fsl_esdhc_cfg *cfg = mmc->priv;
>-	volatile struct fsl_esdhc *regs = (struct fsl_esdhc *)cfg->esdhc_base;
>+	struct fsl_esdhc_priv *priv = mmc->priv;
>+	struct fsl_esdhc *regs = priv->esdhc_regs;
> 
> #ifdef CONFIG_SYS_FSL_ERRATUM_ESDHC111
> 	if (cmd->cmdidx == MMC_CMD_STOP_TRANSMISSION)
>@@ -482,9 +508,9 @@ out:
> static void set_sysctl(struct mmc *mmc, uint clock)
> {
> 	int div, pre_div;
>-	struct fsl_esdhc_cfg *cfg = mmc->priv;
>-	volatile struct fsl_esdhc *regs = (struct fsl_esdhc *)cfg->esdhc_base;
>-	int sdhc_clk = cfg->sdhc_clk;
>+	struct fsl_esdhc_priv *priv = mmc->priv;
>+	struct fsl_esdhc *regs = priv->esdhc_regs;
>+	int sdhc_clk = priv->sdhc_clk;
> 	uint clk;
> 
> 	if (clock < mmc->cfg->f_min)
>@@ -527,8 +553,8 @@ static void set_sysctl(struct mmc *mmc, uint clock)
> #ifdef CONFIG_FSL_ESDHC_USE_PERIPHERAL_CLK
> static void esdhc_clock_control(struct mmc *mmc, bool enable)
> {
>-	struct fsl_esdhc_cfg *cfg = mmc->priv;
>-	struct fsl_esdhc *regs = (struct fsl_esdhc *)cfg->esdhc_base;
>+	struct fsl_esdhc_priv *priv = mmc->priv;
>+	struct fsl_esdhc *regs = priv->esdhc_regs;
> 	u32 value;
> 	u32 time_out;
> 
>@@ -556,8 +582,8 @@ static void esdhc_clock_control(struct mmc *mmc, bool enable)
> 
> static void esdhc_set_ios(struct mmc *mmc)
> {
>-	struct fsl_esdhc_cfg *cfg = mmc->priv;
>-	struct fsl_esdhc *regs = (struct fsl_esdhc *)cfg->esdhc_base;
>+	struct fsl_esdhc_priv *priv = mmc->priv;
>+	struct fsl_esdhc *regs = priv->esdhc_regs;
> 
> #ifdef CONFIG_FSL_ESDHC_USE_PERIPHERAL_CLK
> 	/* Select to use peripheral clock */
>@@ -580,8 +606,8 @@ static void esdhc_set_ios(struct mmc *mmc)
> 
> static int esdhc_init(struct mmc *mmc)
> {
>-	struct fsl_esdhc_cfg *cfg = mmc->priv;
>-	struct fsl_esdhc *regs = (struct fsl_esdhc *)cfg->esdhc_base;
>+	struct fsl_esdhc_priv *priv = mmc->priv;
>+	struct fsl_esdhc *regs = priv->esdhc_regs;
> 	int timeout = 1000;
> 
> 	/* Reset the entire host controller */
>@@ -621,14 +647,23 @@ static int esdhc_init(struct mmc *mmc)
> 
> static int esdhc_getcd(struct mmc *mmc)
> {
>-	struct fsl_esdhc_cfg *cfg = mmc->priv;
>-	struct fsl_esdhc *regs = (struct fsl_esdhc *)cfg->esdhc_base;
>+	struct fsl_esdhc_priv *priv = mmc->priv;
>+	struct fsl_esdhc *regs = priv->esdhc_regs;
> 	int timeout = 1000;
> 
> #ifdef CONFIG_ESDHC_DETECT_QUIRK
> 	if (CONFIG_ESDHC_DETECT_QUIRK)
> 		return 1;
> #endif
>+
>+#ifdef CONFIG_DM_MMC
>+	if (priv->non_removable)
>+		return 1;
>+
>+	if (dm_gpio_is_valid(&priv->cd_gpio))
>+		return dm_gpio_get_value(&priv->cd_gpio);
>+#endif
>+
> 	while (!(esdhc_read32(&regs->prsstat) & PRSSTAT_CINS) && --timeout)
> 		udelay(1000);
> 
>@@ -656,16 +691,29 @@ static const struct mmc_ops esdhc_ops = {
> 	.getcd		= esdhc_getcd,
> };
> 
>-int fsl_esdhc_initialize(bd_t *bis, struct fsl_esdhc_cfg *cfg)
>+static int fsl_esdhc_cfg_to_priv(struct fsl_esdhc_cfg *cfg,
>+				 struct fsl_esdhc_priv *priv)
>+{
>+	if (!cfg || !priv)
>+		return -EINVAL;
>+
>+	priv->esdhc_regs = (struct fsl_esdhc *)(unsigned long)(cfg->esdhc_base);
>+	priv->bus_width = cfg->max_bus_width;
>+	priv->sdhc_clk = cfg->sdhc_clk;
>+
>+	return 0;
>+};
>+
>+static int fsl_esdhc_init(struct fsl_esdhc_priv *priv)
> {
> 	struct fsl_esdhc *regs;
> 	struct mmc *mmc;
> 	u32 caps, voltage_caps;
> 
>-	if (!cfg)
>-		return -1;
>+	if (!priv)
>+		return -EINVAL;
> 
>-	regs = (struct fsl_esdhc *)cfg->esdhc_base;
>+	regs = priv->esdhc_regs;
> 
> 	/* First reset the eSDHC controller */
> 	esdhc_reset(regs);
>@@ -676,7 +724,7 @@ int fsl_esdhc_initialize(bd_t *bis, struct fsl_esdhc_cfg *cfg)
> #endif
> 
> 	writel(SDHCI_IRQ_EN_BITS, &regs->irqstaten);
>-	memset(&cfg->cfg, 0, sizeof(cfg->cfg));
>+	memset(&priv->cfg, 0, sizeof(priv->cfg));
> 
> 	voltage_caps = 0;
> 	caps = esdhc_read32(&regs->hostcapblt);
>@@ -698,47 +746,83 @@ int fsl_esdhc_initialize(bd_t *bis, struct fsl_esdhc_cfg *cfg)
> 	if (caps & ESDHC_HOSTCAPBLT_VS33)
> 		voltage_caps |= MMC_VDD_32_33 | MMC_VDD_33_34;
> 
>-	cfg->cfg.name = "FSL_SDHC";
>-	cfg->cfg.ops = &esdhc_ops;
>+	priv->cfg.name = "FSL_SDHC";
>+	priv->cfg.ops = &esdhc_ops;
> #ifdef CONFIG_SYS_SD_VOLTAGE
>-	cfg->cfg.voltages = CONFIG_SYS_SD_VOLTAGE;
>+	priv->cfg.voltages = CONFIG_SYS_SD_VOLTAGE;
> #else
>-	cfg->cfg.voltages = MMC_VDD_32_33 | MMC_VDD_33_34;
>+	priv->cfg.voltages = MMC_VDD_32_33 | MMC_VDD_33_34;
> #endif
>-	if ((cfg->cfg.voltages & voltage_caps) == 0) {
>+	if ((priv->cfg.voltages & voltage_caps) == 0) {
> 		printf("voltage not supported by controller\n");
> 		return -1;
> 	}
> 
>-	cfg->cfg.host_caps = MMC_MODE_4BIT | MMC_MODE_8BIT;
>+	if (priv->bus_width == 8)
>+		priv->cfg.host_caps = MMC_MODE_4BIT | MMC_MODE_8BIT;
>+	else if (priv->bus_width == 4)
>+		priv->cfg.host_caps = MMC_MODE_4BIT;
>+
>+	priv->cfg.host_caps = MMC_MODE_4BIT | MMC_MODE_8BIT;
> #ifdef CONFIG_SYS_FSL_ESDHC_HAS_DDR_MODE
>-	cfg->cfg.host_caps |= MMC_MODE_DDR_52MHz;
>+	priv->cfg.host_caps |= MMC_MODE_DDR_52MHz;
> #endif
> 
>-	if (cfg->max_bus_width > 0) {
>-		if (cfg->max_bus_width < 8)
>-			cfg->cfg.host_caps &= ~MMC_MODE_8BIT;
>-		if (cfg->max_bus_width < 4)
>-			cfg->cfg.host_caps &= ~MMC_MODE_4BIT;
>+	if (priv->bus_width > 0) {
>+		if (priv->bus_width < 8)
>+			priv->cfg.host_caps &= ~MMC_MODE_8BIT;
>+		if (priv->bus_width < 4)
>+			priv->cfg.host_caps &= ~MMC_MODE_4BIT;
> 	}
> 
> 	if (caps & ESDHC_HOSTCAPBLT_HSS)
>-		cfg->cfg.host_caps |= MMC_MODE_HS_52MHz | MMC_MODE_HS;
>+		priv->cfg.host_caps |= MMC_MODE_HS_52MHz | MMC_MODE_HS;
> 
> #ifdef CONFIG_ESDHC_DETECT_8_BIT_QUIRK
> 	if (CONFIG_ESDHC_DETECT_8_BIT_QUIRK)
>-		cfg->cfg.host_caps &= ~MMC_MODE_8BIT;
>+		priv->cfg.host_caps &= ~MMC_MODE_8BIT;
> #endif
> 
>-	cfg->cfg.f_min = 400000;
>-	cfg->cfg.f_max = min(cfg->sdhc_clk, (u32)52000000);
>+	priv->cfg.f_min = 400000;
>+	priv->cfg.f_max = min(priv->sdhc_clk, (u32)52000000);
> 
>-	cfg->cfg.b_max = CONFIG_SYS_MMC_MAX_BLK_COUNT;
>+	priv->cfg.b_max = CONFIG_SYS_MMC_MAX_BLK_COUNT;
> 
>-	mmc = mmc_create(&cfg->cfg, cfg);
>+	mmc = mmc_create(&priv->cfg, priv);
> 	if (mmc == NULL)
> 		return -1;
> 
>+	priv->mmc = mmc;
>+
>+	return 0;
>+}
>+
>+int fsl_esdhc_initialize(bd_t *bis, struct fsl_esdhc_cfg *cfg)
>+{
>+	struct fsl_esdhc_priv *priv;
>+	int ret;
>+
>+	if (!cfg)
>+		return -EINVAL;
>+
>+	priv = calloc(sizeof(struct fsl_esdhc_priv), 1);
>+	if (!priv)
>+		return -ENOMEM;
>+
>+	ret = fsl_esdhc_cfg_to_priv(cfg, priv);
>+	if (ret) {
>+		debug("%s xlate failure\n", __func__);
>+		free(priv);
>+		return ret;
>+	}
>+
>+	ret = fsl_esdhc_init(priv);
>+	if (ret) {
>+		debug("%s init failure\n", __func__);
>+		free(priv);
>+		return ret;
>+	}
>+
> 	return 0;
> }
> 
>@@ -819,3 +903,92 @@ void fdt_fixup_esdhc(void *blob, bd_t *bd)
> 			   4 + 1, 1);
> }
> #endif
>+
>+#ifdef CONFIG_DM_MMC
>+#include <asm/arch/clock.h>
>+static int fsl_esdhc_probe(struct udevice *dev)
>+{
>+	struct mmc_uclass_priv *upriv = dev_get_uclass_priv(dev);
>+	struct fsl_esdhc_priv *priv = dev_get_priv(dev);
>+	const void *fdt = gd->fdt_blob;
>+	int node = dev->of_offset;
>+	fdt_addr_t addr;
>+	unsigned int val;
>+	int ret;
>+
>+	addr = dev_get_addr(dev);
>+	if (addr == FDT_ADDR_T_NONE)
>+		return -EINVAL;
>+
>+	priv->esdhc_regs = (struct fsl_esdhc *)addr;
>+	priv->dev = dev;
>+
>+	val = fdtdec_get_int(fdt, node, "bus-width", -1);
>+	if (val == 8)
>+		priv->bus_width = 8;
>+	else if (val == 4)
>+		priv->bus_width = 4;
>+	else
>+		priv->bus_width = 1;
>+
>+	if (fdt_get_property(fdt, node, "non-removable", NULL)) {
>+		priv->non_removable = 1;
>+	 } else {
>+		priv->non_removable = 0;
>+		gpio_request_by_name_nodev(fdt, node, "cd-gpios", 0,
>+					   &priv->cd_gpio, GPIOD_IS_IN);
>+	}
>+
>+	/*
>+	 * TODO:
>+	 * Because lack of clk driver, if SDHC clk is not enabled,
>+	 * need to enable it first before this driver is invoked.
>+	 *
>+	 * we use MXC_ESDHC_CLK to get clk freq.
>+	 * If one would like to make this function work,
>+	 * the aliases should be provided in dts as this:
>+	 *
>+	 *  aliases {
>+	 *	mmc0 = &usdhc1;
>+	 *	mmc1 = &usdhc2;
>+	 *	mmc2 = &usdhc3;
>+	 *	mmc3 = &usdhc4;
>+	 *	};
>+	 * Then if your board only supports mmc2 and mmc3, but we can
>+	 * correctly get the seq as 2 and 3, then let mxc_get_clock
>+	 * work as expected.
>+	 */
>+	priv->sdhc_clk = mxc_get_clock(MXC_ESDHC_CLK + dev->seq);
>+	if (priv->sdhc_clk <= 0) {
>+		dev_err(dev, "Unable to get clk for %s\n", dev->name);
>+		return -EINVAL;
>+	}
>+
>+	ret = fsl_esdhc_init(priv);
>+	if (ret) {
>+		dev_err(dev, "fsl_esdhc_init failure\n");
>+		return ret;
>+	}
>+
>+	upriv->mmc = priv->mmc;
>+
>+	return 0;
>+}
>+
>+static const struct udevice_id fsl_esdhc_ids[] = {
>+	{ .compatible = "fsl,imx6ul-usdhc", },
>+	{ .compatible = "fsl,imx6sx-usdhc", },
>+	{ .compatible = "fsl,imx6sl-usdhc", },
>+	{ .compatible = "fsl,imx6q-usdhc", },
>+	{ .compatible = "fsl,imx7d-usdhc", },
>+	{ /* sentinel */ }
>+};
>+
>+U_BOOT_DRIVER(fsl_esdhc) = {
>+	.name	= "fsl-esdhc-mmc",
>+	.id	= UCLASS_MMC,
>+	.of_match = fsl_esdhc_ids,
>+	.probe	= fsl_esdhc_probe,
>+	.priv_auto_alloc_size = sizeof(struct fsl_esdhc_priv),
>+};
>+#endif
>-- 
>2.6.2
>
York Sun March 27, 2016, 4:58 p.m. UTC | #4
(Sorry for top posting. Using OWA)

Yes, I can compile it for PPC and spot check on selected boards.

York
Eric Nelson March 27, 2016, 6:18 p.m. UTC | #5
Hi Peng,

On 03/27/2016 07:57 AM, Peng Fan wrote:
> On Fri, Mar 25, 2016 at 01:12:39PM -0700, Eric Nelson wrote:
>> On 03/24/2016 11:16 PM, Peng Fan wrote:
>>> Support Driver Model for fsl esdhc driver.
>>>
>>> 1. Introduce a new structure struct fsl_esdhc_priv
>>> 2. Refactor fsl_esdhc_initialize which is originally used by board code.
>>>    - Introduce fsl_esdhc_init to be common usage for DM and non-DM
>>>    - Introduce fsl_esdhc_cfg_to_priv to build the bridge for non-DM part.
>>>    - The original API for board code is still there, but we use
>>>      'fsl_esdhc_cfg_to_priv' and 'fsl_esdhc_init' to serve it.
>>> 3. All the functions are changed to use 'struct fsl_esdhc_priv', except
>>>    fsl_esdhc_initialize.
>>> 4. Since clk driver is not implemented, use mxc_get_clock to geth
>>>    the clk and fill 'priv->sdhc_clk'.
>>>
>>> Has been tested on i.MX6UL 14X14 EVK board:
>>> "
>>> =>dm tree
>>> ....
>>>  simple_bus  [ + ]    |   `-- aips-bus@02100000
>>>   mmc        [ + ]    |       |-- usdhc@02190000
>>>   mmc        [ + ]    |       |-- usdhc@02194000
>>> ....
>>> => mmc list
>>> FSL_SDHC: 0 (SD) 
>>> FSL_SDHC: 1 (SD)
>>> "
>>>
>>
>> After pulling in device tree files from the mainline kernel, I was able
>> to test this on a SABRE Lite, so you can add my:
>>
>> Tested-By: Eric Nelson <eric@nelint.com>
> 
> Thanks, Eric.
> 

Thank you for adding this support.

>>
>> Is somebody prepping patches to pull in support for i.MX6DQ?
> 
> I can not follow you on this? you mean adding dts files for i.MX6DQ or else?
>

Yes, or at least the .dtsi and .h files from arch/arm/boot/dts/.

Your i.MX6UL patch pulled in the files needed for UL, and the same
is needed for i.MX6DQ, i.MX6SX and such.

I have a patch pulling i.MX6DQ files from linux-stable (4.4), but
don't want to jump in the middle if somebody else is doing this.

Regards,


Eric
Peng Fan March 28, 2016, 1:14 a.m. UTC | #6
Hi Eric,

On Sun, Mar 27, 2016 at 11:18:00AM -0700, Eric Nelson wrote:
>Hi Peng,
>
>On 03/27/2016 07:57 AM, Peng Fan wrote:
>> On Fri, Mar 25, 2016 at 01:12:39PM -0700, Eric Nelson wrote:
>>> On 03/24/2016 11:16 PM, Peng Fan wrote:
>>>> Support Driver Model for fsl esdhc driver.
>>>>
>>>> 1. Introduce a new structure struct fsl_esdhc_priv
>>>> 2. Refactor fsl_esdhc_initialize which is originally used by board code.
>>>>    - Introduce fsl_esdhc_init to be common usage for DM and non-DM
>>>>    - Introduce fsl_esdhc_cfg_to_priv to build the bridge for non-DM part.
>>>>    - The original API for board code is still there, but we use
>>>>      'fsl_esdhc_cfg_to_priv' and 'fsl_esdhc_init' to serve it.
>>>> 3. All the functions are changed to use 'struct fsl_esdhc_priv', except
>>>>    fsl_esdhc_initialize.
>>>> 4. Since clk driver is not implemented, use mxc_get_clock to geth
>>>>    the clk and fill 'priv->sdhc_clk'.
>>>>
>>>> Has been tested on i.MX6UL 14X14 EVK board:
>>>> "
>>>> =>dm tree
>>>> ....
>>>>  simple_bus  [ + ]    |   `-- aips-bus@02100000
>>>>   mmc        [ + ]    |       |-- usdhc@02190000
>>>>   mmc        [ + ]    |       |-- usdhc@02194000
>>>> ....
>>>> => mmc list
>>>> FSL_SDHC: 0 (SD) 
>>>> FSL_SDHC: 1 (SD)
>>>> "
>>>>
>>>
>>> After pulling in device tree files from the mainline kernel, I was able
>>> to test this on a SABRE Lite, so you can add my:
>>>
>>> Tested-By: Eric Nelson <eric@nelint.com>
>> 
>> Thanks, Eric.
>> 
>
>Thank you for adding this support.
>
>>>
>>> Is somebody prepping patches to pull in support for i.MX6DQ?
>> 
>> I can not follow you on this? you mean adding dts files for i.MX6DQ or else?
>>
>
>Yes, or at least the .dtsi and .h files from arch/arm/boot/dts/.
>
>Your i.MX6UL patch pulled in the files needed for UL, and the same
>is needed for i.MX6DQ, i.MX6SX and such.
>
>I have a patch pulling i.MX6DQ files from linux-stable (4.4), but

That's great.

>don't want to jump in the middle if somebody else is doing this.

I have not began this. If you have patches, feel free to post them out:)

Thanks,
Peng.
>
>Regards,
>
>
>Eric
Eric Nelson March 28, 2016, 5:44 p.m. UTC | #7
On 03/28/2016 10:26 AM, Eric Nelson wrote:
> Copy device tree headers and include (.dtsi) files for
> i.MX6 Dual and Quad variants.
> 
> These are copied as-is from Linux stable (4.4.1).
> 

This is the only patch in the set.

Sorry for not catching the "Patch 1/9" in the header.

I pulled this out of the tree I was working on for the block cache.
York Sun March 28, 2016, 5:55 p.m. UTC | #8
On 03/27/2016 08:02 AM, Peng Fan wrote:
> Hi York,
> 
> Could you test this patch for PPC and layerscape platform? Since I
> only test this on i.MX6 platform, I do not want to break PPC or else.
> 
> Thanks,
> Peng.
> 
> On Fri, Mar 25, 2016 at 02:16:56PM +0800, Peng Fan wrote:
>> Support Driver Model for fsl esdhc driver.
>>
>> 1. Introduce a new structure struct fsl_esdhc_priv
>> 2. Refactor fsl_esdhc_initialize which is originally used by board code.
>>   - Introduce fsl_esdhc_init to be common usage for DM and non-DM
>>   - Introduce fsl_esdhc_cfg_to_priv to build the bridge for non-DM part.
>>   - The original API for board code is still there, but we use
>>     'fsl_esdhc_cfg_to_priv' and 'fsl_esdhc_init' to serve it.
>> 3. All the functions are changed to use 'struct fsl_esdhc_priv', except
>>   fsl_esdhc_initialize.
>> 4. Since clk driver is not implemented, use mxc_get_clock to geth
>>   the clk and fill 'priv->sdhc_clk'.
>>
>> Has been tested on i.MX6UL 14X14 EVK board:
>> "
>> =>dm tree
>> ....
>> simple_bus  [ + ]    |   `-- aips-bus@02100000
>>  mmc        [ + ]    |       |-- usdhc@02190000
>>  mmc        [ + ]    |       |-- usdhc@02194000
>> ....
>> => mmc list
>> FSL_SDHC: 0 (SD)
>> FSL_SDHC: 1 (SD)
>> "
>>
>> Signed-off-by: Peng Fan <van.freenix@gmail.com>
>> Cc: York Sun <york.sun@nxp.com>
>> Cc: Yangbo Lu <yangbo.lu@nxp.com>
>> Cc: Hector Palacios <hector.palacios@digi.com>
>> Cc: Eric Nelson <eric@nelint.com>
>> Cc: Stefano Babic <sbabic@denx.de>
>> Cc: Fabio Estevam <fabio.estevam@nxp.com>
>> Cc: Pantelis Antoniou <panto@antoniou-consulting.com>
>> Cc: Simon Glass <sjg@chromium.org>
>> ---
>>
>> V3:
>> Fix build error reported by York for PPC.
>>
>> V2:
>> restructure the V1 patch.
>> Introduce fsl_esdhc_priv structure.
>> Introduce code to handle cd-gpios and non-removable.

<snip>

>> +
>> +#ifdef CONFIG_DM_MMC
>> +#include <asm/arch/clock.h>
>> +static int fsl_esdhc_probe(struct udevice *dev)
>> +{
>> +	struct mmc_uclass_priv *upriv = dev_get_uclass_priv(dev);
>> +	struct fsl_esdhc_priv *priv = dev_get_priv(dev);
>> +	const void *fdt = gd->fdt_blob;
>> +	int node = dev->of_offset;
>> +	fdt_addr_t addr;
>> +	unsigned int val;
>> +	int ret;
>> +
>> +	addr = dev_get_addr(dev);
>> +	if (addr == FDT_ADDR_T_NONE)
>> +		return -EINVAL;
>> +
>> +	priv->esdhc_regs = (struct fsl_esdhc *)addr;
>> +	priv->dev = dev;
>> +
>> +	val = fdtdec_get_int(fdt, node, "bus-width", -1);
>> +	if (val == 8)
>> +		priv->bus_width = 8;
>> +	else if (val == 4)
>> +		priv->bus_width = 4;
>> +	else
>> +		priv->bus_width = 1;
>> +
>> +	if (fdt_get_property(fdt, node, "non-removable", NULL)) {
>> +		priv->non_removable = 1;
>> +	 } else {
>> +		priv->non_removable = 0;
>> +		gpio_request_by_name_nodev(fdt, node, "cd-gpios", 0,
>> +					   &priv->cd_gpio, GPIOD_IS_IN);
>> +	}
>> +
>> +	/*
>> +	 * TODO:
>> +	 * Because lack of clk driver, if SDHC clk is not enabled,
>> +	 * need to enable it first before this driver is invoked.
>> +	 *
>> +	 * we use MXC_ESDHC_CLK to get clk freq.
>> +	 * If one would like to make this function work,
>> +	 * the aliases should be provided in dts as this:
>> +	 *
>> +	 *  aliases {
>> +	 *	mmc0 = &usdhc1;
>> +	 *	mmc1 = &usdhc2;
>> +	 *	mmc2 = &usdhc3;
>> +	 *	mmc3 = &usdhc4;
>> +	 *	};
>> +	 * Then if your board only supports mmc2 and mmc3, but we can
>> +	 * correctly get the seq as 2 and 3, then let mxc_get_clock
>> +	 * work as expected.
>> +	 */
>> +	priv->sdhc_clk = mxc_get_clock(MXC_ESDHC_CLK + dev->seq);
>> +	if (priv->sdhc_clk <= 0) {
>> +		dev_err(dev, "Unable to get clk for %s\n", dev->name);
>> +		return -EINVAL;
>> +	}
>> +
>> +	ret = fsl_esdhc_init(priv);
>> +	if (ret) {
>> +		dev_err(dev, "fsl_esdhc_init failure\n");
>> +		return ret;
>> +	}
>> +
>> +	upriv->mmc = priv->mmc;
>> +
>> +	return 0;
>> +}
>> +
>> +static const struct udevice_id fsl_esdhc_ids[] = {
>> +	{ .compatible = "fsl,imx6ul-usdhc", },
>> +	{ .compatible = "fsl,imx6sx-usdhc", },
>> +	{ .compatible = "fsl,imx6sl-usdhc", },
>> +	{ .compatible = "fsl,imx6q-usdhc", },
>> +	{ .compatible = "fsl,imx7d-usdhc", },
>> +	{ /* sentinel */ }
>> +};
>> +
>> +U_BOOT_DRIVER(fsl_esdhc) = {
>> +	.name	= "fsl-esdhc-mmc",
>> +	.id	= UCLASS_MMC,
>> +	.of_match = fsl_esdhc_ids,
>> +	.probe	= fsl_esdhc_probe,
>> +	.priv_auto_alloc_size = sizeof(struct fsl_esdhc_priv),
>> +};
>> +#endif
>> -- 
>> 2.6.2
>>
> 

Peng,

You are not breaking PPC yet. The new function you added fsl_esdhc_probe() is
gated by CONFIG_DM_MMC. This macro is not defined for any PPC platform.

York
Peng Fan March 28, 2016, 11:56 p.m. UTC | #9
Hi York,

On Mon, Mar 28, 2016 at 05:55:39PM +0000, york sun wrote:
>On 03/27/2016 08:02 AM, Peng Fan wrote:
>> Hi York,
>> 
>> Could you test this patch for PPC and layerscape platform? Since I
>> only test this on i.MX6 platform, I do not want to break PPC or else.
>> 
>> Thanks,
>> Peng.
>> 
>> On Fri, Mar 25, 2016 at 02:16:56PM +0800, Peng Fan wrote:
>>> Support Driver Model for fsl esdhc driver.
>>>
>>> 1. Introduce a new structure struct fsl_esdhc_priv
>>> 2. Refactor fsl_esdhc_initialize which is originally used by board code.
>>>   - Introduce fsl_esdhc_init to be common usage for DM and non-DM
>>>   - Introduce fsl_esdhc_cfg_to_priv to build the bridge for non-DM part.
>>>   - The original API for board code is still there, but we use
>>>     'fsl_esdhc_cfg_to_priv' and 'fsl_esdhc_init' to serve it.
>>> 3. All the functions are changed to use 'struct fsl_esdhc_priv', except
>>>   fsl_esdhc_initialize.
>>> 4. Since clk driver is not implemented, use mxc_get_clock to geth
>>>   the clk and fill 'priv->sdhc_clk'.
>>>
>>> Has been tested on i.MX6UL 14X14 EVK board:
>>> "
>>> =>dm tree
>>> ....
>>> simple_bus  [ + ]    |   `-- aips-bus@02100000
>>>  mmc        [ + ]    |       |-- usdhc@02190000
>>>  mmc        [ + ]    |       |-- usdhc@02194000
>>> ....
>>> => mmc list
>>> FSL_SDHC: 0 (SD)
>>> FSL_SDHC: 1 (SD)
>>> "
>>>
>>> Signed-off-by: Peng Fan <van.freenix@gmail.com>
>>> Cc: York Sun <york.sun@nxp.com>
>>> Cc: Yangbo Lu <yangbo.lu@nxp.com>
>>> Cc: Hector Palacios <hector.palacios@digi.com>
>>> Cc: Eric Nelson <eric@nelint.com>
>>> Cc: Stefano Babic <sbabic@denx.de>
>>> Cc: Fabio Estevam <fabio.estevam@nxp.com>
>>> Cc: Pantelis Antoniou <panto@antoniou-consulting.com>
>>> Cc: Simon Glass <sjg@chromium.org>
>>> ---
>>>
>>> V3:
>>> Fix build error reported by York for PPC.
>>>
>>> V2:
>>> restructure the V1 patch.
>>> Introduce fsl_esdhc_priv structure.
>>> Introduce code to handle cd-gpios and non-removable.
>
><snip>
>
>>> +
>>> +#ifdef CONFIG_DM_MMC
>>> +#include <asm/arch/clock.h>
>>> +static int fsl_esdhc_probe(struct udevice *dev)
>>> +{
>>> +	struct mmc_uclass_priv *upriv = dev_get_uclass_priv(dev);
>>> +	struct fsl_esdhc_priv *priv = dev_get_priv(dev);
>>> +	const void *fdt = gd->fdt_blob;
>>> +	int node = dev->of_offset;
>>> +	fdt_addr_t addr;
>>> +	unsigned int val;
>>> +	int ret;
>>> +
>>> +	addr = dev_get_addr(dev);
>>> +	if (addr == FDT_ADDR_T_NONE)
>>> +		return -EINVAL;
>>> +
>>> +	priv->esdhc_regs = (struct fsl_esdhc *)addr;
>>> +	priv->dev = dev;
>>> +
>>> +	val = fdtdec_get_int(fdt, node, "bus-width", -1);
>>> +	if (val == 8)
>>> +		priv->bus_width = 8;
>>> +	else if (val == 4)
>>> +		priv->bus_width = 4;
>>> +	else
>>> +		priv->bus_width = 1;
>>> +
>>> +	if (fdt_get_property(fdt, node, "non-removable", NULL)) {
>>> +		priv->non_removable = 1;
>>> +	 } else {
>>> +		priv->non_removable = 0;
>>> +		gpio_request_by_name_nodev(fdt, node, "cd-gpios", 0,
>>> +					   &priv->cd_gpio, GPIOD_IS_IN);
>>> +	}
>>> +
>>> +	/*
>>> +	 * TODO:
>>> +	 * Because lack of clk driver, if SDHC clk is not enabled,
>>> +	 * need to enable it first before this driver is invoked.
>>> +	 *
>>> +	 * we use MXC_ESDHC_CLK to get clk freq.
>>> +	 * If one would like to make this function work,
>>> +	 * the aliases should be provided in dts as this:
>>> +	 *
>>> +	 *  aliases {
>>> +	 *	mmc0 = &usdhc1;
>>> +	 *	mmc1 = &usdhc2;
>>> +	 *	mmc2 = &usdhc3;
>>> +	 *	mmc3 = &usdhc4;
>>> +	 *	};
>>> +	 * Then if your board only supports mmc2 and mmc3, but we can
>>> +	 * correctly get the seq as 2 and 3, then let mxc_get_clock
>>> +	 * work as expected.
>>> +	 */
>>> +	priv->sdhc_clk = mxc_get_clock(MXC_ESDHC_CLK + dev->seq);
>>> +	if (priv->sdhc_clk <= 0) {
>>> +		dev_err(dev, "Unable to get clk for %s\n", dev->name);
>>> +		return -EINVAL;
>>> +	}
>>> +
>>> +	ret = fsl_esdhc_init(priv);
>>> +	if (ret) {
>>> +		dev_err(dev, "fsl_esdhc_init failure\n");
>>> +		return ret;
>>> +	}
>>> +
>>> +	upriv->mmc = priv->mmc;
>>> +
>>> +	return 0;
>>> +}
>>> +
>>> +static const struct udevice_id fsl_esdhc_ids[] = {
>>> +	{ .compatible = "fsl,imx6ul-usdhc", },
>>> +	{ .compatible = "fsl,imx6sx-usdhc", },
>>> +	{ .compatible = "fsl,imx6sl-usdhc", },
>>> +	{ .compatible = "fsl,imx6q-usdhc", },
>>> +	{ .compatible = "fsl,imx7d-usdhc", },
>>> +	{ /* sentinel */ }
>>> +};
>>> +
>>> +U_BOOT_DRIVER(fsl_esdhc) = {
>>> +	.name	= "fsl-esdhc-mmc",
>>> +	.id	= UCLASS_MMC,
>>> +	.of_match = fsl_esdhc_ids,
>>> +	.probe	= fsl_esdhc_probe,
>>> +	.priv_auto_alloc_size = sizeof(struct fsl_esdhc_priv),
>>> +};
>>> +#endif
>>> -- 
>>> 2.6.2
>>>
>> 
>
>Peng,
>
>You are not breaking PPC yet. The new function you added fsl_esdhc_probe() is
>gated by CONFIG_DM_MMC. This macro is not defined for any PPC platform.

Thanks for the test. If CONFIG_DM_MMC is enabled for PPC, I think the
only thing that may break PPC is mxc_get_clock, because this function
is only defined for i.MX.

I am not very sure whether we need a more complicated clk driver
following linux, since it may make board/soc bringup work more
complicated. Anyway, later when PPC want to enable CONFIG_DM_MMC,
mxc_get_clock should be refined to support i.MX and PPC or we develop
a general function to support both.

Thanks,
Peng.
>
>York
York Sun April 6, 2016, 5:21 p.m. UTC | #10
On 03/24/2016 11:26 PM, Peng Fan wrote:
> Support Driver Model for fsl esdhc driver.
> 
> 1. Introduce a new structure struct fsl_esdhc_priv
> 2. Refactor fsl_esdhc_initialize which is originally used by board code.
>    - Introduce fsl_esdhc_init to be common usage for DM and non-DM
>    - Introduce fsl_esdhc_cfg_to_priv to build the bridge for non-DM part.
>    - The original API for board code is still there, but we use
>      'fsl_esdhc_cfg_to_priv' and 'fsl_esdhc_init' to serve it.
> 3. All the functions are changed to use 'struct fsl_esdhc_priv', except
>    fsl_esdhc_initialize.
> 4. Since clk driver is not implemented, use mxc_get_clock to geth
>    the clk and fill 'priv->sdhc_clk'.
> 
> Has been tested on i.MX6UL 14X14 EVK board:
> "
> =>dm tree
> ....
>  simple_bus  [ + ]    |   `-- aips-bus@02100000
>   mmc        [ + ]    |       |-- usdhc@02190000
>   mmc        [ + ]    |       |-- usdhc@02194000
> ....
> => mmc list
> FSL_SDHC: 0 (SD)
> FSL_SDHC: 1 (SD)
> "
> 
> Signed-off-by: Peng Fan <van.freenix@gmail.com>
> Cc: York Sun <york.sun@nxp.com>
> Cc: Yangbo Lu <yangbo.lu@nxp.com>
> Cc: Hector Palacios <hector.palacios@digi.com>
> Cc: Eric Nelson <eric@nelint.com>
> Cc: Stefano Babic <sbabic@denx.de>
> Cc: Fabio Estevam <fabio.estevam@nxp.com>
> Cc: Pantelis Antoniou <panto@antoniou-consulting.com>
> Cc: Simon Glass <sjg@chromium.org>
> ---
> 
> V3:
>  Fix build error reported by York for PPC.
> 
> V2:
>  restructure the V1 patch.
>  Introduce fsl_esdhc_priv structure.
>  Introduce code to handle cd-gpios and non-removable.
> 
>  drivers/mmc/fsl_esdhc.c | 253 ++++++++++++++++++++++++++++++++++++++++--------
>  1 file changed, 213 insertions(+), 40 deletions(-)

Applied to u-boot-fsl-qoriq master. Awaiting upstream.
Thanks.

York
diff mbox

Patch

diff --git a/drivers/mmc/fsl_esdhc.c b/drivers/mmc/fsl_esdhc.c
index ea5f4bf..3acf9e8 100644
--- a/drivers/mmc/fsl_esdhc.c
+++ b/drivers/mmc/fsl_esdhc.c
@@ -20,6 +20,8 @@ 
 #include <fsl_esdhc.h>
 #include <fdt_support.h>
 #include <asm/io.h>
+#include <dm.h>
+#include <asm-generic/gpio.h>
 
 DECLARE_GLOBAL_DATA_PTR;
 
@@ -72,6 +74,30 @@  struct fsl_esdhc {
 	uint    scr;		/* eSDHC control register */
 };
 
+/**
+ * struct fsl_esdhc_priv
+ *
+ * @esdhc_regs: registers of the sdhc controller
+ * @sdhc_clk: Current clk of the sdhc controller
+ * @bus_width: bus width, 1bit, 4bit or 8bit
+ * @cfg: mmc config
+ * @mmc: mmc
+ * Following is used when Driver Model is enabled for MMC
+ * @dev: pointer for the device
+ * @non_removable: 0: removable; 1: non-removable
+ * @cd_gpio: gpio for card detection
+ */
+struct fsl_esdhc_priv {
+	struct fsl_esdhc *esdhc_regs;
+	unsigned int sdhc_clk;
+	unsigned int bus_width;
+	struct mmc_config cfg;
+	struct mmc *mmc;
+	struct udevice *dev;
+	int non_removable;
+	struct gpio_desc cd_gpio;
+};
+
 /* Return the XFERTYP flags for a given command and data packet */
 static uint esdhc_xfertyp(struct mmc_cmd *cmd, struct mmc_data *data)
 {
@@ -118,8 +144,8 @@  static uint esdhc_xfertyp(struct mmc_cmd *cmd, struct mmc_data *data)
 static void
 esdhc_pio_read_write(struct mmc *mmc, struct mmc_data *data)
 {
-	struct fsl_esdhc_cfg *cfg = mmc->priv;
-	struct fsl_esdhc *regs = (struct fsl_esdhc *)cfg->esdhc_base;
+	struct fsl_esdhc_priv *priv = mmc->priv;
+	struct fsl_esdhc *regs = priv->esdhc_regs;
 	uint blocks;
 	char *buffer;
 	uint databuf;
@@ -180,8 +206,8 @@  esdhc_pio_read_write(struct mmc *mmc, struct mmc_data *data)
 static int esdhc_setup_data(struct mmc *mmc, struct mmc_data *data)
 {
 	int timeout;
-	struct fsl_esdhc_cfg *cfg = mmc->priv;
-	struct fsl_esdhc *regs = (struct fsl_esdhc *)cfg->esdhc_base;
+	struct fsl_esdhc_priv *priv = mmc->priv;
+	struct fsl_esdhc *regs = priv->esdhc_regs;
 #ifdef CONFIG_FSL_LAYERSCAPE
 	dma_addr_t addr;
 #endif
@@ -312,8 +338,8 @@  esdhc_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd, struct mmc_data *data)
 	int	err = 0;
 	uint	xfertyp;
 	uint	irqstat;
-	struct fsl_esdhc_cfg *cfg = mmc->priv;
-	volatile struct fsl_esdhc *regs = (struct fsl_esdhc *)cfg->esdhc_base;
+	struct fsl_esdhc_priv *priv = mmc->priv;
+	struct fsl_esdhc *regs = priv->esdhc_regs;
 
 #ifdef CONFIG_SYS_FSL_ERRATUM_ESDHC111
 	if (cmd->cmdidx == MMC_CMD_STOP_TRANSMISSION)
@@ -482,9 +508,9 @@  out:
 static void set_sysctl(struct mmc *mmc, uint clock)
 {
 	int div, pre_div;
-	struct fsl_esdhc_cfg *cfg = mmc->priv;
-	volatile struct fsl_esdhc *regs = (struct fsl_esdhc *)cfg->esdhc_base;
-	int sdhc_clk = cfg->sdhc_clk;
+	struct fsl_esdhc_priv *priv = mmc->priv;
+	struct fsl_esdhc *regs = priv->esdhc_regs;
+	int sdhc_clk = priv->sdhc_clk;
 	uint clk;
 
 	if (clock < mmc->cfg->f_min)
@@ -527,8 +553,8 @@  static void set_sysctl(struct mmc *mmc, uint clock)
 #ifdef CONFIG_FSL_ESDHC_USE_PERIPHERAL_CLK
 static void esdhc_clock_control(struct mmc *mmc, bool enable)
 {
-	struct fsl_esdhc_cfg *cfg = mmc->priv;
-	struct fsl_esdhc *regs = (struct fsl_esdhc *)cfg->esdhc_base;
+	struct fsl_esdhc_priv *priv = mmc->priv;
+	struct fsl_esdhc *regs = priv->esdhc_regs;
 	u32 value;
 	u32 time_out;
 
@@ -556,8 +582,8 @@  static void esdhc_clock_control(struct mmc *mmc, bool enable)
 
 static void esdhc_set_ios(struct mmc *mmc)
 {
-	struct fsl_esdhc_cfg *cfg = mmc->priv;
-	struct fsl_esdhc *regs = (struct fsl_esdhc *)cfg->esdhc_base;
+	struct fsl_esdhc_priv *priv = mmc->priv;
+	struct fsl_esdhc *regs = priv->esdhc_regs;
 
 #ifdef CONFIG_FSL_ESDHC_USE_PERIPHERAL_CLK
 	/* Select to use peripheral clock */
@@ -580,8 +606,8 @@  static void esdhc_set_ios(struct mmc *mmc)
 
 static int esdhc_init(struct mmc *mmc)
 {
-	struct fsl_esdhc_cfg *cfg = mmc->priv;
-	struct fsl_esdhc *regs = (struct fsl_esdhc *)cfg->esdhc_base;
+	struct fsl_esdhc_priv *priv = mmc->priv;
+	struct fsl_esdhc *regs = priv->esdhc_regs;
 	int timeout = 1000;
 
 	/* Reset the entire host controller */
@@ -621,14 +647,23 @@  static int esdhc_init(struct mmc *mmc)
 
 static int esdhc_getcd(struct mmc *mmc)
 {
-	struct fsl_esdhc_cfg *cfg = mmc->priv;
-	struct fsl_esdhc *regs = (struct fsl_esdhc *)cfg->esdhc_base;
+	struct fsl_esdhc_priv *priv = mmc->priv;
+	struct fsl_esdhc *regs = priv->esdhc_regs;
 	int timeout = 1000;
 
 #ifdef CONFIG_ESDHC_DETECT_QUIRK
 	if (CONFIG_ESDHC_DETECT_QUIRK)
 		return 1;
 #endif
+
+#ifdef CONFIG_DM_MMC
+	if (priv->non_removable)
+		return 1;
+
+	if (dm_gpio_is_valid(&priv->cd_gpio))
+		return dm_gpio_get_value(&priv->cd_gpio);
+#endif
+
 	while (!(esdhc_read32(&regs->prsstat) & PRSSTAT_CINS) && --timeout)
 		udelay(1000);
 
@@ -656,16 +691,29 @@  static const struct mmc_ops esdhc_ops = {
 	.getcd		= esdhc_getcd,
 };
 
-int fsl_esdhc_initialize(bd_t *bis, struct fsl_esdhc_cfg *cfg)
+static int fsl_esdhc_cfg_to_priv(struct fsl_esdhc_cfg *cfg,
+				 struct fsl_esdhc_priv *priv)
+{
+	if (!cfg || !priv)
+		return -EINVAL;
+
+	priv->esdhc_regs = (struct fsl_esdhc *)(unsigned long)(cfg->esdhc_base);
+	priv->bus_width = cfg->max_bus_width;
+	priv->sdhc_clk = cfg->sdhc_clk;
+
+	return 0;
+};
+
+static int fsl_esdhc_init(struct fsl_esdhc_priv *priv)
 {
 	struct fsl_esdhc *regs;
 	struct mmc *mmc;
 	u32 caps, voltage_caps;
 
-	if (!cfg)
-		return -1;
+	if (!priv)
+		return -EINVAL;
 
-	regs = (struct fsl_esdhc *)cfg->esdhc_base;
+	regs = priv->esdhc_regs;
 
 	/* First reset the eSDHC controller */
 	esdhc_reset(regs);
@@ -676,7 +724,7 @@  int fsl_esdhc_initialize(bd_t *bis, struct fsl_esdhc_cfg *cfg)
 #endif
 
 	writel(SDHCI_IRQ_EN_BITS, &regs->irqstaten);
-	memset(&cfg->cfg, 0, sizeof(cfg->cfg));
+	memset(&priv->cfg, 0, sizeof(priv->cfg));
 
 	voltage_caps = 0;
 	caps = esdhc_read32(&regs->hostcapblt);
@@ -698,47 +746,83 @@  int fsl_esdhc_initialize(bd_t *bis, struct fsl_esdhc_cfg *cfg)
 	if (caps & ESDHC_HOSTCAPBLT_VS33)
 		voltage_caps |= MMC_VDD_32_33 | MMC_VDD_33_34;
 
-	cfg->cfg.name = "FSL_SDHC";
-	cfg->cfg.ops = &esdhc_ops;
+	priv->cfg.name = "FSL_SDHC";
+	priv->cfg.ops = &esdhc_ops;
 #ifdef CONFIG_SYS_SD_VOLTAGE
-	cfg->cfg.voltages = CONFIG_SYS_SD_VOLTAGE;
+	priv->cfg.voltages = CONFIG_SYS_SD_VOLTAGE;
 #else
-	cfg->cfg.voltages = MMC_VDD_32_33 | MMC_VDD_33_34;
+	priv->cfg.voltages = MMC_VDD_32_33 | MMC_VDD_33_34;
 #endif
-	if ((cfg->cfg.voltages & voltage_caps) == 0) {
+	if ((priv->cfg.voltages & voltage_caps) == 0) {
 		printf("voltage not supported by controller\n");
 		return -1;
 	}
 
-	cfg->cfg.host_caps = MMC_MODE_4BIT | MMC_MODE_8BIT;
+	if (priv->bus_width == 8)
+		priv->cfg.host_caps = MMC_MODE_4BIT | MMC_MODE_8BIT;
+	else if (priv->bus_width == 4)
+		priv->cfg.host_caps = MMC_MODE_4BIT;
+
+	priv->cfg.host_caps = MMC_MODE_4BIT | MMC_MODE_8BIT;
 #ifdef CONFIG_SYS_FSL_ESDHC_HAS_DDR_MODE
-	cfg->cfg.host_caps |= MMC_MODE_DDR_52MHz;
+	priv->cfg.host_caps |= MMC_MODE_DDR_52MHz;
 #endif
 
-	if (cfg->max_bus_width > 0) {
-		if (cfg->max_bus_width < 8)
-			cfg->cfg.host_caps &= ~MMC_MODE_8BIT;
-		if (cfg->max_bus_width < 4)
-			cfg->cfg.host_caps &= ~MMC_MODE_4BIT;
+	if (priv->bus_width > 0) {
+		if (priv->bus_width < 8)
+			priv->cfg.host_caps &= ~MMC_MODE_8BIT;
+		if (priv->bus_width < 4)
+			priv->cfg.host_caps &= ~MMC_MODE_4BIT;
 	}
 
 	if (caps & ESDHC_HOSTCAPBLT_HSS)
-		cfg->cfg.host_caps |= MMC_MODE_HS_52MHz | MMC_MODE_HS;
+		priv->cfg.host_caps |= MMC_MODE_HS_52MHz | MMC_MODE_HS;
 
 #ifdef CONFIG_ESDHC_DETECT_8_BIT_QUIRK
 	if (CONFIG_ESDHC_DETECT_8_BIT_QUIRK)
-		cfg->cfg.host_caps &= ~MMC_MODE_8BIT;
+		priv->cfg.host_caps &= ~MMC_MODE_8BIT;
 #endif
 
-	cfg->cfg.f_min = 400000;
-	cfg->cfg.f_max = min(cfg->sdhc_clk, (u32)52000000);
+	priv->cfg.f_min = 400000;
+	priv->cfg.f_max = min(priv->sdhc_clk, (u32)52000000);
 
-	cfg->cfg.b_max = CONFIG_SYS_MMC_MAX_BLK_COUNT;
+	priv->cfg.b_max = CONFIG_SYS_MMC_MAX_BLK_COUNT;
 
-	mmc = mmc_create(&cfg->cfg, cfg);
+	mmc = mmc_create(&priv->cfg, priv);
 	if (mmc == NULL)
 		return -1;
 
+	priv->mmc = mmc;
+
+	return 0;
+}
+
+int fsl_esdhc_initialize(bd_t *bis, struct fsl_esdhc_cfg *cfg)
+{
+	struct fsl_esdhc_priv *priv;
+	int ret;
+
+	if (!cfg)
+		return -EINVAL;
+
+	priv = calloc(sizeof(struct fsl_esdhc_priv), 1);
+	if (!priv)
+		return -ENOMEM;
+
+	ret = fsl_esdhc_cfg_to_priv(cfg, priv);
+	if (ret) {
+		debug("%s xlate failure\n", __func__);
+		free(priv);
+		return ret;
+	}
+
+	ret = fsl_esdhc_init(priv);
+	if (ret) {
+		debug("%s init failure\n", __func__);
+		free(priv);
+		return ret;
+	}
+
 	return 0;
 }
 
@@ -819,3 +903,92 @@  void fdt_fixup_esdhc(void *blob, bd_t *bd)
 			   4 + 1, 1);
 }
 #endif
+
+#ifdef CONFIG_DM_MMC
+#include <asm/arch/clock.h>
+static int fsl_esdhc_probe(struct udevice *dev)
+{
+	struct mmc_uclass_priv *upriv = dev_get_uclass_priv(dev);
+	struct fsl_esdhc_priv *priv = dev_get_priv(dev);
+	const void *fdt = gd->fdt_blob;
+	int node = dev->of_offset;
+	fdt_addr_t addr;
+	unsigned int val;
+	int ret;
+
+	addr = dev_get_addr(dev);
+	if (addr == FDT_ADDR_T_NONE)
+		return -EINVAL;
+
+	priv->esdhc_regs = (struct fsl_esdhc *)addr;
+	priv->dev = dev;
+
+	val = fdtdec_get_int(fdt, node, "bus-width", -1);
+	if (val == 8)
+		priv->bus_width = 8;
+	else if (val == 4)
+		priv->bus_width = 4;
+	else
+		priv->bus_width = 1;
+
+	if (fdt_get_property(fdt, node, "non-removable", NULL)) {
+		priv->non_removable = 1;
+	 } else {
+		priv->non_removable = 0;
+		gpio_request_by_name_nodev(fdt, node, "cd-gpios", 0,
+					   &priv->cd_gpio, GPIOD_IS_IN);
+	}
+
+	/*
+	 * TODO:
+	 * Because lack of clk driver, if SDHC clk is not enabled,
+	 * need to enable it first before this driver is invoked.
+	 *
+	 * we use MXC_ESDHC_CLK to get clk freq.
+	 * If one would like to make this function work,
+	 * the aliases should be provided in dts as this:
+	 *
+	 *  aliases {
+	 *	mmc0 = &usdhc1;
+	 *	mmc1 = &usdhc2;
+	 *	mmc2 = &usdhc3;
+	 *	mmc3 = &usdhc4;
+	 *	};
+	 * Then if your board only supports mmc2 and mmc3, but we can
+	 * correctly get the seq as 2 and 3, then let mxc_get_clock
+	 * work as expected.
+	 */
+	priv->sdhc_clk = mxc_get_clock(MXC_ESDHC_CLK + dev->seq);
+	if (priv->sdhc_clk <= 0) {
+		dev_err(dev, "Unable to get clk for %s\n", dev->name);
+		return -EINVAL;
+	}
+
+	ret = fsl_esdhc_init(priv);
+	if (ret) {
+		dev_err(dev, "fsl_esdhc_init failure\n");
+		return ret;
+	}
+
+	upriv->mmc = priv->mmc;
+
+	return 0;
+}
+
+static const struct udevice_id fsl_esdhc_ids[] = {
+	{ .compatible = "fsl,imx6ul-usdhc", },
+	{ .compatible = "fsl,imx6sx-usdhc", },
+	{ .compatible = "fsl,imx6sl-usdhc", },
+	{ .compatible = "fsl,imx6q-usdhc", },
+	{ .compatible = "fsl,imx7d-usdhc", },
+	{ /* sentinel */ }
+};
+
+U_BOOT_DRIVER(fsl_esdhc) = {
+	.name	= "fsl-esdhc-mmc",
+	.id	= UCLASS_MMC,
+	.of_match = fsl_esdhc_ids,
+	.probe	= fsl_esdhc_probe,
+	.priv_auto_alloc_size = sizeof(struct fsl_esdhc_priv),
+};
+#endif