mmc: core: HS200 mode support for eMMC 4.5
[~shefty/rdma-dev.git] / drivers / mmc / core / mmc.c
index 67f346e0d1050ebf71f3995132d2b85272fb5c06..59b9ba52e66a1fb420ae26f7da3350b0c8d1d592 100644 (file)
@@ -286,6 +286,27 @@ static int mmc_read_ext_csd(struct mmc_card *card, u8 *ext_csd)
        }
        card->ext_csd.raw_card_type = ext_csd[EXT_CSD_CARD_TYPE];
        switch (ext_csd[EXT_CSD_CARD_TYPE] & EXT_CSD_CARD_TYPE_MASK) {
+       case EXT_CSD_CARD_TYPE_SDR_ALL:
+       case EXT_CSD_CARD_TYPE_SDR_ALL_DDR_1_8V:
+       case EXT_CSD_CARD_TYPE_SDR_ALL_DDR_1_2V:
+       case EXT_CSD_CARD_TYPE_SDR_ALL_DDR_52:
+               card->ext_csd.hs_max_dtr = 200000000;
+               card->ext_csd.card_type = EXT_CSD_CARD_TYPE_SDR_200;
+               break;
+       case EXT_CSD_CARD_TYPE_SDR_1_2V_ALL:
+       case EXT_CSD_CARD_TYPE_SDR_1_2V_DDR_1_8V:
+       case EXT_CSD_CARD_TYPE_SDR_1_2V_DDR_1_2V:
+       case EXT_CSD_CARD_TYPE_SDR_1_2V_DDR_52:
+               card->ext_csd.hs_max_dtr = 200000000;
+               card->ext_csd.card_type = EXT_CSD_CARD_TYPE_SDR_1_2V;
+               break;
+       case EXT_CSD_CARD_TYPE_SDR_1_8V_ALL:
+       case EXT_CSD_CARD_TYPE_SDR_1_8V_DDR_1_8V:
+       case EXT_CSD_CARD_TYPE_SDR_1_8V_DDR_1_2V:
+       case EXT_CSD_CARD_TYPE_SDR_1_8V_DDR_52:
+               card->ext_csd.hs_max_dtr = 200000000;
+               card->ext_csd.card_type = EXT_CSD_CARD_TYPE_SDR_1_8V;
+               break;
        case EXT_CSD_CARD_TYPE_DDR_52 | EXT_CSD_CARD_TYPE_52 |
             EXT_CSD_CARD_TYPE_26:
                card->ext_csd.hs_max_dtr = 52000000;
@@ -699,6 +720,79 @@ static int mmc_select_powerclass(struct mmc_card *card,
        return err;
 }
 
+/*
+ * Selects the desired buswidth and switch to the HS200 mode
+ * if bus width set without error
+ */
+static int mmc_select_hs200(struct mmc_card *card)
+{
+       int idx, err = 0;
+       struct mmc_host *host;
+       static unsigned ext_csd_bits[] = {
+               EXT_CSD_BUS_WIDTH_4,
+               EXT_CSD_BUS_WIDTH_8,
+       };
+       static unsigned bus_widths[] = {
+               MMC_BUS_WIDTH_4,
+               MMC_BUS_WIDTH_8,
+       };
+
+       BUG_ON(!card);
+
+       host = card->host;
+
+       if (card->ext_csd.card_type & EXT_CSD_CARD_TYPE_SDR_1_2V &&
+           host->caps2 & MMC_CAP2_HS200_1_2V_SDR)
+               if (mmc_set_signal_voltage(host, MMC_SIGNAL_VOLTAGE_120, 0))
+                       err = mmc_set_signal_voltage(host,
+                                                    MMC_SIGNAL_VOLTAGE_180, 0);
+
+       /* If fails try again during next card power cycle */
+       if (err)
+               goto err;
+
+       idx = (host->caps & MMC_CAP_8_BIT_DATA) ? 1 : 0;
+
+       /*
+        * Unlike SD, MMC cards dont have a configuration register to notify
+        * supported bus width. So bus test command should be run to identify
+        * the supported bus width or compare the ext csd values of current
+        * bus width and ext csd values of 1 bit mode read earlier.
+        */
+       for (; idx >= 0; idx--) {
+
+               /*
+                * Host is capable of 8bit transfer, then switch
+                * the device to work in 8bit transfer mode. If the
+                * mmc switch command returns error then switch to
+                * 4bit transfer mode. On success set the corresponding
+                * bus width on the host.
+                */
+               err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
+                                EXT_CSD_BUS_WIDTH,
+                                ext_csd_bits[idx],
+                                card->ext_csd.generic_cmd6_time);
+               if (err)
+                       continue;
+
+               mmc_set_bus_width(card->host, bus_widths[idx]);
+
+               if (!(host->caps & MMC_CAP_BUS_WIDTH_TEST))
+                       err = mmc_compare_ext_csds(card, bus_widths[idx]);
+               else
+                       err = mmc_bus_test(card, bus_widths[idx]);
+               if (!err)
+                       break;
+       }
+
+       /* switch to HS200 mode if bus width set successfully */
+       if (!err)
+               err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
+                                EXT_CSD_HS_TIMING, 2, 0);
+err:
+       return err;
+}
+
 /*
  * Handle the detection and initialisation of a card.
  *
@@ -905,11 +999,15 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr,
        /*
         * Activate high speed (if supported)
         */
