Merge git://www.denx.de/git/u-boot-imx
[platform/kernel/u-boot.git] / drivers / mmc / fsl_esdhc.c
index b7b4f14..73748c5 100644 (file)
 #include <config.h>
 #include <common.h>
 #include <command.h>
+#include <errno.h>
 #include <hwconfig.h>
 #include <mmc.h>
 #include <part.h>
 #include <malloc.h>
-#include <mmc.h>
 #include <fsl_esdhc.h>
 #include <fdt_support.h>
 #include <asm/io.h>
@@ -56,21 +56,27 @@ struct fsl_esdhc {
        uint    fevt;           /* Force event register */
        uint    admaes;         /* ADMA error status register */
        uint    adsaddr;        /* ADMA system address register */
-       char    reserved2[100]; /* reserved */
-       uint    vendorspec;     /* Vendor Specific register */
-       char    reserved3[56];  /* reserved */
+       char    reserved2[4];
+       uint    dllctrl;
+       uint    dllstat;
+       uint    clktunectrlstatus;
+       char    reserved3[84];
+       uint    vendorspec;
+       uint    mmcboot;
+       uint    vendorspec2;
+       char    reserved4[48];
        uint    hostver;        /* Host controller version register */
-       char    reserved4[4];   /* reserved */
-       uint    dmaerraddr;     /* DMA error address register */
        char    reserved5[4];   /* reserved */
-       uint    dmaerrattr;     /* DMA error attribute register */
+       uint    dmaerraddr;     /* DMA error address register */
        char    reserved6[4];   /* reserved */
+       uint    dmaerrattr;     /* DMA error attribute register */
+       char    reserved7[4];   /* reserved */
        uint    hostcapblt2;    /* Host controller capabilities register 2 */
-       char    reserved7[8];   /* reserved */
+       char    reserved8[8];   /* reserved */
        uint    tcr;            /* Tuning control register */
-       char    reserved8[28];  /* reserved */
+       char    reserved9[28];  /* reserved */
        uint    sddirctl;       /* SD direction control register */
-       char    reserved9[712]; /* reserved */
+       char    reserved10[712];/* reserved */
        uint    scr;            /* eSDHC control register */
 };
 
@@ -85,7 +91,9 @@ struct fsl_esdhc {
  * Following is used when Driver Model is enabled for MMC
  * @dev: pointer for the device
  * @non_removable: 0: removable; 1: non-removable
+ * @wp_enable: 1: enable checking wp; 0: no check
  * @cd_gpio: gpio for card detection
+ * @wp_gpio: gpio for write protection
  */
 struct fsl_esdhc_priv {
        struct fsl_esdhc *esdhc_regs;
@@ -95,7 +103,11 @@ struct fsl_esdhc_priv {
        struct mmc *mmc;
        struct udevice *dev;
        int non_removable;
+       int wp_enable;
+#ifdef CONFIG_DM_GPIO
        struct gpio_desc cd_gpio;
+       struct gpio_desc wp_gpio;
+#endif
 };
 
 /* Return the XFERTYP flags for a given command and data packet */
@@ -239,9 +251,12 @@ static int esdhc_setup_data(struct mmc *mmc, struct mmc_data *data)
 #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;
+               if (priv->wp_enable) {
+                       if ((esdhc_read32(&regs->prsstat) &
+                           PRSSTAT_WPSPL) == 0) {
+                               printf("\nThe SD card is locked. Can not write to a locked card.\n\n");
+                               return -ETIMEDOUT;
+                       }
                }
 
                esdhc_clrsetbits32(&regs->wml, WML_WR_WML_MASK,
@@ -398,12 +413,12 @@ esdhc_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd, struct mmc_data *data)
        irqstat = esdhc_read32(&regs->irqstat);
 
        if (irqstat & CMD_ERR) {
-               err = COMM_ERR;
+               err = -ECOMM;
                goto out;
        }
 
        if (irqstat & IRQSTAT_CTOE) {
-               err = TIMEOUT;
+               err = -ETIMEDOUT;
                goto out;
        }
 
@@ -429,7 +444,7 @@ esdhc_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd, struct mmc_data *data)
 
                if (timeout <= 0) {
                        printf("Timeout waiting for DAT0 to go high!\n");
-                       err = TIMEOUT;
+                       err = -ETIMEDOUT;
                        goto out;
                }
        }
@@ -458,12 +473,12 @@ esdhc_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd, struct mmc_data *data)
                        irqstat = esdhc_read32(&regs->irqstat);
 
                        if (irqstat & IRQSTAT_DTOE) {
-                               err = TIMEOUT;
+                               err = -ETIMEDOUT;
                                goto out;
                        }
 
                        if (irqstat & DATA_ERR) {
-                               err = COMM_ERR;
+                               err = -ECOMM;
                                goto out;
                        }
                } while ((irqstat & DATA_COMPLETE) != DATA_COMPLETE);
