826 lines
18 KiB
C
Executable File
826 lines
18 KiB
C
Executable File
/*
|
|
* 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);
|
|
}
|