mmc: add HS400 support
authorPeng Fan <peng.fan@nxp.com>
Fri, 10 Aug 2018 06:07:54 +0000 (14:07 +0800)
committerTom Rini <trini@konsulko.com>
Tue, 11 Sep 2018 00:48:19 +0000 (20:48 -0400)
Add HS400 support.
Selecting HS400 needs first select HS200 according to spec, so use
a dedicated function for HS400.
Add HS400 related macros.
Remove the restriction of only using the low 6 bits of
EXT_CSD_CARD_TYPE, using all the 8 bits.

Signed-off-by: Peng Fan <peng.fan@nxp.com>
Cc: Jaehoon Chung <jh80.chung@samsung.com>
Cc: Jean-Jacques Hiblot <jjhiblot@ti.com>
Cc: Stefano Babic <sbabic@denx.de>
Cc: Kishon Vijay Abraham I <kishon@ti.com>
Cc: Faiz Abbas <faiz_abbas@ti.com>
Cc: Marek Vasut <marex@denx.de>
Signed-off-by: Peng Fan <peng.fan@nxp.com>
drivers/mmc/Kconfig
drivers/mmc/mmc.c
include/mmc.h

index 377b1c4b3b0ac35a8a9da9d6619c027b5faf0a09..1616022b91ed961a4fd334c2bb59e05b44d2e503 100644 (file)
@@ -111,6 +111,19 @@ config SPL_MMC_UHS_SUPPORT
          cards. The IO voltage must be switchable from 3.3v to 1.8v. The bus
          frequency can go up to 208MHz (SDR104)
 
+config MMC_HS400_SUPPORT
+       bool "enable HS400 support"
+       select MMC_HS200_SUPPORT
+       help
+         The HS400 mode is support by some eMMC. The bus frequency is up to
+         200MHz. This mode requires tuning the IO.
+
+config SPL_MMC_HS400_SUPPORT
+       bool "enable HS400 support in SPL"
+       help
+         The HS400 mode is support by some eMMC. The bus frequency is up to
+         200MHz. This mode requires tuning the IO.
+
 config MMC_HS200_SUPPORT
        bool "enable HS200 support"
        help
index ad429f49c992849a4b011fb0316f1ad95d2e64ba..585951cd78e917045c2d0ecb113cba06698817ff 100644 (file)
@@ -147,6 +147,7 @@ const char *mmc_mode_name(enum bus_mode mode)
              [MMC_HS_52]       = "MMC High Speed (52MHz)",
              [MMC_DDR_52]      = "MMC DDR52 (52MHz)",
              [MMC_HS_200]      = "HS200 (200MHz)",
+             [MMC_HS_400]      = "HS400 (200MHz)",
        };
 
        if (mode >= MMC_MODES_END)
@@ -171,6 +172,7 @@ static uint mmc_mode2freq(struct mmc *mmc, enum bus_mode mode)
              [UHS_DDR50]       = 50000000,
              [UHS_SDR104]      = 208000000,
              [MMC_HS_200]      = 200000000,
+             [MMC_HS_400]      = 200000000,
        };
 
        if (mode == MMC_LEGACY)
@@ -769,6 +771,11 @@ static int mmc_set_card_speed(struct mmc *mmc, enum bus_mode mode)
        case MMC_HS_200:
                speed_bits = EXT_CSD_TIMING_HS200;
                break;
+#endif
+#if CONFIG_IS_ENABLED(MMC_HS400_SUPPORT)
+       case MMC_HS_400:
+               speed_bits = EXT_CSD_TIMING_HS400;
+               break;
 #endif
        case MMC_LEGACY:
                speed_bits = EXT_CSD_TIMING_LEGACY;
@@ -816,7 +823,7 @@ static int mmc_get_capabilities(struct mmc *mmc)
 
        mmc->card_caps |= MMC_MODE_4BIT | MMC_MODE_8BIT;
 
-       cardtype = ext_csd[EXT_CSD_CARD_TYPE] & 0x3f;
+       cardtype = ext_csd[EXT_CSD_CARD_TYPE];
        mmc->cardtype = cardtype;
 
 #if CONFIG_IS_ENABLED(MMC_HS200_SUPPORT)
@@ -824,6 +831,12 @@ static int mmc_get_capabilities(struct mmc *mmc)
                        EXT_CSD_CARD_TYPE_HS200_1_8V)) {
                mmc->card_caps |= MMC_MODE_HS200;
        }
