Loading drivers/gpu/drm/msm/sde/sde_core_irq.c +15 −3 Original line number Diff line number Diff line Loading @@ -31,23 +31,35 @@ static void sde_core_irq_callback_handler(void *arg, int irq_idx) struct sde_irq *irq_obj = &sde_kms->irq_obj; struct sde_irq_callback *cb; unsigned long irq_flags; bool cb_tbl_error = false; int enable_counts = 0; pr_debug("irq_idx=%d\n", irq_idx); if (list_empty(&irq_obj->irq_cb_tbl[irq_idx])) SDE_ERROR("irq_idx=%d has no registered callback\n", irq_idx); spin_lock_irqsave(&sde_kms->irq_obj.cb_lock, irq_flags); if (list_empty(&irq_obj->irq_cb_tbl[irq_idx])) { /* print error outside lock */ cb_tbl_error = true; enable_counts = atomic_read( &sde_kms->irq_obj.enable_counts[irq_idx]); } atomic_inc(&irq_obj->irq_counts[irq_idx]); /* * Perform registered function callback */ spin_lock_irqsave(&sde_kms->irq_obj.cb_lock, irq_flags); list_for_each_entry(cb, &irq_obj->irq_cb_tbl[irq_idx], list) if (cb->func) cb->func(cb->arg, irq_idx); spin_unlock_irqrestore(&sde_kms->irq_obj.cb_lock, irq_flags); if (cb_tbl_error) { SDE_ERROR("irq has no registered callback, idx %d enables %d\n", irq_idx, enable_counts); SDE_EVT32_IRQ(irq_idx, enable_counts, SDE_EVTLOG_ERROR); } /* * Clear pending interrupt status in HW. * NOTE: sde_core_irq_callback_handler is protected by top-level Loading drivers/gpu/drm/msm/sde/sde_encoder.c +7 −1 Original line number Diff line number Diff line /* * Copyright (c) 2014-2017, The Linux Foundation. All rights reserved. * Copyright (c) 2014-2018, The Linux Foundation. All rights reserved. * Copyright (C) 2013 Red Hat * Author: Rob Clark <robdclark@gmail.com> * Loading Loading @@ -602,6 +602,12 @@ static void sde_encoder_underrun_callback(struct drm_encoder *drm_enc, SDE_ATRACE_BEGIN("encoder_underrun_callback"); atomic_inc(&phy_enc->underrun_cnt); SDE_EVT32(DRMID(drm_enc), atomic_read(&phy_enc->underrun_cnt)); trace_sde_encoder_underrun(DRMID(drm_enc), atomic_read(&phy_enc->underrun_cnt)); SDE_DBG_CTRL("stop_ftrace"); SDE_DBG_CTRL("panic_underrun"); SDE_ATRACE_END("encoder_underrun_callback"); } Loading drivers/gpu/drm/msm/sde/sde_trace.h +17 −1 Original line number Diff line number Diff line /* Copyright (c) 2014-2016, The Linux Foundation. All rights reserved. /* Copyright (c) 2014-2018, 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 Loading Loading @@ -125,6 +125,22 @@ TRACE_EVENT(sde_cmd_release_bw, TP_printk("crtc:%d", __entry->crtc_id) ); TRACE_EVENT(sde_encoder_underrun, TP_PROTO(u32 enc_id, u32 underrun_cnt), TP_ARGS(enc_id, underrun_cnt), TP_STRUCT__entry( __field(u32, enc_id) __field(u32, underrun_cnt) ), TP_fast_assign( __entry->enc_id = enc_id; __entry->underrun_cnt = underrun_cnt; ), TP_printk("enc:%d underrun_cnt:%d", __entry->enc_id, __entry->underrun_cnt) ); TRACE_EVENT(sde_mark_write, TP_PROTO(int pid, const char *name, bool trace_begin), TP_ARGS(pid, name, trace_begin), Loading drivers/gpu/drm/msm/sde_dbg.c +184 −11 Original line number Diff line number Diff line /* Copyright (c) 2009-2017, The Linux Foundation. All rights reserved. /* Copyright (c) 2009-2018, 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 Loading Loading @@ -57,6 +57,9 @@ /* print debug ranges in groups of 4 u32s */ #define REG_DUMP_ALIGN 16 #define DBG_CTRL_STOP_FTRACE BIT(0) #define DBG_CTRL_PANIC_UNDERRUN BIT(1) #define DBG_CTRL_MAX BIT(2) /** * struct sde_dbg_reg_offset - tracking for start and end of region Loading Loading @@ -162,6 +165,7 @@ struct sde_dbg_vbif_debug_bus { * @enable_reg_dump: whether to dump registers into memory, kernel log, or both * @dbgbus_sde: debug bus structure for the sde * @dbgbus_vbif_rt: debug bus structure for the realtime vbif * @dump_all: dump all entries in register dump */ static struct sde_dbg_base { struct sde_dbg_evtlog *evtlog; Loading @@ -180,6 +184,8 @@ static struct sde_dbg_base { struct sde_dbg_sde_debug_bus dbgbus_sde; struct sde_dbg_vbif_debug_bus dbgbus_vbif_rt; bool dump_all; u32 debugfs_ctrl; } sde_dbg_base; /* sde_dbg_base_evtlog - global pointer to main sde event log for macro use */ Loading Loading @@ -1448,7 +1454,7 @@ static void _sde_dbg_dump_vbif_dbg_bus(struct sde_dbg_vbif_debug_bus *bus) */ static void _sde_dump_array(struct sde_dbg_reg_base *blk_arr[], u32 len, bool do_panic, const char *name, bool dump_dbgbus_sde, bool dump_dbgbus_vbif_rt) bool dump_dbgbus_vbif_rt, bool dump_all) { int i; Loading @@ -1460,6 +1466,7 @@ static void _sde_dump_array(struct sde_dbg_reg_base *blk_arr[], sde_dbg_base.enable_reg_dump); } if (dump_all) sde_evtlog_dump_all(sde_dbg_base.evtlog); if (dump_dbgbus_sde) Loading @@ -1484,7 +1491,8 @@ static void _sde_dump_work(struct work_struct *work) ARRAY_SIZE(sde_dbg_base.req_dump_blks), sde_dbg_base.work_panic, "evtlog_workitem", sde_dbg_base.dbgbus_sde.cmn.include_in_deferred_work, sde_dbg_base.dbgbus_vbif_rt.cmn.include_in_deferred_work); sde_dbg_base.dbgbus_vbif_rt.cmn.include_in_deferred_work, sde_dbg_base.dump_all); } void sde_dbg_dump(bool queue_work, const char *name, ...) Loading @@ -1493,6 +1501,7 @@ void sde_dbg_dump(bool queue_work, const char *name, ...) bool do_panic = false; bool dump_dbgbus_sde = false; bool dump_dbgbus_vbif_rt = false; bool dump_all = false; va_list args; char *blk_name = NULL; struct sde_dbg_reg_base *blk_base = NULL; Loading @@ -1510,6 +1519,7 @@ void sde_dbg_dump(bool queue_work, const char *name, ...) memset(sde_dbg_base.req_dump_blks, 0, sizeof(sde_dbg_base.req_dump_blks)); sde_dbg_base.dump_all = false; va_start(args, name); i = 0; Loading @@ -1531,6 +1541,8 @@ void sde_dbg_dump(bool queue_work, const char *name, ...) blk_name); } } if (!strcmp(blk_name, "all")) dump_all = true; if (!strcmp(blk_name, "dbg_bus")) dump_dbgbus_sde = true; Loading @@ -1550,13 +1562,53 @@ void sde_dbg_dump(bool queue_work, const char *name, ...) dump_dbgbus_sde; sde_dbg_base.dbgbus_vbif_rt.cmn.include_in_deferred_work = dump_dbgbus_vbif_rt; sde_dbg_base.dump_all = dump_all; schedule_work(&sde_dbg_base.dump_work); } else { _sde_dump_array(blk_arr, blk_len, do_panic, name, dump_dbgbus_sde, dump_dbgbus_vbif_rt); dump_dbgbus_sde, dump_dbgbus_vbif_rt, dump_all); } } void sde_dbg_ctrl(const char *name, ...) { int i = 0; va_list args; char *blk_name = NULL; /* no debugfs controlled events are enabled, just return */ if (!sde_dbg_base.debugfs_ctrl) return; va_start(args, name); while ((blk_name = va_arg(args, char*))) { if (i++ >= SDE_EVTLOG_MAX_DATA) { pr_err("could not parse all dbg arguments\n"); break; } if (IS_ERR_OR_NULL(blk_name)) break; if (!strcmp(blk_name, "stop_ftrace") && sde_dbg_base.debugfs_ctrl & DBG_CTRL_STOP_FTRACE) { pr_debug("tracing off\n"); tracing_off(); } if (!strcmp(blk_name, "panic_underrun") && sde_dbg_base.debugfs_ctrl & DBG_CTRL_PANIC_UNDERRUN) { pr_debug("panic underrun\n"); panic("underrun"); } } } /* * sde_dbg_debugfs_open - debugfs open handler for evtlog dump * @inode: debugfs inode Loading @@ -1564,6 +1616,9 @@ void sde_dbg_dump(bool queue_work, const char *name, ...) */ static int sde_dbg_debugfs_open(struct inode *inode, struct file *file) { if (!inode || !file) return -EINVAL; /* non-seekable */ file->f_mode &= ~(FMODE_LSEEK | FMODE_PREAD | FMODE_PWRITE); file->private_data = inode->i_private; Loading @@ -1583,8 +1638,11 @@ static ssize_t sde_evtlog_dump_read(struct file *file, char __user *buff, ssize_t len = 0; char evtlog_buf[SDE_EVTLOG_BUF_MAX]; if (!buff || !ppos) return -EINVAL; len = sde_evtlog_dump_to_buffer(sde_dbg_base.evtlog, evtlog_buf, SDE_EVTLOG_BUF_MAX); SDE_EVTLOG_BUF_MAX, true); if (copy_to_user(buff, evtlog_buf, len)) return -EFAULT; *ppos += len; Loading Loading @@ -1621,6 +1679,82 @@ static const struct file_operations sde_evtlog_fops = { .write = sde_evtlog_dump_write, }; /** * sde_dbg_ctrl_read - debugfs read handler for debug ctrl read * @file: file handler * @buff: user buffer content for debugfs * @count: size of user buffer * @ppos: position offset of user buffer */ static ssize_t sde_dbg_ctrl_read(struct file *file, char __user *buff, size_t count, loff_t *ppos) { ssize_t len = 0; char buf[24] = {'\0'}; if (!buff || !ppos) return -EINVAL; if (*ppos) return 0; /* the end */ len = snprintf(buf, sizeof(buf), "0x%x\n", sde_dbg_base.debugfs_ctrl); pr_debug("%s: ctrl:0x%x len:0x%zx\n", __func__, sde_dbg_base.debugfs_ctrl, len); if ((count < sizeof(buf)) || copy_to_user(buff, buf, len)) { pr_err("error copying the buffer! count:0x%zx\n", count); return -EFAULT; } *ppos += len; /* increase offset */ return len; } /** * sde_dbg_ctrl_write - debugfs read handler for debug ctrl write * @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_dbg_ctrl_write(struct file *file, const char __user *user_buf, size_t count, loff_t *ppos) { u32 dbg_ctrl = 0; char buf[24]; if (!file) { pr_err("DbgDbg: %s: error no file --\n", __func__); return -EINVAL; } if (count >= sizeof(buf)) return -EFAULT; if (copy_from_user(buf, user_buf, count)) return -EFAULT; buf[count] = 0; /* end of string */ if (kstrtouint(buf, 0, &dbg_ctrl)) { pr_err("%s: error in the number of bytes\n", __func__); return -EFAULT; } pr_debug("dbg_ctrl_read:0x%x\n", dbg_ctrl); sde_dbg_base.debugfs_ctrl = dbg_ctrl; return count; } static const struct file_operations sde_dbg_ctrl_fops = { .open = sde_dbg_debugfs_open, .read = sde_dbg_ctrl_read, .write = sde_dbg_ctrl_write, }; void sde_dbg_init_dbg_buses(u32 hwversion) { static struct sde_dbg_base *dbg = &sde_dbg_base; Loading Loading @@ -1695,6 +1829,8 @@ int sde_dbg_init(struct dentry *debugfs_root, struct device *dev, for (i = 0; i < SDE_EVTLOG_ENTRY; i++) sde_dbg_base.evtlog->logs[i].counter = i; debugfs_create_file("dbg_ctrl", 0600, sde_dbg_base.root, NULL, &sde_dbg_ctrl_fops); debugfs_create_file("dump", 0600, sde_dbg_base.root, NULL, &sde_evtlog_fops); debugfs_create_u32("enable", 0600, sde_dbg_base.root, Loading Loading @@ -1736,7 +1872,14 @@ void sde_dbg_destroy(void) */ static int sde_dbg_reg_base_release(struct inode *inode, struct file *file) { struct sde_dbg_reg_base *dbg = file->private_data; struct sde_dbg_reg_base *dbg; if (!file) return -EINVAL; dbg = file->private_data; if (!dbg) return -ENODEV; mutex_lock(&sde_dbg_base.mutex); if (dbg && dbg->buf) { Loading @@ -1760,12 +1903,16 @@ static int sde_dbg_reg_base_release(struct inode *inode, struct file *file) static ssize_t sde_dbg_reg_base_offset_write(struct file *file, const char __user *user_buf, size_t count, loff_t *ppos) { struct sde_dbg_reg_base *dbg = file->private_data; struct sde_dbg_reg_base *dbg; u32 off = 0; u32 cnt = DEFAULT_BASE_REG_CNT; char buf[24]; ssize_t rc = count; if (!file) return -EINVAL; dbg = file->private_data; if (!dbg) return -ENODEV; Loading Loading @@ -1799,6 +1946,9 @@ static ssize_t sde_dbg_reg_base_offset_write(struct file *file, goto exit; } if (cnt == 0) return -EINVAL; dbg->off = off; dbg->cnt = cnt; Loading @@ -1819,17 +1969,29 @@ exit: static ssize_t sde_dbg_reg_base_offset_read(struct file *file, char __user *buff, size_t count, loff_t *ppos) { struct sde_dbg_reg_base *dbg = file->private_data; struct sde_dbg_reg_base *dbg; int len = 0; char buf[24] = {'\0'}; if (!file) return -EINVAL; dbg = file->private_data; if (!dbg) return -ENODEV; if (!ppos) return -EINVAL; if (*ppos) return 0; /* the end */ mutex_lock(&sde_dbg_base.mutex); if (dbg->off % sizeof(u32)) { mutex_unlock(&sde_dbg_base.mutex); return -EFAULT; } len = snprintf(buf, sizeof(buf), "0x%08zx %zx\n", dbg->off, dbg->cnt); if (len < 0 || len >= sizeof(buf)) { mutex_unlock(&sde_dbg_base.mutex); Loading Loading @@ -1857,11 +2019,15 @@ static ssize_t sde_dbg_reg_base_offset_read(struct file *file, static ssize_t sde_dbg_reg_base_reg_write(struct file *file, const char __user *user_buf, size_t count, loff_t *ppos) { struct sde_dbg_reg_base *dbg = file->private_data; struct sde_dbg_reg_base *dbg; size_t off; u32 data, cnt; char buf[24]; if (!file) return -EINVAL; dbg = file->private_data; if (!dbg) return -ENODEV; Loading Loading @@ -1907,14 +2073,21 @@ static ssize_t sde_dbg_reg_base_reg_write(struct file *file, static ssize_t sde_dbg_reg_base_reg_read(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) { struct sde_dbg_reg_base *dbg = file->private_data; struct sde_dbg_reg_base *dbg; size_t len; if (!file) return -EINVAL; dbg = file->private_data; if (!dbg) { pr_err("invalid handle\n"); return -ENODEV; } if (!ppos) return -EINVAL; mutex_lock(&sde_dbg_base.mutex); if (!dbg->buf) { char *hwbuf; Loading drivers/gpu/drm/msm/sde_dbg.h +30 −5 Original line number Diff line number Diff line /* Copyright (c) 2016-2017, The Linux Foundation. All rights reserved. /* Copyright (c) 2016-2018, 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 Loading @@ -17,9 +17,10 @@ #include <linux/debugfs.h> #include <linux/list.h> #define SDE_EVTLOG_DATA_LIMITER (-1) #define SDE_EVTLOG_DATA_LIMITER (0xC0DEBEEF) #define SDE_EVTLOG_FUNC_ENTRY 0x1111 #define SDE_EVTLOG_FUNC_EXIT 0x2222 #define SDE_EVTLOG_ERROR 0xebad #define SDE_DBG_DUMP_DATA_LIMITER (NULL) Loading Loading @@ -52,7 +53,7 @@ enum sde_dbg_dump_flag { * number must be greater than print entry to prevent out of bound evtlog * entry array access. */ #define SDE_EVTLOG_ENTRY (SDE_EVTLOG_PRINT_ENTRY * 4) #define SDE_EVTLOG_ENTRY (SDE_EVTLOG_PRINT_ENTRY * 8) #define SDE_EVTLOG_MAX_DATA 15 #define SDE_EVTLOG_BUF_MAX 512 #define SDE_EVTLOG_BUF_ALIGN 32 Loading @@ -77,6 +78,7 @@ struct sde_dbg_evtlog { struct sde_dbg_evtlog_log logs[SDE_EVTLOG_ENTRY]; u32 first; u32 last; u32 last_dump; u32 curr; u32 next; u32 enable; Loading Loading @@ -123,6 +125,13 @@ extern struct sde_dbg_evtlog *sde_dbg_base_evtlog; #define SDE_DBG_DUMP_WQ(...) sde_dbg_dump(true, __func__, ##__VA_ARGS__, \ SDE_DBG_DUMP_DATA_LIMITER) /** * SDE_DBG_EVT_CTRL - trigger a different driver events * event: event that trigger different behavior in the driver */ #define SDE_DBG_CTRL(...) sde_dbg_ctrl(__func__, ##__VA_ARGS__, \ SDE_DBG_DUMP_DATA_LIMITER) #if defined(CONFIG_DEBUG_FS) /** Loading Loading @@ -172,10 +181,12 @@ bool sde_evtlog_is_enabled(struct sde_dbg_evtlog *evtlog, u32 flag); * @evtlog: pointer to evtlog * @evtlog_buf: target buffer to print into * @evtlog_buf_size: size of target buffer * @update_last_entry:» whether or not to stop at most recent entry * Returns: number of bytes written to buffer */ ssize_t sde_evtlog_dump_to_buffer(struct sde_dbg_evtlog *evtlog, char *evtlog_buf, ssize_t evtlog_buf_size); char *evtlog_buf, ssize_t evtlog_buf_size, bool update_last_entry); /** * sde_dbg_init_dbg_buses - initialize debug bus dumping support for the chipset Loading Loading @@ -213,6 +224,15 @@ void sde_dbg_destroy(void); */ void sde_dbg_dump(bool queue_work, const char *name, ...); /** * sde_dbg_ctrl - trigger specific actions for the driver with debugging * purposes. Those actions need to be enabled by the debugfs entry * so the driver executes those actions in the corresponding calls. * @va_args: list of actions to trigger * Returns: none */ void sde_dbg_ctrl(const char *name, ...); /** * sde_dbg_reg_register_base - register a hw register address section for later * dumping. call this before calling sde_dbg_reg_register_dump_range Loading Loading @@ -272,7 +292,8 @@ static inline bool sde_evtlog_is_enabled(struct sde_dbg_evtlog *evtlog, } static inline ssize_t sde_evtlog_dump_to_buffer(struct sde_dbg_evtlog *evtlog, char *evtlog_buf, ssize_t evtlog_buf_size) char *evtlog_buf, ssize_t evtlog_buf_size, bool update_last_entry) { return 0; } Loading @@ -295,6 +316,10 @@ static inline void sde_dbg_dump(bool queue_work, const char *name, ...) { } static inline void sde_dbg_ctrl(const char *name, ...) { } static inline int sde_dbg_reg_register_base(const char *name, void __iomem *base, size_t max_offset) { Loading Loading
drivers/gpu/drm/msm/sde/sde_core_irq.c +15 −3 Original line number Diff line number Diff line Loading @@ -31,23 +31,35 @@ static void sde_core_irq_callback_handler(void *arg, int irq_idx) struct sde_irq *irq_obj = &sde_kms->irq_obj; struct sde_irq_callback *cb; unsigned long irq_flags; bool cb_tbl_error = false; int enable_counts = 0; pr_debug("irq_idx=%d\n", irq_idx); if (list_empty(&irq_obj->irq_cb_tbl[irq_idx])) SDE_ERROR("irq_idx=%d has no registered callback\n", irq_idx); spin_lock_irqsave(&sde_kms->irq_obj.cb_lock, irq_flags); if (list_empty(&irq_obj->irq_cb_tbl[irq_idx])) { /* print error outside lock */ cb_tbl_error = true; enable_counts = atomic_read( &sde_kms->irq_obj.enable_counts[irq_idx]); } atomic_inc(&irq_obj->irq_counts[irq_idx]); /* * Perform registered function callback */ spin_lock_irqsave(&sde_kms->irq_obj.cb_lock, irq_flags); list_for_each_entry(cb, &irq_obj->irq_cb_tbl[irq_idx], list) if (cb->func) cb->func(cb->arg, irq_idx); spin_unlock_irqrestore(&sde_kms->irq_obj.cb_lock, irq_flags); if (cb_tbl_error) { SDE_ERROR("irq has no registered callback, idx %d enables %d\n", irq_idx, enable_counts); SDE_EVT32_IRQ(irq_idx, enable_counts, SDE_EVTLOG_ERROR); } /* * Clear pending interrupt status in HW. * NOTE: sde_core_irq_callback_handler is protected by top-level Loading
drivers/gpu/drm/msm/sde/sde_encoder.c +7 −1 Original line number Diff line number Diff line /* * Copyright (c) 2014-2017, The Linux Foundation. All rights reserved. * Copyright (c) 2014-2018, The Linux Foundation. All rights reserved. * Copyright (C) 2013 Red Hat * Author: Rob Clark <robdclark@gmail.com> * Loading Loading @@ -602,6 +602,12 @@ static void sde_encoder_underrun_callback(struct drm_encoder *drm_enc, SDE_ATRACE_BEGIN("encoder_underrun_callback"); atomic_inc(&phy_enc->underrun_cnt); SDE_EVT32(DRMID(drm_enc), atomic_read(&phy_enc->underrun_cnt)); trace_sde_encoder_underrun(DRMID(drm_enc), atomic_read(&phy_enc->underrun_cnt)); SDE_DBG_CTRL("stop_ftrace"); SDE_DBG_CTRL("panic_underrun"); SDE_ATRACE_END("encoder_underrun_callback"); } Loading
drivers/gpu/drm/msm/sde/sde_trace.h +17 −1 Original line number Diff line number Diff line /* Copyright (c) 2014-2016, The Linux Foundation. All rights reserved. /* Copyright (c) 2014-2018, 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 Loading Loading @@ -125,6 +125,22 @@ TRACE_EVENT(sde_cmd_release_bw, TP_printk("crtc:%d", __entry->crtc_id) ); TRACE_EVENT(sde_encoder_underrun, TP_PROTO(u32 enc_id, u32 underrun_cnt), TP_ARGS(enc_id, underrun_cnt), TP_STRUCT__entry( __field(u32, enc_id) __field(u32, underrun_cnt) ), TP_fast_assign( __entry->enc_id = enc_id; __entry->underrun_cnt = underrun_cnt; ), TP_printk("enc:%d underrun_cnt:%d", __entry->enc_id, __entry->underrun_cnt) ); TRACE_EVENT(sde_mark_write, TP_PROTO(int pid, const char *name, bool trace_begin), TP_ARGS(pid, name, trace_begin), Loading
drivers/gpu/drm/msm/sde_dbg.c +184 −11 Original line number Diff line number Diff line /* Copyright (c) 2009-2017, The Linux Foundation. All rights reserved. /* Copyright (c) 2009-2018, 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 Loading Loading @@ -57,6 +57,9 @@ /* print debug ranges in groups of 4 u32s */ #define REG_DUMP_ALIGN 16 #define DBG_CTRL_STOP_FTRACE BIT(0) #define DBG_CTRL_PANIC_UNDERRUN BIT(1) #define DBG_CTRL_MAX BIT(2) /** * struct sde_dbg_reg_offset - tracking for start and end of region Loading Loading @@ -162,6 +165,7 @@ struct sde_dbg_vbif_debug_bus { * @enable_reg_dump: whether to dump registers into memory, kernel log, or both * @dbgbus_sde: debug bus structure for the sde * @dbgbus_vbif_rt: debug bus structure for the realtime vbif * @dump_all: dump all entries in register dump */ static struct sde_dbg_base { struct sde_dbg_evtlog *evtlog; Loading @@ -180,6 +184,8 @@ static struct sde_dbg_base { struct sde_dbg_sde_debug_bus dbgbus_sde; struct sde_dbg_vbif_debug_bus dbgbus_vbif_rt; bool dump_all; u32 debugfs_ctrl; } sde_dbg_base; /* sde_dbg_base_evtlog - global pointer to main sde event log for macro use */ Loading Loading @@ -1448,7 +1454,7 @@ static void _sde_dbg_dump_vbif_dbg_bus(struct sde_dbg_vbif_debug_bus *bus) */ static void _sde_dump_array(struct sde_dbg_reg_base *blk_arr[], u32 len, bool do_panic, const char *name, bool dump_dbgbus_sde, bool dump_dbgbus_vbif_rt) bool dump_dbgbus_vbif_rt, bool dump_all) { int i; Loading @@ -1460,6 +1466,7 @@ static void _sde_dump_array(struct sde_dbg_reg_base *blk_arr[], sde_dbg_base.enable_reg_dump); } if (dump_all) sde_evtlog_dump_all(sde_dbg_base.evtlog); if (dump_dbgbus_sde) Loading @@ -1484,7 +1491,8 @@ static void _sde_dump_work(struct work_struct *work) ARRAY_SIZE(sde_dbg_base.req_dump_blks), sde_dbg_base.work_panic, "evtlog_workitem", sde_dbg_base.dbgbus_sde.cmn.include_in_deferred_work, sde_dbg_base.dbgbus_vbif_rt.cmn.include_in_deferred_work); sde_dbg_base.dbgbus_vbif_rt.cmn.include_in_deferred_work, sde_dbg_base.dump_all); } void sde_dbg_dump(bool queue_work, const char *name, ...) Loading @@ -1493,6 +1501,7 @@ void sde_dbg_dump(bool queue_work, const char *name, ...) bool do_panic = false; bool dump_dbgbus_sde = false; bool dump_dbgbus_vbif_rt = false; bool dump_all = false; va_list args; char *blk_name = NULL; struct sde_dbg_reg_base *blk_base = NULL; Loading @@ -1510,6 +1519,7 @@ void sde_dbg_dump(bool queue_work, const char *name, ...) memset(sde_dbg_base.req_dump_blks, 0, sizeof(sde_dbg_base.req_dump_blks)); sde_dbg_base.dump_all = false; va_start(args, name); i = 0; Loading @@ -1531,6 +1541,8 @@ void sde_dbg_dump(bool queue_work, const char *name, ...) blk_name); } } if (!strcmp(blk_name, "all")) dump_all = true; if (!strcmp(blk_name, "dbg_bus")) dump_dbgbus_sde = true; Loading @@ -1550,13 +1562,53 @@ void sde_dbg_dump(bool queue_work, const char *name, ...) dump_dbgbus_sde; sde_dbg_base.dbgbus_vbif_rt.cmn.include_in_deferred_work = dump_dbgbus_vbif_rt; sde_dbg_base.dump_all = dump_all; schedule_work(&sde_dbg_base.dump_work); } else { _sde_dump_array(blk_arr, blk_len, do_panic, name, dump_dbgbus_sde, dump_dbgbus_vbif_rt); dump_dbgbus_sde, dump_dbgbus_vbif_rt, dump_all); } } void sde_dbg_ctrl(const char *name, ...) { int i = 0; va_list args; char *blk_name = NULL; /* no debugfs controlled events are enabled, just return */ if (!sde_dbg_base.debugfs_ctrl) return; va_start(args, name); while ((blk_name = va_arg(args, char*))) { if (i++ >= SDE_EVTLOG_MAX_DATA) { pr_err("could not parse all dbg arguments\n"); break; } if (IS_ERR_OR_NULL(blk_name)) break; if (!strcmp(blk_name, "stop_ftrace") && sde_dbg_base.debugfs_ctrl & DBG_CTRL_STOP_FTRACE) { pr_debug("tracing off\n"); tracing_off(); } if (!strcmp(blk_name, "panic_underrun") && sde_dbg_base.debugfs_ctrl & DBG_CTRL_PANIC_UNDERRUN) { pr_debug("panic underrun\n"); panic("underrun"); } } } /* * sde_dbg_debugfs_open - debugfs open handler for evtlog dump * @inode: debugfs inode Loading @@ -1564,6 +1616,9 @@ void sde_dbg_dump(bool queue_work, const char *name, ...) */ static int sde_dbg_debugfs_open(struct inode *inode, struct file *file) { if (!inode || !file) return -EINVAL; /* non-seekable */ file->f_mode &= ~(FMODE_LSEEK | FMODE_PREAD | FMODE_PWRITE); file->private_data = inode->i_private; Loading @@ -1583,8 +1638,11 @@ static ssize_t sde_evtlog_dump_read(struct file *file, char __user *buff, ssize_t len = 0; char evtlog_buf[SDE_EVTLOG_BUF_MAX]; if (!buff || !ppos) return -EINVAL; len = sde_evtlog_dump_to_buffer(sde_dbg_base.evtlog, evtlog_buf, SDE_EVTLOG_BUF_MAX); SDE_EVTLOG_BUF_MAX, true); if (copy_to_user(buff, evtlog_buf, len)) return -EFAULT; *ppos += len; Loading Loading @@ -1621,6 +1679,82 @@ static const struct file_operations sde_evtlog_fops = { .write = sde_evtlog_dump_write, }; /** * sde_dbg_ctrl_read - debugfs read handler for debug ctrl read * @file: file handler * @buff: user buffer content for debugfs * @count: size of user buffer * @ppos: position offset of user buffer */ static ssize_t sde_dbg_ctrl_read(struct file *file, char __user *buff, size_t count, loff_t *ppos) { ssize_t len = 0; char buf[24] = {'\0'}; if (!buff || !ppos) return -EINVAL; if (*ppos) return 0; /* the end */ len = snprintf(buf, sizeof(buf), "0x%x\n", sde_dbg_base.debugfs_ctrl); pr_debug("%s: ctrl:0x%x len:0x%zx\n", __func__, sde_dbg_base.debugfs_ctrl, len); if ((count < sizeof(buf)) || copy_to_user(buff, buf, len)) { pr_err("error copying the buffer! count:0x%zx\n", count); return -EFAULT; } *ppos += len; /* increase offset */ return len; } /** * sde_dbg_ctrl_write - debugfs read handler for debug ctrl write * @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_dbg_ctrl_write(struct file *file, const char __user *user_buf, size_t count, loff_t *ppos) { u32 dbg_ctrl = 0; char buf[24]; if (!file) { pr_err("DbgDbg: %s: error no file --\n", __func__); return -EINVAL; } if (count >= sizeof(buf)) return -EFAULT; if (copy_from_user(buf, user_buf, count)) return -EFAULT; buf[count] = 0; /* end of string */ if (kstrtouint(buf, 0, &dbg_ctrl)) { pr_err("%s: error in the number of bytes\n", __func__); return -EFAULT; } pr_debug("dbg_ctrl_read:0x%x\n", dbg_ctrl); sde_dbg_base.debugfs_ctrl = dbg_ctrl; return count; } static const struct file_operations sde_dbg_ctrl_fops = { .open = sde_dbg_debugfs_open, .read = sde_dbg_ctrl_read, .write = sde_dbg_ctrl_write, }; void sde_dbg_init_dbg_buses(u32 hwversion) { static struct sde_dbg_base *dbg = &sde_dbg_base; Loading Loading @@ -1695,6 +1829,8 @@ int sde_dbg_init(struct dentry *debugfs_root, struct device *dev, for (i = 0; i < SDE_EVTLOG_ENTRY; i++) sde_dbg_base.evtlog->logs[i].counter = i; debugfs_create_file("dbg_ctrl", 0600, sde_dbg_base.root, NULL, &sde_dbg_ctrl_fops); debugfs_create_file("dump", 0600, sde_dbg_base.root, NULL, &sde_evtlog_fops); debugfs_create_u32("enable", 0600, sde_dbg_base.root, Loading Loading @@ -1736,7 +1872,14 @@ void sde_dbg_destroy(void) */ static int sde_dbg_reg_base_release(struct inode *inode, struct file *file) { struct sde_dbg_reg_base *dbg = file->private_data; struct sde_dbg_reg_base *dbg; if (!file) return -EINVAL; dbg = file->private_data; if (!dbg) return -ENODEV; mutex_lock(&sde_dbg_base.mutex); if (dbg && dbg->buf) { Loading @@ -1760,12 +1903,16 @@ static int sde_dbg_reg_base_release(struct inode *inode, struct file *file) static ssize_t sde_dbg_reg_base_offset_write(struct file *file, const char __user *user_buf, size_t count, loff_t *ppos) { struct sde_dbg_reg_base *dbg = file->private_data; struct sde_dbg_reg_base *dbg; u32 off = 0; u32 cnt = DEFAULT_BASE_REG_CNT; char buf[24]; ssize_t rc = count; if (!file) return -EINVAL; dbg = file->private_data; if (!dbg) return -ENODEV; Loading Loading @@ -1799,6 +1946,9 @@ static ssize_t sde_dbg_reg_base_offset_write(struct file *file, goto exit; } if (cnt == 0) return -EINVAL; dbg->off = off; dbg->cnt = cnt; Loading @@ -1819,17 +1969,29 @@ exit: static ssize_t sde_dbg_reg_base_offset_read(struct file *file, char __user *buff, size_t count, loff_t *ppos) { struct sde_dbg_reg_base *dbg = file->private_data; struct sde_dbg_reg_base *dbg; int len = 0; char buf[24] = {'\0'}; if (!file) return -EINVAL; dbg = file->private_data; if (!dbg) return -ENODEV; if (!ppos) return -EINVAL; if (*ppos) return 0; /* the end */ mutex_lock(&sde_dbg_base.mutex); if (dbg->off % sizeof(u32)) { mutex_unlock(&sde_dbg_base.mutex); return -EFAULT; } len = snprintf(buf, sizeof(buf), "0x%08zx %zx\n", dbg->off, dbg->cnt); if (len < 0 || len >= sizeof(buf)) { mutex_unlock(&sde_dbg_base.mutex); Loading Loading @@ -1857,11 +2019,15 @@ static ssize_t sde_dbg_reg_base_offset_read(struct file *file, static ssize_t sde_dbg_reg_base_reg_write(struct file *file, const char __user *user_buf, size_t count, loff_t *ppos) { struct sde_dbg_reg_base *dbg = file->private_data; struct sde_dbg_reg_base *dbg; size_t off; u32 data, cnt; char buf[24]; if (!file) return -EINVAL; dbg = file->private_data; if (!dbg) return -ENODEV; Loading Loading @@ -1907,14 +2073,21 @@ static ssize_t sde_dbg_reg_base_reg_write(struct file *file, static ssize_t sde_dbg_reg_base_reg_read(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) { struct sde_dbg_reg_base *dbg = file->private_data; struct sde_dbg_reg_base *dbg; size_t len; if (!file) return -EINVAL; dbg = file->private_data; if (!dbg) { pr_err("invalid handle\n"); return -ENODEV; } if (!ppos) return -EINVAL; mutex_lock(&sde_dbg_base.mutex); if (!dbg->buf) { char *hwbuf; Loading
drivers/gpu/drm/msm/sde_dbg.h +30 −5 Original line number Diff line number Diff line /* Copyright (c) 2016-2017, The Linux Foundation. All rights reserved. /* Copyright (c) 2016-2018, 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 Loading @@ -17,9 +17,10 @@ #include <linux/debugfs.h> #include <linux/list.h> #define SDE_EVTLOG_DATA_LIMITER (-1) #define SDE_EVTLOG_DATA_LIMITER (0xC0DEBEEF) #define SDE_EVTLOG_FUNC_ENTRY 0x1111 #define SDE_EVTLOG_FUNC_EXIT 0x2222 #define SDE_EVTLOG_ERROR 0xebad #define SDE_DBG_DUMP_DATA_LIMITER (NULL) Loading Loading @@ -52,7 +53,7 @@ enum sde_dbg_dump_flag { * number must be greater than print entry to prevent out of bound evtlog * entry array access. */ #define SDE_EVTLOG_ENTRY (SDE_EVTLOG_PRINT_ENTRY * 4) #define SDE_EVTLOG_ENTRY (SDE_EVTLOG_PRINT_ENTRY * 8) #define SDE_EVTLOG_MAX_DATA 15 #define SDE_EVTLOG_BUF_MAX 512 #define SDE_EVTLOG_BUF_ALIGN 32 Loading @@ -77,6 +78,7 @@ struct sde_dbg_evtlog { struct sde_dbg_evtlog_log logs[SDE_EVTLOG_ENTRY]; u32 first; u32 last; u32 last_dump; u32 curr; u32 next; u32 enable; Loading Loading @@ -123,6 +125,13 @@ extern struct sde_dbg_evtlog *sde_dbg_base_evtlog; #define SDE_DBG_DUMP_WQ(...) sde_dbg_dump(true, __func__, ##__VA_ARGS__, \ SDE_DBG_DUMP_DATA_LIMITER) /** * SDE_DBG_EVT_CTRL - trigger a different driver events * event: event that trigger different behavior in the driver */ #define SDE_DBG_CTRL(...) sde_dbg_ctrl(__func__, ##__VA_ARGS__, \ SDE_DBG_DUMP_DATA_LIMITER) #if defined(CONFIG_DEBUG_FS) /** Loading Loading @@ -172,10 +181,12 @@ bool sde_evtlog_is_enabled(struct sde_dbg_evtlog *evtlog, u32 flag); * @evtlog: pointer to evtlog * @evtlog_buf: target buffer to print into * @evtlog_buf_size: size of target buffer * @update_last_entry:» whether or not to stop at most recent entry * Returns: number of bytes written to buffer */ ssize_t sde_evtlog_dump_to_buffer(struct sde_dbg_evtlog *evtlog, char *evtlog_buf, ssize_t evtlog_buf_size); char *evtlog_buf, ssize_t evtlog_buf_size, bool update_last_entry); /** * sde_dbg_init_dbg_buses - initialize debug bus dumping support for the chipset Loading Loading @@ -213,6 +224,15 @@ void sde_dbg_destroy(void); */ void sde_dbg_dump(bool queue_work, const char *name, ...); /** * sde_dbg_ctrl - trigger specific actions for the driver with debugging * purposes. Those actions need to be enabled by the debugfs entry * so the driver executes those actions in the corresponding calls. * @va_args: list of actions to trigger * Returns: none */ void sde_dbg_ctrl(const char *name, ...); /** * sde_dbg_reg_register_base - register a hw register address section for later * dumping. call this before calling sde_dbg_reg_register_dump_range Loading Loading @@ -272,7 +292,8 @@ static inline bool sde_evtlog_is_enabled(struct sde_dbg_evtlog *evtlog, } static inline ssize_t sde_evtlog_dump_to_buffer(struct sde_dbg_evtlog *evtlog, char *evtlog_buf, ssize_t evtlog_buf_size) char *evtlog_buf, ssize_t evtlog_buf_size, bool update_last_entry) { return 0; } Loading @@ -295,6 +316,10 @@ static inline void sde_dbg_dump(bool queue_work, const char *name, ...) { } static inline void sde_dbg_ctrl(const char *name, ...) { } static inline int sde_dbg_reg_register_base(const char *name, void __iomem *base, size_t max_offset) { Loading