diff options
author | Linux Build Service Account <lnxbuild@localhost> | 2016-09-11 23:19:20 -0700 |
---|---|---|
committer | Gerrit - the friendly Code Review server <code-review@localhost> | 2016-09-11 23:19:20 -0700 |
commit | e5f0209b25937b4d5980468a41ada8c9e92da96c (patch) | |
tree | a094ede17b7a60d661f48493d1d3cc35f3c45f1d /drivers | |
parent | ef33877be24918d0b55d0777f3bae6de2228acc4 (diff) | |
parent | 0d6c14d893d4a13ae9bfde34a867042d586a958f (diff) |
Merge "msm: sde: Add EVTLOG and REG dump support for SDE rotator"
Diffstat (limited to 'drivers')
7 files changed, 805 insertions, 4 deletions
diff --git a/drivers/media/platform/msm/sde/Kconfig b/drivers/media/platform/msm/sde/Kconfig index fcd461bb1167..85f5f4257ddb 100644 --- a/drivers/media/platform/msm/sde/Kconfig +++ b/drivers/media/platform/msm/sde/Kconfig @@ -5,4 +5,13 @@ config MSM_SDE_ROTATOR select VIDEOBUF2_CORE select SW_SYNC if SYNC ---help--- - Enable support of V4L2 rotator driver.
\ No newline at end of file + Enable support of V4L2 rotator driver. + +config MSM_SDE_ROTATOR_EVTLOG_DEBUG + depends on MSM_SDE_ROTATOR + bool "Enable sde rotator debugging" + ---help--- + The SDE rotator debugging provides support to enable rotator debugging + features to: Dump rotator registers during driver errors, panic + driver during fatal errors and enable some rotator driver logging + into an internal buffer (this avoids logging overhead). diff --git a/drivers/media/platform/msm/sde/rotator/sde_rotator_base.h b/drivers/media/platform/msm/sde/rotator/sde_rotator_base.h index b3e81705bdf4..67c6b11329d8 100644 --- a/drivers/media/platform/msm/sde/rotator/sde_rotator_base.h +++ b/drivers/media/platform/msm/sde/rotator/sde_rotator_base.h @@ -92,6 +92,12 @@ enum sde_bus_clients { SDE_MAX_BUS_CLIENTS }; +enum sde_rot_regdump_access { + SDE_ROT_REGDUMP_READ, + SDE_ROT_REGDUMP_WRITE, + SDE_ROT_REGDUMP_MAX +}; + struct reg_bus_client { char name[MAX_CLIENT_NAME_LEN]; short usecase_ndx; @@ -107,6 +113,21 @@ struct sde_smmu_client { bool domain_attached; }; +struct sde_rot_vbif_debug_bus { + u32 disable_bus_addr; + u32 block_bus_addr; + u32 bit_offset; + u32 block_cnt; + u32 test_pnt_cnt; +}; + +struct sde_rot_regdump { + char *name; + u32 offset; + u32 len; + enum sde_rot_regdump_access access; +}; + struct sde_rot_data_type { u32 mdss_version; @@ -140,6 +161,14 @@ struct sde_rot_data_type { int iommu_attached; int iommu_ref_cnt; + + struct sde_rot_vbif_debug_bus *nrt_vbif_dbg_bus; + u32 nrt_vbif_dbg_bus_size; + + struct sde_rot_regdump *regdump; + u32 regdump_size; + + void *sde_rot_hw; }; int sde_rotator_base_init(struct sde_rot_data_type **pmdata, diff --git a/drivers/media/platform/msm/sde/rotator/sde_rotator_core.c b/drivers/media/platform/msm/sde/rotator/sde_rotator_core.c index e3e71936b8e4..3e5cbdecfba4 100644 --- a/drivers/media/platform/msm/sde/rotator/sde_rotator_core.c +++ b/drivers/media/platform/msm/sde/rotator/sde_rotator_core.c @@ -35,6 +35,7 @@ #include "sde_rotator_r1.h" #include "sde_rotator_r3.h" #include "sde_rotator_trace.h" +#include "sde_rotator_debug.h" /* waiting for hw time out, 3 vsync for 30fps*/ #define ROT_HW_ACQUIRE_TIMEOUT_IN_MS 100 @@ -127,6 +128,7 @@ static int sde_rotator_bus_scale_set_quota(struct sde_rot_bus_data_type *bus, bus->curr_bw_uc_idx = new_uc_idx; bus->curr_quota_val = quota; + SDEROT_EVTLOG(new_uc_idx, quota); SDEROT_DBG("uc_idx=%d quota=%llu\n", new_uc_idx, quota); ATRACE_BEGIN("msm_bus_scale_req_rot"); ret = msm_bus_scale_client_update_request(bus->bus_hdl, @@ -274,6 +276,7 @@ static void sde_rotator_footswitch_ctrl(struct sde_rot_mgr *mgr, bool on) return; } + SDEROT_EVTLOG(on); SDEROT_DBG("%s: rotator regulators", on ? "Enable" : "Disable"); ret = sde_rot_enable_vreg(mgr->module_power.vreg_config, mgr->module_power.num_vreg, on); @@ -307,6 +310,7 @@ static int sde_rotator_clk_ctrl(struct sde_rot_mgr *mgr, int enable) } if (changed) { + SDEROT_EVTLOG(enable); SDEROT_DBG("Rotator clk %s\n", enable ? "enable" : "disable"); for (i = 0; i < mgr->num_rot_clk; i++) { clk = mgr->rot_clk[i].clk; @@ -394,6 +398,7 @@ static bool sde_rotator_is_work_pending(struct sde_rot_mgr *mgr, static void sde_rotator_clear_fence(struct sde_rot_entry *entry) { if (entry->input_fence) { + SDEROT_EVTLOG(entry->input_fence, 1111); SDEROT_DBG("sys_fence_put i:%p\n", entry->input_fence); sde_rotator_put_sync_fence(entry->input_fence); entry->input_fence = NULL; @@ -404,6 +409,7 @@ static void sde_rotator_clear_fence(struct sde_rot_entry *entry) if (entry->fenceq && entry->fenceq->timeline) sde_rotator_resync_timeline(entry->fenceq->timeline); + SDEROT_EVTLOG(entry->output_fence, 2222); SDEROT_DBG("sys_fence_put o:%p\n", entry->output_fence); sde_rotator_put_sync_fence(entry->output_fence); entry->output_fence = NULL; @@ -565,6 +571,7 @@ static struct sde_rot_perf *sde_rotator_find_session( static void sde_rotator_release_data(struct sde_rot_entry *entry) { + SDEROT_EVTLOG(entry->src_buf.p[0].addr, entry->dst_buf.p[0].addr); sde_mdp_data_free(&entry->src_buf, true, DMA_TO_DEVICE); sde_mdp_data_free(&entry->dst_buf, true, DMA_FROM_DEVICE); } @@ -719,6 +726,10 @@ static struct sde_rot_hw_resource *sde_rotator_get_hw_resource( } } atomic_inc(&hw->num_active); + SDEROT_EVTLOG(atomic_read(&hw->num_active), hw->pending_count, + mgr->rdot_limit, entry->perf->rdot_limit, + mgr->wrot_limit, entry->perf->wrot_limit, + entry->item.session_id, entry->item.sequence_id); SDEROT_DBG("active=%d pending=%d rdot=%u/%u wrot=%u/%u s:%d.%d\n", atomic_read(&hw->num_active), hw->pending_count, mgr->rdot_limit, entry->perf->rdot_limit, @@ -766,6 +777,8 @@ static void sde_rotator_put_hw_resource(struct sde_rot_queue *queue, if (hw_res) wake_up(&hw_res->wait_queue); } + SDEROT_EVTLOG(atomic_read(&hw->num_active), hw->pending_count, + entry->item.session_id, entry->item.sequence_id); SDEROT_DBG("active=%d pending=%d s:%d.%d\n", atomic_read(&hw->num_active), hw->pending_count, entry->item.session_id, entry->item.sequence_id); @@ -1125,6 +1138,15 @@ static void sde_rotator_commit_handler(struct work_struct *work) mgr = entry->private->mgr; + SDEROT_EVTLOG( + entry->item.session_id, entry->item.sequence_id, + entry->item.src_rect.x, entry->item.src_rect.y, + entry->item.src_rect.w, entry->item.src_rect.h, + entry->item.dst_rect.x, entry->item.dst_rect.y, + entry->item.dst_rect.w, entry->item.dst_rect.h, + entry->item.flags, + entry->dnsc_factor_w, entry->dnsc_factor_h); + SDEDEV_DBG(mgr->device, "commit handler s:%d.%u src:(%d,%d,%d,%d) dst:(%d,%d,%d,%d) f:0x%x dnsc:%u/%u\n", entry->item.session_id, entry->item.sequence_id, @@ -1233,11 +1255,13 @@ static void sde_rotator_done_handler(struct work_struct *work) entry->item.flags, entry->dnsc_factor_w, entry->dnsc_factor_h); + SDEROT_EVTLOG(entry->item.session_id, 0); ret = mgr->ops_wait_for_entry(hw, entry); if (ret) { SDEROT_ERR("fail to wait for completion %d\n", ret); atomic_inc(&request->failed_count); } + SDEROT_EVTLOG(entry->item.session_id, 1); if (entry->item.ts) entry->item.ts[SDE_ROTATOR_TS_DONE] = ktime_get(); diff --git a/drivers/media/platform/msm/sde/rotator/sde_rotator_debug.c b/drivers/media/platform/msm/sde/rotator/sde_rotator_debug.c index dae1b51bfaa8..c609dbd2036e 100644 --- a/drivers/media/platform/msm/sde/rotator/sde_rotator_debug.c +++ b/drivers/media/platform/msm/sde/rotator/sde_rotator_debug.c @@ -22,6 +22,619 @@ #include "sde_rotator_core.h" #include "sde_rotator_dev.h" +#ifdef CONFIG_MSM_SDE_ROTATOR_EVTLOG_DEBUG +#define SDE_EVTLOG_DEFAULT_ENABLE 1 +#else +#define SDE_EVTLOG_DEFAULT_ENABLE 0 +#endif +#define SDE_EVTLOG_DEFAULT_PANIC 1 +#define SDE_EVTLOG_DEFAULT_REGDUMP SDE_ROT_DBG_DUMP_IN_MEM +#define SDE_EVTLOG_DEFAULT_VBIF_DBGBUSDUMP SDE_ROT_DBG_DUMP_IN_MEM + +/* + * evtlog will print this number of entries when it is called through + * sysfs node or panic. This prevents kernel log from evtlog message + * flood. + */ +#define SDE_ROT_EVTLOG_PRINT_ENTRY 256 + +/* + * evtlog keeps this number of entries in memory for debug purpose. This + * number must be greater than print entry to prevent out of bound evtlog + * entry array access. + */ +#define SDE_ROT_EVTLOG_ENTRY (SDE_ROT_EVTLOG_PRINT_ENTRY * 4) +#define SDE_ROT_EVTLOG_MAX_DATA 15 +#define SDE_ROT_EVTLOG_BUF_MAX 512 +#define SDE_ROT_EVTLOG_BUF_ALIGN 32 +#define SDE_ROT_DEBUG_BASE_MAX 10 + +static DEFINE_SPINLOCK(sde_rot_xlock); + +/* + * tlog - EVTLOG entry structure + * @counter - EVTLOG entriy counter + * @time - timestamp of EVTLOG entry + * @name - function name of EVTLOG entry + * @line - line number of EVTLOG entry + * @data - EVTLOG data contents + * @data_cnt - number of data contents + * @pid - pid of current calling thread + */ +struct tlog { + u32 counter; + s64 time; + const char *name; + int line; + u32 data[SDE_ROT_EVTLOG_MAX_DATA]; + u32 data_cnt; + int pid; +}; + +/* + * sde_rot_dbg_evtlog - EVTLOG debug data structure + * @logs - EVTLOG entries + * @first - first entry index in the EVTLOG + * @last - last entry index in the EVTLOG + * @curr - curr entry index in the EVTLOG + * @evtlog - EVTLOG debugfs handle + * @evtlog_enable - boolean indicates EVTLOG enable/disable + * @panic_on_err - boolean indicates issue panic after EVTLOG dump + * @enable_reg_dump - control in-log/memory dump for rotator registers + * @enable_vbif_dbgbus_dump - control in-log/memory dump for VBIF debug bus + * @evtlog_dump_work - schedule work strucutre for timeout handler + * @work_dump_reg - storage for register dump control in schedule work + * @work_panic - storage for panic control in schedule work + * @work_vbif_dbgbus - storage for VBIF debug bus control in schedule work + * @nrt_vbif_dbgbus_dump - memory buffer for VBIF debug bus dumping + * @reg_dump_array - memory buffer for rotator registers dumping + */ +struct sde_rot_dbg_evtlog { + struct tlog logs[SDE_ROT_EVTLOG_ENTRY]; + u32 first; + u32 last; + u32 curr; + struct dentry *evtlog; + u32 evtlog_enable; + u32 panic_on_err; + u32 enable_reg_dump; + u32 enable_vbif_dbgbus_dump; + struct work_struct evtlog_dump_work; + bool work_dump_reg; + bool work_panic; + bool work_vbif_dbgbus; + u32 *nrt_vbif_dbgbus_dump; /* address for the nrt vbif debug bus dump */ + u32 *reg_dump_array[SDE_ROT_DEBUG_BASE_MAX]; +} sde_rot_dbg_evtlog; + +/* + * sde_rot_evtlog_is_enabled - helper function for checking EVTLOG + * enable/disable + * @flag - EVTLOG option flag + */ +static inline bool sde_rot_evtlog_is_enabled(u32 flag) +{ + return (flag & sde_rot_dbg_evtlog.evtlog_enable) || + (flag == SDE_ROT_EVTLOG_ALL && + sde_rot_dbg_evtlog.evtlog_enable); +} + +/* + * __vbif_debug_bus - helper function for VBIF debug bus dump + * @head - VBIF debug bus data structure + * @vbif_base - VBIF IO mapped address + * @dump_addr - output buffer for memory dump option + * @in_log - boolean indicates in-log dump option + */ +static void __vbif_debug_bus(struct sde_rot_vbif_debug_bus *head, + void __iomem *vbif_base, u32 *dump_addr, bool in_log) +{ + int i, j; + u32 val; + + if (!dump_addr && !in_log) + return; + + for (i = 0; i < head->block_cnt; i++) { + writel_relaxed(1 << (i + head->bit_offset), + vbif_base + head->block_bus_addr); + /* make sure that current bus blcok enable */ + wmb(); + for (j = 0; j < head->test_pnt_cnt; j++) { + writel_relaxed(j, vbif_base + head->block_bus_addr + 4); + /* make sure that test point is enabled */ + wmb(); + val = readl_relaxed(vbif_base + MMSS_VBIF_TEST_BUS_OUT); + if (dump_addr) { + *dump_addr++ = head->block_bus_addr; + *dump_addr++ = i; + *dump_addr++ = j; + *dump_addr++ = val; + } + if (in_log) + pr_err("testpoint:%x arb/xin id=%d index=%d val=0x%x\n", + head->block_bus_addr, i, j, val); + } + } +} + +/* + * sde_rot_dump_vbif_debug_bus - VBIF debug bus dump + * @bus_dump_flag - dump flag controlling in-log/memory dump option + * @dump_mem - output buffer for memory dump location + */ +static void sde_rot_dump_vbif_debug_bus(u32 bus_dump_flag, + u32 **dump_mem) +{ + struct sde_rot_data_type *mdata = sde_rot_get_mdata(); + bool in_log, in_mem; + u32 *dump_addr = NULL; + u32 value; + struct sde_rot_vbif_debug_bus *head; + phys_addr_t phys = 0; + int i, list_size = 0; + void __iomem *vbif_base; + struct sde_rot_vbif_debug_bus *dbg_bus; + u32 bus_size; + + pr_info("======== NRT VBIF Debug bus DUMP =========\n"); + vbif_base = mdata->vbif_nrt_io.base; + dbg_bus = mdata->nrt_vbif_dbg_bus; + bus_size = mdata->nrt_vbif_dbg_bus_size; + + if (!vbif_base || !dbg_bus || !bus_size) + return; + + /* allocate memory for each test point */ + for (i = 0; i < bus_size; i++) { + head = dbg_bus + i; + list_size += (head->block_cnt * head->test_pnt_cnt); + } + + /* 4 bytes * 4 entries for each test point*/ + list_size *= 16; + + in_log = (bus_dump_flag & SDE_ROT_DBG_DUMP_IN_LOG); + in_mem = (bus_dump_flag & SDE_ROT_DBG_DUMP_IN_MEM); + + if (in_mem) { + if (!(*dump_mem)) + *dump_mem = dma_alloc_coherent(&mdata->pdev->dev, + list_size, &phys, GFP_KERNEL); + + if (*dump_mem) { + dump_addr = *dump_mem; + pr_info("%s: start_addr:0x%pK end_addr:0x%pK\n", + __func__, dump_addr, dump_addr + list_size); + } else { + in_mem = false; + pr_err("dump_mem: allocation fails\n"); + } + } + + sde_smmu_ctrl(1); + + value = readl_relaxed(vbif_base + MMSS_VBIF_CLKON); + writel_relaxed(value | BIT(1), vbif_base + MMSS_VBIF_CLKON); + + /* make sure that vbif core is on */ + wmb(); + + for (i = 0; i < bus_size; i++) { + head = dbg_bus + i; + + writel_relaxed(0, vbif_base + head->disable_bus_addr); + writel_relaxed(BIT(0), vbif_base + MMSS_VBIF_TEST_BUS_OUT_CTRL); + /* make sure that other bus is off */ + wmb(); + + __vbif_debug_bus(head, vbif_base, dump_addr, in_log); + if (dump_addr) + dump_addr += (head->block_cnt * head->test_pnt_cnt * 4); + } + + sde_smmu_ctrl(0); + + pr_info("========End VBIF Debug bus=========\n"); +} + +/* + * sde_rot_dump_reg - helper function for dumping rotator register set content + * @dump_name - register set name + * @reg_dump_flag - dumping flag controlling in-log/memory dump location + * @addr - starting address offset for dumping + * @len - range of the register set + * @dump_mem - output buffer for memory dump location option + */ +void sde_rot_dump_reg(const char *dump_name, u32 reg_dump_flag, u32 addr, + int len, u32 **dump_mem) +{ + struct sde_rot_data_type *mdata = sde_rot_get_mdata(); + bool in_log, in_mem; + u32 *dump_addr = NULL; + phys_addr_t phys = 0; + int i; + + in_log = (reg_dump_flag & SDE_ROT_DBG_DUMP_IN_LOG); + in_mem = (reg_dump_flag & SDE_ROT_DBG_DUMP_IN_MEM); + + pr_debug("reg_dump_flag=%d in_log=%d in_mem=%d\n", + reg_dump_flag, in_log, in_mem); + + if (len % 16) + len += 16; + len /= 16; + + if (in_mem) { + if (!(*dump_mem)) + *dump_mem = dma_alloc_coherent(&mdata->pdev->dev, + len * 16, &phys, GFP_KERNEL); + + if (*dump_mem) { + dump_addr = *dump_mem; + pr_info("%s: start_addr:0x%p end_addr:0x%p reg_addr=0x%X\n", + dump_name, dump_addr, dump_addr + (u32)len * 16, + addr); + } else { + in_mem = false; + pr_err("dump_mem: kzalloc fails!\n"); + } + } + + + for (i = 0; i < len; i++) { + u32 x0, x4, x8, xc; + + x0 = readl_relaxed(mdata->sde_io.base + addr+0x0); + x4 = readl_relaxed(mdata->sde_io.base + addr+0x4); + x8 = readl_relaxed(mdata->sde_io.base + addr+0x8); + xc = readl_relaxed(mdata->sde_io.base + addr+0xc); + + if (in_log) + pr_info("0x%08X : %08x %08x %08x %08x\n", + addr, x0, x4, x8, xc); + + if (dump_addr && in_mem) { + dump_addr[i*4] = x0; + dump_addr[i*4 + 1] = x4; + dump_addr[i*4 + 2] = x8; + dump_addr[i*4 + 3] = xc; + } + + addr += 16; + } +} + +/* + * sde_rot_dump_reg_all - dumping all SDE rotator registers + */ +static void sde_rot_dump_reg_all(void) +{ + struct sde_rot_data_type *mdata = sde_rot_get_mdata(); + struct sde_rot_regdump *head, *regdump; + u32 regdump_size; + int i; + + regdump = mdata->regdump; + regdump_size = mdata->regdump_size; + + if (!regdump || !regdump_size) + return; + + /* Enable clock to rotator if not yet enabled */ + sde_smmu_ctrl(1); + + for (i = 0; (i < regdump_size) && (i < SDE_ROT_DEBUG_BASE_MAX); i++) { + head = ®dump[i]; + + if (head->access == SDE_ROT_REGDUMP_WRITE) { + writel_relaxed(1, mdata->sde_io.base + head->offset); + /* Make sure write go through */ + wmb(); + } else { + sde_rot_dump_reg(head->name, + sde_rot_dbg_evtlog.enable_reg_dump, + head->offset, head->len, + &sde_rot_dbg_evtlog.reg_dump_array[i]); + } + } + + /* Disable rotator clock */ + sde_smmu_ctrl(0); +} + +/* + * __sde_rot_evtlog_dump_calc_range - calculate dump range for EVTLOG + */ +static bool __sde_rot_evtlog_dump_calc_range(void) +{ + static u32 next; + bool need_dump = true; + unsigned long flags; + struct sde_rot_dbg_evtlog *evtlog = &sde_rot_dbg_evtlog; + + spin_lock_irqsave(&sde_rot_xlock, flags); + + evtlog->first = next; + + if (evtlog->last == evtlog->first) { + need_dump = false; + goto dump_exit; + } + + if (evtlog->last < evtlog->first) { + evtlog->first %= SDE_ROT_EVTLOG_ENTRY; + if (evtlog->last < evtlog->first) + evtlog->last += SDE_ROT_EVTLOG_ENTRY; + } + + if ((evtlog->last - evtlog->first) > SDE_ROT_EVTLOG_PRINT_ENTRY) { + pr_warn("evtlog buffer overflow before dump: %d\n", + evtlog->last - evtlog->first); + evtlog->first = evtlog->last - SDE_ROT_EVTLOG_PRINT_ENTRY; + } + next = evtlog->first + 1; + +dump_exit: + spin_unlock_irqrestore(&sde_rot_xlock, flags); + + return need_dump; +} + +/* + * sde_rot_evtlog_dump_entry - helper function for EVTLOG content dumping + * @evtlog_buf: EVTLOG dump output buffer + * @evtlog_buf_size: EVTLOG output buffer size + */ +static ssize_t sde_rot_evtlog_dump_entry(char *evtlog_buf, + ssize_t evtlog_buf_size) +{ + int i; + ssize_t off = 0; + struct tlog *log, *prev_log; + unsigned long flags; + + spin_lock_irqsave(&sde_rot_xlock, flags); + + log = &sde_rot_dbg_evtlog.logs[sde_rot_dbg_evtlog.first % + SDE_ROT_EVTLOG_ENTRY]; + + prev_log = &sde_rot_dbg_evtlog.logs[(sde_rot_dbg_evtlog.first - 1) % + SDE_ROT_EVTLOG_ENTRY]; + + off = snprintf((evtlog_buf + off), (evtlog_buf_size - off), "%s:%-4d", + log->name, log->line); + + if (off < SDE_ROT_EVTLOG_BUF_ALIGN) { + memset((evtlog_buf + off), 0x20, + (SDE_ROT_EVTLOG_BUF_ALIGN - off)); + off = SDE_ROT_EVTLOG_BUF_ALIGN; + } + + off += snprintf((evtlog_buf + off), (evtlog_buf_size - off), + "=>[%-8d:%-11llu:%9llu][%-4d]:", sde_rot_dbg_evtlog.first, + log->time, (log->time - prev_log->time), log->pid); + + for (i = 0; i < log->data_cnt; i++) + off += snprintf((evtlog_buf + off), (evtlog_buf_size - off), + "%x ", log->data[i]); + + off += snprintf((evtlog_buf + off), (evtlog_buf_size - off), "\n"); + + spin_unlock_irqrestore(&sde_rot_xlock, flags); + + return off; +} + +/* + * sde_rot_evtlog_dump_all - Dumping all content in EVTLOG buffer + */ +static void sde_rot_evtlog_dump_all(void) +{ + char evtlog_buf[SDE_ROT_EVTLOG_BUF_MAX]; + + while (__sde_rot_evtlog_dump_calc_range()) { + sde_rot_evtlog_dump_entry(evtlog_buf, SDE_ROT_EVTLOG_BUF_MAX); + pr_info("%s", evtlog_buf); + } +} + +/* + * sde_rot_evtlog_dump_open - debugfs open handler for evtlog dump + * @inode: debugfs inode + * @file: file handler + */ +static int sde_rot_evtlog_dump_open(struct inode *inode, struct file *file) +{ + /* non-seekable */ + file->f_mode &= ~(FMODE_LSEEK | FMODE_PREAD | FMODE_PWRITE); + file->private_data = inode->i_private; + return 0; +} + +/* + * sde_rot_evtlog_dump_read - debugfs read handler for evtlog dump + * @file: file handler + * @buff: user buffer content for debugfs + * @count: size of user buffer + * @ppos: position offset of user buffer + */ +static ssize_t sde_rot_evtlog_dump_read(struct file *file, char __user *buff, + size_t count, loff_t *ppos) +{ + ssize_t len = 0; + char evtlog_buf[SDE_ROT_EVTLOG_BUF_MAX]; + + if (__sde_rot_evtlog_dump_calc_range()) { + len = sde_rot_evtlog_dump_entry(evtlog_buf, + SDE_ROT_EVTLOG_BUF_MAX); + if (copy_to_user(buff, evtlog_buf, len)) + return -EFAULT; + *ppos += len; + } + + return len; +} + +/* + * sde_rot_evtlog_dump_write - debugfs write handler for evtlog dump + * @file: file handler + * @user_buf: user buffer content from debugfs + * @count: size of user buffer + * @ppos: position offset of user buffer + */ +static ssize_t sde_rot_evtlog_dump_write(struct file *file, + const char __user *user_buf, size_t count, loff_t *ppos) +{ + sde_rot_evtlog_dump_all(); + + sde_rot_dump_reg_all(); + + if (sde_rot_dbg_evtlog.panic_on_err) + panic("evtlog_dump_write"); + + return count; +} + +/* + * sde_rot_evtlog_dump_helper - helper function for evtlog dump + * @dead: boolean indicates panic after dump + * @panic_name: Panic signature name show up in log + * @dump_rot: boolean indicates rotator register dump + * @dump_vbif_debug_bus: boolean indicates VBIF debug bus dump + */ +static void sde_rot_evtlog_dump_helper(bool dead, const char *panic_name, + bool dump_rot, bool dump_vbif_debug_bus) +{ + sde_rot_evtlog_dump_all(); + + if (dump_rot) + sde_rot_dump_reg_all(); + + if (dump_vbif_debug_bus) + sde_rot_dump_vbif_debug_bus( + sde_rot_dbg_evtlog.enable_vbif_dbgbus_dump, + &sde_rot_dbg_evtlog.nrt_vbif_dbgbus_dump); + + if (dead) + panic(panic_name); +} + +/* + * sde_rot_evtlog_debug_work - schedule work function for evtlog dump + * @work: schedule work structure + */ +static void sde_rot_evtlog_debug_work(struct work_struct *work) +{ + sde_rot_evtlog_dump_helper( + sde_rot_dbg_evtlog.work_panic, + "evtlog_workitem", + sde_rot_dbg_evtlog.work_dump_reg, + sde_rot_dbg_evtlog.work_vbif_dbgbus); +} + +/* + * sde_rot_dump_panic - Issue evtlog dump and generic panic + */ +void sde_rot_dump_panic(void) +{ + sde_rot_evtlog_dump_all(); + sde_rot_dump_reg_all(); + + panic("sde_rotator"); +} + +/* + * sde_rot_evtlog_tout_handler - log dump timeout handler + * @queue: boolean indicate putting log dump into queue + * @name: function name having timeout + */ +void sde_rot_evtlog_tout_handler(bool queue, const char *name, ...) +{ + int i; + bool dead = false; + bool dump_rot = false; + bool dump_vbif_dbgbus = false; + char *blk_name = NULL; + va_list args; + + if (!sde_rot_evtlog_is_enabled(SDE_ROT_EVTLOG_DEFAULT)) + return; + + if (queue && work_pending(&sde_rot_dbg_evtlog.evtlog_dump_work)) + return; + + va_start(args, name); + for (i = 0; i < SDE_ROT_EVTLOG_MAX_DATA; i++) { + blk_name = va_arg(args, char*); + if (IS_ERR_OR_NULL(blk_name)) + break; + + if (!strcmp(blk_name, "rot")) + dump_rot = true; + + if (!strcmp(blk_name, "vbif_dbg_bus")) + dump_vbif_dbgbus = true; + + if (!strcmp(blk_name, "panic")) + dead = true; + } + va_end(args); + + if (queue) { + /* schedule work to dump later */ + sde_rot_dbg_evtlog.work_panic = dead; + sde_rot_dbg_evtlog.work_dump_reg = dump_rot; + sde_rot_dbg_evtlog.work_vbif_dbgbus = dump_vbif_dbgbus; + schedule_work(&sde_rot_dbg_evtlog.evtlog_dump_work); + } else { + sde_rot_evtlog_dump_helper(dead, name, dump_rot, + dump_vbif_dbgbus); + } +} + +/* + * sde_rot_evtlog - log contents into memory for dump analysis + * @name: Name of function calling evtlog + * @line: line number of calling function + * @flag: Log control flag + */ +void sde_rot_evtlog(const char *name, int line, int flag, ...) +{ + unsigned long flags; + int i, val = 0; + va_list args; + struct tlog *log; + + if (!sde_rot_evtlog_is_enabled(flag)) + return; + + spin_lock_irqsave(&sde_rot_xlock, flags); + log = &sde_rot_dbg_evtlog.logs[sde_rot_dbg_evtlog.curr]; + log->time = ktime_to_us(ktime_get()); + log->name = name; + log->line = line; + log->data_cnt = 0; + log->pid = current->pid; + + va_start(args, flag); + for (i = 0; i < SDE_ROT_EVTLOG_MAX_DATA; i++) { + + val = va_arg(args, int); + if (val == SDE_ROT_DATA_LIMITER) + break; + + log->data[i] = val; + } + va_end(args); + log->data_cnt = i; + sde_rot_dbg_evtlog.curr = + (sde_rot_dbg_evtlog.curr + 1) % SDE_ROT_EVTLOG_ENTRY; + sde_rot_dbg_evtlog.last++; + + spin_unlock_irqrestore(&sde_rot_xlock, flags); +} + /* * sde_rotator_stat_show - Show statistics on read to this debugfs file * @s: Pointer to sequence file structure @@ -249,6 +862,58 @@ static int sde_rotator_core_create_debugfs( return 0; } +static const struct file_operations sde_rot_evtlog_fops = { + .open = sde_rot_evtlog_dump_open, + .read = sde_rot_evtlog_dump_read, + .write = sde_rot_evtlog_dump_write, +}; + +static int sde_rotator_evtlog_create_debugfs( + struct sde_rot_mgr *mgr, + struct dentry *debugfs_root) +{ + int i; + + sde_rot_dbg_evtlog.evtlog = debugfs_create_dir("evtlog", debugfs_root); + if (IS_ERR_OR_NULL(sde_rot_dbg_evtlog.evtlog)) { + pr_err("debugfs_create_dir fail, error %ld\n", + PTR_ERR(sde_rot_dbg_evtlog.evtlog)); + sde_rot_dbg_evtlog.evtlog = NULL; + return -ENODEV; + } + + INIT_WORK(&sde_rot_dbg_evtlog.evtlog_dump_work, + sde_rot_evtlog_debug_work); + sde_rot_dbg_evtlog.work_panic = false; + + for (i = 0; i < SDE_ROT_EVTLOG_ENTRY; i++) + sde_rot_dbg_evtlog.logs[i].counter = i; + + debugfs_create_file("dump", 0644, sde_rot_dbg_evtlog.evtlog, NULL, + &sde_rot_evtlog_fops); + debugfs_create_u32("enable", 0644, sde_rot_dbg_evtlog.evtlog, + &sde_rot_dbg_evtlog.evtlog_enable); + debugfs_create_u32("panic", 0644, sde_rot_dbg_evtlog.evtlog, + &sde_rot_dbg_evtlog.panic_on_err); + debugfs_create_u32("reg_dump", 0644, sde_rot_dbg_evtlog.evtlog, + &sde_rot_dbg_evtlog.enable_reg_dump); + debugfs_create_u32("vbif_dbgbus_dump", 0644, sde_rot_dbg_evtlog.evtlog, + &sde_rot_dbg_evtlog.enable_vbif_dbgbus_dump); + + sde_rot_dbg_evtlog.evtlog_enable = SDE_EVTLOG_DEFAULT_ENABLE; + sde_rot_dbg_evtlog.panic_on_err = SDE_EVTLOG_DEFAULT_PANIC; + sde_rot_dbg_evtlog.enable_reg_dump = SDE_EVTLOG_DEFAULT_REGDUMP; + sde_rot_dbg_evtlog.enable_vbif_dbgbus_dump = + SDE_EVTLOG_DEFAULT_VBIF_DBGBUSDUMP; + + pr_info("evtlog_status: enable:%d, panic:%d, dump:%d\n", + sde_rot_dbg_evtlog.evtlog_enable, + sde_rot_dbg_evtlog.panic_on_err, + sde_rot_dbg_evtlog.enable_reg_dump); + + return 0; +} + /* * struct sde_rotator_stat_ops - processed statistics file operations */ @@ -335,6 +1000,12 @@ struct dentry *sde_rotator_create_debugfs( return NULL; } + if (sde_rotator_evtlog_create_debugfs(rot_dev->mgr, debugfs_root)) { + SDEROT_ERR("fail create evtlog debugfs\n"); + debugfs_remove_recursive(debugfs_root); + return NULL; + } + return debugfs_root; } diff --git a/drivers/media/platform/msm/sde/rotator/sde_rotator_debug.h b/drivers/media/platform/msm/sde/rotator/sde_rotator_debug.h index 2ed1b759f3e9..dcda54274fad 100644 --- a/drivers/media/platform/msm/sde/rotator/sde_rotator_debug.h +++ b/drivers/media/platform/msm/sde/rotator/sde_rotator_debug.h @@ -16,6 +16,32 @@ #include <linux/types.h> #include <linux/dcache.h> +#define SDE_ROT_DATA_LIMITER (-1) +#define SDE_ROT_EVTLOG_TOUT_DATA_LIMITER (NULL) + +enum sde_rot_dbg_reg_dump_flag { + SDE_ROT_DBG_DUMP_IN_LOG = BIT(0), + SDE_ROT_DBG_DUMP_IN_MEM = BIT(1), +}; + +enum sde_rot_dbg_evtlog_flag { + SDE_ROT_EVTLOG_DEFAULT = BIT(0), + SDE_ROT_EVTLOG_IOMMU = BIT(1), + SDE_ROT_EVTLOG_DBG = BIT(6), + SDE_ROT_EVTLOG_ALL = BIT(7) +}; + +#define SDEROT_EVTLOG(...) sde_rot_evtlog(__func__, __LINE__, \ + SDE_ROT_EVTLOG_DEFAULT, ##__VA_ARGS__, SDE_ROT_DATA_LIMITER) + +#define SDEROT_EVTLOG_TOUT_HANDLER(...) \ + sde_rot_evtlog_tout_handler(false, __func__, ##__VA_ARGS__, \ + SDE_ROT_EVTLOG_TOUT_DATA_LIMITER) + +void sde_rot_evtlog(const char *name, int line, int flag, ...); +void sde_rot_dump_panic(void); +void sde_rot_evtlog_tout_handler(bool queue, const char *name, ...); + struct sde_rotator_device; #if defined(CONFIG_DEBUG_FS) diff --git a/drivers/media/platform/msm/sde/rotator/sde_rotator_r3.c b/drivers/media/platform/msm/sde/rotator/sde_rotator_r3.c index 76fd674d161d..548131393835 100644 --- a/drivers/media/platform/msm/sde/rotator/sde_rotator_r3.c +++ b/drivers/media/platform/msm/sde/rotator/sde_rotator_r3.c @@ -34,6 +34,7 @@ #include "sde_rotator_r3_hwio.h" #include "sde_rotator_r3_debug.h" #include "sde_rotator_trace.h" +#include "sde_rotator_debug.h" /* XIN mapping */ #define XIN_SSPP 0 @@ -198,6 +199,29 @@ static u32 sde_hw_rotator_output_pixfmts[] = { SDE_PIX_FMT_Y_CBCR_H2V2_TP10_UBWC, }; +static struct sde_rot_vbif_debug_bus nrt_vbif_dbg_bus_r3[] = { + {0x214, 0x21c, 16, 1, 0x10}, /* arb clients */ + {0x214, 0x21c, 0, 12, 0x13}, /* xin blocks - axi side */ + {0x21c, 0x214, 0, 12, 0xc}, /* xin blocks - clock side */ +}; + +static struct sde_rot_regdump sde_rot_r3_regdump[] = { + { "SDEROT_ROTTOP", SDE_ROT_ROTTOP_OFFSET, 0x100, SDE_ROT_REGDUMP_READ }, + { "SDEROT_SSPP", SDE_ROT_SSPP_OFFSET, 0x200, SDE_ROT_REGDUMP_READ }, + { "SDEROT_WB", SDE_ROT_WB_OFFSET, 0x300, SDE_ROT_REGDUMP_READ }, + { "SDEROT_REGDMA_CSR", SDE_ROT_REGDMA_OFFSET, 0x100, + SDE_ROT_REGDUMP_READ }, + /* + * Need to perform a SW reset to REGDMA in order to access the + * REGDMA RAM especially if REGDMA is waiting for Rotator IDLE. + * REGDMA RAM should be dump at last. + */ + { "SDEROT_REGDMA_RESET", ROTTOP_SW_RESET_OVERRIDE, 1, + SDE_ROT_REGDUMP_WRITE }, + { "SDEROT_REGDMA_RAM", SDE_ROT_REGDMA_RAM_OFFSET, 0x2000, + SDE_ROT_REGDUMP_READ }, +}; + /* Invalid software timestamp value for initialization */ #define SDE_REGDMA_SWTS_INVALID (~0) @@ -332,6 +356,8 @@ static void sde_hw_rotator_dump_status(struct sde_hw_rotator *rot) REGDMA_CSR_REGDMA_INVALID_CMD_RAM_OFFSET), SDE_ROTREG_READ(rot->mdss_base, REGDMA_CSR_REGDMA_FSM_STATE)); + + SDEROT_EVTLOG_TOUT_HANDLER("rot", "vbif_dbg_bus", "panic"); } /** @@ -1484,6 +1510,10 @@ static int sde_hw_rotator_config(struct sde_rot_hw_resource *hw, &entry->dst_buf); } + SDEROT_EVTLOG(flags, item->input.width, item->input.height, + item->output.width, item->output.height, + entry->src_buf.p[0].addr, entry->dst_buf.p[0].addr); + if (mdata->default_ot_rd_limit) { struct sde_mdp_set_ot_params ot_params; @@ -1677,6 +1707,13 @@ static int sde_rotator_hw_rev_init(struct sde_hw_rotator *rot) set_bit(SDE_CAPS_R3_1P5_DOWNSCALE, mdata->sde_caps_map); } + mdata->nrt_vbif_dbg_bus = nrt_vbif_dbg_bus_r3; + mdata->nrt_vbif_dbg_bus_size = + ARRAY_SIZE(nrt_vbif_dbg_bus_r3); + + mdata->regdump = sde_rot_r3_regdump; + mdata->regdump_size = ARRAY_SIZE(sde_rot_r3_regdump); + return 0; } @@ -2202,6 +2239,7 @@ int sde_rotator_r3_init(struct sde_rot_mgr *mgr) clk_set_flags(mgr->rot_clk[mgr->core_clk_idx].clk, CLKFLAG_NORETAIN_PERIPH); + mdata->sde_rot_hw = rot; return 0; error_hw_rev_init: if (rot->irq_num >= 0) diff --git a/drivers/media/platform/msm/sde/rotator/sde_rotator_smmu.c b/drivers/media/platform/msm/sde/rotator/sde_rotator_smmu.c index 6cc975e22cd4..7bbd8aa53342 100644 --- a/drivers/media/platform/msm/sde/rotator/sde_rotator_smmu.c +++ b/drivers/media/platform/msm/sde/rotator/sde_rotator_smmu.c @@ -33,6 +33,7 @@ #include "sde_rotator_util.h" #include "sde_rotator_io_util.h" #include "sde_rotator_smmu.h" +#include "sde_rotator_debug.h" #define SMMU_SDE_ROT_SEC "qcom,smmu_sde_rot_sec" #define SMMU_SDE_ROT_UNSEC "qcom,smmu_sde_rot_unsec" @@ -332,6 +333,8 @@ int sde_smmu_ctrl(int enable) int rc = 0; mutex_lock(&sde_smmu_ref_cnt_lock); + SDEROT_EVTLOG(__builtin_return_address(0), enable, mdata->iommu_ref_cnt, + mdata->iommu_attached); SDEROT_DBG("%pS: enable:%d ref_cnt:%d attach:%d\n", __builtin_return_address(0), enable, mdata->iommu_ref_cnt, mdata->iommu_attached); @@ -407,9 +410,10 @@ static int sde_smmu_fault_handler(struct iommu_domain *domain, sde_smmu = (struct sde_smmu_client *)token; - /* TODO: trigger rotator panic and dump */ - SDEROT_ERR("TODO: trigger rotator panic and dump, iova=0x%08lx\n", - iova); + /* trigger rotator panic and dump */ + SDEROT_ERR("trigger rotator panic and dump, iova=0x%08lx\n", iova); + + sde_rot_dump_panic(); return rc; } |