@@ -506,7 +521,13 @@ out:
 
 static void set_sysctl(struct mmc *mmc, uint clock)
 {
-       int div, pre_div;
+       int div = 1;
+#ifdef ARCH_MXC
+       int pre_div = 1;
+#else
+       int pre_div = 2;
+#endif
+       int ddr_pre_div = mmc->ddr_mode ? 2 : 1;
        struct fsl_esdhc_priv *priv = mmc->priv;
        struct fsl_esdhc *regs = priv->esdhc_regs;
        int sdhc_clk = priv->sdhc_clk;
@@ -515,24 +536,19 @@ static void set_sysctl(struct mmc *mmc, uint clock)
        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)
-                       if ((sdhc_clk / pre_div) <= (clock * 16))
-                               break;
-       } else
-               pre_div = 2;
+       while (sdhc_clk / (16 * pre_div * ddr_pre_div) > clock && pre_div < 256)
+               pre_div *= 2;
 
-       for (div = 1; div <= 16; div++)
-               if ((sdhc_clk / (div * pre_div)) <= clock)
-                       break;
+       while (sdhc_clk / (div * pre_div * ddr_pre_div) > clock && div < 16)
+               div++;
 
-       pre_div >>= mmc->ddr_mode ? 2 : 1;
+       pre_div >>= 1;
        div -= 1;
 
        clk = (pre_div << 8) | (div << 4);
 
 #ifdef CONFIG_FSL_USDHC
-       esdhc_setbits32(&regs->sysctl, SYSCTL_RSTA);
+       esdhc_clrbits32(&regs->vendorspec, VENDORSPEC_CKEN);
 #else
        esdhc_clrbits32(&regs->sysctl, SYSCTL_CKEN);
 #endif
@@ -542,7 +558,7 @@ static void set_sysctl(struct mmc *mmc, uint clock)
        udelay(10000);
 
 #ifdef CONFIG_FSL_USDHC
-       esdhc_clrbits32(&regs->sysctl, SYSCTL_RSTA);
+       esdhc_setbits32(&regs->vendorspec, VENDORSPEC_PEREN | VENDORSPEC_CKEN);
 #else
        esdhc_setbits32(&regs->sysctl, SYSCTL_PEREN | SYSCTL_CKEN);
 #endif
@@ -579,7 +595,7 @@ static void esdhc_clock_control(struct mmc *mmc, bool enable)
 }
 #endif
 
-static void esdhc_set_ios(struct mmc *mmc)
+static int esdhc_set_ios(struct mmc *mmc)
 {
        struct fsl_esdhc_priv *priv = mmc->priv;
        struct fsl_esdhc *regs = priv->esdhc_regs;
@@ -601,6 +617,7 @@ static void esdhc_set_ios(struct mmc *mmc)
        else if (mmc->bus_width == 8)
                esdhc_setbits32(&regs->proctl, PROCTL_DTW_8);
 
+       return 0;
 }
 
 static int esdhc_init(struct mmc *mmc)
@@ -616,6 +633,20 @@ static int esdhc_init(struct mmc *mmc)
        while ((esdhc_read32(&regs->sysctl) & SYSCTL_RSTA) && --timeout)
                udelay(1000);
 
+#if defined(CONFIG_FSL_USDHC)
+       /* RSTA doesn't reset MMC_BOOT register, so manually reset it */
+       esdhc_write32(&regs->mmcboot, 0x0);
+       /* Reset MIX_CTRL and CLK_TUNE_CTRL_STATUS regs to 0 */
+       esdhc_write32(&regs->mixctrl, 0x0);
+       esdhc_write32(&regs->clktunectrlstatus, 0x0);
+
+       /* Put VEND_SPEC to default value */
+       esdhc_write32(&regs->vendorspec, VENDORSPEC_INIT);
+
+       /* Disable DLL_CTRL delay line */
+       esdhc_write32(&regs->dllctrl, 0x0);
+#endif
+
 #ifndef ARCH_MXC
        /* Enable cache snooping */
        esdhc_write32(&regs->scr, 0x00000040);
@@ -623,6 +654,8 @@ static int esdhc_init(struct mmc *mmc)
 
 #ifndef CONFIG_FSL_USDHC
        esdhc_setbits32(&regs->sysctl, SYSCTL_HCKEN | SYSCTL_IPGEN);
+#else
+       esdhc_setbits32(&regs->vendorspec, VENDORSPEC_HCKEN | VENDORSPEC_IPGEN);
 #endif
 
        /* Set the initial clock speed */
@@ -658,10 +691,11 @@ static int esdhc_getcd(struct mmc *mmc)
 #ifdef CONFIG_DM_MMC
        if (priv->non_removable)
                return 1;
-
+#ifdef CONFIG_DM_GPIO
        if (dm_gpio_is_valid(&priv->cd_gpio))
                return dm_gpio_get_value(&priv->cd_gpio);
 #endif
+#endif
 
        while (!(esdhc_read32(&regs->prsstat) & PRSSTAT_CINS) && --timeout)
                udelay(1000);
@@ -690,19 +724,6 @@ static const struct mmc_ops esdhc_ops = {
        .getcd          = esdhc_getcd,
 };
 
-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;
@@ -720,6 +741,9 @@ static int fsl_esdhc_init(struct fsl_esdhc_priv *priv)
 #ifndef CONFIG_FSL_USDHC
        esdhc_setbits32(&regs->sysctl, SYSCTL_PEREN | SYSCTL_HCKEN
                                | SYSCTL_IPGEN | SYSCTL_CKEN);
