These changes are the raw update to linux-4.4.6-rt14. Kernel sources
[kvmfornfv.git] / kernel / drivers / mmc / core / mmc.c
index f36c76f..3d5087b 100644 (file)
@@ -21,6 +21,7 @@
 #include <linux/mmc/mmc.h>
 
 #include "core.h"
+#include "host.h"
 #include "bus.h"
 #include "mmc_ops.h"
 #include "sd_ops.h"
@@ -266,8 +267,10 @@ static void mmc_manage_enhanced_area(struct mmc_card *card, u8 *ext_csd)
                         * calculate the enhanced data area offset, in bytes
                         */
                        card->ext_csd.enhanced_area_offset =
-                               (ext_csd[139] << 24) + (ext_csd[138] << 16) +
-                               (ext_csd[137] << 8) + ext_csd[136];
+                               (((unsigned long long)ext_csd[139]) << 24) +
+                               (((unsigned long long)ext_csd[138]) << 16) +
+                               (((unsigned long long)ext_csd[137]) << 8) +
+                               (((unsigned long long)ext_csd[136]));
                        if (mmc_card_blockaddr(card))
                                card->ext_csd.enhanced_area_offset <<= 9;
                        /*
@@ -434,6 +437,7 @@ static int mmc_decode_ext_csd(struct mmc_card *card, u8 *ext_csd)
        card->ext_csd.raw_trim_mult =
                ext_csd[EXT_CSD_TRIM_MULT];
        card->ext_csd.raw_partition_support = ext_csd[EXT_CSD_PARTITION_SUPPORT];
+       card->ext_csd.raw_driver_strength = ext_csd[EXT_CSD_DRIVER_STRENGTH];
        if (card->ext_csd.rev >= 4) {
                if (ext_csd[EXT_CSD_PARTITION_SETTING_COMPLETED] &
                    EXT_CSD_PART_SETTING_COMPLETED)
@@ -1036,10 +1040,26 @@ static int mmc_select_hs_ddr(struct mmc_card *card)
        return err;
 }
 
+/* Caller must hold re-tuning */
+static int mmc_switch_status(struct mmc_card *card)
+{
+       u32 status;
+       int err;
+
+       err = mmc_send_status(card, &status);
+       if (err)
+               return err;
+
+       return mmc_switch_status_error(card->host, status);
+}
+
 static int mmc_select_hs400(struct mmc_card *card)
 {
        struct mmc_host *host = card->host;
+       bool send_status = true;
+       unsigned int max_dtr;
        int err = 0;
+       u8 val;
 
        /*
         * HS400 mode requires 8-bit bus width
@@ -1048,23 +1068,35 @@ static int mmc_select_hs400(struct mmc_card *card)
              host->ios.bus_width == MMC_BUS_WIDTH_8))
                return 0;
 
-       /*
-        * Before switching to dual data rate operation for HS400,
-        * it is required to convert from HS200 mode to HS mode.
-        */
-       mmc_set_timing(card->host, MMC_TIMING_MMC_HS);
-       mmc_set_bus_speed(card);
+       if (host->caps & MMC_CAP_WAIT_WHILE_BUSY)
+               send_status = false;
+
+       /* Reduce frequency to HS frequency */
+       max_dtr = card->ext_csd.hs_max_dtr;
+       mmc_set_clock(host, max_dtr);
 
+       /* Switch card to HS mode */
+       val = EXT_CSD_TIMING_HS;
        err = __mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
-                          EXT_CSD_HS_TIMING, EXT_CSD_TIMING_HS,
+                          EXT_CSD_HS_TIMING, val,
                           card->ext_csd.generic_cmd6_time,
-                          true, true, true);
+                          true, send_status, true);
        if (err) {
                pr_err("%s: switch to high-speed from hs200 failed, err:%d\n",
                        mmc_hostname(host), err);
                return err;
        }
 
+       /* Set host controller to HS timing */
+       mmc_set_timing(card->host, MMC_TIMING_MMC_HS);
+
+       if (!send_status) {
+               err = mmc_switch_status(card);
+               if (err)
+                       goto out_err;
+       }
+
+       /* Switch card to DDR */
        err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
                         EXT_CSD_BUS_WIDTH,
                         EXT_CSD_DDR_BUS_WIDTH_8,
@@ -1075,20 +1107,130 @@ static int mmc_select_hs400(struct mmc_card *card)
                return 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, EXT_CSD_TIMING_HS400,
+                          EXT_CSD_HS_TIMING, val,
                           card->ext_csd.generic_cmd6_time,
-                          true, true, true);
+                          true, send_status, true);
        if (err) {
                pr_err("%s: switch to hs400 failed, err:%d\n",
                         mmc_hostname(host), err);
                return err;
        }
 
+       /* Set host controller to HS400 timing and frequency */
        mmc_set_timing(host, MMC_TIMING_MMC_HS400);
        mmc_set_bus_speed(card);
 
+       if (!send_status) {
+               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;
+}
+
+int mmc_hs200_to_hs400(struct mmc_card *card)
+{
+       return mmc_select_hs400(card);
+}
+
+int mmc_hs400_to_hs200(struct mmc_card *card)
+{
+       struct mmc_host *host = card->host;
+       bool send_status = true;
+       unsigned int max_dtr;
+       int err;
+       u8 val;
+
+       if (host->caps & MMC_CAP_WAIT_WHILE_BUSY)
+               send_status = false;
+
+       /* Reduce frequency to HS */
+       max_dtr = card->ext_csd.hs_max_dtr;
+       mmc_set_clock(host, max_dtr);
+
+       /* Switch HS400 to HS DDR */
+       val = EXT_CSD_TIMING_HS;
+       err = __mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, EXT_CSD_HS_TIMING,
+                          val, card->ext_csd.generic_cmd6_time,
+                          true, send_status, true);
+       if (err)
+               goto out_err;
+
+       mmc_set_timing(host, MMC_TIMING_MMC_DDR52);
+
+       if (!send_status) {
+               err = mmc_switch_status(card);
+               if (err)
+                       goto out_err;
+       }
+
+       /* Switch HS DDR to HS */
+       err = __mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, EXT_CSD_BUS_WIDTH,
+                          EXT_CSD_BUS_WIDTH_8, card->ext_csd.generic_cmd6_time,
+                          true, send_status, true);
+       if (err)
+               goto out_err;
+
+       mmc_set_timing(host, MMC_TIMING_MMC_HS);
+
+       if (!send_status) {
+               err = mmc_switch_status(card);
+               if (err)
+                       goto out_err;
+       }
+
+       /* Switch HS to HS200 */
+       val = EXT_CSD_TIMING_HS200 |
+             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,
+                          send_status, true);
+       if (err)
+               goto out_err;
+
+       mmc_set_timing(host, MMC_TIMING_MMC_HS200);
+
+       if (!send_status) {
+               err = mmc_switch_status(card);
+               if (err)
+                       goto out_err;
+       }
+
+       mmc_set_bus_speed(card);
+
+       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;
+
+       card_drv_type = card->ext_csd.raw_driver_strength |
+                       mmc_driver_type_mask(0);
+
+       drive_strength = mmc_select_drive_strength(card,
+                                                  card->ext_csd.hs200_max_dtr,
+                                                  card_drv_type, &drv_type);
+
+       card->drive_strength = drive_strength;
+
+       if (drv_type)
+               mmc_set_driver_type(card->host, drv_type);
 }
 
 /*
@@ -1101,7 +1243,10 @@ static int mmc_select_hs400(struct mmc_card *card)
 static int mmc_select_hs200(struct mmc_card *card)
 {
        struct mmc_host *host = card->host;
+       bool send_status = true;
+       unsigned int old_timing;
        int err = -EINVAL;
+       u8 val;
 
        if (card->mmc_avail_type & EXT_CSD_CARD_TYPE_HS200_1_2V)
                err = __mmc_set_signal_voltage(host, MMC_SIGNAL_VOLTAGE_120);
@@ -1113,20 +1258,41 @@ static int mmc_select_hs200(struct mmc_card *card)
        if (err)
                goto err;
 
+       mmc_select_driver_type(card);
+
+       if (host->caps & MMC_CAP_WAIT_WHILE_BUSY)
+               send_status = false;
+
        /*
         * Set the bus width(4 or 8) with host's support and
         * switch to HS200 mode if bus width is set successfully.
         */
        err = mmc_select_bus_width(card);
        if (!IS_ERR_VALUE(err)) {
+               val = EXT_CSD_TIMING_HS200 |
+                     card->drive_strength << EXT_CSD_DRV_STR_SHIFT;
                err = __mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
-                                  EXT_CSD_HS_TIMING, EXT_CSD_TIMING_HS200,
+                                  EXT_CSD_HS_TIMING, val,
                                   card->ext_csd.generic_cmd6_time,
-                                  true, true, true);
-               if (!err)
-                       mmc_set_timing(host, MMC_TIMING_MMC_HS200);
+                                  true, send_status, true);
+               if (err)
+                       goto err;
+               old_timing = host->ios.timing;
+               mmc_set_timing(host, MMC_TIMING_MMC_HS200);
+               if (!send_status) {
+                       err = mmc_switch_status(card);
+                       /*
+                        * mmc_select_timing() assumes timing has not changed if
+                        * it is a switch error.
+                        */
+                       if (err == -EBADMSG)
+                               mmc_set_timing(host, old_timing);
+               }
        }
 err:
