]> git.dujemihanovic.xyz Git - u-boot.git/commitdiff
mmc: Add support for UHS modes
authorJean-Jacques Hiblot <jjhiblot@ti.com>
Thu, 21 Sep 2017 14:30:07 +0000 (16:30 +0200)
committerJaehoon Chung <jh80.chung@samsung.com>
Fri, 12 Jan 2018 09:11:04 +0000 (18:11 +0900)
Add UHS modes to the list of supported modes, get the UHS capabilites of
the SDcard and implement the procedure to switch the voltage (UHS modes
use 1v8 IO lines)
During the voltage switch procedure, DAT0 is used by the card to signal
when it's ready. The optional card_busy() callback can be used to get this
information from the host driver.

Signed-off-by: Kishon Vijay Abraham I <kishon@ti.com>
Signed-off-by: Jean-Jacques Hiblot <jjhiblot@ti.com>
Reviewed-by: Simon Glass <sjg@chromium.org>
drivers/mmc/mmc-uclass.c
drivers/mmc/mmc.c
include/mmc.h

index 60cc0ac4cc8b63afc108c4fd4c9c3591ca2b5c01..7856e0ad084c19fbb3f366e367ecb7e228cd441e 100644 (file)
@@ -64,6 +64,20 @@ void mmc_send_init_stream(struct mmc *mmc)
        dm_mmc_send_init_stream(mmc->dev);
 }
 
