| /* |
| * Samsung Exynos SoC series VIPx driver |
| * |
| * Copyright (c) 2018 Samsung Electronics Co., Ltd |
| * |
| * This program is free software; you can redistribute it and/or modify |
| * it under the terms of the GNU General Public License version 2 as |
| * published by the Free Software Foundation. |
| */ |
| |
| #include <linux/debugfs.h> |
| #include <linux/uaccess.h> |
| #include <linux/syscalls.h> |
| |
| #include "vipx-log.h" |
| #include "vipx-mailbox.h" |
| #include "vipx-device.h" |
| #include "vipx-pm.h" |
| #include "vipx-debug.h" |
| |
| #define VIPX_DEBUG_LOG_LINE_SIZE (128) |
| #define VIPX_DEBUG_LOG_TIME (10) |
| |
| static struct vipx_device *debug_device; |
| int vipx_debug_log_enable; |
| |
| int vipx_debug_dump_debug_regs(void) |
| { |
| struct vipx_system *sys; |
| |
| vipx_enter(); |
| sys = &debug_device->system; |
| |
| sys->ctrl_ops->debug_dump(sys); |
| vipx_leave(); |
| return 0; |
| } |
| |
| static int vipx_debug_mem_show(struct seq_file *file, void *unused) |
| { |
| struct vipx_debug *debug; |
| struct vipx_memory *mem; |
| |
| vipx_enter(); |
| debug = file->private; |
| mem = &debug->system->memory; |
| |
| seq_printf(file, "%15s : %zu KB\n", |
| mem->fw.name, mem->fw.size / SZ_1K); |
| seq_printf(file, "%15s : %zu KB (%zu Bytes used)\n", |
| mem->mbox.name, mem->mbox.size / SZ_1K, |
| sizeof(struct vipx_mailbox_ctrl)); |
| seq_printf(file, "%15s : %zu KB\n", |
| mem->heap.name, mem->heap.size / SZ_1K); |
| seq_printf(file, "%15s : %zu KB\n", |
| mem->log.name, mem->log.size / SZ_1K); |
| |
| vipx_leave(); |
| return 0; |
| } |
| |
| static int vipx_debug_mem_open(struct inode *inode, struct file *filp) |
| { |
| return single_open(filp, vipx_debug_mem_show, inode->i_private); |
| } |
| |
| static ssize_t vipx_debug_mem_write(struct file *filp, |
| const char __user *user_buf, size_t count, loff_t *ppos) |
| { |
| struct seq_file *file; |
| struct vipx_debug *debug; |
| struct vipx_memory *mem; |
| struct vipx_pm *pm; |
| char buf[128]; |
| int ret; |
| unsigned int fw, mbox, heap, log; |
| ssize_t len; |
| |
| vipx_enter(); |
| file = filp->private_data; |
| debug = file->private; |
| mem = &debug->system->memory; |
| pm = &debug->system->pm; |
| |
| if (count > sizeof(buf)) { |
| vipx_err("[debugfs] writing size(%zd) is larger than buffer\n", |
| count); |
| goto out; |
| } |
| |
| len = simple_write_to_buffer(buf, sizeof(buf), ppos, user_buf, count); |
| if (len <= 0) { |
| vipx_err("[debugfs] Failed to get user buf(%d)\n", len); |
| goto out; |
| } |
| |
| buf[len] = '\0'; |
| |
| ret = sscanf(buf, "%u %u %u %u\n", &fw, &mbox, &heap, &log); |
| if (ret != 4) { |
| vipx_err("[debugfs] Failed to get memory size(%d)\n", ret); |
| goto out; |
| } |
| |
| mutex_lock(&pm->lock); |
| if (vipx_pm_qos_active(pm)) { |
| vipx_warn("[debugfs] size can't be changed (power on)\n"); |
| mutex_unlock(&pm->lock); |
| goto out; |
| } |
| |
| fw = PAGE_ALIGN(fw * SZ_1K); |
| if (fw >= VIPX_CC_DRAM_BIN_SIZE && fw <= VIPX_MEMORY_MAX_SIZE) { |
| vipx_info("[debugfs] size of %s is changed (%zu KB -> %u KB)\n", |
| mem->fw.name, mem->fw.size / SZ_1K, |
| fw / SZ_1K); |
| mem->fw.size = fw; |
| } else { |
| vipx_warn("[debugfs] invalid size %u KB (%s, %u ~ %u)\n", |
| fw / SZ_1K, mem->fw.name, |
| VIPX_CC_DRAM_BIN_SIZE / SZ_1K, |
| VIPX_MEMORY_MAX_SIZE / SZ_1K); |
| } |
| |
| mbox = PAGE_ALIGN(mbox * SZ_1K); |
| if (mbox >= VIPX_MBOX_SIZE && mbox <= VIPX_MEMORY_MAX_SIZE) { |
| vipx_info("[debugfs] size of %s is changed (%zu KB -> %u KB)\n", |
| mem->mbox.name, mem->mbox.size / SZ_1K, |
| mbox / SZ_1K); |
| mem->mbox.size = mbox; |
| } else { |
| vipx_warn("[debugfs] invalid size %u KB (%s, %u ~ %u)\n", |
| mbox / SZ_1K, mem->mbox.name, |
| VIPX_MBOX_SIZE / SZ_1K, |
| VIPX_MEMORY_MAX_SIZE / SZ_1K); |
| } |
| |
| heap = PAGE_ALIGN(heap * SZ_1K); |
| if (heap >= VIPX_HEAP_SIZE && heap <= VIPX_MEMORY_MAX_SIZE) { |
| vipx_info("[debugfs] size of %s is changed (%zu KB -> %u KB)\n", |
| mem->heap.name, mem->heap.size / SZ_1K, |
| heap / SZ_1K); |
| mem->heap.size = heap; |
| } else { |
| vipx_warn("[debugfs] invalid size %u KB (%s, %u ~ %u)\n", |
| heap / SZ_1K, mem->heap.name, |
| VIPX_HEAP_SIZE / SZ_1K, |
| VIPX_MEMORY_MAX_SIZE / SZ_1K); |
| } |
| |
| log = PAGE_ALIGN(log * SZ_1K); |
| if (log >= VIPX_LOG_SIZE && log <= VIPX_MEMORY_MAX_SIZE) { |
| vipx_info("[debugfs] size of %s is changed (%zu KB -> %u KB)\n", |
| mem->log.name, mem->log.size / SZ_1K, |
| log / SZ_1K); |
| mem->log.size = log; |
| } else { |
| vipx_warn("[debugfs] invalid size %u KB (%s, %u ~ %u)\n", |
| log / SZ_1K, mem->log.name, |
| VIPX_LOG_SIZE / SZ_1K, |
| VIPX_MEMORY_MAX_SIZE / SZ_1K); |
| } |
| |
| mutex_unlock(&pm->lock); |
| |
| vipx_leave(); |
| out: |
| return count; |
| } |
| |
| static const struct file_operations vipx_debug_mem_fops = { |
| .open = vipx_debug_mem_open, |
| .read = seq_read, |
| .write = vipx_debug_mem_write, |
| .llseek = seq_lseek, |
| .release = single_release |
| }; |
| |
| static int vipx_debug_devfreq_show(struct seq_file *file, void *unused) |
| { |
| #if defined(CONFIG_PM_DEVFREQ) |
| struct vipx_debug *debug; |
| struct vipx_pm *pm; |
| int idx; |
| |
| vipx_enter(); |
| debug = file->private; |
| pm = &debug->system->pm; |
| |
| mutex_lock(&pm->lock); |
| seq_printf(file, "available level count is [L0 - L%d]\n", |
| pm->qos_count - 1); |
| for (idx = 0; idx < pm->qos_count; ++idx) |
| seq_printf(file, "[L%02d] %d\n", idx, pm->qos_table[idx]); |
| |
| if (pm->default_qos < 0) |
| seq_puts(file, "default: not set\n"); |
| else |
| seq_printf(file, "default: L%d\n", pm->default_qos); |
| |
| if (pm->resume_qos < 0) |
| seq_puts(file, "resume : not set\n"); |
| else |
| seq_printf(file, "resume : L%d\n", pm->resume_qos); |
| |
| if (pm->current_qos < 0) |
| seq_puts(file, "current: off\n"); |
| else |
| seq_printf(file, "current: L%d\n", pm->current_qos); |
| |
| mutex_unlock(&pm->lock); |
| seq_puts(file, "Command to change devfreq level\n"); |
| seq_puts(file, " echo {level} > /d/vipx/devfreq\n"); |
| |
| vipx_leave(); |
| return 0; |
| #else |
| seq_puts(file, "devfreq is not supported\n"); |
| return 0; |
| #endif |
| } |
| |
| static int vipx_debug_devfreq_open(struct inode *inode, struct file *filp) |
| { |
| return single_open(filp, vipx_debug_devfreq_show, inode->i_private); |
| } |
| |
| static ssize_t vipx_debug_devfreq_write(struct file *filp, |
| const char __user *user_buf, size_t count, loff_t *ppos) |
| { |
| struct seq_file *file; |
| struct vipx_debug *debug; |
| struct vipx_pm *pm; |
| char buf[30]; |
| int ret, qos; |
| ssize_t len; |
| |
| vipx_enter(); |
| file = filp->private_data; |
| debug = file->private; |
| pm = &debug->system->pm; |
| |
| if (count > sizeof(buf)) { |
| vipx_err("[debugfs] writing size(%zd) is larger than buffer\n", |
| count); |
| goto out; |
| } |
| |
| len = simple_write_to_buffer(buf, sizeof(buf), ppos, user_buf, count); |
| if (len <= 0) { |
| vipx_err("[debugfs] Failed to get user buf(%d)\n", len); |
| goto out; |
| } |
| |
| buf[len] = '\0'; |
| |
| ret = sscanf(buf, "%d\n", &qos); |
| if (ret != 1) { |
| vipx_err("[debugfs] Failed to get qos value(%d)\n", ret); |
| goto out; |
| } |
| |
| ret = vipx_pm_qos_set_default(pm, qos); |
| if (ret) { |
| vipx_err("[debugfs] Failed to set default qos(%d)\n", ret); |
| goto out; |
| } else { |
| vipx_info("[debugfs] default qos setting\n"); |
| } |
| |
| vipx_leave(); |
| out: |
| return count; |
| } |
| |
| static const struct file_operations vipx_debug_devfreq_fops = { |
| .open = vipx_debug_devfreq_open, |
| .read = seq_read, |
| .write = vipx_debug_devfreq_write, |
| .llseek = seq_lseek, |
| .release = single_release |
| }; |
| |
| static int vipx_debug_clk_show(struct seq_file *file, void *unused) |
| { |
| struct vipx_debug *debug; |
| struct vipx_system *sys; |
| struct vipx_pm *pm; |
| const struct vipx_clk_ops *ops; |
| int count, idx; |
| unsigned long freq; |
| const char *name; |
| |
| vipx_enter(); |
| debug = file->private; |
| sys = debug->system; |
| pm = &sys->pm; |
| ops = sys->clk_ops; |
| |
| mutex_lock(&pm->lock); |
| if (vipx_pm_qos_active(pm)) { |
| count = ops->get_count(sys); |
| for (idx = 0; idx < count; ++idx) { |
| freq = ops->get_freq(sys, idx); |
| name = ops->get_name(sys, idx); |
| seq_printf(file, "%30s(%d) : %3lu.%06lu MHz\n", |
| name, idx, |
| freq / 1000000, freq % 1000000); |
| } |
| } else { |
| seq_puts(file, "power off\n"); |
| } |
| mutex_unlock(&pm->lock); |
| |
| vipx_leave(); |
| return 0; |
| } |
| |
| static int vipx_debug_clk_open(struct inode *inode, struct file *filp) |
| { |
| return single_open(filp, vipx_debug_clk_show, inode->i_private); |
| } |
| |
| static const struct file_operations vipx_debug_clk_fops = { |
| .open = vipx_debug_clk_open, |
| .read = seq_read, |
| .llseek = seq_lseek, |
| .release = single_release |
| }; |
| |
| static int vipx_debug_wait_time_show(struct seq_file *file, void *unused) |
| { |
| struct vipx_debug *debug; |
| struct vipx_interface *itf; |
| |
| vipx_enter(); |
| debug = file->private; |
| itf = &debug->system->interface; |
| |
| seq_printf(file, "response wait time %u ms\n", itf->wait_time); |
| |
| vipx_leave(); |
| return 0; |
| } |
| |
| static int vipx_debug_wait_time_open(struct inode *inode, struct file *filp) |
| { |
| return single_open(filp, vipx_debug_wait_time_show, inode->i_private); |
| } |
| |
| static ssize_t vipx_debug_wait_time_write(struct file *filp, |
| const char __user *user_buf, size_t count, loff_t *ppos) |
| { |
| struct seq_file *file; |
| struct vipx_debug *debug; |
| struct vipx_interface *itf; |
| char buf[30]; |
| int ret, time; |
| ssize_t len; |
| |
| vipx_enter(); |
| file = filp->private_data; |
| debug = file->private; |
| itf = &debug->system->interface; |
| |
| if (count > sizeof(buf)) { |
| vipx_err("[debugfs] writing size(%zd) is larger than buffer\n", |
| count); |
| goto out; |
| } |
| |
| len = simple_write_to_buffer(buf, sizeof(buf), ppos, user_buf, count); |
| if (len <= 0) { |
| vipx_err("[debugfs] Failed to get user buf(%d)\n", len); |
| goto out; |
| } |
| |
| buf[len] = '\0'; |
| |
| ret = sscanf(buf, "%d\n", &time); |
| if (ret != 1) { |
| vipx_err("[debugfs] Failed to get time value(%d)\n", ret); |
| goto out; |
| } |
| |
| vipx_info("[debugfs] wait time is changed form %d ms to %d ms\n", |
| itf->wait_time, time); |
| itf->wait_time = time; |
| |
| vipx_leave(); |
| out: |
| return count; |
| } |
| |
| static const struct file_operations vipx_debug_wait_time_fops = { |
| .open = vipx_debug_wait_time_open, |
| .read = seq_read, |
| .write = vipx_debug_wait_time_write, |
| .llseek = seq_lseek, |
| .release = single_release |
| }; |
| |
| static int vipx_debug_power_show(struct seq_file *file, void *unused) |
| { |
| #if defined(CONFIG_PM_DEVFREQ) |
| struct vipx_debug *debug; |
| struct vipx_pm *pm; |
| |
| vipx_enter(); |
| debug = file->private; |
| pm = &debug->system->pm; |
| |
| mutex_lock(&pm->lock); |
| if (pm->dvfs) |
| seq_puts(file, "dvfs : on\n"); |
| else |
| seq_puts(file, "dvfs : off\n"); |
| mutex_unlock(&pm->lock); |
| |
| seq_puts(file, "Command to change dvfs mode\n"); |
| seq_puts(file, " echo enable/disable > /d/vipx/power\n"); |
| |
| vipx_leave(); |
| return 0; |
| #else |
| seq_puts(file, "devfreq is not supported\n"); |
| return 0; |
| #endif |
| } |
| |
| static int vipx_debug_power_open(struct inode *inode, struct file *filp) |
| { |
| return single_open(filp, vipx_debug_power_show, inode->i_private); |
| } |
| |
| static ssize_t vipx_debug_power_write(struct file *filp, |
| const char __user *user_buf, size_t count, loff_t *ppos) |
| { |
| int ret; |
| struct seq_file *file; |
| struct vipx_debug *debug; |
| struct vipx_pm *pm; |
| char command[10]; |
| ssize_t size; |
| |
| vipx_enter(); |
| file = filp->private_data; |
| debug = file->private; |
| pm = &debug->system->pm; |
| |
| size = simple_write_to_buffer(command, sizeof(command) - 1, ppos, |
| user_buf, count); |
| if (size < 0) { |
| ret = size; |
| vipx_err("Failed to get user parameter(%d)\n", ret); |
| goto p_err; |
| } |
| |
| command[size] = '\0'; |
| if (sysfs_streq(command, "enable")) { |
| mutex_lock(&pm->lock); |
| pm->dvfs = true; |
| mutex_unlock(&pm->lock); |
| } else if (sysfs_streq(command, "disable")) { |
| mutex_lock(&pm->lock); |
| pm->dvfs = false; |
| mutex_unlock(&pm->lock); |
| } else { |
| ret = -EINVAL; |
| vipx_err("command[%s] about power is invalid\n", command); |
| goto p_err; |
| } |
| |
| vipx_leave(); |
| return count; |
| p_err: |
| return ret; |
| } |
| |
| static const struct file_operations vipx_debug_power_fops = { |
| .open = vipx_debug_power_open, |
| .read = seq_read, |
| .write = vipx_debug_power_write, |
| .llseek = seq_lseek, |
| .release = single_release |
| }; |
| |
| static int __vipx_debug_write_file(const char *name, void *kva) |
| { |
| int ret; |
| mm_segment_t old_fs; |
| int fd; |
| struct file *fp; |
| loff_t pos = 0; |
| struct vipx_debug_log_area *area; |
| char head[40]; |
| int write_size; |
| int idx; |
| char line[134]; |
| |
| vipx_enter(); |
| if (!current->fs) { |
| vipx_warn("Failed to write %s as fs is invalid\n", name); |
| return -ESRCH; |
| } |
| |
| old_fs = get_fs(); |
| set_fs(KERNEL_DS); |
| fd = sys_open(name, O_RDWR | O_CREAT | O_TRUNC, 0640); |
| if (fd < 0) { |
| ret = fd; |
| vipx_err("sys_open(%s) is fail(%d)\n", name, ret); |
| goto p_err; |
| } |
| |
| fp = fget(fd); |
| if (!fp) { |
| ret = -EFAULT; |
| vipx_err("fget(%s) is fail\n", name); |
| goto p_err; |
| } |
| |
| area = kva; |
| write_size = snprintf(head, sizeof(head), "%d/%d/%d/%d\n", |
| area->front, area->rear, |
| area->line_size, area->queue_size); |
| |
| vfs_write(fp, head, write_size, &pos); |
| |
| for (idx = 0; idx < area->queue_size; ++idx) { |
| write_size = snprintf(line, sizeof(line), "[%4d]%s", |
| idx, area->queue + (area->line_size * idx)); |
| if (write_size < 9) |
| continue; |
| |
| if (line[write_size - 1] != '\n') |
| line[write_size - 1] = '\n'; |
| |
| if (line[write_size] != '\0') |
| line[write_size] = '\0'; |
| |
| vfs_write(fp, line, write_size, &pos); |
| } |
| |
| fput(fp); |
| sys_close(fd); |
| set_fs(old_fs); |
| |
| vipx_leave(); |
| return 0; |
| p_err: |
| set_fs(old_fs); |
| return ret; |
| } |
| |
| int vipx_debug_write_log_binary(void) |
| { |
| int ret; |
| struct vipx_system *sys; |
| char fname[30]; |
| |
| vipx_enter(); |
| sys = &debug_device->system; |
| |
| if (!sys->memory.log.kvaddr) |
| return -ENOMEM; |
| |
| snprintf(fname, sizeof(fname), "%s/%s", VIPX_DEBUG_BIN_PATH, |
| "vipx_log.bin"); |
| ret = __vipx_debug_write_file(fname, sys->memory.log.kvaddr); |
| if (!ret) |
| vipx_info("%s was created for debugging\n", fname); |
| |
| vipx_leave(); |
| return ret; |
| } |
| |
| static bool __vipx_debug_log_valid(struct vipx_debug_log *log) |
| { |
| vipx_check(); |
| if (!log->area || |
| log->area->front >= log->area->queue_size || |
| log->area->rear >= log->area->queue_size) |
| return false; |
| else |
| return true; |
| } |
| |
| static bool __vipx_debug_log_empty(struct vipx_debug_log *log) |
| { |
| vipx_check(); |
| return (log->area->front == log->area->rear); |
| } |
| |
| static void __vipx_debug_log_increase_front(struct vipx_debug_log *log) |
| { |
| vipx_enter(); |
| log->area->front = (log->area->front + 1) % log->area->queue_size; |
| vipx_leave(); |
| } |
| |
| static void __vipx_debug_log_start(struct vipx_debug *debug) |
| { |
| vipx_enter(); |
| add_timer(&debug->target_log.timer); |
| vipx_leave(); |
| } |
| |
| static void __vipx_debug_log_stop(struct vipx_debug *debug) |
| { |
| vipx_enter(); |
| del_timer_sync(&debug->target_log.timer); |
| vipx_debug_log_flush(debug); |
| vipx_leave(); |
| } |
| |
| static void __vipx_debug_log_open(struct vipx_debug *debug) |
| { |
| struct vipx_debug_log *log; |
| struct vipx_system *sys; |
| |
| vipx_enter(); |
| log = &debug->target_log; |
| sys = &debug_device->system; |
| |
| log->area = sys->memory.log.kvaddr; |
| log->area->front = -1; |
| log->area->rear = -1; |
| log->area->line_size = VIPX_DEBUG_LOG_LINE_SIZE; |
| log->area->queue_size = (sys->memory.log.size - 32) / |
| log->area->line_size; |
| vipx_leave(); |
| } |
| |
| static char *__vipx_debug_log_dequeue(struct vipx_debug *debug) |
| { |
| struct vipx_debug_log *log; |
| int front; |
| char *buf; |
| |
| vipx_enter(); |
| log = &debug->target_log; |
| |
| if (__vipx_debug_log_empty(log)) |
| return NULL; |
| |
| if (!__vipx_debug_log_valid(log)) { |
| vipx_warn("debug log queue is broken(%d/%d)\n", |
| log->area->front, log->area->rear); |
| __vipx_debug_log_open(debug); |
| return NULL; |
| } |
| |
| front = (log->area->front + 1) % log->area->queue_size; |
| if (front < 0) { |
| vipx_warn("debug log queue has invalid value(%d/%d)\n", |
| log->area->front, log->area->rear); |
| return NULL; |
| } |
| |
| buf = log->area->queue + (log->area->line_size * front); |
| if (buf[log->area->line_size - 2] != '\0') |
| buf[log->area->line_size - 2] = '\n'; |
| buf[log->area->line_size - 1] = '\0'; |
| |
| vipx_leave(); |
| return buf; |
| } |
| |
| static void vipx_debug_log_print(unsigned long data) |
| { |
| struct vipx_debug *debug; |
| struct vipx_debug_log *log; |
| char *line; |
| |
| vipx_enter(); |
| debug = (struct vipx_debug *)data; |
| log = &debug->target_log; |
| |
| while (true) { |
| line = __vipx_debug_log_dequeue(debug); |
| if (!line) |
| break; |
| vipx_info("[timer(%4d)] %s", |
| (log->area->front + 1) % log->area->queue_size, |
| line); |
| __vipx_debug_log_increase_front(log); |
| } |
| |
| mod_timer(&log->timer, jiffies + msecs_to_jiffies(VIPX_DEBUG_LOG_TIME)); |
| vipx_leave(); |
| } |
| |
| static void __vipx_debug_log_init(struct vipx_debug *debug) |
| { |
| struct vipx_debug_log *log; |
| |
| vipx_enter(); |
| log = &debug->target_log; |
| |
| init_timer(&log->timer); |
| log->timer.expires = jiffies + msecs_to_jiffies(VIPX_DEBUG_LOG_TIME); |
| log->timer.data = (unsigned long)debug; |
| log->timer.function = vipx_debug_log_print; |
| vipx_leave(); |
| } |
| |
| void vipx_debug_log_flush(struct vipx_debug *debug) |
| { |
| struct vipx_debug_log *log; |
| char *line; |
| |
| vipx_enter(); |
| log = &debug->target_log; |
| |
| while (true) { |
| line = __vipx_debug_log_dequeue(debug); |
| if (!line) |
| break; |
| vipx_info("[flush(%4d)] %s", |
| (log->area->front + 1) % log->area->queue_size, |
| line); |
| __vipx_debug_log_increase_front(log); |
| } |
| vipx_leave(); |
| } |
| |
| int vipx_debug_start(struct vipx_debug *debug) |
| { |
| vipx_enter(); |
| __vipx_debug_log_start(debug); |
| set_bit(VIPX_DEBUG_STATE_START, &debug->state); |
| vipx_leave(); |
| return 0; |
| } |
| |
| int vipx_debug_stop(struct vipx_debug *debug) |
| { |
| vipx_enter(); |
| clear_bit(VIPX_DEBUG_STATE_START, &debug->state); |
| __vipx_debug_log_stop(debug); |
| vipx_leave(); |
| return 0; |
| } |
| |
| int vipx_debug_open(struct vipx_debug *debug) |
| { |
| vipx_enter(); |
| __vipx_debug_log_open(debug); |
| vipx_leave(); |
| return 0; |
| } |
| |
| int vipx_debug_close(struct vipx_debug *debug) |
| { |
| vipx_enter(); |
| vipx_debug_log_flush(debug); |
| if (debug->log_bin_enable) |
| vipx_debug_write_log_binary(); |
| vipx_leave(); |
| return 0; |
| } |
| |
| int vipx_debug_probe(struct vipx_device *device) |
| { |
| struct vipx_debug *debug; |
| |
| vipx_enter(); |
| debug_device = device; |
| debug = &device->debug; |
| debug->system = &device->system; |
| debug->state = 0; |
| |
| debug->root = debugfs_create_dir("vipx", NULL); |
| if (!debug->root) { |
| vipx_err("Failed to create debug root file\n"); |
| goto p_end; |
| } |
| |
| debug->mem = debugfs_create_file("mem", 0640, debug->root, debug, |
| &vipx_debug_mem_fops); |
| if (!debug->mem) |
| vipx_err("Failed to create mem debugfs file\n"); |
| |
| debug->log = debugfs_create_u32("log", 0640, debug->root, |
| &vipx_debug_log_enable); |
| if (!debug->log) |
| vipx_err("Failed to create log debugfs file\n"); |
| |
| debug->log_bin = debugfs_create_u32("log_bin", 0640, debug->root, |
| &debug->log_bin_enable); |
| if (!debug->log_bin) |
| vipx_err("Failed to create log_bin debugfs file\n"); |
| |
| debug->devfreq = debugfs_create_file("devfreq", 0640, debug->root, |
| debug, &vipx_debug_devfreq_fops); |
| if (!debug->devfreq) |
| vipx_err("Failed to create devfreq debugfs file\n"); |
| |
| debug->clk = debugfs_create_file("clk", 0640, debug->root, debug, |
| &vipx_debug_clk_fops); |
| if (!debug->clk) |
| vipx_err("Failed to create clk debugfs file\n"); |
| |
| debug->wait_time = debugfs_create_file("wait_time", 0640, debug->root, |
| debug, &vipx_debug_wait_time_fops); |
| if (!debug->wait_time) |
| vipx_err("Failed to create wait_time debugfs file\n"); |
| |
| debug->power = debugfs_create_file("power", 0640, debug->root, |
| debug, &vipx_debug_power_fops); |
| if (!debug->power) |
| vipx_err("Failed to create power debugfs file\n"); |
| |
| __vipx_debug_log_init(debug); |
| |
| vipx_leave(); |
| p_end: |
| return 0; |
| } |
| |
| void vipx_debug_remove(struct vipx_debug *debug) |
| { |
| debugfs_remove_recursive(debug->root); |
| } |