// SPDX-License-Identifier: GPL-2.0 /* * Copyright (c) 2020 MediaTek Inc. */ #include #include #include #include "vpu_cfg.h" #include "vpu_cmn.h" #include "vpu_algo.h" #include "vpu_debug.h" #include "vpu_hw.h" #include "vpu_power.h" #include "vpu_cmd.h" #include "apu_tags.h" #include "vpu_reg.h" u32 vpu_klog; const char *g_vpu_prop_type_names[VPU_NUM_PROP_TYPES] = { [VPU_PROP_TYPE_CHAR] = "char", [VPU_PROP_TYPE_INT32] = "int32", [VPU_PROP_TYPE_FLOAT] = "float", [VPU_PROP_TYPE_INT64] = "int64", [VPU_PROP_TYPE_DOUBLE] = "double" }; const char *g_vpu_port_usage_names[VPU_NUM_PORT_USAGES] = { [VPU_PORT_USAGE_IMAGE] = "image", [VPU_PORT_USAGE_DATA] = "data", }; const char *g_vpu_port_dir_names[VPU_NUM_PORT_DIRS] = { [VPU_PORT_DIR_IN] = "in", [VPU_PORT_DIR_OUT] = "out", [VPU_PORT_DIR_IN_OUT] = "in-out", }; struct name_tag { uint32_t t; /* tag */ const char *n; /* name */ }; /* driver state, defined at vpu_cmn.h */ #define VS(a) { VS_##a, #a } static const struct name_tag vs_str[] = { VS(UNKNOWN), VS(DISALBED), VS(DOWN), VS(UP), VS(BOOT), VS(IDLE), VS(CMD_ALG), VS(CMD_D2D), VS(CMD_D2D_EXT), VS(REMOVING), {0, NULL} }; #undef VS /* device state, defined at vpu_reg.h */ #define DS(a) { DS_##a, #a } static const struct name_tag ds_str[] = { DS(DSP_RDY), DS(DBG_RDY), DS(ALG_RDY), DS(ALG_DONE), DS(ALG_GOT), DS(PREEMPT_RDY), DS(PREEMPT_DONE), DS(FTRACE_RDY), {0, NULL} }; #undef DS /* command code, defined at vpu_reg.h */ #define VC(a) { VPU_CMD_##a, #a } static const struct name_tag vc_str[] = { VC(DO_EXIT), VC(DO_LOADER), VC(DO_D2D), VC(DO_D2D_EXT), VC(SET_DEBUG), VC(SET_FTRACE_LOG), VC(DO_D2D_EXT_TEST), {0, NULL} }; #undef VC static int vpu_debug_info(struct seq_file *s); /** * vpu_seq_time() - print time to seq_file * * @time: schedule clock time got from sched_clock() */ void vpu_seq_time(struct seq_file *s, uint64_t t) { uint32_t nsec; if (!t) { seq_puts(s, "N/A"); return; } nsec = do_div(t, 1000000000); seq_printf(s, "%lu.%06lu", (unsigned long)t, (unsigned long)nsec/1000); } int vpu_debug_state_seq(struct seq_file *s, uint32_t vs, uint32_t ds, int b) { int i, j; if (!s) return -ENOENT; for (i = 0; vs_str[i].n && (vs != vs_str[i].t); i++) ; seq_printf(s, "driver state: %d (%s)\n", vs, vs_str[i].n ? vs_str[i].n : "unknown"); seq_printf(s, "device state: %xh: ", ds); for (i = 0, j = 0; ds_str[i].n; i++) { if (ds & ds_str[i].t) seq_printf(s, "%s%s", j++ ? "," : "", ds_str[i].n); } seq_puts(s, "\n"); seq_puts(s, "boost: "); vpu_seq_boost(s, b); seq_puts(s, "\n"); return 0; } static inline int vpu_debug_state_dev(struct seq_file *s, struct vpu_device *vd) { return vpu_debug_state_seq(s, vd->state, vd->dev_state, atomic_read(&vd->pw_boost)); } /** * vpu_debug_cmd_entry_seq -Show command control of given priority * * @s: pointer to seq_file * @c: pointer to command control * @prio: priority * * Returns the execution count of given priority. */ static uint64_t vpu_debug_cmd_entry_seq(struct seq_file *s, struct vpu_cmd_ctl *c, int prio) { unsigned int i; seq_printf(s, "priority %d: #%llu: ", prio, c->exe_cnt); for (i = 0; vc_str[i].n; i++) { if (c->cmd == vc_str[i].t) { seq_printf(s, "%s: ", vc_str[i].n); goto algo; } } seq_printf(s, "(unknown cmd 0x%x): ", c->cmd); algo: seq_printf(s, "algorithm: %s, boost: ", c->alg ? c->alg->a.name : "(none)"); vpu_seq_boost(s, c->boost); seq_puts(s, ", start: "); vpu_seq_time(s, c->start_t); seq_puts(s, ", end: "); vpu_seq_time(s, c->end_t); seq_printf(s, ", done: %d, result: 0x%x\n", c->done, c->result); return c->exe_cnt; } int vpu_debug_cmd_seq(struct seq_file *s, struct vpu_device *vd, int prio, int prio_max, int active, struct vpu_cmd_ctl *c, uint64_t timeout) { int i; uint64_t exe_cnt; uint64_t preempt_cnt = 0; seq_printf(s, "priority: current: %d (highest: %d)\n", prio, prio_max - 1); seq_printf(s, "timeout setting: %llu ms\n", timeout); seq_printf(s, "number of active commands: %d\n", active); if (prio_max > VPU_MAX_PRIORITY) prio_max = VPU_MAX_PRIORITY; for (i = 0; i < prio_max; i++) { exe_cnt = vpu_debug_cmd_entry_seq(s, &c[i], i); preempt_cnt += (i) ? exe_cnt : 0; } seq_printf(s, "preemption count: %llu\n", preempt_cnt); return 0; } static inline int vpu_debug_cmd_dev(struct seq_file *s, struct vpu_device *vd) { return vpu_debug_cmd_seq(s, vd, atomic_read(&vd->cmd_prio), vd->cmd_prio_max, atomic_read(&vd->cmd_active), vd->cmd, vd->cmd_timeout); } static int vpu_debug_reg_dev(struct seq_file *s, struct vpu_device *vd) { int ret; struct vpu_register *r; if (!vd) return -ENODEV; mutex_lock_nested(&vd->lock, VPU_MUTEX_DEV); if (!vd_is_available(vd)) goto out; if (vd->state == VS_DOWN) { seq_printf(s, "vpu%d is unpowered\n", vd->id); goto out; } ret = vpu_pwr_get_locked_nb(vd); if (ret) goto out; r = vd_reg(vd); seq_puts(s, "name\toffset\tvalue\n"); #define seq_vpu_reg(a) \ seq_printf(s, "%s\t%4xh\t%08xh\n", #a, r->a, vpu_reg_read(vd, a)) seq_vpu_reg(cg_con); seq_vpu_reg(sw_rst); seq_vpu_reg(done_st); seq_vpu_reg(ctrl); seq_vpu_reg(xtensa_int); seq_vpu_reg(ctl_xtensa_int); seq_vpu_reg(default0); seq_vpu_reg(default1); seq_vpu_reg(xtensa_info00); seq_vpu_reg(xtensa_info01); seq_vpu_reg(xtensa_info02); seq_vpu_reg(xtensa_info03); seq_vpu_reg(xtensa_info04); seq_vpu_reg(xtensa_info05); seq_vpu_reg(xtensa_info06); seq_vpu_reg(xtensa_info07); seq_vpu_reg(xtensa_info08); seq_vpu_reg(xtensa_info09); seq_vpu_reg(xtensa_info10); seq_vpu_reg(xtensa_info11); seq_vpu_reg(xtensa_info12); seq_vpu_reg(xtensa_info13); seq_vpu_reg(xtensa_info14); seq_vpu_reg(xtensa_info15); seq_vpu_reg(xtensa_info16); seq_vpu_reg(xtensa_info17); seq_vpu_reg(xtensa_info18); seq_vpu_reg(xtensa_info19); seq_vpu_reg(xtensa_info20); seq_vpu_reg(xtensa_info21); seq_vpu_reg(xtensa_info22); seq_vpu_reg(xtensa_info23); seq_vpu_reg(xtensa_info24); seq_vpu_reg(xtensa_info25); seq_vpu_reg(xtensa_info26); seq_vpu_reg(xtensa_info27); seq_vpu_reg(xtensa_info28); seq_vpu_reg(xtensa_info29); seq_vpu_reg(xtensa_info30); seq_vpu_reg(xtensa_info31); seq_vpu_reg(mbox_inbox_0); seq_vpu_reg(mbox_inbox_1); seq_vpu_reg(mbox_inbox_2); seq_vpu_reg(mbox_inbox_3); seq_vpu_reg(mbox_inbox_4); seq_vpu_reg(mbox_inbox_5); seq_vpu_reg(mbox_inbox_6); seq_vpu_reg(mbox_inbox_7); seq_vpu_reg(mbox_inbox_8); seq_vpu_reg(mbox_inbox_9); seq_vpu_reg(mbox_inbox_10); seq_vpu_reg(mbox_inbox_11); seq_vpu_reg(mbox_inbox_12); seq_vpu_reg(mbox_inbox_13); seq_vpu_reg(mbox_inbox_14); seq_vpu_reg(mbox_inbox_15); seq_vpu_reg(mbox_inbox_16); seq_vpu_reg(mbox_inbox_17); seq_vpu_reg(mbox_inbox_18); seq_vpu_reg(mbox_inbox_19); seq_vpu_reg(mbox_inbox_20); seq_vpu_reg(mbox_inbox_21); seq_vpu_reg(mbox_inbox_22); seq_vpu_reg(mbox_inbox_23); seq_vpu_reg(mbox_inbox_24); seq_vpu_reg(mbox_inbox_25); seq_vpu_reg(mbox_inbox_26); seq_vpu_reg(mbox_inbox_27); seq_vpu_reg(mbox_inbox_28); seq_vpu_reg(mbox_inbox_29); seq_vpu_reg(mbox_inbox_30); seq_vpu_reg(mbox_inbox_31); seq_vpu_reg(debug_info00); seq_vpu_reg(debug_info01); seq_vpu_reg(debug_info02); seq_vpu_reg(debug_info03); seq_vpu_reg(debug_info04); seq_vpu_reg(debug_info05); seq_vpu_reg(debug_info06); seq_vpu_reg(debug_info07); seq_vpu_reg(xtensa_altresetvec); #undef seq_vpu_reg vpu_pwr_put_locked_nb(vd); out: mutex_unlock(&vd->lock); return 0; } static char *vpu_debug_simple_write(const char __user *buffer, size_t count) { char *buf; int ret; buf = kzalloc(count + 1, GFP_KERNEL); if (!buf) goto out; ret = copy_from_user(buf, buffer, count); if (ret) { pr_info("%s: copy_from_user: ret=%d\n", __func__, ret); kfree(buf); buf = NULL; goto out; } buf[count] = '\0'; out: return buf; } #if IS_ENABLED(CONFIG_MTK_APUSYS_VPU_DEBUG) static int vpu_debug_algo_entry(struct seq_file *s, struct vpu_algo_list *al, struct __vpu_algo *alg) { if (!alg) return -ENOENT; if (!al || !al->ops || !al->ops->get || !al->ops->put) goto out; al->ops->get(al, NULL, alg); seq_printf(s, "[%s: prog: 0x%llx/0x%x, iram: 0x%llx/0x%x, entry: 0x%x, ref: %d, builtin: %d]\n", alg->a.name, alg->a.mva, alg->a.len, alg->a.iram_mva, alg->a.iram_len, alg->a.entry_off, kref_read(&alg->ref), alg->builtin); al->ops->put(alg); out: return 0; } static int vpu_debug_algo_list(struct seq_file *s, struct vpu_algo_list *al) { struct __vpu_algo *alg; int ret; struct list_head *ptr, *tmp; if (!al) return -ENOENT; seq_printf(s, "\n<%s Algorithms>\n", al->name); list_for_each_safe(ptr, tmp, &al->a) { alg = list_entry(ptr, struct __vpu_algo, list); ret = vpu_debug_algo_entry(s, al, alg); if (ret) break; } return ret; } static int vpu_debug_algo(struct seq_file *s) { struct vpu_device *vd; if (!s) return -ENOENT; vd = (struct vpu_device *) s->private; if (!vd) return -ENODEV; vpu_debug_algo_list(s, &vd->aln); if (bin_type(vd) == VPU_IMG_PRELOAD) vpu_debug_algo_list(s, &vd->alp); return 0; } static int vpu_debug_reg(struct seq_file *s) { if (!s) return -ENOENT; return vpu_debug_reg_dev(s, s->private); } /** * vpu_debug_jtag_set - enable/disable jtag * * @data: vpu device * @val: user write value * * return 0: enable/disable success */ static int vpu_debug_jtag_set(void *data, u64 val) { struct vpu_device *vd = data; int ret = 0; if (!vd) return -ENOENT; if (val) { /* enable jtag */ vd->jtag_enabled = 1; } else { /* disable jtag */ vd->jtag_enabled = 0; } return ret; } /** * vpu_debug_jtag_get - show jtag status of vpu * @data: vpu device * @val: return value * * return 0: operation success */ static int vpu_debug_jtag_get(void *data, u64 *val) { struct vpu_device *vd = data; if (!vd) return -ENOENT; *val = vd->jtag_enabled; return 0; } static int vpu_debug_dump(struct seq_file *s) { struct vpu_device *vd; if (!s) return -ENOENT; vd = (struct vpu_device *) s->private; vpu_dmp_seq_core(s, vd); return 0; } static ssize_t vpu_debug_dump_write(struct file *filp, const char __user *buffer, size_t count, loff_t *f_pos) { char *buf, *cmd, *cur; struct vpu_device *vd; if (!filp || !filp->f_inode) goto out; vd = (struct vpu_device *) filp->f_inode->i_private; if (!vd) goto out; buf = vpu_debug_simple_write(buffer, count); if (!buf) goto out; cur = buf; cmd = strsep(&cur, " \t\n"); if (!strcmp(cmd, "free")) vpu_dmp_free(vd); else if (!strcmp(cmd, "dump")) vpu_dmp_create(vd, NULL, "Dump trigger by user"); kfree(buf); out: return count; } static int vpu_debug_cmd(struct seq_file *s) { if (!s || !s->private) return -ENOENT; return vpu_debug_cmd_dev(s, s->private); } static int vpu_debug_state(struct seq_file *s) { if (!s || !s->private) return -ENOENT; return vpu_debug_state_dev(s, s->private); } #endif /* end of CONFIG_MTK_APUSYS_VPU_DEBUG */ static void *vpu_mesg_pa_to_va(struct vpu_mem *work_buf, unsigned int phys_addr) { unsigned long ret = 0; int offset = 0; if (!phys_addr) return NULL; offset = phys_addr - work_buf->pa; ret = work_buf->va + offset; return (void *)(ret); } static void vpu_mesg_clr(struct vpu_device *vd) { char *data = NULL; struct vpu_message_ctrl *msg = vpu_mesg(vd); if (!msg) return; data = (char *)vpu_mesg_pa_to_va(&vd->iova_work.m, msg->data); msg->head = 0; msg->tail = 0; if (data) memset(data, 0, vd->wb_log_data - sizeof(struct vpu_message_ctrl)); } int vpu_mesg_seq(struct seq_file *s, struct vpu_device *vd) { int i, wrap = false; char *data = NULL; struct vpu_message_ctrl *msg = vpu_mesg(vd); if (!s || !msg) return -ENOENT; vd_mops(vd)->sync_for_cpu(vd->dev, &vd->iova_work); data = (char *)vpu_mesg_pa_to_va(&vd->iova_work.m, msg->data); i = msg->head; do { if (msg->head == msg->tail || i == msg->tail) seq_printf(s, "%s", "\n"); while (i != msg->tail && data) { if (i > msg->tail && wrap) break; seq_printf(s, "%s", data + i); i += strlen(data + i) + 1; if (i >= msg->buf_size) { i = 0; wrap = true; } } } while (0); return 0; } static int vpu_proc_vpu_memory(struct seq_file *s) { vpu_debug_info(s); seq_puts(s, "======== Tags ========\n"); apu_tags_seq(vpu_drv->tags, s); vpu_dmp_seq(s); return 0; } static ssize_t vpu_proc_vpu_memory_write(struct file *filp, const char __user *buffer, size_t count, loff_t *f_pos) { char *buf, *cmd, *cur; pr_info("%s:\n", __func__); buf = vpu_debug_simple_write(buffer, count); if (!buf) goto out; cur = buf; cmd = strsep(&cur, " \t\n"); if (!strcmp(cmd, "free")) vpu_dmp_free_all(); kfree(buf); out: return count; } static int vpu_proc_mesg(struct seq_file *s) { if (!s) return -ENOENT; return vpu_mesg_seq(s, (struct vpu_device *)s->private); } static ssize_t vpu_proc_mesg_level_write(struct file *filp, const char __user *buffer, size_t count, loff_t *f_pos) { char *buf, *cmd, *cur; struct vpu_device *vd; struct vpu_message_ctrl *msg = NULL; int level = 0; int ret = 0; if (!filp || !filp->f_inode) goto out; vd = (struct vpu_device *) PDE_DATA(filp->f_inode); if (!vd) goto out; buf = vpu_debug_simple_write(buffer, count); if (!buf) goto out; cur = buf; cmd = strsep(&cur, " \t\n"); ret = kstrtoint(cmd, 10, &level); if (ret) goto free_buf; if (!level) { vpu_mesg_clr(vd); goto free_buf; } if (level < -1 || level >= VPU_DBG_MSG_LEVEL_TOTAL) { pr_info("val: %d\n", level); goto free_buf; } msg = vpu_mesg(vd); if (!msg) goto free_buf; if (level != -1) msg->level_mask ^= (1 << level); else msg->level_mask = 0; free_buf: kfree(buf); out: return count; } static int vpu_proc_mesg_level(struct seq_file *s) { struct vpu_device *vd; struct vpu_message_ctrl *msg; if (!s) return -ENOENT; vd = (struct vpu_device *) s->private; msg = vpu_mesg(vd); if (!msg) return -ENOENT; seq_printf(s, "%u\n", msg->level_mask); return 0; } void vpu_seq_boost(struct seq_file *s, int boost) { if (boost == VPU_PWR_NO_BOOST) seq_puts(s, "unset"); else seq_printf(s, "%d", boost); } const char *vpu_debug_cmd_str(int cmd) { unsigned int i; for (i = 0; vc_str[i].n; i++) { if (cmd == vc_str[i].t) return vc_str[i].n; } return "(unknown)"; } static int vpu_debug_iova_seq(struct seq_file *s, struct vpu_iova *i, const char *prefix) { if (i->bin == VPU_MEM_ALLOC) { if (i->addr) seq_printf(s, "<%s> iova: 0x%llx, size: 0x%x (static alloc)\n", prefix, i->iova, i->size); else { seq_printf(s, "<%s> iova: 0x%llx, size: 0x%x (dynamic alloc)\n", prefix, i->iova, i->size); } } else if (i->size) { seq_printf(s, "<%s> iova: 0x%llx, size: 0x%x, bin offset: 0x%x\n", prefix, i->iova, i->size, i->bin); } return 0; } static int vpu_debug_iomem_seq(struct seq_file *s, struct vpu_iomem *i, const char *prefix) { struct resource *r; if (!i || !i->res) return -ENOENT; r = i->res; seq_printf(s, "<%s> start: 0x%lx, end: 0x%lx, size: 0x%lx\n", prefix, (unsigned long)r->start, (unsigned long)r->end, (unsigned long)(r->end - r->start + 1)); return 0; } static int vpu_debug_info_dev(struct seq_file *s, struct vpu_device *vd) { if (!s || !vd) return -ENOENT; if (!vd_is_available(vd)) return 0; seq_printf(s, "\n----- %s: settings -----\n", vd->name); seq_printf(s, "mva_iram: 0x%llx\n", vd->mva_iram); seq_printf(s, "cmd_prio_max: %d\n", vd->cmd_prio_max); seq_printf(s, "cmd_timeout: %llu\n", vd->cmd_timeout); seq_printf(s, "pw_off_latency: %llu\n", vd->pw_off_latency); seq_printf(s, "wb_log_size: 0x%x\n", vd->wb_log_size); seq_printf(s, "wb_log_data: 0x%x\n", vd->wb_log_data); seq_puts(s, "iova:\n"); vpu_debug_iova_seq(s, &vd->iova_reset, "reset"); vpu_debug_iova_seq(s, &vd->iova_main, "main"); vpu_debug_iova_seq(s, &vd->iova_kernel, "kernel"); vpu_debug_iova_seq(s, &vd->iova_iram, "iram"); vpu_debug_iova_seq(s, &vd->iova_work, "work"); seq_puts(s, "iomem:\n"); vpu_debug_iomem_seq(s, &vd->reg, "reg"); vpu_debug_iomem_seq(s, &vd->dmem, "dmem"); vpu_debug_iomem_seq(s, &vd->imem, "imem"); vpu_debug_iomem_seq(s, &vd->dbg, "dbg"); seq_printf(s, "----- %s: current state -----\n", vd->name); vpu_debug_state_dev(s, vd); seq_printf(s, "----- %s: current commands -----\n", vd->name); vpu_debug_cmd_dev(s, vd); seq_printf(s, "----- %s: current registers -----\n", vd->name); vpu_debug_reg_dev(s, vd); return 0; } static int vpu_debug_info(struct seq_file *s) { struct vpu_device *vd; struct list_head *ptr, *tmp; if (!s) return -ENOENT; mutex_lock(&vpu_drv->lock); seq_puts(s, "======== Driver Info ========\n"); seq_puts(s, "Queried Time: "); vpu_seq_time(s, sched_clock()); seq_printf(s, "\n%s%s%s\n", VPU_XOS ? "XOS" : "Non-XOS", VPU_IMG_LEGACY ? ",Legacy Image" : "", VPU_IMG_PRELOAD ? ",Preload Image" : ""); seq_printf(s, "bin_pa: 0x%lx\n", vpu_drv->bin_pa); seq_printf(s, "bin_size: 0x%x\n", vpu_drv->bin_size); seq_printf(s, "bin_head_ofs: 0x%x\n", vpu_drv->bin_head_ofs); seq_printf(s, "bin_preload_ofs: 0x%x\n", vpu_drv->bin_preload_ofs); seq_printf(s, "mva_algo: 0x%llx\n", vpu_drv->mva_algo); vpu_debug_iova_seq(s, &vpu_drv->iova_algo, "algo"); list_for_each_safe(ptr, tmp, &vpu_drv->devs) { vd = list_entry(ptr, struct vpu_device, list); vpu_debug_info_dev(s, vd); } if (vpu_drv->vp && vpu_drv->vp->mops && vpu_drv->vp->mops->show) { seq_puts(s, "======== IOVA Info ========\n"); vpu_drv->vp->mops->show(s); } mutex_unlock(&vpu_drv->lock); seq_puts(s, "\n"); return 0; } #if IS_ENABLED(CONFIG_MTK_APUSYS_VPU_DEBUG) #define VPU_DEBUGFS_FOP_DEF(name) \ static struct dentry *vpu_d##name; \ static int vpu_debug_## name ##_show(struct seq_file *s, void *unused) \ { \ vpu_debug_## name(s); \ return 0; \ } \ static int vpu_debug_## name ##_open(struct inode *inode, struct file *file) \ { \ return single_open(file, vpu_debug_ ## name ## _show, \ inode->i_private); \ } \ #define VPU_DEBUGFS_DEF(name) \ VPU_DEBUGFS_FOP_DEF(name) \ static const struct file_operations vpu_debug_ ## name ## _fops = { \ .open = vpu_debug_ ## name ## _open, \ .read = seq_read, \ .llseek = seq_lseek, \ .release = single_release, \ } #define VPU_DEBUGFS_RW_DEF(name) \ VPU_DEBUGFS_FOP_DEF(name) \ static const struct file_operations vpu_debug_ ## name ## _fops = { \ .open = vpu_debug_ ## name ## _open, \ .read = seq_read, \ .write = vpu_debug_ ## name ## _write, \ .llseek = seq_lseek, \ .release = single_release, \ } VPU_DEBUGFS_DEF(algo); VPU_DEBUGFS_RW_DEF(dump); VPU_DEBUGFS_DEF(reg); VPU_DEBUGFS_DEF(state); VPU_DEBUGFS_DEF(cmd); VPU_DEBUGFS_DEF(info); #undef VPU_DEBUGFS_DEF static struct dentry *vpu_djtag; DEFINE_SIMPLE_ATTRIBUTE(vpu_debug_jtag_fops, vpu_debug_jtag_get, vpu_debug_jtag_set, "%lld\n"); #define VPU_DEBUGFS_MDOE 0644 #define VPU_DEBUGFS_CREATE(name) \ { \ vpu_d##name = debugfs_create_file(#name, VPU_DEBUGFS_MDOE, \ droot, \ NULL, &vpu_debug_ ## name ## _fops); \ if (IS_ERR_OR_NULL(vpu_d##name)) { \ ret = PTR_ERR(vpu_d##name); \ pr_info("%s: vpu%d: " #name "): %d\n", \ __func__, (vd) ? (vd->id) : 0, ret); \ goto out; \ } \ vpu_d##name->d_inode->i_private = vd; \ } #endif /* end of CONFIG_MTK_APUSYS_VPU_DEBUG */ #define VPU_PROCFS_FOP_DEF(name) \ static struct proc_dir_entry *vpu_proc_entry_##name; \ static int vpu_proc_## name ##_show(struct seq_file *s, void *unused) \ { \ vpu_proc_## name(s); \ return 0; \ } \ static int vpu_proc_## name ##_open(struct inode *inode, struct file *file) \ { \ return single_open(file, vpu_proc_ ## name ## _show, \ PDE_DATA(inode)); \ } \ #define VPU_PROCFS_DEF(name) \ VPU_PROCFS_FOP_DEF(name) \ static const struct proc_ops vpu_proc_ ## name ## _ops = { \ .proc_open = vpu_proc_ ## name ## _open, \ .proc_read = seq_read, \ .proc_lseek = seq_lseek, \ .proc_release = single_release, \ } #define VPU_PROCFS_RW_DEF(name) \ VPU_PROCFS_FOP_DEF(name) \ static const struct proc_ops vpu_proc_ ## name ## _ops = { \ .proc_open = vpu_proc_ ## name ## _open, \ .proc_read = seq_read, \ .proc_write = vpu_proc_ ## name ## _write, \ .proc_lseek = seq_lseek, \ .proc_release = single_release, \ } VPU_PROCFS_RW_DEF(vpu_memory); VPU_PROCFS_DEF(mesg); VPU_PROCFS_RW_DEF(mesg_level); #undef VPU_PROCFS_DEF #undef VPU_PROCFS_RW_DEF #define VPU_PROCFS_CREATE(name) \ { \ vpu_proc_entry_##name = proc_create_data(#name, 0440, \ proc_root, &vpu_proc_ ## name ## _ops, vd); \ if (IS_ERR_OR_NULL(vpu_proc_entry_##name)) { \ ret = PTR_ERR(vpu_proc_entry_##name); \ pr_info("%s: vpu%d: " #name "): %d\n", \ __func__, (vd) ? (vd->id) : 0, ret); \ goto out; \ } \ } #define VPU_PROCFS_CREATE_RW(name) \ { \ vpu_proc_entry_##name = proc_create_data(#name, 0640, \ proc_root, &vpu_proc_ ## name ## _ops, vd); \ if (IS_ERR_OR_NULL(vpu_proc_entry_##name)) { \ ret = PTR_ERR(vpu_proc_entry_##name); \ pr_info("%s: vpu%d: " #name "): %d\n", \ __func__, (vd) ? (vd->id) : 0, ret); \ goto out; \ } \ } int vpu_init_dev_debug(struct platform_device *pdev, struct vpu_device *vd) { int ret = 0; struct proc_dir_entry *proc_root; #if IS_ENABLED(CONFIG_MTK_APUSYS_VPU_DEBUG) struct dentry *droot; #endif vd->proc_root = NULL; vpu_mesg_init(vd); vpu_dmp_init(vd); if (!vpu_drv->proc_root) return -ENODEV; proc_root = proc_mkdir(vd->name, vpu_drv->proc_root); if (IS_ERR_OR_NULL(proc_root)) { ret = PTR_ERR(proc_root); pr_info("%s: failed to create procfs node: vpu/%s: %d\n", __func__, vd->name, ret); goto out; } vd->proc_root = proc_root; VPU_PROCFS_CREATE(mesg); VPU_PROCFS_CREATE_RW(mesg_level); #if IS_ENABLED(CONFIG_MTK_APUSYS_VPU_DEBUG) vd->droot = NULL; if (!vpu_drv->droot) return -ENODEV; droot = debugfs_create_dir(vd->name, vpu_drv->droot); if (IS_ERR_OR_NULL(droot)) { ret = PTR_ERR(droot); pr_info("%s: failed to create debugfs node: vpu/%s: %d\n", __func__, vd->name, ret); goto out; } vd->droot = droot; debugfs_create_u64("pw_off_latency", 0660, droot, &vd->pw_off_latency); debugfs_create_u64("cmd_timeout", 0660, droot, &vd->cmd_timeout); VPU_DEBUGFS_CREATE(algo); VPU_DEBUGFS_CREATE(dump); VPU_DEBUGFS_CREATE(reg); VPU_DEBUGFS_CREATE(jtag); VPU_DEBUGFS_CREATE(state); VPU_DEBUGFS_CREATE(cmd); #endif out: return ret; } void vpu_exit_dev_debug(struct platform_device *pdev, struct vpu_device *vd) { if (!vd) return; if (!IS_ERR_OR_NULL(vd->proc_root)) { remove_proc_subtree(vd->name, NULL); vd->proc_root = NULL; } #if IS_ENABLED(CONFIG_MTK_APUSYS_VPU_DEBUG) if (!IS_ERR_OR_NULL(vd->droot)) { debugfs_remove_recursive(vd->droot); vd->droot = NULL; } #endif vpu_dmp_exit(vd); } int vpu_init_debug(void) { int ret = 0; struct proc_dir_entry *proc_root; struct vpu_device *vd = NULL; #if IS_ENABLED(CONFIG_MTK_APUSYS_VPU_DEBUG) struct dentry *droot; #endif proc_root = proc_mkdir("vpu", NULL); if (IS_ERR_OR_NULL(proc_root)) { ret = PTR_ERR(proc_root); pr_info("%s: fail to create procfs node: %d\n", __func__, ret); goto out; } vpu_drv->proc_root = proc_root; VPU_PROCFS_CREATE_RW(vpu_memory); #if IS_ENABLED(CONFIG_MTK_APUSYS_VPU_DEBUG) droot = debugfs_create_dir("vpu", NULL); if (IS_ERR_OR_NULL(droot)) { ret = PTR_ERR(droot); pr_info("%s: failed to create debugfs node: %d\n", __func__, ret); goto out; } vpu_drv->droot = droot; vpu_klog = VPU_DBG_DRV; debugfs_create_u32("klog", 0660, droot, &vpu_klog); VPU_DEBUGFS_CREATE(info); #endif out: return ret; } #undef VPU_DEBUGFS_CREATE #undef VPU_PROCFS_CREATE #undef VPU_PROCFS_CREATE_RW void vpu_exit_debug(void) { if (!vpu_drv) return; if (!IS_ERR_OR_NULL(vpu_drv->proc_root)) { remove_proc_subtree("vpu", NULL); vpu_drv->proc_root = NULL; } #if IS_ENABLED(CONFIG_MTK_APUSYS_VPU_DEBUG) if (!IS_ERR_OR_NULL(vpu_drv->droot)) { debugfs_remove_recursive(vpu_drv->droot); vpu_drv->droot = NULL; } #endif }