+int dm_mmc_wait_dat0(struct udevice *dev, int state, int timeout)
+{
+       struct dm_mmc_ops *ops = mmc_get_ops(dev);
+
+       if (!ops->wait_dat0)
+               return -ENOSYS;
+       return ops->wait_dat0(dev, state, timeout);
+}
+
+int mmc_wait_dat0(struct mmc *mmc, int state, int timeout)
+{
+       return dm_mmc_wait_dat0(mmc->dev, state, timeout);
+}
+
 int dm_mmc_get_wp(struct udevice *dev)
 {
        struct dm_mmc_ops *ops = mmc_get_ops(dev);
index d59cce611e4f5707081460c726dc53e4990e3c02..fe31540ba29664057ffc112ede3e1b7204742bf1 100644 (file)
@@ -57,6 +57,12 @@ struct blk_desc *mmc_get_blk_desc(struct mmc *mmc)
 #endif
 
 #if !CONFIG_IS_ENABLED(DM_MMC)
+
+static int mmc_wait_dat0(struct mmc *mmc, int state, int timeout)
+{
+       return -ENOSYS;
+}
+
 __weak int board_mmc_getwp(struct mmc *mmc)
 {
        return -1;
@@ -402,7 +408,67 @@ static int mmc_go_idle(struct mmc *mmc)
        return 0;
 }
 
-static int sd_send_op_cond(struct mmc *mmc)
+static int mmc_switch_voltage(struct mmc *mmc, int signal_voltage)
+{
+       struct mmc_cmd cmd;
+       int err = 0;
+
+       /*
+        * Send CMD11 only if the request is to switch the card to
+        * 1.8V signalling.
+        */
+       if (signal_voltage == MMC_SIGNAL_VOLTAGE_330)
+               return mmc_set_signal_voltage(mmc, signal_voltage);
+
+       cmd.cmdidx = SD_CMD_SWITCH_UHS18V;
+       cmd.cmdarg = 0;
+       cmd.resp_type = MMC_RSP_R1;
+
+       err = mmc_send_cmd(mmc, &cmd, NULL);
+       if (err)
+               return err;
+
+       if (!mmc_host_is_spi(mmc) && (cmd.response[0] & MMC_STATUS_ERROR))
+               return -EIO;
+
+       /*
+        * The card should drive cmd and dat[0:3] low immediately
+        * after the response of cmd11, but wait 100 us to be sure
+        */
+       err = mmc_wait_dat0(mmc, 0, 100);
+       if (err == -ENOSYS)
+               udelay(100);
+       else if (err)
+               return -ETIMEDOUT;
+
+       /*
+        * During a signal voltage level switch, the clock must be gated
+        * for 5 ms according to the SD spec
+        */
+       mmc_set_clock(mmc, mmc->clock, true);
+
+       err = mmc_set_signal_voltage(mmc, signal_voltage);
+       if (err)
+               return err;
+
+       /* Keep clock gated for at least 10 ms, though spec only says 5 ms */
+       mdelay(10);
+       mmc_set_clock(mmc, mmc->clock, false);
+
+       /*
+        * Failure to switch is indicated by the card holding
+        * dat[0:3] low. Wait for at least 1 ms according to spec
+        */
+       err = mmc_wait_dat0(mmc, 1, 1000);
+       if (err == -ENOSYS)
+               udelay(1000);
+       else if (err)
+               return -ETIMEDOUT;
+
+       return 0;
+}
+
+static int sd_send_op_cond(struct mmc *mmc, bool uhs_en)
 {
        int timeout = 1000;
        int err;
@@ -434,6 +500,9 @@ static int sd_send_op_cond(struct mmc *mmc)
                if (mmc->version == SD_VERSION_2)
                        cmd.cmdarg |= OCR_HCS;
 
+               if (uhs_en)
+                       cmd.cmdarg |= OCR_S18R;
+
                err = mmc_send_cmd(mmc, &cmd, NULL);
 
                if (err)
@@ -464,6 +533,13 @@ static int sd_send_op_cond(struct mmc *mmc)
 
        mmc->ocr = cmd.response[0];
 
+       if (uhs_en && !(mmc_host_is_spi(mmc)) && (cmd.response[0] & 0x41000000)
+           == 0x41000000) {
+               err = mmc_switch_voltage(mmc, MMC_SIGNAL_VOLTAGE_180);
+               if (err)
+                       return err;
+       }
+
        mmc->high_capacity = ((mmc->ocr & OCR_HCS) == OCR_HCS);
        mmc->rca = 0;
 
@@ -977,6 +1053,7 @@ static int sd_get_capabilities(struct mmc *mmc)
        ALLOC_CACHE_ALIGN_BUFFER(__be32, switch_status, 16);
        struct mmc_data data;
        int timeout;
+       u32 sd3_bus_mode;
 
        mmc->card_caps = MMC_MODE_1BIT;
 
@@ -1058,6 +1135,22 @@ retry_scr:
        if (__be32_to_cpu(switch_status[3]) & SD_HIGHSPEED_SUPPORTED)
                mmc->card_caps |= MMC_CAP(SD_HS);
 
+       /* Version before 3.0 don't support UHS modes */
+       if (mmc->version < SD_VERSION_3)
+               return 0;
+
+       sd3_bus_mode = __be32_to_cpu(switch_status[3]) >> 16 & 0x1f;
+       if (sd3_bus_mode & SD_MODE_UHS_SDR104)
+               mmc->card_caps |= MMC_CAP(UHS_SDR104);
+       if (sd3_bus_mode & SD_MODE_UHS_SDR50)
+               mmc->card_caps |= MMC_CAP(UHS_SDR50);
+       if (sd3_bus_mode & SD_MODE_UHS_SDR25)
+               mmc->card_caps |= MMC_CAP(UHS_SDR25);
+       if (sd3_bus_mode & SD_MODE_UHS_SDR12)
+               mmc->card_caps |= MMC_CAP(UHS_SDR12);
+       if (sd3_bus_mode & SD_MODE_UHS_DDR50)
+               mmc->card_caps |= MMC_CAP(UHS_DDR50);
+
        return 0;
 }
 
@@ -1066,12 +1159,35 @@ static int sd_set_card_speed(struct mmc *mmc, enum bus_mode mode)
        int err;
 
        ALLOC_CACHE_ALIGN_BUFFER(uint, switch_status, 16);
+       int speed;
 
-       err = sd_switch(mmc, SD_SWITCH_SWITCH, 0, 1, (u8 *)switch_status);
+       switch (mode) {
+       case SD_LEGACY:
+       case UHS_SDR12:
+               speed = UHS_SDR12_BUS_SPEED;
+               break;
+       case SD_HS:
+       case UHS_SDR25:
+               speed = UHS_SDR25_BUS_SPEED;
+               break;
+       case UHS_SDR50:
+               speed = UHS_SDR50_BUS_SPEED;
+               break;
+       case UHS_DDR50:
+               speed = UHS_DDR50_BUS_SPEED;
+               break;
+       case UHS_SDR104:
+               speed = UHS_SDR104_BUS_SPEED;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       err = sd_switch(mmc, SD_SWITCH_SWITCH, 0, speed, (u8 *)switch_status);
        if (err)
                return err;
 
-       if ((__be32_to_cpu(switch_status[4]) & 0x0f000000) != 0x01000000)
+       if ((__be32_to_cpu(switch_status[4]) >> 24) != speed)
                return -ENOTSUPP;
 
        return 0;
@@ -1285,10 +1401,31 @@ static int mmc_set_signal_voltage(struct mmc *mmc, uint signal_voltage)
 }
 
 static const struct mode_width_tuning sd_modes_by_pref[] = {
+       {
+               .mode = UHS_SDR104,
+               .widths = MMC_MODE_4BIT | MMC_MODE_1BIT,
+               .tuning = MMC_CMD_SEND_TUNING_BLOCK
+       },
+       {
+               .mode = UHS_SDR50,
+               .widths = MMC_MODE_4BIT | MMC_MODE_1BIT,
+       },
+       {
+               .mode = UHS_DDR50,
+               .widths = MMC_MODE_4BIT | MMC_MODE_1BIT,
+       },
+       {
+               .mode = UHS_SDR25,
+               .widths = MMC_MODE_4BIT | MMC_MODE_1BIT,
+       },
        {
                .mode = SD_HS,
                .widths = MMC_MODE_4BIT | MMC_MODE_1BIT,
        },
+       {
+               .mode = UHS_SDR12,
+               .widths = MMC_MODE_4BIT | MMC_MODE_1BIT,
+       },
        {
                .mode = SD_LEGACY,
                .widths = MMC_MODE_4BIT | MMC_MODE_1BIT,
@@ -1306,18 +1443,24 @@ static int sd_select_mode_and_width(struct mmc *mmc)
        int err;
        uint widths[] = {MMC_MODE_4BIT, MMC_MODE_1BIT};
        const struct mode_width_tuning *mwt;
+       bool uhs_en = (mmc->ocr & OCR_S18R) ? true : false;
+       uint caps;
+
 
        err = sd_get_capabilities(mmc);
        if (err)
                return err;
        /* Restrict card's capabilities by what the host can do */
-       mmc->card_caps &= (mmc->cfg->host_caps | MMC_MODE_1BIT);
+       caps = mmc->card_caps & (mmc->cfg->host_caps | MMC_MODE_1BIT);
 
-       for_each_sd_mode_by_pref(mmc->card_caps, mwt) {
+       if (!uhs_en)
+               caps &= ~UHS_CAPS;
+
+       for_each_sd_mode_by_pref(caps, mwt) {
                uint *w;
 
                for (w = widths; w < widths + ARRAY_SIZE(widths); w++) {
-                       if (*w & mmc->card_caps & mwt->widths) {
+                       if (*w & caps & mwt->widths) {
                                debug("trying mode %s width %d (at %d MHz)\n",
                                      mmc_mode_name(mwt->mode),
                                      bus_width(*w),
@@ -1338,6 +1481,16 @@ static int sd_select_mode_and_width(struct mmc *mmc)
                                mmc_select_mode(mmc, mwt->mode);
                                mmc_set_clock(mmc, mmc->tran_speed, false);
 
+                               /* execute tuning if needed */
+                               if (mwt->tuning && !mmc_host_is_spi(mmc)) {
+                                       err = mmc_execute_tuning(mmc,
+                                                                mwt->tuning);
+                                       if (err) {
+                                               debug("tuning failed\n");
+                                               goto error;
+                                       }
+                               }
+
                                err = sd_read_ssr(mmc);
                                if (!err)
                                        return 0;
@@ -2000,7 +2153,7 @@ static int mmc_power_off(struct mmc *mmc)
                int ret = regulator_set_enable(mmc->vmmc_supply, false);
 
                if (ret) {
-                       puts("Error disabling VMMC supply\n");
+                       debug("Error disabling VMMC supply\n");
                        return ret;
                }
        }
@@ -2026,6 +2179,7 @@ static int mmc_power_cycle(struct mmc *mmc)
 int mmc_start_init(struct mmc *mmc)
 {
        bool no_card;
+       bool uhs_en = supports_uhs(mmc->cfg->host_caps);
        int err;
 
        /* we pretend there's no card when init is NULL */
@@ -2065,6 +2219,7 @@ int mmc_start_init(struct mmc *mmc)
 #endif
        mmc->ddr_mode = 0;
 
+retry:
        mmc_set_initial_state(mmc);
        mmc_send_init_stream(mmc);
 
@@ -2081,7 +2236,12 @@ int mmc_start_init(struct mmc *mmc)
        err = mmc_send_if_cond(mmc);
 
        /* Now try to get the SD card's operating condition */
-       err = sd_send_op_cond(mmc);
+       err = sd_send_op_cond(mmc, uhs_en);
+       if (err && uhs_en) {
+               uhs_en = false;
+               mmc_power_cycle(mmc);
+               goto retry;
+       }
 
        /* If the command timed out, we check for an MMC card */
        if (err == -ETIMEDOUT) {
index 407fddf94c3ff39372f215a6e967d27574029822..ba4a13e8617df1f521109426465b49e1a480f7cf 100644 (file)
@@ -87,6 +87,7 @@
 #define MMC_CMD_SET_BLOCKLEN           16
 #define MMC_CMD_READ_SINGLE_BLOCK      17
 #define MMC_CMD_READ_MULTIPLE_BLOCK    18
+#define MMC_CMD_SEND_TUNING_BLOCK              19
 #define MMC_CMD_SEND_TUNING_BLOCK_HS200        21
 #define MMC_CMD_SET_BLOCK_COUNT         23
 #define MMC_CMD_WRITE_SINGLE_BLOCK     24
 
 static inline bool mmc_is_tuning_cmd(uint cmdidx)
 {
-       if (cmdidx == MMC_CMD_SEND_TUNING_BLOCK_HS200)
+       if ((cmdidx == MMC_CMD_SEND_TUNING_BLOCK_HS200) ||
+           (cmdidx == MMC_CMD_SEND_TUNING_BLOCK))
                return true;
        return false;
 }
@@ -126,8 +128,22 @@ static inline bool mmc_is_tuning_cmd(uint cmdidx)
 #define SD_HIGHSPEED_BUSY      0x00020000
 #define SD_HIGHSPEED_SUPPORTED 0x00020000
 
+#define UHS_SDR12_BUS_SPEED    0
+#define HIGH_SPEED_BUS_SPEED   1
+#define UHS_SDR25_BUS_SPEED    1
+#define UHS_SDR50_BUS_SPEED    2
+#define UHS_SDR104_BUS_SPEED   3
+#define UHS_DDR50_BUS_SPEED    4
+
+#define SD_MODE_UHS_SDR12      BIT(UHS_SDR12_BUS_SPEED)
+#define SD_MODE_UHS_SDR25      BIT(UHS_SDR25_BUS_SPEED)
+#define SD_MODE_UHS_SDR50      BIT(UHS_SDR50_BUS_SPEED)
+#define SD_MODE_UHS_SDR104     BIT(UHS_SDR104_BUS_SPEED)
+#define SD_MODE_UHS_DDR50      BIT(UHS_DDR50_BUS_SPEED)
+
 #define OCR_BUSY               0x80000000
 #define OCR_HCS                        0x40000000
+#define OCR_S18R               0x1000000
 #define OCR_VOLTAGE_MASK       0x007FFF80
 #define OCR_ACCESS_MODE                0x60000000
 
@@ -410,6 +426,17 @@ struct dm_mmc_ops {
         * @return 0 if OK, -ve on error
         */
        int (*execute_tuning)(struct udevice *dev, uint opcode);
+
+       /**
+        * wait_dat0() - wait until dat0 is in the target state
+        *              (CLK must be running during the wait)
+        *
+        * @dev:        Device to check
+        * @state:      target state
+        * @timeout:    timeout in us
+        * @return 0 if dat0 is in the target state, -ve on error
+        */
+       int (*wait_dat0)(struct udevice *dev, int state, int timeout);
 };
 
 #define mmc_get_ops(dev)        ((struct dm_mmc_ops *)(dev)->driver->ops)
@@ -421,6 +448,7 @@ void dm_mmc_send_init_stream(struct udevice *dev);
 int dm_mmc_get_cd(struct udevice *dev);
 int dm_mmc_get_wp(struct udevice *dev);
 int dm_mmc_execute_tuning(struct udevice *dev, uint opcode);
+int dm_mmc_wait_dat0(struct udevice *dev, int state, int timeout);
 
 /* Transition functions for compatibility */
 int mmc_set_ios(struct mmc *mmc);
@@ -428,6 +456,7 @@ void mmc_send_init_stream(struct mmc *mmc);
 int mmc_getcd(struct mmc *mmc);
 int mmc_getwp(struct mmc *mmc);
 int mmc_execute_tuning(struct mmc *mmc, uint opcode);
+int mmc_wait_dat0(struct mmc *mmc, int state, int timeout);
 
 #else
 struct mmc_ops {
@@ -486,6 +515,15 @@ static inline bool mmc_is_mode_ddr(enum bus_mode mode)
                return false;
 }
 
+#define UHS_CAPS (MMC_CAP(UHS_SDR12) | MMC_CAP(UHS_SDR25) | \
+                 MMC_CAP(UHS_SDR50) | MMC_CAP(UHS_SDR104) | \
+                 MMC_CAP(UHS_DDR50))
+
+static inline bool supports_uhs(uint caps)
+{
+       return (caps & UHS_CAPS) ? true : false;
+}
+
 /*
  * With CONFIG_DM_MMC enabled, struct mmc can be accessed from the MMC device
  * with mmc_get_mmc_dev().