diff options
author | Jean-Jacques Hiblot <jjhiblot@ti.com> | 2019-07-02 10:53:56 +0200 |
---|---|---|
committer | Peng Fan <peng.fan@nxp.com> | 2019-07-15 10:16:49 +0800 |
commit | bb98b8c5c06a5a9befb74aef843f7cd698c52d5d (patch) | |
tree | cd46206f7e099dac7eabba328906d3481f151e72 /drivers | |
parent | 513e00b64e63c277ad6dd667b823282ef4d177c1 (diff) |
mmc: During a switch, poll on dat0 if available and check the final status
The switch operation can sometimes make the bus unreliable, in that case
the send_status parameter should be false to indicate not to poll using
CMD13. If polling on dat0 is possible, we should use it to detect the end
of the operation.
At the end of the operation it is safe to use CMD13 to get the status of
the card. It is important to do so because the operation may have failed.
Signed-off-by: Jean-Jacques Hiblot <jjhiblot@ti.com>
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/mmc/mmc.c | 49 |
1 files changed, 36 insertions, 13 deletions
diff --git a/drivers/mmc/mmc.c b/drivers/mmc/mmc.c index e5cee7dbc8..1ad35fff7d 100644 --- a/drivers/mmc/mmc.c +++ b/drivers/mmc/mmc.c @@ -746,6 +746,7 @@ static int mmc_send_ext_csd(struct mmc *mmc, u8 *ext_csd) static int __mmc_switch(struct mmc *mmc, u8 set, u8 index, u8 value, bool send_status) { + unsigned int status, start; struct mmc_cmd cmd; int timeout = DEFAULT_CMD6_TIMEOUT_MS; bool is_part_switch = (set == EXT_CSD_CMD_SET_NORMAL) && @@ -765,25 +766,47 @@ static int __mmc_switch(struct mmc *mmc, u8 set, u8 index, u8 value, (index << 16) | (value << 8); - while (retries > 0) { + do { ret = mmc_send_cmd(mmc, &cmd, NULL); + } while (ret && retries-- > 0); - if (ret) { - retries--; - continue; - } + if (ret) + return ret; - if (!send_status) { - mdelay(50); - return 0; - } + start = get_timer(0); - /* Waiting for the ready status */ - return mmc_poll_for_busy(mmc, timeout); - } + /* poll dat0 for rdy/buys status */ + ret = mmc_wait_dat0(mmc, 1, timeout); + if (ret && ret != -ENOSYS) + return ret; - return ret; + /* + * In cases when not allowed to poll by using CMD13 or because we aren't + * capable of polling by using mmc_wait_dat0, then rely on waiting the + * stated timeout to be sufficient. + */ + if (ret == -ENOSYS && !send_status) + mdelay(timeout); + + /* Finally wait until the card is ready or indicates a failure + * to switch. It doesn't hurt to use CMD13 here even if send_status + * is false, because by now (after 'timeout' ms) the bus should be + * reliable. + */ + do { + ret = mmc_send_status(mmc, &status); + + if (!ret && (status & MMC_STATUS_SWITCH_ERROR)) { + pr_debug("switch failed %d/%d/0x%x !\n", set, index, + value); + return -EIO; + } + if (!ret && (status & MMC_STATUS_RDY_FOR_DATA)) + return 0; + udelay(100); + } while (get_timer(start) < timeout); + return -ETIMEDOUT; } int mmc_switch(struct mmc *mmc, u8 set, u8 index, u8 value) |