+       if (err)
+               pr_err("%s: %s failed, error %d\n", mmc_hostname(card->host),
+                      __func__, err);
        return err;
 }
 
@@ -1511,9 +1677,12 @@ static int mmc_sleep(struct mmc_host *host)
        unsigned int timeout_ms = DIV_ROUND_UP(card->ext_csd.sa_timeout, 10000);
        int err;
 
+       /* Re-tuning can't be done once the card is deselected */
+       mmc_retune_hold(host);
+
        err = mmc_deselect_cards(host);
        if (err)
-               return err;
+               goto out_release;
 
        cmd.opcode = MMC_SLEEP_AWAKE;
        cmd.arg = card->rca << 16;
@@ -1534,7 +1703,7 @@ static int mmc_sleep(struct mmc_host *host)
 
        err = mmc_wait_for_cmd(host, &cmd, 0);
        if (err)
-               return err;
+               goto out_release;
 
        /*
         * If the host does not wait while the card signals busy, then we will
@@ -1545,6 +1714,8 @@ static int mmc_sleep(struct mmc_host *host)
        if (!cmd.busy_timeout || !(host->caps & MMC_CAP_WAIT_WHILE_BUSY))
                mmc_delay(timeout_ms);
 
+out_release:
+       mmc_retune_release(host);
        return err;
 }
 
@@ -1782,17 +1953,6 @@ static int mmc_runtime_resume(struct mmc_host *host)
        return 0;
 }
 
-static int mmc_power_restore(struct mmc_host *host)
-{
-       int ret;
-
-       mmc_claim_host(host);
-       ret = mmc_init_card(host, host->card->ocr, host->card);
-       mmc_release_host(host);
-
-       return ret;
-}
-
 int mmc_can_reset(struct mmc_card *card)
 {
        u8 rst_n_function;
@@ -1807,7 +1967,6 @@ EXPORT_SYMBOL(mmc_can_reset);
 static int mmc_reset(struct mmc_host *host)
 {
        struct mmc_card *card = host->card;
-       u32 status;
 
        if (!(host->caps & MMC_CAP_HW_RESET) || !host->ops->hw_reset)
                return -EOPNOTSUPP;
@@ -1815,22 +1974,14 @@ static int mmc_reset(struct mmc_host *host)
        if (!mmc_can_reset(card))
                return -EOPNOTSUPP;
 
-       mmc_host_clk_hold(host);
        mmc_set_clock(host, host->f_init);
 
        host->ops->hw_reset(host);
 
-       /* If the reset has happened, then a status command will fail */
-       if (!mmc_send_status(card, &status)) {
-               mmc_host_clk_release(host);
-               return -ENOSYS;
-       }
-
        /* Set initial state and call mmc_set_ios */
        mmc_set_initial_state(host);
-       mmc_host_clk_release(host);
 
-       return mmc_power_restore(host);
+       return mmc_init_card(host, card->ocr, card);
 }
 
 static const struct mmc_bus_ops mmc_ops = {
@@ -1840,7 +1991,6 @@ static const struct mmc_bus_ops mmc_ops = {
        .resume = mmc_resume,
        .runtime_suspend = mmc_runtime_suspend,
        .runtime_resume = mmc_runtime_resume,
-       .power_restore = mmc_power_restore,
        .alive = mmc_alive,
        .shutdown = mmc_shutdown,
        .reset = mmc_reset,
@@ -1897,14 +2047,13 @@ int mmc_attach_mmc(struct mmc_host *host)
 
        mmc_release_host(host);
        err = mmc_add_card(host->card);
-       mmc_claim_host(host);
        if (err)
                goto remove_card;
 
+       mmc_claim_host(host);
        return 0;
 
 remove_card:
-       mmc_release_host(host);
        mmc_remove_card(host->card);
        mmc_claim_host(host);
        host->card = NULL;