Loading drivers/media/platform/msm/sde/Kconfig +10 −1 Original line number Diff line number Diff line Loading @@ -6,3 +6,12 @@ config MSM_SDE_ROTATOR select SW_SYNC if SYNC ---help--- 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). drivers/media/platform/msm/sde/rotator/sde_rotator_base.h +29 −0 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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; Loading Loading @@ -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, Loading drivers/media/platform/msm/sde/rotator/sde_rotator_core.c +24 −0 Original line number Diff line number Diff line Loading @@ -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 Loading Loading @@ -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, Loading Loading @@ -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); Loading Loading @@ -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; Loading Loading @@ -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; Loading @@ -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; Loading Loading @@ -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); } Loading Loading @@ -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, Loading Loading @@ -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); Loading Loading @@ -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, Loading Loading @@ -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(); Loading drivers/media/platform/msm/sde/rotator/sde_rotator_debug.c +671 −0 Original line number Diff line number Diff line Loading @@ -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 Loading Loading @@ -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 */ Loading Loading @@ -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; } Loading drivers/media/platform/msm/sde/rotator/sde_rotator_debug.h +26 −0 Original line number Diff line number Diff line Loading @@ -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) Loading Loading
drivers/media/platform/msm/sde/Kconfig +10 −1 Original line number Diff line number Diff line Loading @@ -6,3 +6,12 @@ config MSM_SDE_ROTATOR select SW_SYNC if SYNC ---help--- 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).
drivers/media/platform/msm/sde/rotator/sde_rotator_base.h +29 −0 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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; Loading Loading @@ -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, Loading
drivers/media/platform/msm/sde/rotator/sde_rotator_core.c +24 −0 Original line number Diff line number Diff line Loading @@ -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 Loading Loading @@ -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, Loading Loading @@ -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); Loading Loading @@ -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; Loading Loading @@ -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; Loading @@ -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; Loading Loading @@ -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); } Loading Loading @@ -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, Loading Loading @@ -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); Loading Loading @@ -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, Loading Loading @@ -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(); Loading
drivers/media/platform/msm/sde/rotator/sde_rotator_debug.c +671 −0 Original line number Diff line number Diff line Loading @@ -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 Loading Loading @@ -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 */ Loading Loading @@ -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; } Loading
drivers/media/platform/msm/sde/rotator/sde_rotator_debug.h +26 −0 Original line number Diff line number Diff line Loading @@ -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) Loading