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
diff --git a/include/linux/mmc/card.h b/include/linux/mmc/card.h
index 99a8fff..8c0a4a6 100644
--- a/include/linux/mmc/card.h
+++ b/include/linux/mmc/card.h
@@ -316,11 +316,14 @@
/**
* struct mmc_bkops_info - BKOPS data
* @stats: statistic information regarding bkops
- * @need_manual: indication whether have to send START_BKOPS
+ * @needs_check: indication whether need to check with the device
+ * whether it requires handling of BKOPS (CMD8)
+ * @needs_manual: indication whether have to send START_BKOPS
* to the device
*/
struct mmc_bkops_info {
struct mmc_bkops_stats stats;
+ bool needs_check;
bool needs_manual;
};
@@ -667,6 +670,13 @@
return c->ext_csd.man_bkops_en & EXT_CSD_BKOPS_MANUAL_EN;
}
+/*
+ * By software design, auto_bkops will be enabled on the device if it supports
+ * it. Therefore, testing if a card is configured to run bkops is synonymous
+ * to checking if it supports bkops
+ */
+#define mmc_card_configured_auto_bkops(c) mmc_card_support_auto_bkops(c)
+
#define mmc_card_name(c) ((c)->cid.prod_name)
#define mmc_card_id(c) (dev_name(&(c)->dev))
diff --git a/include/linux/mmc/core.h b/include/linux/mmc/core.h
index 9290a86..e23ac1e 100644
--- a/include/linux/mmc/core.h
+++ b/include/linux/mmc/core.h
@@ -215,6 +215,7 @@
extern void mmc_get_card(struct mmc_card *card);
extern void mmc_put_card(struct mmc_card *card);
+extern void __mmc_put_card(struct mmc_card *card);
extern void mmc_set_ios(struct mmc_host *host);
extern int mmc_flush_cache(struct mmc_card *);