mmc: core: implement enhanced strobe support
authorShawn Lin <shawn.lin@rock-chips.com>
Thu, 26 May 2016 01:56:22 +0000 (09:56 +0800)
committerUlf Hansson <ulf.hansson@linaro.org>
Mon, 25 Jul 2016 08:34:05 +0000 (10:34 +0200)
Controllers use data strobe line to latch data from devices
under hs400 mode, but not for cmd line. So since emmc 5.1, JEDEC
introduces enhanced strobe mode for latching cmd response from
emmc devices to host controllers. This new feature is optional,
so it depends both on device's cap and host's cap to decide
whether to use it or not.

Signed-off-by: Shawn Lin <shawn.lin@rock-chips.com>
Reviewed-by: Jaehoon Chung <jh80.chung@samsung.com>
Tested-by: Douglas Anderson <dianders@chromium.org>
Tested-by: Jaehoon Chung <jh80.chung@samsung.com>
Acked-by: Adrian Hunter <adrian.hunter@intel.com>
Signed-off-by: Ulf Hansson <ulf.hansson@linaro.org>
drivers/mmc/core/bus.c
drivers/mmc/core/core.c
drivers/mmc/core/mmc.c
include/linux/mmc/card.h
include/linux/mmc/host.h
include/linux/mmc/mmc.h

index 4bc48f10452fb02dc7ada8d6a8fd4cef11fde41f..c64266f5a399b3c6ee2535d2b8539460a75ed5e2 100644 (file)
@@ -332,12 +332,13 @@ int mmc_add_card(struct mmc_card *card)
                        mmc_card_ddr52(card) ? "DDR " : "",
                        type);
        } else {
-               pr_info("%s: new %s%s%s%s%s card at address %04x\n",
+               pr_info("%s: new %s%s%s%s%s%s card at address %04x\n",
                        mmc_hostname(card->host),
                        mmc_card_uhs(card) ? "ultra high speed " :
                        (mmc_card_hs(card) ? "high speed " : ""),
                        mmc_card_hs400(card) ? "HS400 " :
                        (mmc_card_hs200(card) ? "HS200 " : ""),
+                       mmc_card_hs400es(card) ? "Enhanced strobe " : "",
                        mmc_card_ddr52(card) ? "DDR " : "",
                        uhs_bus_speed_mode, type, card->rca);
        }
index 8b4dfd45433b73c1ccf6fe4cd4f9ca3338546cff..e8641873dee7a396ad0edb97bd7fd1b44215bae0 100644 (file)
@@ -1127,6 +1127,15 @@ void mmc_set_initial_state(struct mmc_host *host)
        host->ios.bus_width = MMC_BUS_WIDTH_1;
        host->ios.timing = MMC_TIMING_LEGACY;
        host->ios.drv_type = 0;
+       host->ios.enhanced_strobe = false;
+
+       /*
+        * Make sure we are in non-enhanced strobe mode before we
+        * actually enable it in ext_csd.
+        */
+       if ((host->caps2 & MMC_CAP2_HS400_ES) &&
+            host->ops->hs400_enhanced_strobe)
+               host->ops->hs400_enhanced_strobe(host, &host->ios);
 
        mmc_set_ios(host);
 }
index 5d438ad3ee32c994eab11f33c267d7d0d62dbbae..97a664f758b01e114a9af7a37b6ed2c0c2642486 100644 (file)
@@ -235,6 +235,11 @@ static void mmc_select_card_type(struct mmc_card *card)
                avail_type |= EXT_CSD_CARD_TYPE_HS400_1_2V;
        }
 
+       if ((caps2 & MMC_CAP2_HS400_ES) &&
+           card->ext_csd.strobe_support &&
+           (avail_type & EXT_CSD_CARD_TYPE_HS400))
+               avail_type |= EXT_CSD_CARD_TYPE_HS400ES;
+
        card->ext_csd.hs_max_dtr = hs_max_dtr;
        card->ext_csd.hs200_max_dtr = hs200_max_dtr;
        card->mmc_avail_type = avail_type;