+#else
+       esdhc_setbits32(&regs->vendorspec, VENDORSPEC_PEREN |
+                       VENDORSPEC_HCKEN | VENDORSPEC_IPGEN | VENDORSPEC_CKEN);
 #endif
 
        writel(SDHCI_IRQ_EN_BITS, &regs->irqstaten);
@@ -796,6 +820,21 @@ static int fsl_esdhc_init(struct fsl_esdhc_priv *priv)
        return 0;
 }
 
+#ifndef CONFIG_DM_MMC
+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;
+       priv->wp_enable  = cfg->wp_enable;
+
+       return 0;
+};
+
 int fsl_esdhc_initialize(bd_t *bis, struct fsl_esdhc_cfg *cfg)
 {
        struct fsl_esdhc_priv *priv;
@@ -834,6 +873,7 @@ int fsl_esdhc_mmc_init(bd_t *bis)
        cfg->sdhc_clk = gd->arch.sdhc_clk;
        return fsl_esdhc_initialize(bis, cfg);
 }
+#endif
 
 #ifdef CONFIG_FSL_ESDHC_ADAPTER_IDENT
 void mmc_adapter_card_type_ident(void)
@@ -875,17 +915,26 @@ void mmc_adapter_card_type_ident(void)
 #endif
 
 #ifdef CONFIG_OF_LIBFDT
-void fdt_fixup_esdhc(void *blob, bd_t *bd)
+__weak int esdhc_status_fixup(void *blob, const char *compat)
 {
-       const char *compat = "fsl,esdhc";
-
 #ifdef CONFIG_FSL_ESDHC_PIN_MUX
        if (!hwconfig("esdhc")) {
                do_fixup_by_compat(blob, compat, "status", "disabled",
-                               8 + 1, 1);
-               return;
+                               sizeof("disabled"), 1);
+               return 1;
        }
 #endif
+       do_fixup_by_compat(blob, compat, "status", "okay",
+                          sizeof("okay"), 1);
+       return 0;
+}
+
+void fdt_fixup_esdhc(void *blob, bd_t *bd)
+{
+       const char *compat = "fsl,esdhc";
+
+       if (esdhc_status_fixup(blob, compat))
+               return;
 
 #ifdef CONFIG_FSL_ESDHC_USE_PERIPHERAL_CLK
        do_fixup_by_compat_u32(blob, compat, "peripheral-frequency",
@@ -898,24 +947,26 @@ void fdt_fixup_esdhc(void *blob, bd_t *bd)
        do_fixup_by_compat_u32(blob, compat, "adapter-type",
                               (u32)(gd->arch.sdhc_adapter), 1);
 #endif
-       do_fixup_by_compat(blob, compat, "status", "okay",
-                          4 + 1, 1);
 }
 #endif
 
 #ifdef CONFIG_DM_MMC
 #include <asm/arch/clock.h>
+__weak void init_clk_usdhc(u32 index)
+{
+}
+
 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;
+       int node = dev_of_offset(dev);
        fdt_addr_t addr;
        unsigned int val;
        int ret;
 
-       addr = dev_get_addr(dev);
+       addr = devfdt_get_addr(dev);
        if (addr == FDT_ADDR_T_NONE)
                return -EINVAL;
 
@@ -934,10 +985,20 @@ static int fsl_esdhc_probe(struct udevice *dev)
                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);
+#ifdef CONFIG_DM_GPIO
+               gpio_request_by_name_nodev(offset_to_ofnode(node), "cd-gpios",
+                                          0, &priv->cd_gpio, GPIOD_IS_IN);
+#endif
        }
 
+       priv->wp_enable = 1;
+
+#ifdef CONFIG_DM_GPIO
+       ret = gpio_request_by_name_nodev(offset_to_ofnode(node), "wp-gpios", 0,
+                                        &priv->wp_gpio, GPIOD_IS_IN);
+       if (ret)
+               priv->wp_enable = 0;
+#endif
        /*
         * TODO:
         * Because lack of clk driver, if SDHC clk is not enabled,
@@ -957,6 +1018,9 @@ static int fsl_esdhc_probe(struct udevice *dev)
         * correctly get the seq as 2 and 3, then let mxc_get_clock
         * work as expected.
         */
+
+       init_clk_usdhc(dev->seq);
+
        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);
@@ -970,6 +1034,7 @@ static int fsl_esdhc_probe(struct udevice *dev)
        }
 
        upriv->mmc = priv->mmc;
+       priv->mmc->dev = dev;
 
        return 0;
 }
@@ -980,6 +1045,8 @@ static const struct udevice_id fsl_esdhc_ids[] = {
        { .compatible = "fsl,imx6sl-usdhc", },
        { .compatible = "fsl,imx6q-usdhc", },
        { .compatible = "fsl,imx7d-usdhc", },
+       { .compatible = "fsl,imx7ulp-usdhc", },
+       { .compatible = "fsl,esdhc", },
        { /* sentinel */ }
 };