Loading drivers/mmc/core/Kconfig +11 −0 Original line number Diff line number Diff line Loading @@ -12,6 +12,17 @@ config MMC_CLKGATE If unsure, say N. config MMC_RING_BUFFER bool "MMC_RING_BUFFER" depends on MMC default n help This enables the ring buffer tracing of significant events for mmc driver to provide command history for debugging purpose. If unsure, say N. config MMC_EMBEDDED_SDIO boolean "MMC embedded SDIO device support (EXPERIMENTAL)" help Loading drivers/mmc/core/Makefile +1 −0 Original line number Diff line number Diff line Loading @@ -10,3 +10,4 @@ mmc_core-y := core.o bus.o host.o \ quirks.o slot-gpio.o mmc_core-$(CONFIG_DEBUG_FS) += debugfs.o obj-$(CONFIG_MMC_RING_BUFFER) += ring_buffer.o drivers/mmc/core/host.c +3 −0 Original line number Diff line number Diff line Loading @@ -26,6 +26,8 @@ #include <linux/mmc/host.h> #include <linux/mmc/card.h> #include <linux/mmc/ring_buffer.h> #include <linux/mmc/slot-gpio.h> #include "core.h" Loading Loading @@ -787,6 +789,7 @@ int mmc_add_host(struct mmc_host *host) mmc_add_host_debugfs(host); #endif mmc_host_clk_sysfs_init(host); mmc_trace_init(host); err = sysfs_create_group(&host->class_dev.kobj, &clk_scaling_attr_grp); if (err) Loading drivers/mmc/core/ring_buffer.c 0 → 100644 +123 −0 Original line number Diff line number Diff line /* * Copyright (c) 2016, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and * only version 2 as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. */ #include <linux/mmc/ring_buffer.h> #include <linux/mmc/host.h> void mmc_stop_tracing(struct mmc_host *mmc) { mmc->trace_buf.stop_tracing = true; } void mmc_trace_write(struct mmc_host *mmc, const char *fmt, ...) { unsigned int idx; va_list args; char *event; unsigned long flags; char str[MMC_TRACE_EVENT_SZ]; if (unlikely(!mmc->trace_buf.data) || unlikely(mmc->trace_buf.stop_tracing)) return; /* * Here an increment and modulus is used to keep * index within array bounds. The cast to unsigned is * necessary so increment and rolover wraps to 0 correctly */ spin_lock_irqsave(&mmc->trace_buf.trace_lock, flags); mmc->trace_buf.wr_idx += 1; idx = ((unsigned int)mmc->trace_buf.wr_idx) & (MMC_TRACE_RBUF_NUM_EVENTS - 1); spin_unlock_irqrestore(&mmc->trace_buf.trace_lock, flags); /* Catch some unlikely machine specific wrap-around bug */ if (unlikely(idx > (MMC_TRACE_RBUF_NUM_EVENTS - 1))) { pr_err("%s: %s: Invalid idx:%d for mmc trace, tracing stopped !\n", mmc_hostname(mmc), __func__, idx); mmc_stop_tracing(mmc); return; } event = &mmc->trace_buf.data[idx * MMC_TRACE_EVENT_SZ]; va_start(args, fmt); snprintf(str, MMC_TRACE_EVENT_SZ, "<%d> %lld: %s: %s", raw_smp_processor_id(), ktime_to_ns(ktime_get()), mmc_hostname(mmc), fmt); memset(event, '\0', MMC_TRACE_EVENT_SZ); vscnprintf(event, MMC_TRACE_EVENT_SZ, str, args); va_end(args); } void mmc_trace_init(struct mmc_host *mmc) { BUILD_BUG_ON_NOT_POWER_OF_2(MMC_TRACE_RBUF_NUM_EVENTS); mmc->trace_buf.data = (char *) __get_free_pages(GFP_KERNEL|__GFP_ZERO, MMC_TRACE_RBUF_SZ_ORDER); if (!mmc->trace_buf.data) { pr_err("%s: %s: Unable to allocate trace for mmc\n", __func__, mmc_hostname(mmc)); return; } spin_lock_init(&mmc->trace_buf.trace_lock); mmc->trace_buf.wr_idx = -1; } void mmc_trace_free(struct mmc_host *mmc) { if (mmc->trace_buf.data) free_pages((unsigned long)mmc->trace_buf.data, MMC_TRACE_RBUF_SZ_ORDER); } void mmc_dump_trace_buffer(struct mmc_host *mmc, struct seq_file *s) { unsigned int idx, cur_idx; unsigned int N = MMC_TRACE_RBUF_NUM_EVENTS - 1; char *event; unsigned long flags; if (!mmc->trace_buf.data) return; spin_lock_irqsave(&mmc->trace_buf.trace_lock, flags); idx = ((unsigned int)mmc->trace_buf.wr_idx) & N; cur_idx = (idx + 1) & N; do { event = &mmc->trace_buf.data[cur_idx * MMC_TRACE_EVENT_SZ]; if (s) seq_printf(s, "%s", (char *)event); else pr_err("%s", (char *)event); cur_idx = (cur_idx + 1) & N; if (cur_idx == idx) { event = &mmc->trace_buf.data[cur_idx * MMC_TRACE_EVENT_SZ]; if (s) seq_printf(s, "latest_event: %s", (char *)event); else pr_err("latest_event: %s", (char *)event); break; } } while (1); spin_unlock_irqrestore(&mmc->trace_buf.trace_lock, flags); } drivers/mmc/host/sdhci-msm.c +12 −1 Original line number Diff line number Diff line Loading @@ -2582,9 +2582,17 @@ static void sdhci_msm_check_power_status(struct sdhci_host *host, u32 req_type) if (done) init_completion(&msm_host->pwr_irq_completion); else if (!wait_for_completion_timeout(&msm_host->pwr_irq_completion, msecs_to_jiffies(MSM_PWR_IRQ_TIMEOUT_MS))) msecs_to_jiffies(MSM_PWR_IRQ_TIMEOUT_MS))) { __WARN_printf("%s: request(%d) timed out waiting for pwr_irq\n", mmc_hostname(host->mmc), req_type); MMC_TRACE(host->mmc, "request(%d) timed out waiting for pwr_irq, 0xDC: 0x%08x | 0xE0: 0x%08x | 0xE8: 0x%08x\n", req_type, readl_relaxed(msm_host->core_mem + CORE_PWRCTL_STATUS), readl_relaxed(msm_host->core_mem + CORE_PWRCTL_MASK), readl_relaxed(msm_host->core_mem + CORE_PWRCTL_CTL)); mmc_stop_tracing(host->mmc); } pr_debug("%s: %s: request %d done\n", mmc_hostname(host->mmc), __func__, req_type); Loading Loading @@ -3089,6 +3097,9 @@ void sdhci_msm_dump_vendor_regs(struct sdhci_host *host) if (host->cq_host) sdhci_msm_cmdq_dump_debug_ram(host); MMC_TRACE(host->mmc, "Data cnt: 0x%08x | Fifo cnt: 0x%08x\n", readl_relaxed(msm_host->core_mem + CORE_MCI_DATA_CNT), readl_relaxed(msm_host->core_mem + CORE_MCI_FIFO_CNT)); pr_info("Data cnt: 0x%08x | Fifo cnt: 0x%08x | Int sts: 0x%08x\n", readl_relaxed(msm_host->core_mem + CORE_MCI_DATA_CNT), readl_relaxed(msm_host->core_mem + CORE_MCI_FIFO_CNT), Loading Loading
drivers/mmc/core/Kconfig +11 −0 Original line number Diff line number Diff line Loading @@ -12,6 +12,17 @@ config MMC_CLKGATE If unsure, say N. config MMC_RING_BUFFER bool "MMC_RING_BUFFER" depends on MMC default n help This enables the ring buffer tracing of significant events for mmc driver to provide command history for debugging purpose. If unsure, say N. config MMC_EMBEDDED_SDIO boolean "MMC embedded SDIO device support (EXPERIMENTAL)" help Loading
drivers/mmc/core/Makefile +1 −0 Original line number Diff line number Diff line Loading @@ -10,3 +10,4 @@ mmc_core-y := core.o bus.o host.o \ quirks.o slot-gpio.o mmc_core-$(CONFIG_DEBUG_FS) += debugfs.o obj-$(CONFIG_MMC_RING_BUFFER) += ring_buffer.o
drivers/mmc/core/host.c +3 −0 Original line number Diff line number Diff line Loading @@ -26,6 +26,8 @@ #include <linux/mmc/host.h> #include <linux/mmc/card.h> #include <linux/mmc/ring_buffer.h> #include <linux/mmc/slot-gpio.h> #include "core.h" Loading Loading @@ -787,6 +789,7 @@ int mmc_add_host(struct mmc_host *host) mmc_add_host_debugfs(host); #endif mmc_host_clk_sysfs_init(host); mmc_trace_init(host); err = sysfs_create_group(&host->class_dev.kobj, &clk_scaling_attr_grp); if (err) Loading
drivers/mmc/core/ring_buffer.c 0 → 100644 +123 −0 Original line number Diff line number Diff line /* * Copyright (c) 2016, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and * only version 2 as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. */ #include <linux/mmc/ring_buffer.h> #include <linux/mmc/host.h> void mmc_stop_tracing(struct mmc_host *mmc) { mmc->trace_buf.stop_tracing = true; } void mmc_trace_write(struct mmc_host *mmc, const char *fmt, ...) { unsigned int idx; va_list args; char *event; unsigned long flags; char str[MMC_TRACE_EVENT_SZ]; if (unlikely(!mmc->trace_buf.data) || unlikely(mmc->trace_buf.stop_tracing)) return; /* * Here an increment and modulus is used to keep * index within array bounds. The cast to unsigned is * necessary so increment and rolover wraps to 0 correctly */ spin_lock_irqsave(&mmc->trace_buf.trace_lock, flags); mmc->trace_buf.wr_idx += 1; idx = ((unsigned int)mmc->trace_buf.wr_idx) & (MMC_TRACE_RBUF_NUM_EVENTS - 1); spin_unlock_irqrestore(&mmc->trace_buf.trace_lock, flags); /* Catch some unlikely machine specific wrap-around bug */ if (unlikely(idx > (MMC_TRACE_RBUF_NUM_EVENTS - 1))) { pr_err("%s: %s: Invalid idx:%d for mmc trace, tracing stopped !\n", mmc_hostname(mmc), __func__, idx); mmc_stop_tracing(mmc); return; } event = &mmc->trace_buf.data[idx * MMC_TRACE_EVENT_SZ]; va_start(args, fmt); snprintf(str, MMC_TRACE_EVENT_SZ, "<%d> %lld: %s: %s", raw_smp_processor_id(), ktime_to_ns(ktime_get()), mmc_hostname(mmc), fmt); memset(event, '\0', MMC_TRACE_EVENT_SZ); vscnprintf(event, MMC_TRACE_EVENT_SZ, str, args); va_end(args); } void mmc_trace_init(struct mmc_host *mmc) { BUILD_BUG_ON_NOT_POWER_OF_2(MMC_TRACE_RBUF_NUM_EVENTS); mmc->trace_buf.data = (char *) __get_free_pages(GFP_KERNEL|__GFP_ZERO, MMC_TRACE_RBUF_SZ_ORDER); if (!mmc->trace_buf.data) { pr_err("%s: %s: Unable to allocate trace for mmc\n", __func__, mmc_hostname(mmc)); return; } spin_lock_init(&mmc->trace_buf.trace_lock); mmc->trace_buf.wr_idx = -1; } void mmc_trace_free(struct mmc_host *mmc) { if (mmc->trace_buf.data) free_pages((unsigned long)mmc->trace_buf.data, MMC_TRACE_RBUF_SZ_ORDER); } void mmc_dump_trace_buffer(struct mmc_host *mmc, struct seq_file *s) { unsigned int idx, cur_idx; unsigned int N = MMC_TRACE_RBUF_NUM_EVENTS - 1; char *event; unsigned long flags; if (!mmc->trace_buf.data) return; spin_lock_irqsave(&mmc->trace_buf.trace_lock, flags); idx = ((unsigned int)mmc->trace_buf.wr_idx) & N; cur_idx = (idx + 1) & N; do { event = &mmc->trace_buf.data[cur_idx * MMC_TRACE_EVENT_SZ]; if (s) seq_printf(s, "%s", (char *)event); else pr_err("%s", (char *)event); cur_idx = (cur_idx + 1) & N; if (cur_idx == idx) { event = &mmc->trace_buf.data[cur_idx * MMC_TRACE_EVENT_SZ]; if (s) seq_printf(s, "latest_event: %s", (char *)event); else pr_err("latest_event: %s", (char *)event); break; } } while (1); spin_unlock_irqrestore(&mmc->trace_buf.trace_lock, flags); }
drivers/mmc/host/sdhci-msm.c +12 −1 Original line number Diff line number Diff line Loading @@ -2582,9 +2582,17 @@ static void sdhci_msm_check_power_status(struct sdhci_host *host, u32 req_type) if (done) init_completion(&msm_host->pwr_irq_completion); else if (!wait_for_completion_timeout(&msm_host->pwr_irq_completion, msecs_to_jiffies(MSM_PWR_IRQ_TIMEOUT_MS))) msecs_to_jiffies(MSM_PWR_IRQ_TIMEOUT_MS))) { __WARN_printf("%s: request(%d) timed out waiting for pwr_irq\n", mmc_hostname(host->mmc), req_type); MMC_TRACE(host->mmc, "request(%d) timed out waiting for pwr_irq, 0xDC: 0x%08x | 0xE0: 0x%08x | 0xE8: 0x%08x\n", req_type, readl_relaxed(msm_host->core_mem + CORE_PWRCTL_STATUS), readl_relaxed(msm_host->core_mem + CORE_PWRCTL_MASK), readl_relaxed(msm_host->core_mem + CORE_PWRCTL_CTL)); mmc_stop_tracing(host->mmc); } pr_debug("%s: %s: request %d done\n", mmc_hostname(host->mmc), __func__, req_type); Loading Loading @@ -3089,6 +3097,9 @@ void sdhci_msm_dump_vendor_regs(struct sdhci_host *host) if (host->cq_host) sdhci_msm_cmdq_dump_debug_ram(host); MMC_TRACE(host->mmc, "Data cnt: 0x%08x | Fifo cnt: 0x%08x\n", readl_relaxed(msm_host->core_mem + CORE_MCI_DATA_CNT), readl_relaxed(msm_host->core_mem + CORE_MCI_FIFO_CNT)); pr_info("Data cnt: 0x%08x | Fifo cnt: 0x%08x | Int sts: 0x%08x\n", readl_relaxed(msm_host->core_mem + CORE_MCI_DATA_CNT), readl_relaxed(msm_host->core_mem + CORE_MCI_FIFO_CNT), Loading