mmc: msm_sdcc: use DATA_PEND bit for write operation

DATA_PEND bit (in MCI_DATA_CTL register) is designed to be used
with CMD24 (WRITE_SINGLE_BLOCK) and CMD25 (WRITE_MULTIPLE_BLOCK) to
automatically start the DPSM (Data Path State Machine) after a normal
(non-error) response is received for CMD24/CMD25. To use this feature,
MCI_DATA_CTL register should be written with the enable bit and the
pending bit asserted before ENABLE bit is set in MCI_CMD register.

As of now SDCC driver is not using the DATA_PEND bit for any of the
write commands which means for write operation, driver first sends the
write command to card and then waits for the CMD_RESPOND_END interrupt
and then configures ADM/BAM and DATA_CTL register (with ENABLE bit set)
in interrupt context.

Instead driver can configure the DPSM (have to set DATA_PEND bit as well)
before configuring CPSM (Command Path State Machine) to send write
commands. So basically this will be the configuration sequence if
we use DATA_PEND bit:
	1. Configure ADM/BAM.
	2. Configure DATA_CTL with both DATA_PEND and ENABLE bits set.
	3. Configure MCI_CMD register for sending the write command.

All of the above configuration will now happen at the same time in thread
context. But note that as DATA_PEND bit only works with CMD24/CMD25
writes, so this change uses the DATA_PEND capability for only for
SD/MMC block write commands (CMD24/CMD25).

Change-Id: Ic1b8cbf625c08c3aec382c713d2e2b0acf3ca041
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 25195b7..4967c4e 100644
--- a/drivers/mmc/host/msm_sdcc.c
+++ b/drivers/mmc/host/msm_sdcc.c
@@ -1167,6 +1167,8 @@
 
 	if (data->flags & MMC_DATA_READ)
 		datactrl |= (MCI_DPSM_DIRECTION | MCI_RX_DATA_PEND);
+	else if (host->curr.use_wr_data_pend)
+		datactrl |= MCI_DATA_PEND;
 
 	clks = (unsigned long long)data->timeout_ns * host->clk_rate;
 	do_div(clks, 1000000000UL);
@@ -1642,13 +1644,11 @@
 				msmsdcc_request_end(host, cmd->mrq);
 			}
 		}
-	} else if ((cmd == host->curr.mrq->sbc) && cmd->data) {
-		if (cmd->data->flags & MMC_DATA_READ)
-			msmsdcc_start_command(host, host->curr.mrq->cmd, 0);
-		else
-			msmsdcc_request_start(host, host->curr.mrq);
 	} else if (cmd->data) {
-		if (!(cmd->data->flags & MMC_DATA_READ))
+		if (cmd == host->curr.mrq->sbc)
+			msmsdcc_start_command(host, host->curr.mrq->cmd, 0);
+		else if ((cmd->data->flags & MMC_DATA_WRITE) &&
+			   !host->curr.use_wr_data_pend)
 			msmsdcc_start_data(host, cmd->data, NULL, 0);
 	}
 }
@@ -1924,12 +1924,17 @@
 static void
 msmsdcc_request_start(struct msmsdcc_host *host, struct mmc_request *mrq)
 {
-	if (mrq->data && mrq->data->flags & MMC_DATA_READ) {
+	if (mrq->data) {
 		/* Queue/read data, daisy-chain command when data starts */
-		if (mrq->sbc)
-			msmsdcc_start_data(host, mrq->data, mrq->sbc, 0);
+		if ((mrq->data->flags & MMC_DATA_READ) ||
+		    host->curr.use_wr_data_pend)
+			msmsdcc_start_data(host, mrq->data,
+					   mrq->sbc ? mrq->sbc : mrq->cmd,
+					   0);
 		else
-			msmsdcc_start_data(host, mrq->data, mrq->cmd, 0);
+			msmsdcc_start_command(host,
+					      mrq->sbc ? mrq->sbc : mrq->cmd,
+					      0);
 	} else {
 		msmsdcc_start_command(host, mrq->cmd, 0);
 	}
@@ -2025,20 +2030,18 @@
 				host->sdcc_version) {
 			host->curr.wait_for_auto_prog_done = 1;
 		}
+		if ((mrq->cmd->opcode == MMC_WRITE_BLOCK) ||
+		    (mrq->cmd->opcode == MMC_WRITE_MULTIPLE_BLOCK))
+			host->curr.use_wr_data_pend = true;
 	}
 
 	if (mrq->data && mrq->sbc) {
 		mrq->sbc->mrq = mrq;
 		mrq->sbc->data = mrq->data;
-		if (mrq->data->flags & MMC_DATA_WRITE) {
+		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);
-		}
-	} else {
-		msmsdcc_request_start(host, mrq);
 	}
+	msmsdcc_request_start(host, mrq);
 
 	spin_unlock_irqrestore(&host->lock, flags);
 }
diff --git a/drivers/mmc/host/msm_sdcc.h b/drivers/mmc/host/msm_sdcc.h
index e29ed91..78c12c1 100644
--- a/drivers/mmc/host/msm_sdcc.h
+++ b/drivers/mmc/host/msm_sdcc.h
@@ -78,6 +78,7 @@
 #define MCI_DPSM_DIRECTION	(1 << 1)
 #define MCI_DPSM_MODE		(1 << 2)
 #define MCI_DPSM_DMAENABLE	(1 << 3)
+#define MCI_DATA_PEND		(1 << 17)
 #define MCI_AUTO_PROG_DONE	(1 << 19)
 #define MCI_RX_DATA_PEND	(1 << 20)
 
@@ -294,6 +295,7 @@
 	int			got_dataend;
 	int			wait_for_auto_prog_done;
 	int			got_auto_prog_done;
+	bool			use_wr_data_pend;
 	int			user_pages;
 };