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 <riteshh@codeaurora.org> [subhashj@codeaurora.org: fixed merge conflicts] Signed-off-by: Subhash Jadavani <subhashj@codeaurora.org>
This commit is contained in:
parent
68393ba371
commit
2f69844aa8
2 changed files with 83 additions and 0 deletions
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
Loading…
Add table
Reference in a new issue