summaryrefslogtreecommitdiff
path: root/drivers/mmc
diff options
context:
space:
mode:
authorUlf Hansson <ulf.hansson@linaro.org>2014-01-28 17:15:34 +0400
committerChris Ball <chris@printf.net>2014-02-23 19:40:38 +0400
commitb9ec26160f998493509b782be999ff59e4372971 (patch)
tree06c0af48fe14643e4252a50d153eef81461c5a25 /drivers/mmc
parent636bd13c12d2331ad835b89681428eccb4cf102e (diff)
downloadlinux-b9ec26160f998493509b782be999ff59e4372971.tar.xz
mmc: core: Fixup busy detection for mmc switch operations
If the host controller supports busy detection in HW, we expect the MMC_CAP_WAIT_WHILE_BUSY to be set. Likewise the corresponding host->max_busy_timeout should reflect the maximum busy detection timeout supported by the host. Previously we expected a host that supported MMC_CAP_WAIT_WHILE_BUSY to cope with any timeout, which just isn't feasible due to HW limitations. For most switch operations, R1B responses are expected and thus we need to check for busy detection completion. To cope with cases where the requested busy detection timeout is greater than what the host are able to support, we fallback to use a R1 response instead. This will prevent the host from doing HW busy detection. In those cases, busy detection completion is handled by polling the for the card's status using CMD13. This is the same mechanism used when the host doesn't support MMC_CAP_WAIT_WHILE_BUSY. Do note, a host->max_busy_timeout set to zero, is interpreted by the mmc core as it don't know what the host supports. It will then provide the host with whatever timeout the mmc core finds suitable. For some cases the mmc core has unfurtunate no clue of what timeout to use. In these cases we provide the host with a timeout value of zero, which the host may interpret as use whatever timeout it finds suitable. Signed-off-by: Ulf Hansson <ulf.hansson@linaro.org> Signed-off-by: Chris Ball <chris@printf.net>
Diffstat (limited to 'drivers/mmc')
-rw-r--r--drivers/mmc/core/mmc_ops.c33
1 files changed, 26 insertions, 7 deletions
diff --git a/drivers/mmc/core/mmc_ops.c b/drivers/mmc/core/mmc_ops.c
index 04ecdb913aa0..f51b5ba3bbea 100644
--- a/drivers/mmc/core/mmc_ops.c
+++ b/drivers/mmc/core/mmc_ops.c
@@ -418,6 +418,17 @@ int __mmc_switch(struct mmc_card *card, u8 set, u8 index, u8 value,
struct mmc_command cmd = {0};
unsigned long timeout;
u32 status = 0;
+ bool use_r1b_resp = use_busy_signal;
+
+ /*
+ * If the cmd timeout and the max_busy_timeout of the host are both
+ * specified, let's validate them. A failure means we need to prevent
+ * the host from doing hw busy detection, which is done by converting
+ * to a R1 response instead of a R1B.
+ */
+ if (timeout_ms && host->max_busy_timeout &&
+ (timeout_ms > host->max_busy_timeout))
+ use_r1b_resp = false;
cmd.opcode = MMC_SWITCH;
cmd.arg = (MMC_SWITCH_MODE_WRITE_BYTE << 24) |
@@ -425,13 +436,17 @@ int __mmc_switch(struct mmc_card *card, u8 set, u8 index, u8 value,
(value << 8) |
set;
cmd.flags = MMC_CMD_AC;
- if (use_busy_signal)
+ if (use_r1b_resp) {
cmd.flags |= MMC_RSP_SPI_R1B | MMC_RSP_R1B;
- else
+ /*
+ * A busy_timeout of zero means the host can decide to use
+ * whatever value it finds suitable.
+ */
+ cmd.busy_timeout = timeout_ms;
+ } else {
cmd.flags |= MMC_RSP_SPI_R1 | MMC_RSP_R1;
+ }
-
- cmd.busy_timeout = timeout_ms;
if (index == EXT_CSD_SANITIZE_START)
cmd.sanitize_busy = true;
@@ -447,18 +462,22 @@ int __mmc_switch(struct mmc_card *card, u8 set, u8 index, u8 value,
* CRC errors shall only be ignored in cases were CMD13 is used to poll
* to detect busy completion.
*/
- if (host->caps & MMC_CAP_WAIT_WHILE_BUSY)
+ if ((host->caps & MMC_CAP_WAIT_WHILE_BUSY) && use_r1b_resp)
ignore_crc = false;
+ /* We have an unspecified cmd timeout, use the fallback value. */
+ if (!timeout_ms)
+ timeout_ms = MMC_OPS_TIMEOUT_MS;
+
/* Must check status to be sure of no errors. */
- timeout = jiffies + msecs_to_jiffies(MMC_OPS_TIMEOUT_MS);
+ timeout = jiffies + msecs_to_jiffies(timeout_ms);
do {
if (send_status) {
err = __mmc_send_status(card, &status, ignore_crc);
if (err)
return err;
}
- if (host->caps & MMC_CAP_WAIT_WHILE_BUSY)
+ if ((host->caps & MMC_CAP_WAIT_WHILE_BUSY) && use_r1b_resp)
break;
if (mmc_host_is_spi(host))
break;