Merge tag 'u-boot-imx-20191009' of https://gitlab.denx.de/u-boot/custodians/u-boot-imx
[oweals/u-boot.git] / drivers / mmc / mmc.c
index cec39a9acf40730099d3a0cb6f6ef81d61270e9a..6bece7f3073a51fd75c9c996d5d0680dfc3f40cc 100644 (file)
@@ -31,7 +31,7 @@ static int mmc_select_mode_and_width(struct mmc *mmc, uint card_caps);
 
 #if !CONFIG_IS_ENABLED(DM_MMC)
 
-static int mmc_wait_dat0(struct mmc *mmc, int state, int timeout)
+static int mmc_wait_dat0(struct mmc *mmc, int state, int timeout_us)
 {
        return -ENOSYS;
 }
@@ -148,6 +148,7 @@ const char *mmc_mode_name(enum bus_mode mode)
              [MMC_DDR_52]      = "MMC DDR52 (52MHz)",
              [MMC_HS_200]      = "HS200 (200MHz)",
              [MMC_HS_400]      = "HS400 (200MHz)",
+             [MMC_HS_400_ES]   = "HS400ES (200MHz)",
        };
 
        if (mode >= MMC_MODES_END)
@@ -173,6 +174,7 @@ static uint mmc_mode2freq(struct mmc *mmc, enum bus_mode mode)
              [UHS_SDR104]      = 208000000,
              [MMC_HS_200]      = 200000000,
              [MMC_HS_400]      = 200000000,
+             [MMC_HS_400_ES]   = 200000000,
        };
 
        if (mode == MMC_LEGACY)
@@ -228,12 +230,12 @@ int mmc_send_status(struct mmc *mmc, unsigned int *status)
        return -ECOMM;
 }
 
-int mmc_poll_for_busy(struct mmc *mmc, int timeout)
+int mmc_poll_for_busy(struct mmc *mmc, int timeout_ms)
 {
        unsigned int status;
        int err;
 
-       err = mmc_wait_dat0(mmc, 1, timeout);
+       err = mmc_wait_dat0(mmc, 1, timeout_ms * 1000);
        if (err != -ENOSYS)
                return err;
 
@@ -254,13 +256,13 @@ int mmc_poll_for_busy(struct mmc *mmc, int timeout)
                        return -ECOMM;
                }
 
-               if (timeout-- <= 0)
+               if (timeout_ms-- <= 0)
                        break;
 
                udelay(1000);
        }
 