-       if ((card->ext_csd.hs_max_dtr != 0) &&
-               (host->caps & MMC_CAP_MMC_HIGHSPEED)) {
-               err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
-                                EXT_CSD_HS_TIMING, 1,
-                                card->ext_csd.generic_cmd6_time);
+       if (card->ext_csd.hs_max_dtr != 0) {
+               err = 0;
+               if (card->ext_csd.hs_max_dtr > 52000000 &&
+                   host->caps2 & MMC_CAP2_HS200)
+                       err = mmc_select_hs200(card);
+               else if (host->caps & MMC_CAP_MMC_HIGHSPEED)
+                       err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
+                                        EXT_CSD_HS_TIMING, 1, 0);
+
                if (err && err != -EBADMSG)
                        goto free_card;
 
@@ -918,8 +1016,15 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr,
                               mmc_hostname(card->host));
                        err = 0;
                } else {
-                       mmc_card_set_highspeed(card);
-                       mmc_set_timing(card->host, MMC_TIMING_MMC_HS);
+                       if (card->ext_csd.hs_max_dtr > 52000000 &&
+                           host->caps2 & MMC_CAP2_HS200) {
+                               mmc_card_set_hs200(card);
+                               mmc_set_timing(card->host,
+                                              MMC_TIMING_MMC_HS200);
+                       } else {
+                               mmc_card_set_highspeed(card);
+                               mmc_set_timing(card->host, MMC_TIMING_MMC_HS);
+                       }
                }
        }
 
@@ -944,7 +1049,7 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr,
         */
        max_dtr = (unsigned int)-1;
 
-       if (mmc_card_highspeed(card)) {
+       if (mmc_card_highspeed(card) || mmc_card_hs200(card)) {
                if (max_dtr > card->ext_csd.hs_max_dtr)
                        max_dtr = card->ext_csd.hs_max_dtr;
        } else if (max_dtr > card->csd.max_dtr) {
@@ -969,10 +1074,49 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr,
                                ddr = MMC_1_2V_DDR_MODE;
        }
 
+       /*
+        * Indicate HS200 SDR mode (if supported).
+        */
+       if (mmc_card_hs200(card)) {
+               u32 ext_csd_bits;
+               u32 bus_width = card->host->ios.bus_width;
+
+               /*
+                * For devices supporting HS200 mode, the bus width has
+                * to be set before executing the tuning function. If
+                * set before tuning, then device will respond with CRC
+                * errors for responses on CMD line. So for HS200 the
+                * sequence will be
+                * 1. set bus width 4bit / 8 bit (1 bit not supported)
+                * 2. switch to HS200 mode
+                * 3. set the clock to > 52Mhz <=200MHz and
+                * 4. execute tuning for HS200
+                */
+               if ((host->caps2 & MMC_CAP2_HS200) &&
+                   card->host->ops->execute_tuning)
+                       err = card->host->ops->execute_tuning(card->host,
+                               MMC_SEND_TUNING_BLOCK_HS200);
+               if (err) {
+                       pr_warning("%s: tuning execution failed\n",
+                                  mmc_hostname(card->host));
+                       goto err;
+               }
+
+               ext_csd_bits = (bus_width == MMC_BUS_WIDTH_8) ?
+                               EXT_CSD_BUS_WIDTH_8 : EXT_CSD_BUS_WIDTH_4;
+               err = mmc_select_powerclass(card, ext_csd_bits, ext_csd);
+               if (err) {
+                       pr_err("%s: power class selection to bus width %d failed\n",
+                               mmc_hostname(card->host), 1 << bus_width);
+                       goto err;
+               }
+       }
+
        /*
         * Activate wide bus and DDR (if supported).
         */
-       if ((card->csd.mmca_vsn >= CSD_SPEC_VER_4) &&
+       if (!mmc_card_hs200(card) &&
+           (card->csd.mmca_vsn >= CSD_SPEC_VER_3) &&
            (host->caps & (MMC_CAP_4_BIT_DATA | MMC_CAP_8_BIT_DATA))) {
                static unsigned ext_csd_bits[][2] = {
                        { EXT_CSD_BUS_WIDTH_8, EXT_CSD_DDR_BUS_WIDTH_8 },