]> git.sur5r.net Git - u-boot/blobdiff - drivers/mmc/fsl_esdhc.c
Merge branch 'master' of git://git.denx.de/u-boot-avr32
[u-boot] / drivers / mmc / fsl_esdhc.c
index 73d5cd3826afb5efe5434e8c498f475f76f8b338..55416136ab2a46b15371be43517837b7c46b4aae 100644 (file)
@@ -1,28 +1,12 @@
 /*
- * Copyright 2007,2010 Freescale Semiconductor, Inc
+ * Copyright 2007, 2010-2011 Freescale Semiconductor, Inc
  * Andy Fleming
  *
  * Based vaguely on the pxa mmc code:
  * (C) Copyright 2003
  * Kyle Harris, Nexus Technologies, Inc. kharris@nexus-tech.net
  *
- * See file CREDITS for list of people who contributed to this
- * project.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation; either version 2 of
- * the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
- * MA 02111-1307 USA
+ * SPDX-License-Identifier:    GPL-2.0+
  */
 
 #include <config.h>
 DECLARE_GLOBAL_DATA_PTR;
 
 struct fsl_esdhc {
-       uint    dsaddr;
-       uint    blkattr;
-       uint    cmdarg;
-       uint    xfertyp;
-       uint    cmdrsp0;
-       uint    cmdrsp1;
-       uint    cmdrsp2;
-       uint    cmdrsp3;
-       uint    datport;
-       uint    prsstat;
-       uint    proctl;
-       uint    sysctl;
-       uint    irqstat;
-       uint    irqstaten;
-       uint    irqsigen;
-       uint    autoc12err;
-       uint    hostcapblt;
-       uint    wml;
-       char    reserved1[8];
-       uint    fevt;
-       char    reserved2[168];
-       uint    hostver;
-       char    reserved3[780];
-       uint    scr;
+       uint    dsaddr;         /* SDMA system address register */
+       uint    blkattr;        /* Block attributes register */
+       uint    cmdarg;         /* Command argument register */
+       uint    xfertyp;        /* Transfer type register */
+       uint    cmdrsp0;        /* Command response 0 register */
+       uint    cmdrsp1;        /* Command response 1 register */
+       uint    cmdrsp2;        /* Command response 2 register */
+       uint    cmdrsp3;        /* Command response 3 register */
+       uint    datport;        /* Buffer data port register */
+       uint    prsstat;        /* Present state register */
+       uint    proctl;         /* Protocol control register */
+       uint    sysctl;         /* System Control Register */
+       uint    irqstat;        /* Interrupt status register */
+       uint    irqstaten;      /* Interrupt status enable register */
+       uint    irqsigen;       /* Interrupt signal enable register */
+       uint    autoc12err;     /* Auto CMD error status register */
+       uint    hostcapblt;     /* Host controller capabilities register */
+       uint    wml;            /* Watermark level register */
+       uint    mixctrl;        /* For USDHC */
+       char    reserved1[4];   /* reserved */
+       uint    fevt;           /* Force event register */
+       uint    admaes;         /* ADMA error status register */
+       uint    adsaddr;        /* ADMA system address register */
+       char    reserved2[160]; /* reserved */
+       uint    hostver;        /* Host controller version register */
+       char    reserved3[4];   /* reserved */
+       uint    dmaerraddr;     /* DMA error address register */
+       char    reserved4[4];   /* reserved */
+       uint    dmaerrattr;     /* DMA error attribute register */
+       char    reserved5[4];   /* reserved */
+       uint    hostcapblt2;    /* Host controller capabilities register 2 */
+       char    reserved6[8];   /* reserved */
+       uint    tcr;            /* Tuning control register */
+       char    reserved7[28];  /* reserved */
+       uint    sddirctl;       /* SD direction control register */
+       char    reserved8[712]; /* reserved */
+       uint    scr;            /* eSDHC control register */
 };
 
 /* Return the XFERTYP flags for a given command and data packet */