-       if (timeout <= 0) {
+       if (timeout_ms <= 0) {
 #if !defined(CONFIG_SPL_BUILD) || defined(CONFIG_SPL_LIBCOMMON_SUPPORT)
                pr_err("Timeout waiting card ready\n");
 #endif
@@ -748,17 +750,17 @@ static int __mmc_switch(struct mmc *mmc, u8 set, u8 index, u8 value,
 {
        unsigned int status, start;
        struct mmc_cmd cmd;
-       int timeout = DEFAULT_CMD6_TIMEOUT_MS;
+       int timeout_ms = DEFAULT_CMD6_TIMEOUT_MS;
        bool is_part_switch = (set == EXT_CSD_CMD_SET_NORMAL) &&
                              (index == EXT_CSD_PART_CONF);
        int retries = 3;
        int ret;
 
        if (mmc->gen_cmd6_time)
-               timeout = mmc->gen_cmd6_time * 10;
+               timeout_ms = mmc->gen_cmd6_time * 10;
 
        if (is_part_switch  && mmc->part_switch_time)
-               timeout = mmc->part_switch_time * 10;
+               timeout_ms = mmc->part_switch_time * 10;
 
        cmd.cmdidx = MMC_CMD_SWITCH;
        cmd.resp_type = MMC_RSP_R1b;
@@ -776,7 +778,7 @@ static int __mmc_switch(struct mmc *mmc, u8 set, u8 index, u8 value,
        start = get_timer(0);
 
        /* poll dat0 for rdy/buys status */
-       ret = mmc_wait_dat0(mmc, 1, timeout);
+       ret = mmc_wait_dat0(mmc, 1, timeout_ms * 1000);
        if (ret && ret != -ENOSYS)
                return ret;
 
@@ -786,11 +788,11 @@ static int __mmc_switch(struct mmc *mmc, u8 set, u8 index, u8 value,
         * stated timeout to be sufficient.
         */
        if (ret == -ENOSYS && !send_status)
-               mdelay(timeout);
+               mdelay(timeout_ms);
 
        /* Finally wait until the card is ready or indicates a failure
         * to switch. It doesn't hurt to use CMD13 here even if send_status
-        * is false, because by now (after 'timeout' ms) the bus should be
+        * is false, because by now (after 'timeout_ms' ms) the bus should be
         * reliable.
         */
        do {
@@ -804,7 +806,7 @@ static int __mmc_switch(struct mmc *mmc, u8 set, u8 index, u8 value,
                if (!ret && (status & MMC_STATUS_RDY_FOR_DATA))
                        return 0;
                udelay(100);
-       } while (get_timer(start) < timeout);
+       } while (get_timer(start) < timeout_ms);
 
        return -ETIMEDOUT;
 }
@@ -838,6 +840,11 @@ static int mmc_set_card_speed(struct mmc *mmc, enum bus_mode mode,
        case MMC_HS_400:
                speed_bits = EXT_CSD_TIMING_HS400;
                break;
+#endif
+#if CONFIG_IS_ENABLED(MMC_HS400_ES_SUPPORT)
+       case MMC_HS_400_ES:
+               speed_bits = EXT_CSD_TIMING_HS400;
+               break;
 #endif
        case MMC_LEGACY:
                speed_bits = EXT_CSD_TIMING_LEGACY;
@@ -909,7 +916,8 @@ static int mmc_get_capabilities(struct mmc *mmc)
                mmc->card_caps |= MMC_MODE_HS200;
        }
 #endif
-#if CONFIG_IS_ENABLED(MMC_HS400_SUPPORT)
+#if CONFIG_IS_ENABLED(MMC_HS400_SUPPORT) || \
+       CONFIG_IS_ENABLED(MMC_HS400_ES_SUPPORT)
        if (cardtype & (EXT_CSD_CARD_TYPE_HS400_1_2V |
                        EXT_CSD_CARD_TYPE_HS400_1_8V)) {
                mmc->card_caps |= MMC_MODE_HS400;
@@ -923,6 +931,13 @@ static int mmc_get_capabilities(struct mmc *mmc)
        if (cardtype & EXT_CSD_CARD_TYPE_26)
                mmc->card_caps |= MMC_MODE_HS;
 
+#if CONFIG_IS_ENABLED(MMC_HS400_ES_SUPPORT)
+       if (ext_csd[EXT_CSD_STROBE_SUPPORT] &&
+           (mmc->card_caps & MMC_MODE_HS400)) {
+               mmc->card_caps |= MMC_MODE_HS400_ES;
+       }
+#endif
+
        return 0;
 }
 #endif
@@ -1686,6 +1701,13 @@ static int sd_select_mode_and_width(struct mmc *mmc, uint card_caps)
        mmc_dump_capabilities("host", mmc->host_caps);
 #endif
 
+       if (mmc_host_is_spi(mmc)) {
+               mmc_set_bus_width(mmc, 1);
+               mmc_select_mode(mmc, SD_LEGACY);
+               mmc_set_clock(mmc, mmc->tran_speed, MMC_CLK_ENABLE);
+               return 0;
+       }
+
        /* Restrict card's capabilities by what the host can do */
        caps = card_caps & mmc->host_caps;
 
@@ -1792,6 +1814,7 @@ static int mmc_set_lowest_voltage(struct mmc *mmc, enum bus_mode mode,
        u32 card_mask = 0;
 
        switch (mode) {
+       case MMC_HS_400_ES:
        case MMC_HS_400:
        case MMC_HS_200:
                if (mmc->cardtype & (EXT_CSD_CARD_TYPE_HS200_1_8V |
@@ -1834,6 +1857,12 @@ static inline int mmc_set_lowest_voltage(struct mmc *mmc, enum bus_mode mode,
 #endif
 
 static const struct mode_width_tuning mmc_modes_by_pref[] = {
+#if CONFIG_IS_ENABLED(MMC_HS400_ES_SUPPORT)
+       {
+               .mode = MMC_HS_400_ES,
+               .widths = MMC_MODE_8BIT,
+       },
+#endif
 #if CONFIG_IS_ENABLED(MMC_HS400_SUPPORT)
        {
                .mode = MMC_HS_400,
@@ -1931,6 +1960,47 @@ static int mmc_select_hs400(struct mmc *mmc)
 }
 #endif
 
+#if CONFIG_IS_ENABLED(MMC_HS400_ES_SUPPORT)
+#if !CONFIG_IS_ENABLED(DM_MMC)
+static int mmc_set_enhanced_strobe(struct mmc *mmc)
+{
+       return -ENOTSUPP;
+}
+#endif
+static int mmc_select_hs400es(struct mmc *mmc)
+{
+       int err;
+
+       err = mmc_set_card_speed(mmc, MMC_HS, true);
+       if (err)
+               return err;
+
+       err = mmc_switch(mmc, EXT_CSD_CMD_SET_NORMAL, EXT_CSD_BUS_WIDTH,
+                        EXT_CSD_BUS_WIDTH_8 | EXT_CSD_DDR_FLAG |
+                        EXT_CSD_BUS_WIDTH_STROBE);
+       if (err) {
+               printf("switch to bus width for hs400 failed\n");
+               return err;
+       }
+       /* TODO: driver strength */
+       err = mmc_set_card_speed(mmc, MMC_HS_400_ES, false);
+       if (err)
+               return err;
+
+       mmc_select_mode(mmc, MMC_HS_400_ES);
+       err = mmc_set_clock(mmc, mmc->tran_speed, false);
+       if (err)
+               return err;
+
+       return mmc_set_enhanced_strobe(mmc);
+}
+#else
+static int mmc_select_hs400es(struct mmc *mmc)
+{
+       return -ENOTSUPP;
+}
+#endif
+
 #define for_each_supported_width(caps, ddr, ecbv) \
        for (ecbv = ext_csd_bus_width;\
            ecbv < ext_csd_bus_width + ARRAY_SIZE(ext_csd_bus_width);\
@@ -1948,6 +2018,13 @@ static int mmc_select_mode_and_width(struct mmc *mmc, uint card_caps)
        mmc_dump_capabilities("host", mmc->host_caps);
 #endif
 
+       if (mmc_host_is_spi(mmc)) {
+               mmc_set_bus_width(mmc, 1);
+               mmc_select_mode(mmc, MMC_LEGACY);
+               mmc_set_clock(mmc, mmc->tran_speed, MMC_CLK_ENABLE);
+               return 0;
+       }
+
        /* Restrict card's capabilities by what the host can do */
        card_caps &= mmc->host_caps;
 
@@ -2002,6 +2079,13 @@ static int mmc_select_mode_and_width(struct mmc *mmc, uint card_caps)
                                        printf("Select HS400 failed %d\n", err);
                                        goto error;
                                }
+                       } else if (mwt->mode == MMC_HS_400_ES) {
+                               err = mmc_select_hs400es(mmc);
+                               if (err) {
+                                       printf("Select HS400ES failed %d\n",
+                                              err);
+                                       goto error;
+                               }
                        } else {
                                /* configure the bus speed (card) */
                                err = mmc_set_card_speed(mmc, mwt->mode, false);
@@ -2493,7 +2577,7 @@ static int mmc_startup(struct mmc *mmc)
        bdesc->lba = lldiv(mmc->capacity, mmc->read_bl_len);
 #if !defined(CONFIG_SPL_BUILD) || \
                (defined(CONFIG_SPL_LIBCOMMON_SUPPORT) && \
-               !defined(CONFIG_USE_TINY_PRINTF))
+               !CONFIG_IS_ENABLED(USE_TINY_PRINTF))
        sprintf(bdesc->vendor, "Man %06x Snr %04x%04x",
                mmc->cid[0] >> 24, (mmc->cid[2] & 0xffff),
                (mmc->cid[3] >> 16) & 0xffff);
@@ -2735,12 +2819,12 @@ int mmc_start_init(struct mmc *mmc)
                         MMC_CAP(MMC_LEGACY) | MMC_MODE_1BIT;
 
 #if !defined(CONFIG_MMC_BROKEN_CD)
-       /* we pretend there's no card when init is NULL */
        no_card = mmc_getcd(mmc) == 0;
 #else
        no_card = 0;
 #endif
 #if !CONFIG_IS_ENABLED(DM_MMC)
+       /* we pretend there's no card when init is NULL */
        no_card = no_card || (mmc->cfg->ops->init == NULL);
 #endif
        if (no_card) {