752 lines
18 KiB
C
752 lines
18 KiB
C
/* Copyright (c) 2014-2015, 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/delay.h>
|
|
#include <linux/spinlock.h>
|
|
#include <linux/ktime.h>
|
|
#include <linux/debugfs.h>
|
|
#include <linux/uaccess.h>
|
|
#include <linux/dma-buf.h>
|
|
|
|
#include "mdss.h"
|
|
#include "mdss_mdp.h"
|
|
#include "mdss_debug.h"
|
|
|
|
#ifdef CONFIG_FB_MSM_MDSS_XLOG_DEBUG
|
|
#define XLOG_DEFAULT_ENABLE 1
|
|
#else
|
|
#define XLOG_DEFAULT_ENABLE 0
|
|
#endif
|
|
|
|
#define XLOG_DEFAULT_PANIC 1
|
|
#define XLOG_DEFAULT_REGDUMP 0x2 /* dump in RAM */
|
|
#define XLOG_DEFAULT_DBGBUSDUMP 0x2 /* dump in RAM */
|
|
#define XLOG_DEFAULT_VBIF_DBGBUSDUMP 0x2 /* dump in RAM */
|
|
|
|
/*
|
|
* xlog will print this number of entries when it is called through
|
|
* sysfs node or panic. This prevents kernel log from xlog message
|
|
* flood.
|
|
*/
|
|
#define MDSS_XLOG_PRINT_ENTRY 256
|
|
|
|
/*
|
|
* xlog keeps this number of entries in memory for debug purpose. This
|
|
* number must be greater than print entry to prevent out of bound xlog
|
|
* entry array access.
|
|
*/
|
|
#define MDSS_XLOG_ENTRY (MDSS_XLOG_PRINT_ENTRY * 4)
|
|
#define MDSS_XLOG_MAX_DATA 15
|
|
#define MDSS_XLOG_BUF_MAX 512
|
|
#define MDSS_XLOG_BUF_ALIGN 32
|
|
|
|
DEFINE_SPINLOCK(xlock);
|
|
|
|
struct tlog {
|
|
u32 counter;
|
|
s64 time;
|
|
const char *name;
|
|
int line;
|
|
u32 data[MDSS_XLOG_MAX_DATA];
|
|
u32 data_cnt;
|
|
int pid;
|
|
};
|
|
|
|
struct mdss_dbg_xlog {
|
|
struct tlog logs[MDSS_XLOG_ENTRY];
|
|
u32 first;
|
|
u32 last;
|
|
u32 curr;
|
|
struct dentry *xlog;
|
|
u32 xlog_enable;
|
|
u32 panic_on_err;
|
|
u32 enable_reg_dump;
|
|
u32 enable_dbgbus_dump;
|
|
u32 enable_vbif_dbgbus_dump;
|
|
struct work_struct xlog_dump_work;
|
|
struct mdss_debug_base *blk_arr[MDSS_DEBUG_BASE_MAX];
|
|
bool work_panic;
|
|
bool work_dbgbus;
|
|
bool work_vbif_dbgbus;
|
|
u32 *dbgbus_dump; /* address for the debug bus dump */
|
|
u32 *vbif_dbgbus_dump; /* address for the vbif debug bus dump */
|
|
u32 *nrt_vbif_dbgbus_dump; /* address for the nrt vbif debug bus dump */
|
|
} mdss_dbg_xlog;
|
|
|
|
static inline bool mdss_xlog_is_enabled(u32 flag)
|
|
{
|
|
return (flag & mdss_dbg_xlog.xlog_enable) ||
|
|
(flag == MDSS_XLOG_ALL && mdss_dbg_xlog.xlog_enable);
|
|
}
|
|
|
|
void mdss_xlog(const char *name, int line, int flag, ...)
|
|
{
|
|
unsigned long flags;
|
|
int i, val = 0;
|
|
va_list args;
|
|
struct tlog *log;
|
|
|
|
if (!mdss_xlog_is_enabled(flag))
|
|
return;
|
|
|
|
spin_lock_irqsave(&xlock, flags);
|
|
log = &mdss_dbg_xlog.logs[mdss_dbg_xlog.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 < MDSS_XLOG_MAX_DATA; i++) {
|
|
|
|
val = va_arg(args, int);
|
|
if (val == DATA_LIMITER)
|
|
break;
|
|
|
|
log->data[i] = val;
|
|
}
|
|
va_end(args);
|
|
log->data_cnt = i;
|
|
mdss_dbg_xlog.curr = (mdss_dbg_xlog.curr + 1) % MDSS_XLOG_ENTRY;
|
|
mdss_dbg_xlog.last++;
|
|
|
|
spin_unlock_irqrestore(&xlock, flags);
|
|
}
|
|
|
|
/* always dump the last entries which are not dumped yet */
|
|
static bool __mdss_xlog_dump_calc_range(void)
|
|
{
|
|
static u32 next;
|
|
bool need_dump = true;
|
|
unsigned long flags;
|
|
struct mdss_dbg_xlog *xlog = &mdss_dbg_xlog;
|
|
|
|
spin_lock_irqsave(&xlock, flags);
|
|
|
|
xlog->first = next;
|
|
|
|
if (xlog->last == xlog->first) {
|
|
need_dump = false;
|
|
goto dump_exit;
|
|
}
|
|
|
|
if (xlog->last < xlog->first) {
|
|
xlog->first %= MDSS_XLOG_ENTRY;
|
|
if (xlog->last < xlog->first)
|
|
xlog->last += MDSS_XLOG_ENTRY;
|
|
}
|
|
|
|
if ((xlog->last - xlog->first) > MDSS_XLOG_PRINT_ENTRY) {
|
|
pr_warn("xlog buffer overflow before dump: %d\n",
|
|
xlog->last - xlog->first);
|
|
xlog->first = xlog->last - MDSS_XLOG_PRINT_ENTRY;
|
|
}
|
|
next = xlog->first + 1;
|
|
|
|
dump_exit:
|
|
spin_unlock_irqrestore(&xlock, flags);
|
|
|
|
return need_dump;
|
|
}
|
|
|
|
static ssize_t mdss_xlog_dump_entry(char *xlog_buf, ssize_t xlog_buf_size)
|
|
{
|
|
int i;
|
|
ssize_t off = 0;
|
|
struct tlog *log, *prev_log;
|
|
unsigned long flags;
|
|
|
|
spin_lock_irqsave(&xlock, flags);
|
|
|
|
log = &mdss_dbg_xlog.logs[mdss_dbg_xlog.first %
|
|
MDSS_XLOG_ENTRY];
|
|
|
|
prev_log = &mdss_dbg_xlog.logs[(mdss_dbg_xlog.first - 1) %
|
|
MDSS_XLOG_ENTRY];
|
|
|
|
off = snprintf((xlog_buf + off), (xlog_buf_size - off), "%s:%-4d",
|
|
log->name, log->line);
|
|
|
|
if (off < MDSS_XLOG_BUF_ALIGN) {
|
|
memset((xlog_buf + off), 0x20, (MDSS_XLOG_BUF_ALIGN - off));
|
|
off = MDSS_XLOG_BUF_ALIGN;
|
|
}
|
|
|
|
off += snprintf((xlog_buf + off), (xlog_buf_size - off),
|
|
"=>[%-8d:%-11llu:%9llu][%-4d]:", mdss_dbg_xlog.first,
|
|
log->time, (log->time - prev_log->time), log->pid);
|
|
|
|
for (i = 0; i < log->data_cnt; i++)
|
|
off += snprintf((xlog_buf + off), (xlog_buf_size - off),
|
|
"%x ", log->data[i]);
|
|
|
|
off += snprintf((xlog_buf + off), (xlog_buf_size - off), "\n");
|
|
|
|
spin_unlock_irqrestore(&xlock, flags);
|
|
|
|
return off;
|
|
}
|
|
|
|
static void mdss_xlog_dump_all(void)
|
|
{
|
|
char xlog_buf[MDSS_XLOG_BUF_MAX];
|
|
|
|
while (__mdss_xlog_dump_calc_range()) {
|
|
mdss_xlog_dump_entry(xlog_buf, MDSS_XLOG_BUF_MAX);
|
|
pr_info("%s", xlog_buf);
|
|
}
|
|
}
|
|
|
|
u32 get_dump_range(struct dump_offset *range_node, size_t max_offset)
|
|
{
|
|
u32 length = 0;
|
|
|
|
if ((range_node->start > range_node->end) ||
|
|
(range_node->end > max_offset) || (range_node->start == 0
|
|
&& range_node->end == 0)) {
|
|
length = max_offset;
|
|
} else {
|
|
length = range_node->end - range_node->start;
|
|
}
|
|
|
|
return length;
|
|
}
|
|
|
|
static void mdss_dump_debug_bus(u32 bus_dump_flag,
|
|
u32 **dump_mem)
|
|
{
|
|
struct mdss_data_type *mdata = mdss_mdp_get_mdata();
|
|
bool in_log, in_mem;
|
|
u32 *dump_addr = NULL;
|
|
u32 status = 0;
|
|
struct debug_bus *head;
|
|
phys_addr_t phys = 0;
|
|
int list_size = mdata->dbg_bus_size;
|
|
int i;
|
|
|
|
if (!(mdata->dbg_bus && list_size))
|
|
return;
|
|
|
|
/* will keep in memory 4 entries of 4 bytes each */
|
|
list_size = (list_size * 4 * 4);
|
|
|
|
in_log = (bus_dump_flag & MDSS_DBG_DUMP_IN_LOG);
|
|
in_mem = (bus_dump_flag & MDSS_DBG_DUMP_IN_MEM);
|
|
|
|
pr_info("======== Debug bus DUMP =========\n");
|
|
|
|
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%p end_addr:0x%p\n",
|
|
__func__, dump_addr, dump_addr + list_size);
|
|
} else {
|
|
in_mem = false;
|
|
pr_err("dump_mem: allocation fails\n");
|
|
}
|
|
}
|
|
|
|
mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_ON);
|
|
for (i = 0; i < mdata->dbg_bus_size; i++) {
|
|
head = mdata->dbg_bus + i;
|
|
writel_relaxed(TEST_MASK(head->block_id, head->test_id),
|
|
mdss_res->mdp_base + head->wr_addr);
|
|
wmb(); /* make sure test bits were written */
|
|
status = readl_relaxed(mdss_res->mdp_base +
|
|
head->wr_addr + 0x4);
|
|
|
|
if (in_log)
|
|
pr_err("waddr=0x%x blk=%d tst=%d val=0x%x\n",
|
|
head->wr_addr, head->block_id, head->test_id,
|
|
status);
|
|
|
|
if (dump_addr && in_mem) {
|
|
dump_addr[i*4] = head->wr_addr;
|
|
dump_addr[i*4 + 1] = head->block_id;
|
|
dump_addr[i*4 + 2] = head->test_id;
|
|
dump_addr[i*4 + 3] = status;
|
|
}
|
|
|
|
/* Disable debug bus once we are done */
|
|
writel_relaxed(0, mdss_res->mdp_base + head->wr_addr);
|
|
|
|
}
|
|
mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_OFF);
|
|
|
|
pr_info("========End Debug bus=========\n");
|
|
}
|
|
|
|
static void __vbif_debug_bus(struct 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);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void mdss_dump_vbif_debug_bus(u32 bus_dump_flag,
|
|
u32 **dump_mem, bool real_time)
|
|
{
|
|
struct mdss_data_type *mdata = mdss_mdp_get_mdata();
|
|
bool in_log, in_mem;
|
|
u32 *dump_addr = NULL;
|
|
u32 value;
|
|
struct vbif_debug_bus *head;
|
|
phys_addr_t phys = 0;
|
|
int i, list_size = 0;
|
|
void __iomem *vbif_base;
|
|
struct vbif_debug_bus *dbg_bus;
|
|
u32 bus_size;
|
|
|
|
if (real_time) {
|
|
pr_info("======== VBIF Debug bus DUMP =========\n");
|
|
vbif_base = mdata->vbif_io.base;
|
|
dbg_bus = mdata->vbif_dbg_bus;
|
|
bus_size = mdata->vbif_dbg_bus_size;
|
|
} else {
|
|
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 (!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 & MDSS_DBG_DUMP_IN_LOG);
|
|
in_mem = (bus_dump_flag & MDSS_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%p end_addr:0x%p\n",
|
|
__func__, dump_addr, dump_addr + list_size);
|
|
} else {
|
|
in_mem = false;
|
|
pr_err("dump_mem: allocation fails\n");
|
|
}
|
|
}
|
|
|
|
mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_ON);
|
|
|
|
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);
|
|
}
|
|
|
|
mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_OFF);
|
|
|
|
pr_info("========End VBIF Debug bus=========\n");
|
|
}
|
|
|
|
static void mdss_dump_reg(const char *dump_name, u32 reg_dump_flag,
|
|
char *addr, int len, u32 **dump_mem)
|
|
{
|
|
struct mdss_data_type *mdata = mdss_mdp_get_mdata();
|
|
bool in_log, in_mem;
|
|
u32 *dump_addr = NULL;
|
|
phys_addr_t phys = 0;
|
|
int i;
|
|
|
|
in_log = (reg_dump_flag & MDSS_DBG_DUMP_IN_LOG);
|
|
in_mem = (reg_dump_flag & MDSS_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%p\n",
|
|
dump_name, dump_addr, dump_addr + (u32)len * 16,
|
|
addr);
|
|
} else {
|
|
in_mem = false;
|
|
pr_err("dump_mem: kzalloc fails!\n");
|
|
}
|
|
}
|
|
|
|
mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_ON);
|
|
for (i = 0; i < len; i++) {
|
|
u32 x0, x4, x8, xc;
|
|
|
|
x0 = readl_relaxed(addr+0x0);
|
|
x4 = readl_relaxed(addr+0x4);
|
|
x8 = readl_relaxed(addr+0x8);
|
|
xc = readl_relaxed(addr+0xc);
|
|
|
|
if (in_log)
|
|
pr_info("%p : %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;
|
|
}
|
|
mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_OFF);
|
|
}
|
|
|
|
static void mdss_dump_reg_by_ranges(struct mdss_debug_base *dbg,
|
|
u32 reg_dump_flag)
|
|
{
|
|
char *addr;
|
|
int len;
|
|
struct range_dump_node *xlog_node, *xlog_tmp;
|
|
|
|
if (!dbg || !dbg->base) {
|
|
pr_err("dbg base is null!\n");
|
|
return;
|
|
}
|
|
|
|
pr_info("%s:=========%s DUMP=========\n", __func__, dbg->name);
|
|
|
|
/* If there is a list to dump the registers by ranges, use the ranges */
|
|
if (!list_empty(&dbg->dump_list)) {
|
|
list_for_each_entry_safe(xlog_node, xlog_tmp,
|
|
&dbg->dump_list, head) {
|
|
len = get_dump_range(&xlog_node->offset,
|
|
dbg->max_offset);
|
|
addr = dbg->base + xlog_node->offset.start;
|
|
pr_debug("%s: range_base=0x%p start=0x%x end=0x%x\n",
|
|
xlog_node->range_name,
|
|
addr, xlog_node->offset.start,
|
|
xlog_node->offset.end);
|
|
mdss_dump_reg((const char *)xlog_node->range_name,
|
|
reg_dump_flag, addr, len, &xlog_node->reg_dump);
|
|
}
|
|
} else {
|
|
/* If there is no list to dump ranges, dump all registers */
|
|
pr_info("Ranges not found, will dump full registers");
|
|
pr_info("base:0x%p len:0x%zu\n", dbg->base, dbg->max_offset);
|
|
addr = dbg->base;
|
|
len = dbg->max_offset;
|
|
mdss_dump_reg((const char *)dbg->name, reg_dump_flag, addr,
|
|
len, &dbg->reg_dump);
|
|
}
|
|
}
|
|
|
|
static void mdss_dump_reg_by_blk(const char *blk_name)
|
|
{
|
|
struct mdss_data_type *mdata = mdss_mdp_get_mdata();
|
|
struct mdss_debug_data *mdd = mdata->debug_inf.debug_data;
|
|
struct mdss_debug_base *blk_base, *tmp;
|
|
|
|
if (!mdd)
|
|
return;
|
|
|
|
list_for_each_entry_safe(blk_base, tmp, &mdd->base_list, head) {
|
|
if (strlen(blk_base->name) &&
|
|
!strcmp(blk_base->name, blk_name)) {
|
|
mdss_dump_reg_by_ranges(blk_base,
|
|
mdss_dbg_xlog.enable_reg_dump);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
static void mdss_dump_reg_all(void)
|
|
{
|
|
struct mdss_data_type *mdata = mdss_mdp_get_mdata();
|
|
struct mdss_debug_data *mdd = mdata->debug_inf.debug_data;
|
|
struct mdss_debug_base *blk_base, *tmp;
|
|
|
|
if (!mdd)
|
|
return;
|
|
|
|
list_for_each_entry_safe(blk_base, tmp, &mdd->base_list, head) {
|
|
if (strlen(blk_base->name))
|
|
mdss_dump_reg_by_blk(blk_base->name);
|
|
}
|
|
}
|
|
|
|
static void clear_dump_blk_arr(struct mdss_debug_base *blk_arr[],
|
|
u32 blk_len)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < blk_len; i++)
|
|
blk_arr[i] = NULL;
|
|
}
|
|
|
|
struct mdss_debug_base *get_dump_blk_addr(const char *blk_name)
|
|
{
|
|
struct mdss_data_type *mdata = mdss_mdp_get_mdata();
|
|
struct mdss_debug_data *mdd = mdata->debug_inf.debug_data;
|
|
struct mdss_debug_base *blk_base, *tmp;
|
|
|
|
if (!mdd)
|
|
return NULL;
|
|
|
|
list_for_each_entry_safe(blk_base, tmp, &mdd->base_list, head) {
|
|
if (strlen(blk_base->name) &&
|
|
!strcmp(blk_base->name, blk_name))
|
|
return blk_base;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static void mdss_xlog_dump_array(struct mdss_debug_base *blk_arr[],
|
|
u32 len, bool dead, const char *name, bool dump_dbgbus,
|
|
bool dump_vbif_dbgbus)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < len; i++) {
|
|
if (blk_arr[i] != NULL)
|
|
mdss_dump_reg_by_ranges(blk_arr[i],
|
|
mdss_dbg_xlog.enable_reg_dump);
|
|
}
|
|
|
|
mdss_xlog_dump_all();
|
|
|
|
if (dump_dbgbus)
|
|
mdss_dump_debug_bus(mdss_dbg_xlog.enable_dbgbus_dump,
|
|
&mdss_dbg_xlog.dbgbus_dump);
|
|
|
|
if (dump_vbif_dbgbus) {
|
|
mdss_dump_vbif_debug_bus(mdss_dbg_xlog.enable_vbif_dbgbus_dump,
|
|
&mdss_dbg_xlog.vbif_dbgbus_dump, true);
|
|
|
|
mdss_dump_vbif_debug_bus(mdss_dbg_xlog.enable_vbif_dbgbus_dump,
|
|
&mdss_dbg_xlog.nrt_vbif_dbgbus_dump, false);
|
|
}
|
|
|
|
if (dead && mdss_dbg_xlog.panic_on_err)
|
|
panic(name);
|
|
}
|
|
|
|
static void xlog_debug_work(struct work_struct *work)
|
|
{
|
|
|
|
mdss_xlog_dump_array(mdss_dbg_xlog.blk_arr,
|
|
ARRAY_SIZE(mdss_dbg_xlog.blk_arr),
|
|
mdss_dbg_xlog.work_panic, "xlog_workitem",
|
|
mdss_dbg_xlog.work_dbgbus,
|
|
mdss_dbg_xlog.work_vbif_dbgbus);
|
|
}
|
|
|
|
void mdss_xlog_tout_handler_default(bool queue, const char *name, ...)
|
|
{
|
|
int i, index = 0;
|
|
bool dead = false;
|
|
bool dump_dbgbus = false, dump_vbif_dbgbus = false;
|
|
va_list args;
|
|
char *blk_name = NULL;
|
|
struct mdss_debug_base *blk_base = NULL;
|
|
struct mdss_debug_base **blk_arr;
|
|
u32 blk_len;
|
|
|
|
if (!mdss_xlog_is_enabled(MDSS_XLOG_DEFAULT))
|
|
return;
|
|
|
|
if (queue && work_pending(&mdss_dbg_xlog.xlog_dump_work))
|
|
return;
|
|
|
|
blk_arr = &mdss_dbg_xlog.blk_arr[0];
|
|
blk_len = ARRAY_SIZE(mdss_dbg_xlog.blk_arr);
|
|
|
|
clear_dump_blk_arr(blk_arr, blk_len);
|
|
|
|
va_start(args, name);
|
|
for (i = 0; i < MDSS_XLOG_MAX_DATA; i++) {
|
|
blk_name = va_arg(args, char*);
|
|
if (IS_ERR_OR_NULL(blk_name))
|
|
break;
|
|
|
|
blk_base = get_dump_blk_addr(blk_name);
|
|
if (blk_base && (index < blk_len)) {
|
|
blk_arr[index] = blk_base;
|
|
index++;
|
|
}
|
|
|
|
if (!strcmp(blk_name, "dbg_bus"))
|
|
dump_dbgbus = 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 */
|
|
mdss_dbg_xlog.work_panic = dead;
|
|
mdss_dbg_xlog.work_dbgbus = dump_dbgbus;
|
|
mdss_dbg_xlog.work_vbif_dbgbus = dump_vbif_dbgbus;
|
|
schedule_work(&mdss_dbg_xlog.xlog_dump_work);
|
|
} else {
|
|
mdss_xlog_dump_array(blk_arr, blk_len, dead, name, dump_dbgbus,
|
|
dump_vbif_dbgbus);
|
|
}
|
|
}
|
|
|
|
static int mdss_xlog_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;
|
|
}
|
|
|
|
static ssize_t mdss_xlog_dump_read(struct file *file, char __user *buff,
|
|
size_t count, loff_t *ppos)
|
|
{
|
|
ssize_t len = 0;
|
|
char xlog_buf[MDSS_XLOG_BUF_MAX];
|
|
|
|
if (__mdss_xlog_dump_calc_range()) {
|
|
len = mdss_xlog_dump_entry(xlog_buf, MDSS_XLOG_BUF_MAX);
|
|
if (copy_to_user(buff, xlog_buf, len))
|
|
return -EFAULT;
|
|
*ppos += len;
|
|
}
|
|
|
|
return len;
|
|
}
|
|
|
|
static ssize_t mdss_xlog_dump_write(struct file *file,
|
|
const char __user *user_buf, size_t count, loff_t *ppos)
|
|
{
|
|
mdss_dump_reg_all();
|
|
|
|
mdss_xlog_dump_all();
|
|
|
|
if (mdss_dbg_xlog.panic_on_err)
|
|
panic("mdss");
|
|
|
|
return count;
|
|
}
|
|
|
|
|
|
static const struct file_operations mdss_xlog_fops = {
|
|
.open = mdss_xlog_dump_open,
|
|
.read = mdss_xlog_dump_read,
|
|
.write = mdss_xlog_dump_write,
|
|
};
|
|
|
|
int mdss_create_xlog_debug(struct mdss_debug_data *mdd)
|
|
{
|
|
int i;
|
|
|
|
mdss_dbg_xlog.xlog = debugfs_create_dir("xlog", mdd->root);
|
|
if (IS_ERR_OR_NULL(mdss_dbg_xlog.xlog)) {
|
|
pr_err("debugfs_create_dir fail, error %ld\n",
|
|
PTR_ERR(mdss_dbg_xlog.xlog));
|
|
mdss_dbg_xlog.xlog = NULL;
|
|
return -ENODEV;
|
|
}
|
|
|
|
INIT_WORK(&mdss_dbg_xlog.xlog_dump_work, xlog_debug_work);
|
|
mdss_dbg_xlog.work_panic = false;
|
|
|
|
for (i = 0; i < MDSS_XLOG_ENTRY; i++)
|
|
mdss_dbg_xlog.logs[i].counter = i;
|
|
|
|
debugfs_create_file("dump", 0644, mdss_dbg_xlog.xlog, NULL,
|
|
&mdss_xlog_fops);
|
|
debugfs_create_u32("enable", 0644, mdss_dbg_xlog.xlog,
|
|
&mdss_dbg_xlog.xlog_enable);
|
|
debugfs_create_bool("panic", 0644, mdss_dbg_xlog.xlog,
|
|
&mdss_dbg_xlog.panic_on_err);
|
|
debugfs_create_u32("reg_dump", 0644, mdss_dbg_xlog.xlog,
|
|
&mdss_dbg_xlog.enable_reg_dump);
|
|
debugfs_create_u32("dbgbus_dump", 0644, mdss_dbg_xlog.xlog,
|
|
&mdss_dbg_xlog.enable_dbgbus_dump);
|
|
debugfs_create_u32("vbif_dbgbus_dump", 0644, mdss_dbg_xlog.xlog,
|
|
&mdss_dbg_xlog.enable_vbif_dbgbus_dump);
|
|
|
|
mdss_dbg_xlog.xlog_enable = XLOG_DEFAULT_ENABLE;
|
|
mdss_dbg_xlog.panic_on_err = XLOG_DEFAULT_PANIC;
|
|
mdss_dbg_xlog.enable_reg_dump = XLOG_DEFAULT_REGDUMP;
|
|
mdss_dbg_xlog.enable_dbgbus_dump = XLOG_DEFAULT_DBGBUSDUMP;
|
|
mdss_dbg_xlog.enable_vbif_dbgbus_dump = XLOG_DEFAULT_VBIF_DBGBUSDUMP;
|
|
|
|
pr_info("xlog_status: enable:%d, panic:%d, dump:%d\n",
|
|
mdss_dbg_xlog.xlog_enable, mdss_dbg_xlog.panic_on_err,
|
|
mdss_dbg_xlog.enable_reg_dump);
|
|
|
|
return 0;
|
|
}
|