diff --git a/drivers/mmc/core/debugfs.c b/drivers/mmc/core/debugfs.c index 7e3d82f658e7..0c55824a449b 100644 --- a/drivers/mmc/core/debugfs.c +++ b/drivers/mmc/core/debugfs.c @@ -363,6 +363,11 @@ void mmc_add_host_debugfs(struct mmc_host *host) &host->clk_scaling.skip_clk_scale_freq_update)) goto err_node; + if (!debugfs_create_bool("cmdq_task_history", + S_IRUSR | S_IWUSR, root, + &host->cmdq_thist_enabled)) + goto err_node; + #ifdef CONFIG_MMC_CLKGATE if (!debugfs_create_u32("clk_delay", (S_IRUSR | S_IWUSR), root, &host->clk_delay)) diff --git a/drivers/mmc/host/cmdq_hci.c b/drivers/mmc/host/cmdq_hci.c index 73a586e72b04..de55c0649ea3 100644 --- a/drivers/mmc/host/cmdq_hci.c +++ b/drivers/mmc/host/cmdq_hci.c @@ -132,6 +132,31 @@ static void cmdq_clear_set_irqs(struct cmdq_host *cq_host, u32 clear, u32 set) #define DRV_NAME "cmdq-host" +static void cmdq_dump_task_history(struct cmdq_host *cq_host) +{ + int i; + + if (likely(!cq_host->mmc->cmdq_thist_enabled)) + return; + + if (!cq_host->thist) { + pr_err("%s: %s: CMDQ task history buffer not allocated\n", + mmc_hostname(cq_host->mmc), __func__); + return; + } + + pr_err("---- Circular Task History ----\n"); + pr_err(DRV_NAME ": Last entry index: %d", cq_host->thist_idx - 1); + + for (i = 0; i < cq_host->num_slots; i++) { + pr_err(DRV_NAME ": [%02d]%s Task: 0x%08x | Args: 0x%08x\n", i, + (cq_host->thist[i].is_dcmd) ? "DCMD" : "DATA", + lower_32_bits(cq_host->thist[i].task), + upper_32_bits(cq_host->thist[i].task)); + } + pr_err("-------------------------\n"); +} + static void cmdq_dumpregs(struct cmdq_host *cq_host) { struct mmc_host *mmc = cq_host->mmc; @@ -176,6 +201,7 @@ static void cmdq_dumpregs(struct cmdq_host *cq_host) cmdq_readl(cq_host, CQ_VENDOR_CFG)); pr_err(DRV_NAME ": ===========================================\n"); + cmdq_dump_task_history(cq_host); if (cq_host->ops->dump_vendor_regs) cq_host->ops->dump_vendor_regs(mmc); } @@ -252,6 +278,10 @@ static int cmdq_host_alloc_tdl(struct cmdq_host *cq_host) data_size, &cq_host->trans_desc_dma_base, GFP_KERNEL); + cq_host->thist = devm_kzalloc(mmc_dev(cq_host->mmc), + (sizeof(*cq_host->thist) * + cq_host->num_slots), + GFP_KERNEL); if (!cq_host->desc_base || !cq_host->trans_desc_base) return -ENOMEM; @@ -526,6 +556,26 @@ static int cmdq_prep_tran_desc(struct mmc_request *mrq, return 0; } +static void cmdq_log_task_desc_history(struct cmdq_host *cq_host, u64 task, + bool is_dcmd) +{ + if (likely(!cq_host->mmc->cmdq_thist_enabled)) + return; + + if (!cq_host->thist) { + pr_err("%s: %s: CMDQ task history buffer not allocated\n", + mmc_hostname(cq_host->mmc), __func__); + return; + } + + if (cq_host->thist_idx >= cq_host->num_slots) + cq_host->thist_idx = 0; + + cq_host->thist[cq_host->thist_idx].is_dcmd = is_dcmd; + memcpy(&cq_host->thist[cq_host->thist_idx++].task, + &task, cq_host->task_desc_len); +} + static void cmdq_prep_dcmd_desc(struct mmc_host *mmc, struct mmc_request *mrq) { @@ -565,7 +615,7 @@ static void cmdq_prep_dcmd_desc(struct mmc_host *mmc, mrq->cmd->opcode, timing, resp_type); dataddr = (__le64 __force *)(desc + 4); dataddr[0] = cpu_to_le64((u64)mrq->cmd->arg); - + cmdq_log_task_desc_history(cq_host, *task_desc, true); } static void cmdq_pm_qos_vote(struct sdhci_host *host, struct mmc_request *mrq) @@ -623,6 +673,7 @@ static int cmdq_request(struct mmc_host *mmc, struct mmc_request *mrq) cmdq_prep_task_desc(mrq, &data, 1, (mrq->cmdq_req->cmdq_req_flags & QBR)); *task_desc = cpu_to_le64(data); + cmdq_log_task_desc_history(cq_host, *task_desc, false); err = cmdq_prep_tran_desc(mrq, cq_host, tag); if (err) { diff --git a/drivers/mmc/host/cmdq_hci.h b/drivers/mmc/host/cmdq_hci.h index bda6fdc1ffb5..a17c5c29ccb8 100644 --- a/drivers/mmc/host/cmdq_hci.h +++ b/drivers/mmc/host/cmdq_hci.h @@ -144,6 +144,11 @@ #define CQ_VENDOR_CFG 0x100 #define CMDQ_SEND_STATUS_TRIGGER (1 << 31) +struct task_history { + u64 task; + bool is_dcmd; +}; + struct cmdq_host { const struct cmdq_host_ops *ops; void __iomem *mmio; @@ -183,6 +188,9 @@ struct cmdq_host { dma_addr_t desc_dma_base; dma_addr_t trans_desc_dma_base; + struct task_history *thist; + u8 thist_idx; + struct completion halt_comp; struct mmc_request **mrq_slot; void *private; diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h index 3b028e566282..959713837423 100644 --- a/include/linux/mmc/host.h +++ b/include/linux/mmc/host.h @@ -568,6 +568,7 @@ struct mmc_host { enum dev_state dev_status; bool wakeup_on_idle; struct mmc_cmdq_context_info cmdq_ctx; + bool cmdq_thist_enabled; /* * several cmdq supporting host controllers are extensions * of legacy controllers. This variable can be used to store