mmc: cmdq_hci: Add cyclic buffer to keep history of last 32 tasks
Keep track of task history for the last 32 tasks and dump it along with registers in case of any error for debugging. The log is of the following format: [index] <DATA|DCMD> Task: <lower 32bits> | Args: <upper 32bits> of the task descriptor structure. The values need to be decoded accordingly depending on the data or dcmd task descriptor. The last entry index denotes the latest entry in this list. ---- Circular Task History ---- cmdq-host: Last entry index: 1 cmdq-host: [00]DATA Task: 0x0400002f | Args: 0x00d37d18 cmdq-host: [01]DATA Task: 0x0400002f | Args: 0x00d38118 cmdq-host: [02]DCMD Task: 0x0186402f | Args: 0x03200101 Add a debugfs entry to enable/disable this dynamically. It is disabled by default. This applies only to eMMC devices. Usage: echo Y > /sys/kernel/debug/mmcX/cmdq_task_history X - denotes the slot id Change-Id: I6abf29aa928d3d8270405cfc104241043dadfe45 Signed-off-by: Venkat Gopalakrishnan <venkatg@codeaurora.org> [subhashj@codeaurora.org: fixed compilation error] Signed-off-by: Subhash Jadavani <subhashj@codeaurora.org>
This commit is contained in:
parent
71728d8db1
commit
603e5ef719
4 changed files with 66 additions and 1 deletions
|
@ -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))
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Add table
Reference in a new issue