-uint esdhc_xfertyp(struct mmc_cmd *cmd, struct mmc_data *data)
+static uint esdhc_xfertyp(struct mmc_cmd *cmd, struct mmc_data *data)
 {
        uint xfertyp = 0;
 
@@ -79,6 +76,9 @@ uint esdhc_xfertyp(struct mmc_cmd *cmd, struct mmc_data *data)
                if (data->blocks > 1) {
                        xfertyp |= XFERTYP_MSBSEL;
                        xfertyp |= XFERTYP_BCEN;
+#ifdef CONFIG_SYS_FSL_ERRATUM_ESDHC111
+                       xfertyp |= XFERTYP_AC12EN;
+#endif
                }
 
                if (data->flags & MMC_DATA_READ)
@@ -96,6 +96,10 @@ uint esdhc_xfertyp(struct mmc_cmd *cmd, struct mmc_data *data)
        else if (cmd->resp_type & MMC_RSP_PRESENT)
                xfertyp |= XFERTYP_RSPTYP_48;
 
+#if defined(CONFIG_MX53) || defined(CONFIG_PPC_T4240)
+       if (cmd->cmdidx == MMC_CMD_STOP_TRANSMISSION)
+               xfertyp |= XFERTYP_CMDTYP_ABORT;
+#endif
        return XFERTYP_CMD(cmd->cmdidx) | xfertyp;
 }
 
@@ -106,7 +110,8 @@ 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 *regs = mmc->priv;
+       struct fsl_esdhc_cfg *cfg = mmc->priv;
+       struct fsl_esdhc *regs = (struct fsl_esdhc *)cfg->esdhc_base;
        uint blocks;
        char *buffer;
        uint databuf;
@@ -167,22 +172,29 @@ 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 = (struct fsl_esdhc_cfg *)mmc->priv;
+       struct fsl_esdhc_cfg *cfg = mmc->priv;
        struct fsl_esdhc *regs = (struct fsl_esdhc *)cfg->esdhc_base;
-#ifndef CONFIG_SYS_FSL_ESDHC_USE_PIO
+
        uint wml_value;
 
        wml_value = data->blocksize/4;
 
        if (data->flags & MMC_DATA_READ) {
-               if (wml_value > 0x10)
-                       wml_value = 0x10;
+               if (wml_value > WML_RD_WML_MAX)
+                       wml_value = WML_RD_WML_MAX_VAL;
 
                esdhc_clrsetbits32(&regs->wml, WML_RD_WML_MASK, wml_value);
+#ifndef CONFIG_SYS_FSL_ESDHC_USE_PIO
                esdhc_write32(&regs->dsaddr, (u32)data->dest);
+#endif
        } else {
-               if (wml_value > 0x80)
-                       wml_value = 0x80;
+#ifndef CONFIG_SYS_FSL_ESDHC_USE_PIO
+               flush_dcache_range((ulong)data->src,
+                                  (ulong)data->src+data->blocks
+                                        *data->blocksize);
+#endif
+               if (wml_value > WML_WR_WML_MAX)
+                       wml_value = WML_WR_WML_MAX_VAL;
                if ((esdhc_read32(&regs->prsstat) & PRSSTAT_WPSPL) == 0) {
                        printf("\nThe SD card is locked. Can not write to a locked card.\n\n");
                        return TIMEOUT;
@@ -190,24 +202,29 @@ static int esdhc_setup_data(struct mmc *mmc, struct mmc_data *data)
 
                esdhc_clrsetbits32(&regs->wml, WML_WR_WML_MASK,
                                        wml_value << 16);
+#ifndef CONFIG_SYS_FSL_ESDHC_USE_PIO
                esdhc_write32(&regs->dsaddr, (u32)data->src);
+#endif
        }
-#else  /* CONFIG_SYS_FSL_ESDHC_USE_PIO */
-       if (!(data->flags & MMC_DATA_READ)) {
-               if ((esdhc_read32(&regs->prsstat) & PRSSTAT_WPSPL) == 0) {
-                       printf("\nThe SD card is locked. "
-                               "Can not write to a locked card.\n\n");
-                       return TIMEOUT;
-               }
-               esdhc_write32(&regs->dsaddr, (u32)data->src);
-       } else
-               esdhc_write32(&regs->dsaddr, (u32)data->dest);
-#endif /* CONFIG_SYS_FSL_ESDHC_USE_PIO */
 
        esdhc_write32(&regs->blkattr, data->blocks << 16 | data->blocksize);
 
        /* Calculate the timeout period for data transactions */
-       timeout = fls(mmc->tran_speed/10) - 1;
+       /*
+        * 1)Timeout period = (2^(timeout+13)) SD Clock cycles
+        * 2)Timeout period should be minimum 0.250sec as per SD Card spec
+        *  So, Number of SD Clock cycles for 0.25sec should be minimum
+        *              (SD Clock/sec * 0.25 sec) SD Clock cycles
+        *              = (mmc->clock * 1/4) SD Clock cycles
+        * As 1) >=  2)
+        * => (2^(timeout+13)) >= mmc->clock * 1/4
+        * Taking log2 both the sides
+        * => timeout + 13 >= log2(mmc->clock/4)
+        * Rounding up to next power of 2
+        * => timeout + 13 = log2(mmc->clock/4) + 1
+        * => timeout + 13 = fls(mmc->clock/4)
+        */
+       timeout = fls(mmc->clock/4);
        timeout -= 13;
 
        if (timeout > 14)
@@ -216,11 +233,30 @@ static int esdhc_setup_data(struct mmc *mmc, struct mmc_data *data)
        if (timeout < 0)
                timeout = 0;
 
+#ifdef CONFIG_SYS_FSL_ERRATUM_ESDHC_A001
+       if ((timeout == 4) || (timeout == 8) || (timeout == 12))
+               timeout++;
+#endif
+
+#ifdef ESDHCI_QUIRK_BROKEN_TIMEOUT_VALUE
+       timeout = 0xE;
+#endif
        esdhc_clrsetbits32(&regs->sysctl, SYSCTL_TIMEOUT_MASK, timeout << 16);
 
        return 0;
 }
 
