335 lines
7.8 KiB
C
Executable File
335 lines
7.8 KiB
C
Executable File
/*
|
|
* Copyright (c) 2013 Samsung Electronics Co., Ltd.
|
|
* http://www.samsung.com
|
|
*
|
|
* Exynos-SnapShot debugging framework for Exynos SoC
|
|
*
|
|
* Author: Hosung Kim <Hosung0.kim@samsung.com>
|
|
*
|
|
* 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/kernel.h>
|
|
#include <linux/slab.h>
|
|
#include <linux/module.h>
|
|
#include <linux/ktime.h>
|
|
#include <linux/kallsyms.h>
|
|
#include <linux/platform_device.h>
|
|
#include <linux/clk-provider.h>
|
|
#include <linux/pstore_ram.h>
|
|
#include <linux/sched/clock.h>
|
|
#include <linux/ftrace.h>
|
|
|
|
#include "debug-snapshot-local.h"
|
|
#include <asm/irq.h>
|
|
#include <asm/traps.h>
|
|
#include <asm/hardirq.h>
|
|
#include <asm/stacktrace.h>
|
|
#include <linux/debug-snapshot.h>
|
|
#include <linux/kernel_stat.h>
|
|
#include <linux/irqnr.h>
|
|
#include <linux/irq.h>
|
|
#include <linux/irqdesc.h>
|
|
|
|
/*
|
|
* sysfs implementation for debug-snapshot
|
|
* you can access the sysfs of debug-snapshot to /sys/devices/system/debug_snapshot
|
|
* path.
|
|
*/
|
|
static struct bus_type dss_subsys = {
|
|
.name = "debug-snapshot",
|
|
.dev_name = "debug-snapshot",
|
|
};
|
|
|
|
extern int dss_irqlog_exlist[DSS_EX_MAX_NUM];
|
|
extern int dss_irqexit_exlist[DSS_EX_MAX_NUM];
|
|
extern unsigned int dss_irqexit_threshold;
|
|
|
|
static ssize_t dss_enable_show(struct kobject *kobj,
|
|
struct kobj_attribute *attr, char *buf)
|
|
{
|
|
struct dbg_snapshot_item *item;
|
|
unsigned long i;
|
|
ssize_t n = 0;
|
|
|
|
/* item */
|
|
for (i = 0; i < dss_desc.log_cnt; i++) {
|
|
item = &dss_items[i];
|
|
n += scnprintf(buf + n, 24, "%-12s : %sable\n",
|
|
item->name, item->entry.enabled ? "en" : "dis");
|
|
}
|
|
|
|
/* base */
|
|
n += scnprintf(buf + n, 24, "%-12s : %sable\n",
|
|
"base", dss_base.enabled ? "en" : "dis");
|
|
|
|
return n;
|
|
}
|
|
|
|
static ssize_t dss_enable_store(struct kobject *kobj,
|
|
struct kobj_attribute *attr,
|
|
const char *buf, size_t count)
|
|
{
|
|
int en;
|
|
char *name;
|
|
|
|
name = (char *)kstrndup(buf, count, GFP_KERNEL);
|
|
if (!name)
|
|
return count;
|
|
|
|
name[count - 1] = '\0';
|
|
|
|
en = dbg_snapshot_get_enable(name);
|
|
|
|
if (en == -1)
|
|
pr_info("echo name > enabled\n");
|
|
else {
|
|
if (en)
|
|
dbg_snapshot_set_enable(name, false);
|
|
else
|
|
dbg_snapshot_set_enable(name, true);
|
|
}
|
|
|
|
kfree(name);
|
|
return count;
|
|
}
|
|
|
|
static ssize_t dss_callstack_show(struct kobject *kobj,
|
|
struct kobj_attribute *attr, char *buf)
|
|
{
|
|
ssize_t n = 0;
|
|
|
|
n = scnprintf(buf, 24, "callstack depth : %d\n", dss_desc.callstack);
|
|
|
|
return n;
|
|
}
|
|
|
|
static ssize_t dss_callstack_store(struct kobject *kobj, struct kobj_attribute *attr,
|
|
const char *buf, size_t count)
|
|
{
|
|
unsigned long callstack;
|
|
|
|
callstack = simple_strtoul(buf, NULL, 0);
|
|
pr_info("callstack depth(min 1, max 4) : %lu\n", callstack);
|
|
|
|
if (callstack < 5 && callstack > 0) {
|
|
dss_desc.callstack = (unsigned int)callstack;
|
|
pr_info("success inserting %lu to callstack value\n", callstack);
|
|
}
|
|
return count;
|
|
}
|
|
|
|
static ssize_t dss_irqlog_exlist_show(struct kobject *kobj,
|
|
struct kobj_attribute *attr, char *buf)
|
|
{
|
|
unsigned long i;
|
|
ssize_t n = 0;
|
|
|
|
n = scnprintf(buf, 24, "excluded irq number\n");
|
|
|
|
for (i = 0; i < ARRAY_SIZE(dss_irqlog_exlist); i++) {
|
|
if (dss_irqlog_exlist[i] == 0)
|
|
break;
|
|
n += scnprintf(buf + n, 24, "irq num: %-4d\n", dss_irqlog_exlist[i]);
|
|
}
|
|
return n;
|
|
}
|
|
|
|
static ssize_t dss_irqlog_exlist_store(struct kobject *kobj,
|
|
struct kobj_attribute *attr,
|
|
const char *buf, size_t count)
|
|
{
|
|
unsigned long i;
|
|
unsigned long irq;
|
|
|
|
irq = simple_strtoul(buf, NULL, 0);
|
|
pr_info("irq number : %lu\n", irq);
|
|
|
|
for (i = 0; i < ARRAY_SIZE(dss_irqlog_exlist); i++) {
|
|
if (dss_irqlog_exlist[i] == 0)
|
|
break;
|
|
}
|
|
|
|
if (i == ARRAY_SIZE(dss_irqlog_exlist)) {
|
|
pr_err("list is full\n");
|
|
return count;
|
|
}
|
|
|
|
if (irq != 0) {
|
|
dss_irqlog_exlist[i] = irq;
|
|
pr_info("success inserting %lu to list\n", irq);
|
|
}
|
|
return count;
|
|
}
|
|
|
|
#ifdef CONFIG_DEBUG_SNAPSHOT_IRQ_EXIT
|
|
static ssize_t dss_irqexit_exlist_show(struct kobject *kobj,
|
|
struct kobj_attribute *attr, char *buf)
|
|
{
|
|
unsigned long i;
|
|
ssize_t n = 0;
|
|
|
|
n = scnprintf(buf, 36, "Excluded irq number\n");
|
|
for (i = 0; i < ARRAY_SIZE(dss_irqexit_exlist); i++) {
|
|
if (dss_irqexit_exlist[i] == 0)
|
|
break;
|
|
n += scnprintf(buf + n, 24, "IRQ num: %-4d\n", dss_irqexit_exlist[i]);
|
|
}
|
|
return n;
|
|
}
|
|
|
|
static ssize_t dss_irqexit_exlist_store(struct kobject *kobj,
|
|
struct kobj_attribute *attr,
|
|
const char *buf, size_t count)
|
|
{
|
|
unsigned long i;
|
|
unsigned long irq;
|
|
|
|
irq = simple_strtoul(buf, NULL, 0);
|
|
pr_info("irq number : %lu\n", irq);
|
|
|
|
for (i = 0; i < ARRAY_SIZE(dss_irqexit_exlist); i++) {
|
|
if (dss_irqexit_exlist[i] == 0)
|
|
break;
|
|
}
|
|
|
|
if (i == ARRAY_SIZE(dss_irqexit_exlist)) {
|
|
pr_err("list is full\n");
|
|
return count;
|
|
}
|
|
|
|
if (irq != 0) {
|
|
dss_irqexit_exlist[i] = irq;
|
|
pr_info("success inserting %lu to list\n", irq);
|
|
}
|
|
return count;
|
|
}
|
|
|
|
static ssize_t dss_irqexit_threshold_show(struct kobject *kobj,
|
|
struct kobj_attribute *attr, char *buf)
|
|
{
|
|
ssize_t n;
|
|
|
|
n = scnprintf(buf, 46, "threshold : %12u us\n", dss_irqexit_threshold);
|
|
return n;
|
|
}
|
|
|
|
static ssize_t dss_irqexit_threshold_store(struct kobject *kobj,
|
|
struct kobj_attribute *attr,
|
|
const char *buf, size_t count)
|
|
{
|
|
unsigned long val;
|
|
|
|
val = simple_strtoul(buf, NULL, 0);
|
|
pr_info("threshold value : %lu\n", val);
|
|
|
|
if (val != 0) {
|
|
dss_irqexit_threshold = (unsigned int)val;
|
|
pr_info("success %lu to threshold\n", val);
|
|
}
|
|
return count;
|
|
}
|
|
#endif
|
|
|
|
#ifdef CONFIG_DEBUG_SNAPSHOT_REG
|
|
static ssize_t dss_reg_exlist_show(struct kobject *kobj,
|
|
struct kobj_attribute *attr, char *buf)
|
|
{
|
|
unsigned long i;
|
|
ssize_t n = 0;
|
|
|
|
n = scnprintf(buf, 36, "excluded register address\n");
|
|
for (i = 0; i < ARRAY_SIZE(dss_reg_exlist); i++) {
|
|
if (dss_reg_exlist[i].addr == 0)
|
|
break;
|
|
n += scnprintf(buf + n, 40, "register addr: %08zx size: %08zx\n",
|
|
dss_reg_exlist[i].addr, dss_reg_exlist[i].size);
|
|
}
|
|
return n;
|
|
}
|
|
|
|
static ssize_t dss_reg_exlist_store(struct kobject *kobj,
|
|
struct kobj_attribute *attr,
|
|
const char *buf, size_t count)
|
|
{
|
|
unsigned long i;
|
|
size_t addr;
|
|
|
|
addr = simple_strtoul(buf, NULL, 0);
|
|
pr_info("register addr: %zx\n", addr);
|
|
|
|
for (i = 0; i < ARRAY_SIZE(dss_reg_exlist); i++) {
|
|
if (dss_reg_exlist[i].addr == 0)
|
|
break;
|
|
}
|
|
if (addr != 0) {
|
|
dss_reg_exlist[i].size = SZ_4K;
|
|
dss_reg_exlist[i].addr = addr;
|
|
pr_info("success %zx to threshold\n", (addr));
|
|
}
|
|
return count;
|
|
}
|
|
#endif
|
|
|
|
|
|
static struct kobj_attribute dss_enable_attr =
|
|
__ATTR(enabled, 0644, dss_enable_show, dss_enable_store);
|
|
|
|
static struct kobj_attribute dss_callstack_attr =
|
|
__ATTR(callstack, 0644, dss_callstack_show, dss_callstack_store);
|
|
|
|
static struct kobj_attribute dss_irqlog_attr =
|
|
__ATTR(exlist_irqdisabled, 0644, dss_irqlog_exlist_show,
|
|
dss_irqlog_exlist_store);
|
|
#ifdef CONFIG_DEBUG_SNAPSHOT_IRQ_EXIT
|
|
static struct kobj_attribute dss_irqexit_attr =
|
|
__ATTR(exlist_irqexit, 0644, dss_irqexit_exlist_show,
|
|
dss_irqexit_exlist_store);
|
|
|
|
static struct kobj_attribute dss_irqexit_threshold_attr =
|
|
__ATTR(threshold_irqexit, 0644, dss_irqexit_threshold_show,
|
|
dss_irqexit_threshold_store);
|
|
#endif
|
|
#ifdef CONFIG_DEBUG_SNAPSHOT_REG
|
|
|
|
static struct kobj_attribute dss_reg_attr =
|
|
__ATTR(exlist_reg, 0644, dss_reg_exlist_show, dss_reg_exlist_store);
|
|
#endif
|
|
|
|
static struct attribute *dss_sysfs_attrs[] = {
|
|
&dss_enable_attr.attr,
|
|
&dss_callstack_attr.attr,
|
|
&dss_irqlog_attr.attr,
|
|
#ifdef CONFIG_DEBUG_SNAPSHOT_IRQ_EXIT
|
|
&dss_irqexit_attr.attr,
|
|
&dss_irqexit_threshold_attr.attr,
|
|
#endif
|
|
#ifdef CONFIG_DEBUG_SNAPSHOT_REG
|
|
&dss_reg_attr.attr,
|
|
#endif
|
|
NULL,
|
|
};
|
|
|
|
static struct attribute_group dss_sysfs_group = {
|
|
.attrs = dss_sysfs_attrs,
|
|
};
|
|
|
|
static const struct attribute_group *dss_sysfs_groups[] = {
|
|
&dss_sysfs_group,
|
|
NULL,
|
|
};
|
|
|
|
static int __init dbg_snapshot_sysfs_init(void)
|
|
{
|
|
int ret = 0;
|
|
|
|
ret = subsys_system_register(&dss_subsys, dss_sysfs_groups);
|
|
if (ret)
|
|
pr_err("fail to register debug-snapshop subsys\n");
|
|
|
|
return ret;
|
|
}
|
|
late_initcall(dbg_snapshot_sysfs_init);
|