mmc: core: Enable __mmc_switch() to change bus speed timing for the host
authorUlf Hansson <ulf.hansson@linaro.org>
Wed, 9 Nov 2016 16:33:36 +0000 (17:33 +0100)
committerUlf Hansson <ulf.hansson@linaro.org>
Tue, 29 Nov 2016 08:05:25 +0000 (09:05 +0100)
In cases when a speed mode change is requested for mmc cards, a CMD6 is
sent by calling __mmc_switch() during the card initialization. The CMD6
leads to the card entering a busy period. When that is completed, the host
must parse the CMD6 status to find out whether the change of the speed mode
succeeded.

To enable the mmc core to poll the card by using CMD13 to find out when the
busy period is completed, it's reasonable to make sure polling is done by
having the mmc host and the mmc card, being configured to operate at the
same selected bus speed timing.

Therefore, let's extend __mmc_switch() to take yet another parameter, which
allow its callers to update the bus speed timing of the mmc host. In this
way, __mmc_switch() also becomes capable of reading and validating the CMD6
status by sending a CMD13, in cases when that's desired.

If __mmc_switch() encounters a failure, we make sure to restores the old
bus speed timing for the mmc host, before propagating the error code.

Signed-off-by: Ulf Hansson <ulf.hansson@linaro.org>
Tested-by: Linus Walleij <linus.walleij@linaro.org>
Acked-by: Adrian Hunter <adrian.hunter@intel.com>
drivers/mmc/core/core.c
drivers/mmc/core/mmc.c
drivers/mmc/core/mmc_ops.c
drivers/mmc/core/mmc_ops.h

index 50bb9a16380deca01eabc20d8d029b16e7b5c28d..060f76742550ffced22aaa4fe55a15d05053257f 100644 (file)
@@ -380,7 +380,7 @@ void mmc_start_bkops(struct mmc_card *card, bool from_exception)
        mmc_retune_hold(card->host);
 
        err = __mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