+#ifndef CONFIG_SYS_FSL_ESDHC_USE_PIO
+static void check_and_invalidate_dcache_range
+       (struct mmc_cmd *cmd,
+        struct mmc_data *data) {
+       unsigned start = (unsigned)data->dest ;
+       unsigned size = roundup(ARCH_DMA_MINALIGN,
+                               data->blocks*data->blocksize);
+       unsigned end = start+size ;
+       invalidate_dcache_range(start, end);
+}
+#endif
 
 /*
  * Sends a command out on the bus.  Takes the mmc pointer,
@@ -229,11 +265,17 @@ static int esdhc_setup_data(struct mmc *mmc, struct mmc_data *data)
 static int
 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 = (struct fsl_esdhc_cfg *)mmc->priv;
+       struct fsl_esdhc_cfg *cfg = mmc->priv;
        volatile struct fsl_esdhc *regs = (struct fsl_esdhc *)cfg->esdhc_base;
 
+#ifdef CONFIG_SYS_FSL_ERRATUM_ESDHC111
+       if (cmd->cmdidx == MMC_CMD_STOP_TRANSMISSION)
+               return 0;
+#endif
+
        esdhc_write32(&regs->irqstat, -1);
 
        sync();
@@ -255,8 +297,6 @@ esdhc_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd, struct mmc_data *data)
 
        /* Set up for a data transfer if we have one */
        if (data) {
-               int err;
-
                err = esdhc_setup_data(mmc, data);
                if(err)
                        return err;
@@ -265,22 +305,52 @@ esdhc_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd, struct mmc_data *data)
        /* Figure out the transfer arguments */
        xfertyp = esdhc_xfertyp(cmd, data);
 
+       /* Mask all irqs */
+       esdhc_write32(&regs->irqsigen, 0);
+
        /* Send the command */
        esdhc_write32(&regs->cmdarg, cmd->cmdarg);
+#if defined(CONFIG_FSL_USDHC)
+       esdhc_write32(&regs->mixctrl,
+       (esdhc_read32(&regs->mixctrl) & 0xFFFFFF80) | (xfertyp & 0x7F));
+       esdhc_write32(&regs->xfertyp, xfertyp & 0xFFFF0000);
+#else
        esdhc_write32(&regs->xfertyp, xfertyp);
+#endif
 
        /* Wait for the command to complete */
-       while (!(esdhc_read32(&regs->irqstat) & IRQSTAT_CC))
+       while (!(esdhc_read32(&regs->irqstat) & (IRQSTAT_CC | IRQSTAT_CTOE)))
                ;
 
        irqstat = esdhc_read32(&regs->irqstat);
-       esdhc_write32(&regs->irqstat, irqstat);
 
-       if (irqstat & CMD_ERR)
-               return COMM_ERR;
+       if (irqstat & CMD_ERR) {
+               err = COMM_ERR;
+               goto out;
+       }
 
