Commit aa33ce3c authored by Ulf Hansson's avatar Ulf Hansson

mmc: core: Enable __mmc_switch() to change bus speed timing for the host

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: default avatarUlf Hansson <ulf.hansson@linaro.org>
Tested-by: default avatarLinus Walleij <linus.walleij@linaro.org>
Acked-by: default avatarAdrian Hunter <adrian.hunter@intel.com>
parent 5ec32f84
...@@ -380,7 +380,7 @@ void mmc_start_bkops(struct mmc_card *card, bool from_exception) ...@@ -380,7 +380,7 @@ void mmc_start_bkops(struct mmc_card *card, bool from_exception)
mmc_retune_hold(card->host); mmc_retune_hold(card->host);
err = __mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, 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); use_busy_signal, true, false);
if (err) { if (err) {
pr_warn("%s: Error %d starting bkops\n", pr_warn("%s: Error %d starting bkops\n",
......
...@@ -1012,7 +1012,7 @@ static int mmc_select_hs(struct mmc_card *card) ...@@ -1012,7 +1012,7 @@ static int mmc_select_hs(struct mmc_card *card)
err = __mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, err = __mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
EXT_CSD_HS_TIMING, EXT_CSD_TIMING_HS, EXT_CSD_HS_TIMING, EXT_CSD_TIMING_HS,
card->ext_csd.generic_cmd6_time, card->ext_csd.generic_cmd6_time, 0,
true, false, true); true, false, true);
if (!err) { if (!err) {
mmc_set_timing(card->host, MMC_TIMING_MMC_HS); mmc_set_timing(card->host, MMC_TIMING_MMC_HS);
...@@ -1115,7 +1115,7 @@ static int mmc_select_hs400(struct mmc_card *card) ...@@ -1115,7 +1115,7 @@ static int mmc_select_hs400(struct mmc_card *card)
val = EXT_CSD_TIMING_HS; val = EXT_CSD_TIMING_HS;
err = __mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, err = __mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
EXT_CSD_HS_TIMING, val, EXT_CSD_HS_TIMING, val,
card->ext_csd.generic_cmd6_time, card->ext_csd.generic_cmd6_time, 0,
true, false, true); true, false, true);
if (err) { if (err) {
pr_err("%s: switch to high-speed from hs200 failed, err:%d\n", 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) ...@@ -1150,7 +1150,7 @@ static int mmc_select_hs400(struct mmc_card *card)
card->drive_strength << EXT_CSD_DRV_STR_SHIFT; card->drive_strength << EXT_CSD_DRV_STR_SHIFT;
err = __mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, err = __mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
EXT_CSD_HS_TIMING, val, EXT_CSD_HS_TIMING, val,
card->ext_csd.generic_cmd6_time, card->ext_csd.generic_cmd6_time, 0,
true, false, true); true, false, true);
if (err) { if (err) {
pr_err("%s: switch to hs400 failed, err:%d\n", pr_err("%s: switch to hs400 failed, err:%d\n",
...@@ -1193,7 +1193,7 @@ int mmc_hs400_to_hs200(struct mmc_card *card) ...@@ -1193,7 +1193,7 @@ int mmc_hs400_to_hs200(struct mmc_card *card)
/* Switch HS400 to HS DDR */ /* Switch HS400 to HS DDR */
val = EXT_CSD_TIMING_HS; val = EXT_CSD_TIMING_HS;
err = __mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, EXT_CSD_HS_TIMING, 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); true, false, true);
if (err) if (err)
goto out_err; goto out_err;
...@@ -1207,7 +1207,7 @@ int mmc_hs400_to_hs200(struct mmc_card *card) ...@@ -1207,7 +1207,7 @@ int mmc_hs400_to_hs200(struct mmc_card *card)
/* Switch HS DDR to HS */ /* Switch HS DDR to HS */
err = __mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, EXT_CSD_BUS_WIDTH, err = __mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, EXT_CSD_BUS_WIDTH,
EXT_CSD_BUS_WIDTH_8, card->ext_csd.generic_cmd6_time, EXT_CSD_BUS_WIDTH_8, card->ext_csd.generic_cmd6_time,
true, false, true); 0, true, false, true);
if (err) if (err)
goto out_err; goto out_err;
...@@ -1221,7 +1221,7 @@ int mmc_hs400_to_hs200(struct mmc_card *card) ...@@ -1221,7 +1221,7 @@ int mmc_hs400_to_hs200(struct mmc_card *card)
val = EXT_CSD_TIMING_HS200 | val = EXT_CSD_TIMING_HS200 |
card->drive_strength << EXT_CSD_DRV_STR_SHIFT; card->drive_strength << EXT_CSD_DRV_STR_SHIFT;
err = __mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, EXT_CSD_HS_TIMING, 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); true, false, true);
if (err) if (err)
goto out_err; goto out_err;
...@@ -1295,7 +1295,7 @@ static int mmc_select_hs400es(struct mmc_card *card) ...@@ -1295,7 +1295,7 @@ static int mmc_select_hs400es(struct mmc_card *card)
card->drive_strength << EXT_CSD_DRV_STR_SHIFT; card->drive_strength << EXT_CSD_DRV_STR_SHIFT;
err = __mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, err = __mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
EXT_CSD_HS_TIMING, val, EXT_CSD_HS_TIMING, val,
card->ext_csd.generic_cmd6_time, card->ext_csd.generic_cmd6_time, 0,
true, false, true); true, false, true);
if (err) { if (err) {
pr_err("%s: switch to hs400es failed, err:%d\n", pr_err("%s: switch to hs400es failed, err:%d\n",
...@@ -1377,7 +1377,7 @@ static int mmc_select_hs200(struct mmc_card *card) ...@@ -1377,7 +1377,7 @@ static int mmc_select_hs200(struct mmc_card *card)
card->drive_strength << EXT_CSD_DRV_STR_SHIFT; card->drive_strength << EXT_CSD_DRV_STR_SHIFT;
err = __mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, err = __mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
EXT_CSD_HS_TIMING, val, EXT_CSD_HS_TIMING, val,
card->ext_csd.generic_cmd6_time, card->ext_csd.generic_cmd6_time, 0,
true, false, true); true, false, true);
if (err) if (err)
goto err; goto err;
...@@ -1841,7 +1841,7 @@ static int mmc_poweroff_notify(struct mmc_card *card, unsigned int notify_type) ...@@ -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, err = __mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
EXT_CSD_POWER_OFF_NOTIFICATION, EXT_CSD_POWER_OFF_NOTIFICATION,
notify_type, timeout, true, false, false); notify_type, timeout, 0, true, false, false);
if (err) if (err)
pr_err("%s: Power Off Notification timed out, %u\n", pr_err("%s: Power Off Notification timed out, %u\n",
mmc_hostname(card->host), timeout); mmc_hostname(card->host), timeout);
......
...@@ -530,6 +530,7 @@ static int mmc_poll_for_busy(struct mmc_card *card, unsigned int timeout_ms, ...@@ -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 * @value: value to program into EXT_CSD register
* @timeout_ms: timeout (ms) for operation performed by register write, * @timeout_ms: timeout (ms) for operation performed by register write,
* timeout of zero implies maximum possible timeout * timeout of zero implies maximum possible timeout
* @timing: new timing to change to
* @use_busy_signal: use the busy signal as response type * @use_busy_signal: use the busy signal as response type
* @send_status: send status cmd to poll for busy * @send_status: send status cmd to poll for busy
* @retry_crc_err: retry when CRC errors when polling with CMD13 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, ...@@ -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. * Modifies the EXT_CSD register for selected card.
*/ */
int __mmc_switch(struct mmc_card *card, u8 set, u8 index, u8 value, int __mmc_switch(struct mmc_card *card, u8 set, u8 index, u8 value,
unsigned int timeout_ms, bool use_busy_signal, bool send_status, unsigned int timeout_ms, unsigned char timing,
bool retry_crc_err) bool use_busy_signal, bool send_status, bool retry_crc_err)
{ {
struct mmc_host *host = card->host; struct mmc_host *host = card->host;
int err; int err;
struct mmc_command cmd = {0}; struct mmc_command cmd = {0};
bool use_r1b_resp = use_busy_signal; bool use_r1b_resp = use_busy_signal;
unsigned char old_timing = host->ios.timing;
mmc_retune_hold(host); mmc_retune_hold(host);
...@@ -585,16 +587,24 @@ int __mmc_switch(struct mmc_card *card, u8 set, u8 index, u8 value, ...@@ -585,16 +587,24 @@ int __mmc_switch(struct mmc_card *card, u8 set, u8 index, u8 value,
if (!use_busy_signal) if (!use_busy_signal)
goto out; 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 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) || if (((host->caps & MMC_CAP_WAIT_WHILE_BUSY) && use_r1b_resp) ||
mmc_host_is_spi(host)) { mmc_host_is_spi(host)) {
if (send_status) if (send_status)
err = mmc_switch_status(card); err = mmc_switch_status(card);
goto out; goto out_tim;
} }
/* Let's try to poll to find out when the command is completed. */ /* 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); 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: out:
mmc_retune_release(host); mmc_retune_release(host);
...@@ -604,8 +614,8 @@ int __mmc_switch(struct mmc_card *card, u8 set, u8 index, u8 value, ...@@ -604,8 +614,8 @@ int __mmc_switch(struct mmc_card *card, u8 set, u8 index, u8 value,
int mmc_switch(struct mmc_card *card, u8 set, u8 index, u8 value, int mmc_switch(struct mmc_card *card, u8 set, u8 index, u8 value,
unsigned int timeout_ms) unsigned int timeout_ms)
{ {
return __mmc_switch(card, set, index, value, timeout_ms, true, true, return __mmc_switch(card, set, index, value, timeout_ms, 0,
false); true, true, false);
} }
EXPORT_SYMBOL_GPL(mmc_switch); EXPORT_SYMBOL_GPL(mmc_switch);
......
...@@ -29,8 +29,8 @@ int mmc_send_hpi_cmd(struct mmc_card *card, u32 *status); ...@@ -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_can_ext_csd(struct mmc_card *card);
int mmc_switch_status(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, int __mmc_switch(struct mmc_card *card, u8 set, u8 index, u8 value,
unsigned int timeout_ms, bool use_busy_signal, bool send_status, unsigned int timeout_ms, unsigned char timing,
bool retry_crc_err); bool use_busy_signal, bool send_status, bool retry_crc_err);
#endif #endif
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment