From 2f69844aa86701a0a4c1ea8ca8fa8e55506a1329 Mon Sep 17 00:00:00 2001 From: Ritesh Harjani Date: Tue, 27 Oct 2015 11:51:25 +0530 Subject: [PATCH] mmc: cmdq_hci: Helper API/info in cmdq for halt This patch adds following helper API/info - 1. cmdq_halt_poll to halt the controller using polling method. This is to be mainly used in case of an error from cmdq_irq context. 2. Adds num_cq_slots & dcmd_cq_slot info to mmc_host structure. This can be useful info for mmc host structure like in case of handling of multiple error requests 3. Adds CMDQ_STATE_CQ_DISABLE for cmdq host. In case of an error if halt also fails, CQE error handling code will disable CQ. So block layer needs to know - to not pull any requests in such case. Change-Id: I8e9a8d5094db82336917fcca4361ce84316c34ef Signed-off-by: Ritesh Harjani [subhashj@codeaurora.org: fixed merge conflicts] Signed-off-by: Subhash Jadavani --- drivers/mmc/host/cmdq_hci.c | 65 +++++++++++++++++++++++++++++++++++++ include/linux/mmc/host.h | 18 ++++++++++ 2 files changed, 83 insertions(+) diff --git a/drivers/mmc/host/cmdq_hci.c b/drivers/mmc/host/cmdq_hci.c index e68e94ad909d..ba9ec9d85d6f 100644 --- a/drivers/mmc/host/cmdq_hci.c +++ b/drivers/mmc/host/cmdq_hci.c @@ -37,6 +37,8 @@ /* 1 sec */ #define HALT_TIMEOUT_MS 1000 +static int cmdq_halt_poll(struct mmc_host *mmc); + #ifdef CONFIG_PM_RUNTIME static int cmdq_runtime_pm_get(struct cmdq_host *host) { @@ -116,6 +118,20 @@ static void setup_trans_desc(struct cmdq_host *cq_host, u8 tag) } } +static void cmdq_set_halt_irq(struct cmdq_host *cq_host, bool enable) +{ + u32 ier; + + ier = cmdq_readl(cq_host, CQISTE); + if (enable) { + cmdq_writel(cq_host, ier | HALT, CQISTE); + cmdq_writel(cq_host, ier | HALT, CQISGE); + } else { + cmdq_writel(cq_host, ier & ~HALT, CQISTE); + cmdq_writel(cq_host, ier & ~HALT, CQISGE); + } +} + static void cmdq_clear_set_irqs(struct cmdq_host *cq_host, u32 clear, u32 set) { u32 ier; @@ -369,6 +385,7 @@ static int cmdq_enable(struct mmc_host *mmc) mb(); cq_host->enabled = true; + mmc_host_clr_cq_disable(mmc); if (cq_host->ops->set_block_size) cq_host->ops->set_block_size(cq_host->mmc); @@ -403,6 +420,7 @@ static void cmdq_disable(struct mmc_host *mmc, bool soft) cmdq_runtime_pm_put(cq_host); cq_host->enabled = false; + mmc_host_set_cq_disable(mmc); } static void cmdq_reset(struct mmc_host *mmc, bool soft) @@ -448,6 +466,7 @@ static void cmdq_reset(struct mmc_host *mmc, bool soft) cmdq_writel(cq_host, cqcfg, CQCFG); cmdq_runtime_pm_put(cq_host); cq_host->enabled = true; + mmc_host_clr_cq_disable(mmc); } static void cmdq_prep_task_desc(struct mmc_request *mrq, @@ -729,6 +748,7 @@ irqreturn_t cmdq_irq(struct mmc_host *mmc, int err) struct cmdq_host *cq_host = (struct cmdq_host *)mmc_cmdq_private(mmc); unsigned long err_info = 0; struct mmc_request *mrq; + int ret; status = cmdq_readl(cq_host, CQIS); cmdq_writel(cq_host, status, CQIS); @@ -741,6 +761,17 @@ irqreturn_t cmdq_irq(struct mmc_host *mmc, int err) pr_err("%s: err: %d status: 0x%08x task-err-info (0x%08lx)\n", mmc_hostname(mmc), err, status, err_info); + /* + * Need to halt CQE in case of error in interrupt context itself + * otherwise CQE may proceed with sending CMD to device even if + * CQE/card is in error state. + * CMDQ error handling will make sure that it is unhalted after + * handling all the errors. + */ + ret = cmdq_halt_poll(mmc); + if (ret) + pr_err("%s: %s: halt failed ret=%d\n", + mmc_hostname(mmc), __func__, ret); cmdq_dumpregs(cq_host); if (err_info & CQ_RMEFV) { @@ -823,6 +854,38 @@ out: } EXPORT_SYMBOL(cmdq_irq); +/* cmdq_halt_poll - Halting CQE using polling method. + * @mmc: struct mmc_host + * This is used mainly from interrupt context to halt + * CQE engine. + */ +static int cmdq_halt_poll(struct mmc_host *mmc) +{ + struct cmdq_host *cq_host = (struct cmdq_host *)mmc_cmdq_private(mmc); + int retries = 100; + + cmdq_set_halt_irq(cq_host, false); + cmdq_writel(cq_host, cmdq_readl(cq_host, CQCTL) | HALT, CQCTL); + while (retries) { + if (!(cmdq_readl(cq_host, CQCTL) & HALT)) { + udelay(5); + retries--; + continue; + } else { + if (cq_host->ops->post_cqe_halt) + cq_host->ops->post_cqe_halt(mmc); + /* halt done: re-enable legacy interrupts */ + if (cq_host->ops->clear_set_irqs) + cq_host->ops->clear_set_irqs(mmc, + false); + mmc_host_set_halt(mmc); + break; + } + } + cmdq_set_halt_irq(cq_host, true); + return retries ? 0 : -ETIMEDOUT; +} + /* May sleep */ static int cmdq_halt(struct mmc_host *mmc, bool halt) { @@ -965,6 +1028,8 @@ int cmdq_init(struct cmdq_host *cq_host, struct mmc_host *mmc, cq_host->dcmd_slot = DCMD_SLOT; mmc->cmdq_ops = &cmdq_host_ops; + mmc->num_cq_slots = NUM_SLOTS; + mmc->dcmd_cq_slot = DCMD_SLOT; cq_host->mrq_slot = kzalloc(sizeof(cq_host->mrq_slot) * cq_host->num_slots, GFP_KERNEL); diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h index cd79dd903a8b..4743f46bf9b3 100644 --- a/include/linux/mmc/host.h +++ b/include/linux/mmc/host.h @@ -257,6 +257,7 @@ struct mmc_cmdq_context_info { #define CMDQ_STATE_ERR 0 #define CMDQ_STATE_DCMD_ACTIVE 1 #define CMDQ_STATE_HALT 2 +#define CMDQ_STATE_CQ_DISABLE 3 wait_queue_head_t queue_empty_wq; wait_queue_head_t wait; int active_small_sector_read_reqs; @@ -569,6 +570,8 @@ struct mmc_host { enum dev_state dev_status; bool wakeup_on_idle; struct mmc_cmdq_context_info cmdq_ctx; + int num_cq_slots; + int dcmd_cq_slot; bool cmdq_thist_enabled; /* * several cmdq supporting host controllers are extensions @@ -717,6 +720,21 @@ static inline int mmc_host_halt(struct mmc_host *host) return test_bit(CMDQ_STATE_HALT, &host->cmdq_ctx.curr_state); } +static inline void mmc_host_set_cq_disable(struct mmc_host *host) +{ + set_bit(CMDQ_STATE_CQ_DISABLE, &host->cmdq_ctx.curr_state); +} + +static inline void mmc_host_clr_cq_disable(struct mmc_host *host) +{ + clear_bit(CMDQ_STATE_CQ_DISABLE, &host->cmdq_ctx.curr_state); +} + +static inline int mmc_host_cq_disable(struct mmc_host *host) +{ + return test_bit(CMDQ_STATE_CQ_DISABLE, &host->cmdq_ctx.curr_state); +} + #ifdef CONFIG_MMC_CLKGATE void mmc_host_clk_hold(struct mmc_host *host); void mmc_host_clk_release(struct mmc_host *host);