mmc: core: add support for bkops during cmdq

Add support for handling both manual and auto
bkops when command queuing is running.

Change-Id: Ib967ca3c0420f4e54b3e93c497eb538d7347199a
Signed-off-by: Dov Levenglick <dovl@codeaurora.org>
[subhashj@codeaurora.org: fixed trivial merge conflicts]
Signed-off-by: Subhash Jadavani <subhashj@codeaurora.org>
[xiaonian@codeaurora.org: fixed trivial merge conflicts]
Signed-off-by: Xiaonian Wang <xiaonian@codeaurora.org>
diff --git a/drivers/mmc/card/block.c b/drivers/mmc/card/block.c
index a030691..bd1ba9b 100644
--- a/drivers/mmc/card/block.c
+++ b/drivers/mmc/card/block.c
@@ -3353,7 +3353,7 @@
 
 out:
 	pm_runtime_mark_last_busy(&card->dev);
-	pm_runtime_put_autosuspend(&card->dev);
+	__mmc_put_card(card);
 
 	if (test_and_clear_bit(0, &ctx_info->req_starved))
 		blk_run_queue(mrq->req->q);
diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c
index 790fb41..263611f 100644
--- a/drivers/mmc/core/core.c
+++ b/drivers/mmc/core/core.c
@@ -2152,7 +2152,7 @@
  * This is a helper function, which drops the runtime
  * pm reference for the card device.
  */
-static void __mmc_put_card(struct mmc_card *card)
+void __mmc_put_card(struct mmc_card *card)
 {
 	/* In case of runtime_idle, it will handle the suspend */
 	if (card->host->bus_ops->runtime_idle)
@@ -2160,6 +2160,7 @@
 	else
 		pm_runtime_put_autosuspend(&card->dev);
 }
+EXPORT_SYMBOL(__mmc_put_card);
 
 /*
  * This is a helper function, which releases the host and drops the runtime
diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c
index 01a8540..b8f7ed5 100644
--- a/drivers/mmc/core/mmc.c
+++ b/drivers/mmc/core/mmc.c
@@ -2699,21 +2699,60 @@
 #define NO_AUTOSUSPEND	(-1)
 static int mmc_runtime_idle(struct mmc_host *host)
 {
+	int err = 0;
+	bool halt_cmdq;
+
 	BUG_ON(!host->card);
+	halt_cmdq = mmc_card_cmdq(host->card) &&
+			(host->card->bkops.needs_check ||
+			 host->card->bkops.needs_manual);
+
+	/*
+	 * If there are active requests, can't check for bkops. Postpone
+	 * until the next time that runtime_idle is scheduled.
+	 */
+	if (host->cmdq_ctx.active_reqs)
+		goto no_suspend;
+
+	if (halt_cmdq) {
+		err = mmc_cmdq_halt(host, true);
+		if (err) {
+			pr_err("%s: %s failed to halt cmdq (%d)\n",
+					mmc_hostname(host), __func__, err);
+			goto out;
+		}
+	}
+
+	if (host->card->bkops.needs_manual)
+		host->card->bkops.needs_check = false;
+
+	if (host->card->bkops.needs_check) {
+		mmc_claim_host(host);
+		mmc_check_bkops(host->card);
+		host->card->bkops.needs_check = false;
+		mmc_release_host(host);
+	}
 
 	if (host->card->bkops.needs_manual)
 		mmc_start_manual_bkops(host->card);
 
-	pm_runtime_mark_last_busy(&host->card->dev);
+	if (halt_cmdq) {
+		err = mmc_cmdq_halt(host, false);
+		if (err)
+			pr_err("%s: %s failed to unhalt cmdq (%d)\n",
+					mmc_hostname(host), __func__, err);
+	}
+out:
 	/*
 	 * TODO: consider prolonging suspend when bkops
 	 * is running in order to allow a longer time for
 	 * bkops to complete
 	 */
 	pm_schedule_suspend(&host->card->dev, MMC_AUTOSUSPEND_DELAY_MS);
-
+no_suspend:
+	pm_runtime_mark_last_busy(&host->card->dev);
 	/* return negative value in order to avoid autosuspend */
-	return NO_AUTOSUSPEND;
+	return (err) ? err : NO_AUTOSUSPEND;
 }
 
 static const struct mmc_bus_ops mmc_ops = {
diff --git a/drivers/mmc/host/cmdq_hci.c b/drivers/mmc/host/cmdq_hci.c
index de896f0..4601657 100644
--- a/drivers/mmc/host/cmdq_hci.c
+++ b/drivers/mmc/host/cmdq_hci.c
@@ -342,6 +342,11 @@
 	cmdq_writel(cq_host, cmdq_readl(cq_host, CQSSC1) | SEND_QSR_INTERVAL,
 				CQSSC1);
 
+	/* enable bkops exception indication */
+	if (mmc_card_configured_manual_bkops(mmc->card))
+		cmdq_writel(cq_host, cmdq_readl(cq_host, CQRMEM) | CQ_EXCEPTION,
+				CQRMEM);
+
 	/* ensure the writes are done before enabling CQE */
 	mb();
 
@@ -666,9 +671,18 @@
 		 * In most cases, this would require a reset.
 		 */
 		if (status & CQIS_RED) {
+			/*
+			 * will check if the RED error is due to a bkops
+			 * exception once the queue is empty
+			 */
+			BUG_ON(!mmc->card);
+			if (mmc_card_configured_manual_bkops(mmc->card) &&
+			    !mmc_card_configured_auto_bkops(mmc->card))
+				mmc->card->bkops.needs_check = true;
+
 			mrq->cmdq_req->resp_err = true;
 			pr_err("%s: Response error (0x%08x) from card !!!",
-					mmc_hostname(mmc), status);
+				mmc_hostname(mmc), status);
 		} else {
 			mrq->cmdq_req->resp_idx = cmdq_readl(cq_host, CQCRI);
 			mrq->cmdq_req->resp_arg = cmdq_readl(cq_host, CQCRA);
diff --git a/drivers/mmc/host/cmdq_hci.h b/drivers/mmc/host/cmdq_hci.h
index 6b21228..07583a9 100644
--- a/drivers/mmc/host/cmdq_hci.h
+++ b/drivers/mmc/host/cmdq_hci.h
@@ -88,6 +88,7 @@
 
 /* response mode error mask */
 #define CQRMEM		0x50
+#define CQ_EXCEPTION	(1 << 6)
 
 /* task error info */
 #define CQTERRI		0x54