+#endif
+#if CONFIG_IS_ENABLED(MMC_HS400_SUPPORT)
+       if (cardtype & (EXT_CSD_CARD_TYPE_HS400_1_2V |
+                       EXT_CSD_CARD_TYPE_HS400_1_8V)) {
+               mmc->card_caps |= MMC_MODE_HS400;
+       }
 #endif
        if (cardtype & EXT_CSD_CARD_TYPE_52) {
                if (cardtype & EXT_CSD_CARD_TYPE_DDR_52)
@@ -1734,10 +1747,13 @@ static int mmc_set_lowest_voltage(struct mmc *mmc, enum bus_mode mode,
        u32 card_mask = 0;
 
        switch (mode) {
+       case MMC_HS_400:
        case MMC_HS_200:
-               if (mmc->cardtype & EXT_CSD_CARD_TYPE_HS200_1_8V)
+               if (mmc->cardtype & (EXT_CSD_CARD_TYPE_HS200_1_8V |
+                   EXT_CSD_CARD_TYPE_HS400_1_8V))
                        card_mask |= MMC_SIGNAL_VOLTAGE_180;
-               if (mmc->cardtype & EXT_CSD_CARD_TYPE_HS200_1_2V)
+               if (mmc->cardtype & (EXT_CSD_CARD_TYPE_HS200_1_2V |
+                   EXT_CSD_CARD_TYPE_HS400_1_2V))
                        card_mask |= MMC_SIGNAL_VOLTAGE_120;
                break;
        case MMC_DDR_52:
@@ -1773,6 +1789,13 @@ 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_SUPPORT)
+       {
+               .mode = MMC_HS_400,
+               .widths = MMC_MODE_8BIT,
+               .tuning = MMC_CMD_SEND_TUNING_BLOCK_HS200
+       },
+#endif
 #if CONFIG_IS_ENABLED(MMC_HS200_SUPPORT)
        {
                .mode = MMC_HS_200,
@@ -1816,6 +1839,54 @@ static const struct ext_csd_bus_width {
        {MMC_MODE_1BIT, false, EXT_CSD_BUS_WIDTH_1},
 };
 
+#if CONFIG_IS_ENABLED(MMC_HS400_SUPPORT)
+static int mmc_select_hs400(struct mmc *mmc)
+{
+       int err;
+
+       /* Set timing to HS200 for tuning */
+       err = mmc_set_card_speed(mmc, MMC_HS_200);
+       if (err)
+               return err;
+
+       /* configure the bus mode (host) */
+       mmc_select_mode(mmc, MMC_HS_200);
+       mmc_set_clock(mmc, mmc->tran_speed, false);
+
+       /* execute tuning if needed */
+       err = mmc_execute_tuning(mmc, MMC_CMD_SEND_TUNING_BLOCK_HS200);
+       if (err) {
+               debug("tuning failed\n");
+               return err;
+       }
+
+       /* Set back to HS */
+       mmc_set_card_speed(mmc, MMC_HS);
+       mmc_set_clock(mmc, mmc_mode2freq(mmc, MMC_HS), false);
+
+       err = mmc_switch(mmc, EXT_CSD_CMD_SET_NORMAL, EXT_CSD_BUS_WIDTH,
+                        EXT_CSD_BUS_WIDTH_8 | EXT_CSD_DDR_FLAG);
+       if (err)
+               return err;
+
+       err = mmc_set_card_speed(mmc, MMC_HS_400);
+       if (err)
+               return err;
+
+       mmc_select_mode(mmc, MMC_HS_400);
+       err = mmc_set_clock(mmc, mmc->tran_speed, false);
+       if (err)
+               return err;
+
+       return 0;
+}
+#else
+static int mmc_select_hs400(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);\
@@ -1869,37 +1940,49 @@ static int mmc_select_mode_and_width(struct mmc *mmc, uint card_caps)
                                goto error;
                        mmc_set_bus_width(mmc, bus_width(ecbw->cap));
 
-                       /* configure the bus speed (card) */
-                       err = mmc_set_card_speed(mmc, mwt->mode);
-                       if (err)
-                               goto error;
-
-                       /*
-                        * configure the bus width AND the ddr mode (card)
-                        * The host side will be taken care of in the next step
-                        */
-                       if (ecbw->ext_csd_bits & EXT_CSD_DDR_FLAG) {
-                               err = mmc_switch(mmc, EXT_CSD_CMD_SET_NORMAL,
-                                                EXT_CSD_BUS_WIDTH,
-                                                ecbw->ext_csd_bits);
+                       if (mwt->mode == MMC_HS_400) {
+                               err = mmc_select_hs400(mmc);
+                               if (err) {
+                                       printf("Select HS400 failed %d\n", err);
+                                       goto error;
+                               }
+                       } else {
+                               /* configure the bus speed (card) */
+                               err = mmc_set_card_speed(mmc, mwt->mode);
                                if (err)
                                        goto error;
-                       }
 
-                       /* configure the bus mode (host) */
-                       mmc_select_mode(mmc, mwt->mode);
-                       mmc_set_clock(mmc, mmc->tran_speed, MMC_CLK_ENABLE);
+                               /*
+                                * configure the bus width AND the ddr mode
+                                * (card). The host side will be taken care
+                                * of in the next step
+                                */
+                               if (ecbw->ext_csd_bits & EXT_CSD_DDR_FLAG) {
+                                       err = mmc_switch(mmc,
+                                                        EXT_CSD_CMD_SET_NORMAL,
+                                                        EXT_CSD_BUS_WIDTH,
+                                                        ecbw->ext_csd_bits);
+                                       if (err)
+                                               goto error;
+                               }
+
+                               /* configure the bus mode (host) */
+                               mmc_select_mode(mmc, mwt->mode);
+                               mmc_set_clock(mmc, mmc->tran_speed,
+                                             MMC_CLK_ENABLE);
 #ifdef MMC_SUPPORTS_TUNING
 
-                       /* execute tuning if needed */
-                       if (mwt->tuning) {
-                               err = mmc_execute_tuning(mmc, mwt->tuning);
-                               if (err) {
-                                       pr_debug("tuning failed\n");
-                                       goto error;
+                               /* execute tuning if needed */
+                               if (mwt->tuning) {
+                                       err = mmc_execute_tuning(mmc,
+                                                                mwt->tuning);
+                                       if (err) {
+                                               pr_debug("tuning failed\n");
+                                               goto error;
+                                       }
                                }
-                       }
 #endif
+                       }
 
                        /* do a transfer to check the configuration */
                        err = mmc_read_and_compare_ext_csd(mmc);
index df4255b828a71eb5420967f5c0bd70ba45ffb394..9b9cbedadc81c9d7b659fa4ef714c346138856cd 100644 (file)
@@ -64,6 +64,7 @@
 #define MMC_MODE_HS_52MHz      MMC_CAP(MMC_HS_52)
 #define MMC_MODE_DDR_52MHz     MMC_CAP(MMC_DDR_52)
 #define MMC_MODE_HS200         MMC_CAP(MMC_HS_200)
+#define MMC_MODE_HS400         MMC_CAP(MMC_HS_400)
 
 #define MMC_MODE_8BIT          BIT(30)
 #define MMC_MODE_4BIT          BIT(29)
@@ -248,6 +249,10 @@ static inline bool mmc_is_tuning_cmd(uint cmdidx)
                                                /* SDR mode @1.2V I/O */
 #define EXT_CSD_CARD_TYPE_HS200                (EXT_CSD_CARD_TYPE_HS200_1_8V | \
                                         EXT_CSD_CARD_TYPE_HS200_1_2V)
+#define EXT_CSD_CARD_TYPE_HS400_1_8V   BIT(6)
+#define EXT_CSD_CARD_TYPE_HS400_1_2V   BIT(7)
+#define EXT_CSD_CARD_TYPE_HS400                (EXT_CSD_CARD_TYPE_HS400_1_8V | \
+                                        EXT_CSD_CARD_TYPE_HS400_1_2V)
 
 #define EXT_CSD_BUS_WIDTH_1    0       /* Card is in 1 bit mode */
 #define EXT_CSD_BUS_WIDTH_4    1       /* Card is in 4 bit mode */
@@ -259,6 +264,7 @@ static inline bool mmc_is_tuning_cmd(uint cmdidx)
 #define EXT_CSD_TIMING_LEGACY  0       /* no high speed */
 #define EXT_CSD_TIMING_HS      1       /* HS */
 #define EXT_CSD_TIMING_HS200   2       /* HS200 */
+#define EXT_CSD_TIMING_HS400   3       /* HS400 */
 
 #define EXT_CSD_BOOT_ACK_ENABLE                        (1 << 6)
 #define EXT_CSD_BOOT_PARTITION_ENABLE          (1 << 3)
@@ -519,6 +525,7 @@ enum bus_mode {
        UHS_DDR50,
        UHS_SDR104,
        MMC_HS_200,
+       MMC_HS_400,
        MMC_MODES_END
 };
 
@@ -532,6 +539,10 @@ static inline bool mmc_is_mode_ddr(enum bus_mode mode)
 #if CONFIG_IS_ENABLED(MMC_UHS_SUPPORT)
        else if (mode == UHS_DDR50)
                return true;
+#endif
+#if CONFIG_IS_ENABLED(MMC_HS400_SUPPORT)
+       else if (mode == MMC_HS_400)
+               return true;
 #endif
        else
                return false;