-       if (irqstat & IRQSTAT_CTOE)
-               return TIMEOUT;
+       if (irqstat & IRQSTAT_CTOE) {
+               err = TIMEOUT;
+               goto out;
+       }
+
+       /* Workaround for ESDHC errata ENGcm03648 */
+       if (!data && (cmd->resp_type & MMC_RSP_BUSY)) {
+               int timeout = 2500;
+
+               /* Poll on DATA0 line for cmd with busy signal for 250 ms */
+               while (timeout > 0 && !(esdhc_read32(&regs->prsstat) &
+                                       PRSSTAT_DAT0)) {
+                       udelay(100);
+                       timeout--;
+               }
+
+               if (timeout <= 0) {
+                       printf("Timeout waiting for DAT0 to go high!\n");
+                       err = TIMEOUT;
+                       goto out;
+               }
+       }
 
        /* Copy the response to the response buffer */
        if (cmd->resp_type & MMC_RSP_136) {
@@ -305,31 +375,54 @@ esdhc_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd, struct mmc_data *data)
                do {
                        irqstat = esdhc_read32(&regs->irqstat);
 
-                       if (irqstat & DATA_ERR)
-                               return COMM_ERR;
+                       if (irqstat & IRQSTAT_DTOE) {
+                               err = TIMEOUT;
+                               goto out;
+                       }
+
+                       if (irqstat & DATA_ERR) {
+                               err = COMM_ERR;
+                               goto out;
+                       }
+               } while ((irqstat & DATA_COMPLETE) != DATA_COMPLETE);
 
-                       if (irqstat & IRQSTAT_DTOE)
-                               return TIMEOUT;
-               } while (!(irqstat & IRQSTAT_TC) &&
-                               (esdhc_read32(&regs->prsstat) & PRSSTAT_DLA));
+               if (data->flags & MMC_DATA_READ)
+                       check_and_invalidate_dcache_range(cmd, data);
 #endif
        }
 
+out:
+       /* Reset CMD and DATA portions on error */
+       if (err) {
+               esdhc_write32(&regs->sysctl, esdhc_read32(&regs->sysctl) |
+                             SYSCTL_RSTC);
+               while (esdhc_read32(&regs->sysctl) & SYSCTL_RSTC)
+                       ;
+
+               if (data) {
+                       esdhc_write32(&regs->sysctl,
+                                     esdhc_read32(&regs->sysctl) |
+                                     SYSCTL_RSTD);
+                       while ((esdhc_read32(&regs->sysctl) & SYSCTL_RSTD))
+                               ;
+               }
+       }
+
        esdhc_write32(&regs->irqstat, -1);
 
-       return 0;
+       return err;
 }
 
