diff options
| author | Linux Build Service Account <lnxbuild@localhost> | 2017-03-05 20:08:26 -0800 |
|---|---|---|
| committer | Gerrit - the friendly Code Review server <code-review@localhost> | 2017-03-05 20:08:25 -0800 |
| commit | ded703d63ef5270ea80ae742c2f47c2965b01282 (patch) | |
| tree | 02a299ee92a5f58996e7c3499fe83cc4b46eaae4 | |
| parent | ea58c08a4d251b0bb6d770834a01946dc175ab1d (diff) | |
| parent | 2c7676c7d7bff911652015685f74b5cb97cd8a6d (diff) | |
Merge "mmc: card: block: Add support for completing cmdq requests with error"
| -rw-r--r-- | drivers/mmc/card/block.c | 13 | ||||
| -rw-r--r-- | drivers/mmc/host/cmdq_hci.c | 88 | ||||
| -rw-r--r-- | drivers/mmc/host/cmdq_hci.h | 4 | ||||
| -rw-r--r-- | include/linux/mmc/host.h | 1 |
4 files changed, 102 insertions, 4 deletions
diff --git a/drivers/mmc/card/block.c b/drivers/mmc/card/block.c index fb5ffde61e8b..ce0ecd1e9b7a 100644 --- a/drivers/mmc/card/block.c +++ b/drivers/mmc/card/block.c @@ -3544,7 +3544,7 @@ void mmc_blk_cmdq_complete_rq(struct request *rq) else if (mrq->data && mrq->data->error) err = mrq->data->error; - if (err || cmdq_req->resp_err) { + if ((err || cmdq_req->resp_err) && !cmdq_req->skip_err_handling) { pr_err("%s: %s: txfr error(%d)/resp_err(%d)\n", mmc_hostname(mrq->host), __func__, err, cmdq_req->resp_err); @@ -3581,6 +3581,17 @@ void mmc_blk_cmdq_complete_rq(struct request *rq) blk_end_request_all(rq, err); goto out; } + /* + * In case of error, cmdq_req->data.bytes_xfered is set to 0. + * If we call blk_end_request() with nr_bytes as 0 then the request + * never gets completed. So in case of error, to complete a request + * with error we should use blk_end_request_all(). + */ + if (err && cmdq_req->skip_err_handling) { + cmdq_req->skip_err_handling = false; + blk_end_request_all(rq, err); + goto out; + } blk_end_request(rq, err, cmdq_req->data.bytes_xfered); diff --git a/drivers/mmc/host/cmdq_hci.c b/drivers/mmc/host/cmdq_hci.c index 96d4fbf1a823..1ad5fd0e0a78 100644 --- a/drivers/mmc/host/cmdq_hci.c +++ b/drivers/mmc/host/cmdq_hci.c @@ -145,6 +145,29 @@ static void cmdq_clear_set_irqs(struct cmdq_host *cq_host, u32 clear, u32 set) mb(); } +static int cmdq_clear_task_poll(struct cmdq_host *cq_host, unsigned int tag) +{ + int retries = 100; + + cmdq_clear_set_irqs(cq_host, CQIS_TCL, 0); + cmdq_writel(cq_host, 1<<tag, CQTCLR); + while (retries) { + /* + * Task Clear register and doorbell, + * both should indicate that task is cleared + */ + if ((cmdq_readl(cq_host, CQTCLR) & 1<<tag) || + (cmdq_readl(cq_host, CQTDBR) & 1<<tag)) { + udelay(5); + retries--; + continue; + } else + break; + } + + cmdq_clear_set_irqs(cq_host, 0, CQIS_TCL); + return retries ? 0 : -ETIMEDOUT; +} #define DRV_NAME "cmdq-host" @@ -857,6 +880,8 @@ irqreturn_t cmdq_irq(struct mmc_host *mmc, int err) struct mmc_request *mrq; int ret; u32 dbr_set = 0; + u32 dev_pend_set = 0; + int stat_err = 0; status = cmdq_readl(cq_host, CQIS); @@ -865,7 +890,9 @@ irqreturn_t cmdq_irq(struct mmc_host *mmc, int err) MMC_TRACE(mmc, "%s: CQIS: 0x%x err: %d\n", __func__, status, err); - if (err || (status & CQIS_RED)) { + stat_err = status & (CQIS_RED | CQIS_GCE | CQIS_ICCE); + + if (err || stat_err) { err_info = cmdq_readl(cq_host, CQTERRI); pr_err("%s: err: %d status: 0x%08x task-err-info (0x%08lx)\n", mmc_hostname(mmc), err, status, err_info); @@ -968,7 +995,7 @@ skip_cqterri: * CQE detected a reponse error from device * In most cases, this would require a reset. */ - if (status & CQIS_RED) { + if (stat_err & CQIS_RED) { /* * will check if the RED error is due to a bkops * exception once the queue is empty @@ -987,6 +1014,62 @@ skip_cqterri: mrq->cmdq_req->resp_arg = cmdq_readl(cq_host, CQCRA); } + /* + * Generic Crypto error detected by CQE. + * Its a fatal, would require cmdq reset. + */ + if (stat_err & CQIS_GCE) { + if (mrq->data) + mrq->data->error = -EIO; + pr_err("%s: Crypto generic error while processing task %lu!", + mmc_hostname(mmc), tag); + MMC_TRACE(mmc, "%s: GCE error detected with tag %lu\n", + __func__, tag); + } + /* + * Invalid crypto config error detected by CQE, clear the task. + * Task can be cleared only when CQE is halt state. + */ + if (stat_err & CQIS_ICCE) { + /* + * Invalid Crypto Config Error is detected at the + * beginning of the transfer before the actual execution + * started. So just clear the task in CQE. No need to + * clear in device. Only the task which caused ICCE has + * to be cleared. Other tasks can be continue processing + * The first task which is about to be prepared would + * cause ICCE Error. + */ + dbr_set = cmdq_readl(cq_host, CQTDBR); + dev_pend_set = cmdq_readl(cq_host, CQDPT); + if (dbr_set ^ dev_pend_set) + tag = ffs(dbr_set ^ dev_pend_set) - 1; + mrq = get_req_by_tag(cq_host, tag); + pr_err("%s: Crypto config error while processing task %lu!", + mmc_hostname(mmc), tag); + MMC_TRACE(mmc, "%s: ICCE error with tag %lu\n", + __func__, tag); + if (mrq->data) + mrq->data->error = -EIO; + else if (mrq->cmd) + mrq->cmd->error = -EIO; + /* + * If CQE is halted and tag is valid then clear the task + * then un-halt CQE and set flag to skip error recovery. + * If any of the condtions is not met thene it will + * enter into default error recovery path. + */ + if (!ret && (dbr_set ^ dev_pend_set)) { + ret = cmdq_clear_task_poll(cq_host, tag); + if (ret) { + pr_err("%s: %s: task[%lu] clear failed ret=%d\n", + mmc_hostname(mmc), + __func__, tag, ret); + } else if (!cmdq_halt_poll(mmc, false)) { + mrq->cmdq_req->skip_err_handling = true; + } + } + } cmdq_finish_data(mmc, tag); } else { cmdq_writel(cq_host, status, CQIS); @@ -1052,6 +1135,7 @@ static int cmdq_halt_poll(struct mmc_host *mmc, bool halt) cq_host->ops->clear_set_irqs(mmc, true); cmdq_writel(cq_host, cmdq_readl(cq_host, CQCTL) & ~HALT, CQCTL); + mmc_host_clr_halt(mmc); return 0; } diff --git a/drivers/mmc/host/cmdq_hci.h b/drivers/mmc/host/cmdq_hci.h index 6c10ab3859d1..db0cd956ae90 100644 --- a/drivers/mmc/host/cmdq_hci.h +++ b/drivers/mmc/host/cmdq_hci.h @@ -37,6 +37,8 @@ #define CQIS_TCC (1 << 1) #define CQIS_RED (1 << 2) #define CQIS_TCL (1 << 3) +#define CQIS_GCE (1 << 4) +#define CQIS_ICCE (1 << 5) /* interrupt status enable */ #define CQISTE 0x14 @@ -112,7 +114,7 @@ /* command response argument */ #define CQCRA 0x5C -#define CQ_INT_ALL 0xF +#define CQ_INT_ALL 0x3F #define CQIC_DEFAULT_ICCTH 31 #define CQIC_DEFAULT_ICTOVAL 1 diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h index 804d89a825fc..5374bd6c4cbe 100644 --- a/include/linux/mmc/host.h +++ b/include/linux/mmc/host.h @@ -208,6 +208,7 @@ struct mmc_cmdq_req { unsigned int resp_arg; unsigned int dev_pend_tasks; bool resp_err; + bool skip_err_handling; int tag; /* used for command queuing */ u8 ctx_id; }; |
