mmc: msm_sdcc: add support for CMD23
This patch adds following support in driver:
1. Send CMD23 if requested by MMC core layer for multiblock
read/write commands.
2. If multi block read/write fails (which was preceded by CMD23)
then send stop command (CMD12).
Signed-off-by: Subhash Jadavani <subhashj@codeaurora.org>
diff --git a/drivers/mmc/host/msm_sdcc.c b/drivers/mmc/host/msm_sdcc.c
index 2dea65c..2c4aca7 100644
--- a/drivers/mmc/host/msm_sdcc.c
+++ b/drivers/mmc/host/msm_sdcc.c
@@ -456,7 +456,8 @@
goto out;
}
msmsdcc_stop_data(host);
- if (!mrq->data->stop || mrq->cmd->error) {
+ if (!mrq->data->stop || mrq->cmd->error ||
+ (mrq->sbc && !mrq->data->error)) {
host->curr.mrq = NULL;
host->curr.cmd = NULL;
mrq->data->bytes_xfered = host->curr.data_xfered;
@@ -465,8 +466,10 @@
mmc_request_done(host->mmc, mrq);
return;
- } else
+ } else if (mrq->data->stop && ((mrq->sbc && mrq->data->error)
+ || !mrq->sbc)) {
msmsdcc_start_command(host, mrq->data->stop, 0);
+ }
}
out:
@@ -609,7 +612,8 @@
return;
}
msmsdcc_stop_data(host);
- if (!mrq->data->stop || mrq->cmd->error) {
+ if (!mrq->data->stop || mrq->cmd->error ||
+ (mrq->sbc && !mrq->data->error)) {
host->curr.mrq = NULL;
host->curr.cmd = NULL;
mrq->data->bytes_xfered = host->curr.data_xfered;
@@ -618,7 +622,8 @@
mmc_request_done(host->mmc, mrq);
return;
- } else {
+ } else if (mrq->data->stop && ((mrq->sbc && mrq->data->error)
+ || !mrq->sbc)) {
msmsdcc_start_command(host, mrq->data->stop, 0);
}
}
@@ -656,9 +661,11 @@
if (host->curr.data)
msmsdcc_stop_data(host);
- if (!mrq->data->stop || mrq->cmd->error)
+ if (!mrq->data->stop || mrq->cmd->error ||
+ (mrq->sbc && !mrq->data->error))
msmsdcc_request_end(host, mrq);
- else
+ else if (mrq->data->stop && ((mrq->sbc && mrq->data->error)
+ || !mrq->sbc))
msmsdcc_start_command(host, mrq->data->stop, 0);
}
@@ -930,13 +937,6 @@
mmc_hostname(host->mmc));
}
host->curr.cmd = cmd;
-
- /*
- * Kick the software command timeout timer here.
- * Timer expires in 10 secs.
- */
- mod_timer(&host->req_tout_timer,
- (jiffies + msecs_to_jiffies(MSM_MMC_REQ_TIMEOUT)));
}
static void
@@ -1022,7 +1022,7 @@
host->dma.hdr.exec_func = msmsdcc_dma_exec_func;
host->dma.hdr.user = (void *)host;
host->dma.busy = 1;
- if (data->flags & MMC_DATA_WRITE)
+ if ((data->flags & MMC_DATA_WRITE) && !host->curr.mrq->sbc)
host->prog_scan = 1;
if (cmd) {
@@ -1036,7 +1036,7 @@
msm_dmov_enqueue_cmd_ext(host->dma.channel, &host->dma.hdr);
} else {
/* SPS-BAM mode or PIO mode */
- if (data->flags & MMC_DATA_WRITE)
+ if ((data->flags & MMC_DATA_WRITE) && !host->curr.mrq->sbc)
host->prog_scan = 1;
writel_relaxed(timeout, base + MMCIDATATIMER);
@@ -1339,6 +1339,8 @@
msmsdcc_request_end(host, cmd->mrq);
}
}
+ } else if (cmd == host->curr.mrq->sbc) {
+ msmsdcc_request_start(host, host->curr.mrq);
} else if (cmd->data) {
if (!(cmd->data->flags & MMC_DATA_READ))
msmsdcc_start_data(host, cmd->data, NULL, 0);
@@ -1447,16 +1449,18 @@
else if (host->sps.sg && host->is_sps_mode) {
/* Stop current SPS transfer */
msmsdcc_sps_exit_curr_xfer(host);
- }
- else {
+ } else {
msmsdcc_reset_and_restore(host);
if (host->curr.data)
msmsdcc_stop_data(host);
- if (!data->stop)
+ if (!data->stop || (host->curr.mrq->sbc
+ && !data->error))
timer |=
msmsdcc_request_end(host,
data->mrq);
- else {
+ else if ((host->curr.mrq->sbc
+ && data->error) ||
+ !host->curr.mrq->sbc) {
msmsdcc_start_command(host,
data->stop,
0);
@@ -1505,11 +1509,15 @@
if (!host->dummy_52_needed) {
msmsdcc_stop_data(host);
- if (!data->stop) {
+ if (!data->stop ||
+ (host->curr.mrq->sbc
+ && !data->error))
msmsdcc_request_end(
host,
data->mrq);
- } else {
+ else if ((host->curr.mrq->sbc
+ && data->error) ||
+ !host->curr.mrq->sbc) {
msmsdcc_start_command(
host,
data->stop, 0);
@@ -1582,8 +1590,14 @@
return;
}
- host->curr.mrq = mrq;
+ /*
+ * Kick the software command timeout timer here.
+ * Timer expires in 10 secs.
+ */
+ mod_timer(&host->req_tout_timer,
+ (jiffies + msecs_to_jiffies(MSM_MMC_REQ_TIMEOUT)));
+ host->curr.mrq = mrq;
if (mrq->data && mrq->data->flags == MMC_DATA_WRITE) {
if (mrq->cmd->opcode == SD_IO_RW_EXTENDED ||
mrq->cmd->opcode == 54) {
@@ -1601,7 +1615,16 @@
}
}
- msmsdcc_request_start(host, mrq);
+ if (mrq->sbc) {
+ mrq->sbc->mrq = mrq;
+ mrq->sbc->data = mrq->data;
+ if (mrq->data->flags == MMC_DATA_WRITE)
+ host->curr.wait_for_auto_prog_done = 1;
+ msmsdcc_start_command(host, mrq->sbc, 0);
+ } else {
+ msmsdcc_request_start(host, mrq);
+ }
+
spin_unlock_irqrestore(&host->lock, flags);
}
@@ -3587,6 +3610,17 @@
mmc->caps |= plat->mmc_bus_width;
mmc->caps |= MMC_CAP_MMC_HIGHSPEED | MMC_CAP_SD_HIGHSPEED;
+
+ /*
+ * If we send the CMD23 before multi block write/read command
+ * then we need not to send CMD12 at the end of the transfer.
+ * If we don't send the CMD12 then only way to detect the PROG_DONE
+ * status is to use the AUTO_PROG_DONE status provided by SDCC4
+ * controller. So let's enable the CMD23 for SDCC4 only.
+ */
+ if (host->plat->sdcc_v4_sup)
+ mmc->caps |= MMC_CAP_CMD23;
+
mmc->caps |= plat->uhs_caps;
/*
* XPC controls the maximum current in the default speed mode of SDXC