mmc: msm_sdcc: call sps_disconnect/sps_connect APIs from non-atomic context

If SDCC driver encounters any errors during the data transfer, it resets
SDCC controller core and along with that it also resets the BAM pipes.
In order to reset BAM pipes, SDCC driver needs to call sps_disconnect()
followed by sps_connect() APIs of SPS driver. But these APIs can
only be called from non-atomic context.

Currently SDCC driver error handling function is always triggered from
atomic context which means driver can't call BAM pipes reset APIs from
this error handling function. So this change defers the BAM pipe reset
operation in thread context when next SDCC request is issued to driver.

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 9d21c71..2dea65c 100644
--- a/drivers/mmc/host/msm_sdcc.c
+++ b/drivers/mmc/host/msm_sdcc.c
@@ -159,10 +159,45 @@
 #endif /* CONFIG_MMC_MSM_SPS_SUPPORT */
 
 /**
+ * Apply soft reset to all SDCC BAM pipes
+ *
+ * This function applies soft reset to SDCC BAM pipe.
+ *
+ * This function should be called to recover from error
+ * conditions encountered during CMD/DATA tranfsers with card.
+ *
+ * @host - Pointer to driver's host structure
+ *
+ */
+static void msmsdcc_sps_pipes_reset_and_restore(struct msmsdcc_host *host)
+{
+	int rc;
+
+	/* Reset all SDCC BAM pipes */
+	rc = msmsdcc_sps_reset_ep(host, &host->sps.prod);
+	if (rc)
+		pr_err("%s:msmsdcc_sps_reset_ep(prod) error=%d\n",
+				mmc_hostname(host->mmc), rc);
+	rc = msmsdcc_sps_reset_ep(host, &host->sps.cons);
+	if (rc)
+		pr_err("%s:msmsdcc_sps_reset_ep(cons) error=%d\n",
+				mmc_hostname(host->mmc), rc);
+
+	/* Restore all BAM pipes connections */
+	rc = msmsdcc_sps_restore_ep(host, &host->sps.prod);
+	if (rc)
+		pr_err("%s:msmsdcc_sps_restore_ep(prod) error=%d\n",
+				mmc_hostname(host->mmc), rc);
+	rc = msmsdcc_sps_restore_ep(host, &host->sps.cons);
+	if (rc)
+		pr_err("%s:msmsdcc_sps_restore_ep(cons) error=%d\n",
+				mmc_hostname(host->mmc), rc);
+}
+
+/**
  * Apply soft reset
  *
- * This function applies soft reset to SDCC core and
- * BAM, DML core.
+ * This function applies soft reset to SDCC core and DML core.
  *
  * This function should be called to recover from error
  * conditions encountered with CMD/DATA tranfsers with card.
@@ -174,20 +209,15 @@
  */
 static void msmsdcc_soft_reset_and_restore(struct msmsdcc_host *host)
 {
-	int rc;
-
 	if (host->is_sps_mode) {
 		/* Reset DML first */
 		msmsdcc_dml_reset(host);
-		/* Now reset all BAM pipes connections */
-		rc = msmsdcc_sps_reset_ep(host, &host->sps.prod);
-		if (rc)
-			pr_err("%s:msmsdcc_sps_reset_ep() error=%d\n",
-					mmc_hostname(host->mmc), rc);
-		rc = msmsdcc_sps_reset_ep(host, &host->sps.cons);
-		if (rc)
-			pr_err("%s:msmsdcc_sps_reset_ep() error=%d\n",
-					mmc_hostname(host->mmc), rc);
+		/*
+		 * delay the SPS pipe reset in thread context as
+		 * sps_connect/sps_disconnect APIs can be called
+		 * only from non-atomic context.
+		 */
+		host->sps.pipe_reset_pending = true;
 	}
 	/*
 	 * Reset SDCC controller's DPSM (data path state machine
@@ -201,18 +231,8 @@
 	pr_debug("%s: Applied soft reset to Controller\n",
 			mmc_hostname(host->mmc));
 
-	if (host->is_sps_mode) {
-		/* Restore all BAM pipes connections */
-		rc = msmsdcc_sps_restore_ep(host, &host->sps.prod);
-		if (rc)
-			pr_err("%s:msmsdcc_sps_restore_ep() error=%d\n",
-					mmc_hostname(host->mmc), rc);
-		rc = msmsdcc_sps_restore_ep(host, &host->sps.cons);
-		if (rc)
-			pr_err("%s:msmsdcc_sps_restore_ep() error=%d\n",
-					mmc_hostname(host->mmc), rc);
+	if (host->is_sps_mode)
 		msmsdcc_dml_init(host);
-	}
 }
 
 static void msmsdcc_reset_and_restore(struct msmsdcc_host *host)
@@ -1537,6 +1557,12 @@
 	if (host->plat->is_sdio_al_client)
 		msmsdcc_sdio_al_lpm(mmc, false);
 
+	/* check if sps pipe reset is pending? */
+	if (host->is_sps_mode && host->sps.pipe_reset_pending) {
+		msmsdcc_sps_pipes_reset_and_restore(host);
+		host->sps.pipe_reset_pending = false;
+	}
+
 	spin_lock_irqsave(&host->lock, flags);
 	WARN(host->curr.mrq, "Request in progress\n");
 	WARN(!host->pwr, "SDCC power is turned off\n");