-                       EXT_CSD_BKOPS_START, 1, timeout,
+                       EXT_CSD_BKOPS_START, 1, timeout, 0,
                        use_busy_signal, true, false);
        if (err) {
                pr_warn("%s: Error %d starting bkops\n",
index 9355366d325935480a77937952316b2540c02ecf..cb1cf4e5d9169267f46dac8e2af98ef58a65b825 100644 (file)
@@ -1012,7 +1012,7 @@ static int mmc_select_hs(struct mmc_card *card)
 
        err = __mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
                           EXT_CSD_HS_TIMING, EXT_CSD_TIMING_HS,
-                          card->ext_csd.generic_cmd6_time,
+                          card->ext_csd.generic_cmd6_time, 0,
                           true, false, true);
        if (!err) {
                mmc_set_timing(card->host, MMC_TIMING_MMC_HS);
@@ -1115,7 +1115,7 @@ static int mmc_select_hs400(struct mmc_card *card)
        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,
+                          card->ext_csd.generic_cmd6_time, 0,
                           true, false, true);
        if (err) {
                pr_err("%s: switch to high-speed from hs200 failed, err:%d\n",
@@ -1150,7 +1150,7 @@ static int mmc_select_hs400(struct mmc_card *card)
              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,
+                          card->ext_csd.generic_cmd6_time, 0,
                           true, false, true);
        if (err) {
                pr_err("%s: switch to hs400 failed, err:%d\n",
@@ -1193,7 +1193,7 @@ int mmc_hs400_to_hs200(struct mmc_card *card)
        /* 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,
+                          val, card->ext_csd.generic_cmd6_time, 0,
                           true, false, true);
        if (err)
                goto out_err;
@@ -1207,7 +1207,7 @@ int mmc_hs400_to_hs200(struct mmc_card *card)
        /* 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, false, true);
+                          0, true, false, true);
        if (err)
                goto out_err;
 
@@ -1221,7 +1221,7 @@ int mmc_hs400_to_hs200(struct mmc_card *card)
        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,
+                          val, card->ext_csd.generic_cmd6_time, 0,
                           true, false, true);
        if (err)
                goto out_err;
@@ -1295,7 +1295,7 @@ static int mmc_select_hs400es(struct mmc_card *card)
              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,
+                          card->ext_csd.generic_cmd6_time, 0,
                           true, false, true);
        if (err) {
                pr_err("%s: switch to hs400es failed, err:%d\n",
@@ -1377,7 +1377,7 @@ static int mmc_select_hs200(struct mmc_card *card)
                      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,
+                                  card->ext_csd.generic_cmd6_time, 0,
                                   true, false, true);
                if (err)
                        goto err;
@@ -1841,7 +1841,7 @@ static int mmc_poweroff_notify(struct mmc_card *card, unsigned int notify_type)
 
        err = __mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
                        EXT_CSD_POWER_OFF_NOTIFICATION,
-                       notify_type, timeout, true, false, false);
+                       notify_type, timeout, 0, true, false, false);
        if (err)
                pr_err("%s: Power Off Notification timed out, %u\n",
                       mmc_hostname(card->host), timeout);
index fba5d2954165ab3cae46e76affa2c9fb8bde6abe..9b2617cfff6712dc7a8ba7b29e4ca50518199206 100644 (file)
@@ -530,6 +530,7 @@ static int mmc_poll_for_busy(struct mmc_card *card, unsigned int timeout_ms,
  *     @value: value to program into EXT_CSD register
  *     @timeout_ms: timeout (ms) for operation performed by register write,
  *                   timeout of zero implies maximum possible timeout
+ *     @timing: new timing to change to
  *     @use_busy_signal: use the busy signal as response type
  *     @send_status: send status cmd to poll for busy
  *     @retry_crc_err: retry when CRC errors when polling with CMD13 for busy
@@ -537,13 +538,14 @@ static int mmc_poll_for_busy(struct mmc_card *card, unsigned int timeout_ms,
  *     Modifies the EXT_CSD register for selected card.
  */
 int __mmc_switch(struct mmc_card *card, u8 set, u8 index, u8 value,
-               unsigned int timeout_ms, bool use_busy_signal, bool send_status,
-               bool retry_crc_err)
+               unsigned int timeout_ms, unsigned char timing,
+               bool use_busy_signal, bool send_status, bool retry_crc_err)
 {
        struct mmc_host *host = card->host;
        int err;
        struct mmc_command cmd = {0};
        bool use_r1b_resp = use_busy_signal;
+       unsigned char old_timing = host->ios.timing;
 
        mmc_retune_hold(host);
 
@@ -585,16 +587,24 @@ int __mmc_switch(struct mmc_card *card, u8 set, u8 index, u8 value,
        if (!use_busy_signal)
                goto out;
 
+       /* Switch to new timing before poll and check switch status. */
+       if (timing)
+               mmc_set_timing(host, timing);
+
        /*If SPI or used HW busy detection above, then we don't need to poll. */
        if (((host->caps & MMC_CAP_WAIT_WHILE_BUSY) && use_r1b_resp) ||
                mmc_host_is_spi(host)) {
                if (send_status)
                        err = mmc_switch_status(card);
-               goto out;
+               goto out_tim;
        }
 
        /* Let's try to poll to find out when the command is completed. */
        err = mmc_poll_for_busy(card, timeout_ms, send_status, retry_crc_err);
+
+out_tim:
+       if (err && timing)
+               mmc_set_timing(host, old_timing);
 out:
        mmc_retune_release(host);
 
@@ -604,8 +614,8 @@ out:
 int mmc_switch(struct mmc_card *card, u8 set, u8 index, u8 value,
                unsigned int timeout_ms)
 {
-       return __mmc_switch(card, set, index, value, timeout_ms, true, true,
-                               false);
+       return __mmc_switch(card, set, index, value, timeout_ms, 0,
+                       true, true, false);
 }
 EXPORT_SYMBOL_GPL(mmc_switch);
 
index 9fccddb48148dc8634a154a3bcb54a8f1773d127..761cb69c46afeab63af9706ab030f4901c722b5b 100644 (file)
@@ -29,8 +29,8 @@ int mmc_send_hpi_cmd(struct mmc_card *card, u32 *status);
 int mmc_can_ext_csd(struct mmc_card *card);
 int mmc_switch_status(struct mmc_card *card);
 int __mmc_switch(struct mmc_card *card, u8 set, u8 index, u8 value,
-               unsigned int timeout_ms, bool use_busy_signal, bool send_status,
-               bool retry_crc_err);
+               unsigned int timeout_ms, unsigned char timing,
+               bool use_busy_signal, bool send_status, bool retry_crc_err);
 
 #endif