-void set_sysctl(struct mmc *mmc, uint clock)
+static void set_sysctl(struct mmc *mmc, uint clock)
 {
-       int sdhc_clk = gd->sdhc_clk;
        int div, pre_div;
-       struct fsl_esdhc_cfg *cfg = (struct fsl_esdhc_cfg *)mmc->priv;
+       struct fsl_esdhc_cfg *cfg = mmc->priv;
        volatile struct fsl_esdhc *regs = (struct fsl_esdhc *)cfg->esdhc_base;
+       int sdhc_clk = cfg->sdhc_clk;
        uint clk;
 
-       if (clock < mmc->f_min)
-               clock = mmc->f_min;
+       if (clock < mmc->cfg->f_min)
+               clock = mmc->cfg->f_min;
 
        if (sdhc_clk / 16 > clock) {
                for (pre_div = 2; pre_div < 256; pre_div *= 2)
@@ -360,7 +453,7 @@ void set_sysctl(struct mmc *mmc, uint clock)
 
 static void esdhc_set_ios(struct mmc *mmc)
 {
-       struct fsl_esdhc_cfg *cfg = (struct fsl_esdhc_cfg *)mmc->priv;
+       struct fsl_esdhc_cfg *cfg = mmc->priv;
        struct fsl_esdhc *regs = (struct fsl_esdhc *)cfg->esdhc_base;
 
        /* Set the clock speed */
@@ -378,24 +471,23 @@ static void esdhc_set_ios(struct mmc *mmc)
 
 static int esdhc_init(struct mmc *mmc)
 {
-       struct fsl_esdhc_cfg *cfg = (struct fsl_esdhc_cfg *)mmc->priv;
+       struct fsl_esdhc_cfg *cfg = mmc->priv;
        struct fsl_esdhc *regs = (struct fsl_esdhc *)cfg->esdhc_base;
        int timeout = 1000;
-       int ret = 0;
-       u8 card_absent;
 
        /* Reset the entire host controller */
-       esdhc_write32(&regs->sysctl, SYSCTL_RSTA);
+       esdhc_setbits32(&regs->sysctl, SYSCTL_RSTA);
 
        /* Wait until the controller is available */
        while ((esdhc_read32(&regs->sysctl) & SYSCTL_RSTA) && --timeout)
                udelay(1000);
 
+#ifndef ARCH_MXC
        /* Enable cache snooping */
-       if (cfg && !cfg->no_snoop)
-               esdhc_write32(&regs->scr, 0x00000040);
+       esdhc_write32(&regs->scr, 0x00000040);
+#endif
 
-       esdhc_write32(&regs->sysctl, SYSCTL_HCKEN | SYSCTL_IPGEN);
+       esdhc_setbits32(&regs->sysctl, SYSCTL_HCKEN | SYSCTL_IPGEN);
 
        /* Set the initial clock speed */
        mmc_set_clock(mmc, 400000);
@@ -409,21 +501,23 @@ static int esdhc_init(struct mmc *mmc)
        /* Set timout to the maximum value */
        esdhc_clrsetbits32(&regs->sysctl, SYSCTL_TIMEOUT_MASK, 14 << 16);
 
-       /* Check if there is a callback for detecting the card */
-       if (board_mmc_getcd(&card_absent, mmc)) {
-               timeout = 1000;
-               while (!(esdhc_read32(&regs->prsstat) & PRSSTAT_CINS) &&
-                               --timeout)
-                       udelay(1000);
+       return 0;
+}
 
-               if (timeout <= 0)
-                       ret = NO_CARD_ERR;
-       } else {
-               if (card_absent)
-                       ret = NO_CARD_ERR;
-       }
+static int esdhc_getcd(struct mmc *mmc)
+{
+       struct fsl_esdhc_cfg *cfg = mmc->priv;
+       struct fsl_esdhc *regs = (struct fsl_esdhc *)cfg->esdhc_base;
+       int timeout = 1000;
 
-       return ret;
+#ifdef CONFIG_ESDHC_DETECT_QUIRK
+       if (CONFIG_ESDHC_DETECT_QUIRK)
+               return 1;
+#endif
+       while (!(esdhc_read32(&regs->prsstat) & PRSSTAT_CINS) && --timeout)
+               udelay(1000);
+
+       return timeout > 0;
 }
 
 static void esdhc_reset(struct fsl_esdhc *regs)
@@ -431,7 +525,7 @@ static void esdhc_reset(struct fsl_esdhc *regs)
        unsigned long timeout = 100; /* wait max 100 ms */
 
        /* reset the controller */
-       esdhc_write32(&regs->sysctl, SYSCTL_RSTA);
+       esdhc_setbits32(&regs->sysctl, SYSCTL_RSTA);
 
        /* hardware clears the bit when it is done */
        while ((esdhc_read32(&regs->sysctl) & SYSCTL_RSTA) && --timeout)
@@ -440,46 +534,89 @@ static void esdhc_reset(struct fsl_esdhc *regs)
                printf("MMC/SD: Reset never completed.\n");
 }
 
+static const struct mmc_ops esdhc_ops = {
+       .send_cmd       = esdhc_send_cmd,
+       .set_ios        = esdhc_set_ios,
+       .init           = esdhc_init,
+       .getcd          = esdhc_getcd,
+};
+
 int fsl_esdhc_initialize(bd_t *bis, struct fsl_esdhc_cfg *cfg)
 {
        struct fsl_esdhc *regs;
        struct mmc *mmc;
-       u32 caps;
+       u32 caps, voltage_caps;
 
        if (!cfg)
                return -1;
 
-       mmc = malloc(sizeof(struct mmc));
-
-       sprintf(mmc->name, "FSL_ESDHC");
        regs = (struct fsl_esdhc *)cfg->esdhc_base;
 
        /* First reset the eSDHC controller */
        esdhc_reset(regs);
 
-       mmc->priv = cfg;
-       mmc->send_cmd = esdhc_send_cmd;
-       mmc->set_ios = esdhc_set_ios;
-       mmc->init = esdhc_init;
+       esdhc_setbits32(&regs->sysctl, SYSCTL_PEREN | SYSCTL_HCKEN
+                               | SYSCTL_IPGEN | SYSCTL_CKEN);
+
+       memset(&cfg->cfg, 0, sizeof(cfg->cfg));
 
+       voltage_caps = 0;
        caps = regs->hostcapblt;
 
+#ifdef CONFIG_SYS_FSL_ERRATUM_ESDHC135
+       caps = caps & ~(ESDHC_HOSTCAPBLT_SRS |
+                       ESDHC_HOSTCAPBLT_VS18 | ESDHC_HOSTCAPBLT_VS30);
+#endif
+
+/* T4240 host controller capabilities register should have VS33 bit */
+#ifdef CONFIG_SYS_FSL_MMC_HAS_CAPBLT_VS33
+       caps = caps | ESDHC_HOSTCAPBLT_VS33;
+#endif
+
        if (caps & ESDHC_HOSTCAPBLT_VS18)
-               mmc->voltages |= MMC_VDD_165_195;
+               voltage_caps |= MMC_VDD_165_195;
        if (caps & ESDHC_HOSTCAPBLT_VS30)
-               mmc->voltages |= MMC_VDD_29_30 | MMC_VDD_30_31;
+               voltage_caps |= MMC_VDD_29_30 | MMC_VDD_30_31;
        if (caps & ESDHC_HOSTCAPBLT_VS33)
-               mmc->voltages |= MMC_VDD_32_33 | MMC_VDD_33_34;
+               voltage_caps |= MMC_VDD_32_33 | MMC_VDD_33_34;
+
+       cfg->cfg.name = "FSL_SDHC";
+       cfg->cfg.ops = &esdhc_ops;
+#ifdef CONFIG_SYS_SD_VOLTAGE
+       cfg->cfg.voltages = CONFIG_SYS_SD_VOLTAGE;
+#else
+       cfg->cfg.voltages = MMC_VDD_32_33 | MMC_VDD_33_34;
+#endif
+       if ((cfg->cfg.voltages & voltage_caps) == 0) {
+               printf("voltage not supported by controller\n");
+               return -1;
+       }
+
+       cfg->cfg.host_caps = MMC_MODE_4BIT | MMC_MODE_8BIT | MMC_MODE_HC;
 
-       mmc->host_caps = MMC_MODE_4BIT | MMC_MODE_8BIT;
+       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 (caps & ESDHC_HOSTCAPBLT_HSS)
-               mmc->host_caps |= MMC_MODE_HS_52MHz | MMC_MODE_HS;
+               cfg->cfg.host_caps |= MMC_MODE_HS_52MHz | MMC_MODE_HS;
 
-       mmc->f_min = 400000;
-       mmc->f_max = MIN(gd->sdhc_clk, 50000000);
+#ifdef CONFIG_ESDHC_DETECT_8_BIT_QUIRK
+       if (CONFIG_ESDHC_DETECT_8_BIT_QUIRK)
+               cfg->cfg.host_caps &= ~MMC_MODE_8BIT;
+#endif
+
+       cfg->cfg.f_min = 400000;
+       cfg->cfg.f_max = MIN(gd->arch.sdhc_clk, 52000000);
 
-       mmc_register(mmc);
+       cfg->cfg.b_max = CONFIG_SYS_MMC_MAX_BLK_COUNT;
+
+       mmc = mmc_create(&cfg->cfg, cfg);
+       if (mmc == NULL)
+               return -1;
 
        return 0;
 }
@@ -488,9 +625,9 @@ int fsl_esdhc_mmc_init(bd_t *bis)
 {
        struct fsl_esdhc_cfg *cfg;
 
-       cfg = malloc(sizeof(struct fsl_esdhc_cfg));
-       memset(cfg, 0, sizeof(struct fsl_esdhc_cfg));
+       cfg = calloc(sizeof(struct fsl_esdhc_cfg), 1);
        cfg->esdhc_base = CONFIG_SYS_FSL_ESDHC_ADDR;
+       cfg->sdhc_clk = gd->arch.sdhc_clk;
        return fsl_esdhc_initialize(bis, cfg);
 }
 
@@ -498,17 +635,19 @@ int fsl_esdhc_mmc_init(bd_t *bis)
 void fdt_fixup_esdhc(void *blob, bd_t *bd)
 {
        const char *compat = "fsl,esdhc";
-       const char *status = "okay";
 
+#ifdef CONFIG_FSL_ESDHC_PIN_MUX
        if (!hwconfig("esdhc")) {
-               status = "disabled";
-               goto out;
+               do_fixup_by_compat(blob, compat, "status", "disabled",
+                               8 + 1, 1);
+               return;
        }
+#endif
 
        do_fixup_by_compat_u32(blob, compat, "clock-frequency",
-                              gd->sdhc_clk, 1);
-out:
-       do_fixup_by_compat(blob, compat, "status", status,
-                          strlen(status) + 1, 1);
+                              gd->arch.sdhc_clk, 1);
+
+       do_fixup_by_compat(blob, compat, "status", "okay",
+                          4 + 1, 1);
 }
 #endif