@@ -386,6 +391,7 @@ static int mmc_decode_ext_csd(struct mmc_card *card, u8 *ext_csd)
                        mmc_card_set_blockaddr(card);
        }
 
+       card->ext_csd.strobe_support = ext_csd[EXT_CSD_STROBE_SUPPORT];
        card->ext_csd.raw_card_type = ext_csd[EXT_CSD_CARD_TYPE];
        mmc_select_card_type(card);
 
@@ -1223,6 +1229,78 @@ out_err:
        return err;
 }
 
+static int mmc_select_hs400es(struct mmc_card *card)
+{
+       struct mmc_host *host = card->host;
+       int err = 0;
+       u8 val;
+
+       if (!(host->caps & MMC_CAP_8_BIT_DATA)) {
+               err = -ENOTSUPP;
+               goto out_err;
+       }
+
+       err = mmc_select_bus_width(card);
+       if (err < 0)
+               goto out_err;
+
+       /* Switch card to HS mode */
+       err = mmc_select_hs(card);
+       if (err) {
+               pr_err("%s: switch to high-speed failed, err:%d\n",
+                       mmc_hostname(host), err);
+               goto out_err;
+       }
+
+       err = mmc_switch_status(card);
+       if (err)
+               goto out_err;
+
+       /* Switch card to DDR with strobe bit */
+       val = EXT_CSD_DDR_BUS_WIDTH_8 | EXT_CSD_BUS_WIDTH_STROBE;
+       err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
+                        EXT_CSD_BUS_WIDTH,
+                        val,
+                        card->ext_csd.generic_cmd6_time);
+       if (err) {
+               pr_err("%s: switch to bus width for hs400es failed, err:%d\n",
+                       mmc_hostname(host), err);
+               goto out_err;
+       }
+
+       /* Switch card to HS400 */
+       val = EXT_CSD_TIMING_HS400 |
+             card->drive_strength << EXT_CSD_DRV_STR_SHIFT;
+       err = __mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
+                          EXT_CSD_HS_TIMING, val,
+                          card->ext_csd.generic_cmd6_time,
+                          true, false, true);
+       if (err) {
+               pr_err("%s: switch to hs400es failed, err:%d\n",
+                       mmc_hostname(host), err);
+               goto out_err;
+       }
+
+       /* Set host controller to HS400 timing and frequency */
+       mmc_set_timing(host, MMC_TIMING_MMC_HS400);
+
+       /* Controller enable enhanced strobe function */
+       host->ios.enhanced_strobe = true;
+       if (host->ops->hs400_enhanced_strobe)
+               host->ops->hs400_enhanced_strobe(host, &host->ios);
+
+       err = mmc_switch_status(card);
+       if (err)
+               goto out_err;
+
+       return 0;
+
+out_err:
+       pr_err("%s: %s failed, error %d\n", mmc_hostname(card->host),
+              __func__, err);
+       return err;
+}
+
 static void mmc_select_driver_type(struct mmc_card *card)
 {
        int card_drv_type, drive_strength, drv_type;
@@ -1310,7 +1388,7 @@ err:
 }
 
 /*
- * Activate High Speed or HS200 mode if supported.
+ * Activate High Speed, HS200 or HS400ES mode if supported.
  */
 static int mmc_select_timing(struct mmc_card *card)
 {
@@ -1319,7 +1397,9 @@ static int mmc_select_timing(struct mmc_card *card)
        if (!mmc_can_ext_csd(card))
                goto bus_speed;
 
-       if (card->mmc_avail_type & EXT_CSD_CARD_TYPE_HS200)
+       if (card->mmc_avail_type & EXT_CSD_CARD_TYPE_HS400ES)
+               err = mmc_select_hs400es(card);
+       else if (card->mmc_avail_type & EXT_CSD_CARD_TYPE_HS200)
                err = mmc_select_hs200(card);
        else if (card->mmc_avail_type & EXT_CSD_CARD_TYPE_HS)
                err = mmc_select_hs(card);
index eb0151bac50c1fd796f479d017bc3c3d7017c9e9..22defc2a83b78ca720f2e8666d540120f5fccc6b 100644 (file)
@@ -95,6 +95,7 @@ struct mmc_ext_csd {
        u8                      raw_partition_support;  /* 160 */
        u8                      raw_rpmb_size_mult;     /* 168 */
        u8                      raw_erased_mem_count;   /* 181 */
+       u8                      strobe_support;         /* 184 */
        u8                      raw_ext_csd_structure;  /* 194 */
        u8                      raw_card_type;          /* 196 */
        u8                      raw_driver_strength;    /* 197 */
index b836a271e2dfbd8183f6472480e772b73140bc5c..d72c0c34c21dfacce83eb010987c3e567e38c776 100644 (file)
@@ -19,6 +19,7 @@
 
 #include <linux/mmc/core.h>
 #include <linux/mmc/card.h>
+#include <linux/mmc/mmc.h>
 #include <linux/mmc/pm.h>
 
 struct mmc_ios {
@@ -77,6 +78,8 @@ struct mmc_ios {
 #define MMC_SET_DRIVER_TYPE_A  1
 #define MMC_SET_DRIVER_TYPE_C  2
 #define MMC_SET_DRIVER_TYPE_D  3
+
+       bool enhanced_strobe;                   /* hs400es selection */
 };
 
 struct mmc_host_ops {
@@ -143,6 +146,9 @@ struct mmc_host_ops {
 
        /* Prepare HS400 target operating frequency depending host driver */
        int     (*prepare_hs400_tuning)(struct mmc_host *host, struct mmc_ios *ios);
+       /* Prepare enhanced strobe depending host driver */
+       void    (*hs400_enhanced_strobe)(struct mmc_host *host,
+                                        struct mmc_ios *ios);
        int     (*select_drive_strength)(struct mmc_card *card,
                                         unsigned int max_dtr, int host_drv,
                                         int card_drv, int *drv_type);
@@ -514,6 +520,11 @@ static inline bool mmc_card_hs400(struct mmc_card *card)
        return card->host->ios.timing == MMC_TIMING_MMC_HS400;
 }
 
+static inline bool mmc_card_hs400es(struct mmc_card *card)
+{
+       return card->host->ios.enhanced_strobe;
+}
+
 void mmc_retune_timer_stop(struct mmc_host *host);
 
 static inline void mmc_retune_needed(struct mmc_host *host)
index 15f2c4a0a62cbfa3d7751d91be7ebc3bbf6ec645..c376209c70ef4424e19e342c441670439c8a7d68 100644 (file)
@@ -297,6 +297,7 @@ struct _mmc_csd {
 #define EXT_CSD_PART_CONFIG            179     /* R/W */
 #define EXT_CSD_ERASED_MEM_CONT                181     /* RO */
 #define EXT_CSD_BUS_WIDTH              183     /* R/W */
+#define EXT_CSD_STROBE_SUPPORT         184     /* RO */
 #define EXT_CSD_HS_TIMING              185     /* R/W */
 #define EXT_CSD_POWER_CLASS            187     /* R/W */
 #define EXT_CSD_REV                    192     /* RO */
@@ -380,12 +381,14 @@ struct _mmc_csd {
 #define EXT_CSD_CARD_TYPE_HS400_1_2V   (1<<7)  /* Card can run at 200MHz DDR, 1.2V */
 #define EXT_CSD_CARD_TYPE_HS400                (EXT_CSD_CARD_TYPE_HS400_1_8V | \
                                         EXT_CSD_CARD_TYPE_HS400_1_2V)
+#define EXT_CSD_CARD_TYPE_HS400ES      (1<<8)  /* Card can run at HS400ES */
 
 #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 */
 #define EXT_CSD_BUS_WIDTH_8    2       /* Card is in 8 bit mode */
 #define EXT_CSD_DDR_BUS_WIDTH_4        5       /* Card is in 4 bit DDR mode */
 #define EXT_CSD_DDR_BUS_WIDTH_8        6       /* Card is in 8 bit DDR mode */
+#define EXT_CSD_BUS_WIDTH_STROBE BIT(7)        /* Enhanced strobe mode */
 
 #define EXT_CSD_TIMING_BC      0       /* Backwards compatility */
 #define EXT_CSD_TIMING_HS      1       /* High speed */