mirror of
https://github.com/physwizz/a155-U-u1.git
synced 2024-11-19 13:27:49 +00:00
258 lines
6.1 KiB
C
258 lines
6.1 KiB
C
// SPDX-License-Identifier: GPL-2.0
|
|
/*
|
|
* Copyright (C) 2015 MediaTek Inc.
|
|
*/
|
|
|
|
#include <linux/module.h>
|
|
#include <linux/proc_fs.h>
|
|
#include <linux/tracepoint.h>
|
|
|
|
#include <mt-plat/aee.h>
|
|
#define CREATE_TRACE_POINTS
|
|
#include <mt-plat/slog.h>
|
|
|
|
/**
|
|
* Data structures to store tracepoints information
|
|
*/
|
|
struct tracepoints_table {
|
|
const char *name;
|
|
const char *mod_name;
|
|
bool module;
|
|
void *func;
|
|
struct tracepoint *tp;
|
|
bool init;
|
|
};
|
|
|
|
static unsigned int ufs_count;
|
|
static unsigned int ccci_count;
|
|
static unsigned int gpu_count;
|
|
static unsigned int threshold;
|
|
static struct proc_dir_entry *dir;
|
|
|
|
void slog(const char *fmt, ...)
|
|
{
|
|
struct va_format vaf;
|
|
va_list args;
|
|
|
|
va_start(args, fmt);
|
|
vaf.fmt = fmt;
|
|
vaf.va = &args;
|
|
trace_slog(&vaf);
|
|
va_end(args);
|
|
}
|
|
|
|
static void probe_ufs_mtk_event(void *data, unsigned int type,
|
|
unsigned int val)
|
|
{
|
|
ufs_count++;
|
|
slog("#$#ufs#@#event#%lu:%lu(0x%x)#%d", type, val, val, ufs_count);
|
|
if ((threshold > 0) && ((ufs_count % threshold) == 0)) {
|
|
aee_kernel_exception("ufs", "occur:%d, threshold:%d\n", ufs_count, threshold);
|
|
}
|
|
}
|
|
|
|
static void probe_ccci_event(void *data, char *string, char *sub_string,
|
|
unsigned int sub_type, unsigned int resv)
|
|
{
|
|
ccci_count++;
|
|
slog("#$#%s#@#%s#%d:%d#%d", string, sub_string, sub_type, resv, ccci_count);
|
|
}
|
|
|
|
static void probe_gpu_hardstop_event(void *data, char *string, char *sub_string,
|
|
unsigned int gpu_freq, unsigned int gpu_volt, unsigned int gpu_vsram,
|
|
unsigned int stack_freq, unsigned int stack_volt, unsigned int stack_vsram)
|
|
{
|
|
gpu_count++;
|
|
slog("#$#%s#@#%s#%d:%d:%d:%d:%d:%d#%d",
|
|
string, sub_string, gpu_freq, gpu_volt, gpu_vsram,
|
|
stack_freq, stack_volt, stack_vsram, gpu_count);
|
|
}
|
|
|
|
static struct tracepoints_table interests[] = {
|
|
{.name = "ufs_mtk_event", .mod_name = NULL, .module = false, .func = probe_ufs_mtk_event},
|
|
{.name = "ccci_event", .mod_name = NULL, .module = false, .func = probe_ccci_event},
|
|
{.name = "gpu_hardstop", .mod_name = "mtk_gpufreq_wrapper", .module = true, .func = probe_gpu_hardstop_event},
|
|
};
|
|
|
|
/**
|
|
* Find the struct tracepoint* associated with a given tracepoint
|
|
* name.
|
|
*/
|
|
static void lookup_tracepoints(struct tracepoint *tp, void *ignore)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < ARRAY_SIZE(interests); i++) {
|
|
if ((interests[i].module == false) && (strcmp(interests[i].name, tp->name) == 0)) {
|
|
interests[i].tp = tp;
|
|
tracepoint_probe_register(interests[i].tp, interests[i].func, NULL);
|
|
interests[i].init = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
static void cleanup(void)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < ARRAY_SIZE(interests); i++) {
|
|
if (interests[i].init) {
|
|
tracepoint_probe_unregister(interests[i].tp,
|
|
interests[i].func, NULL);
|
|
}
|
|
}
|
|
}
|
|
|
|
#ifdef MODULE
|
|
static int slog_module_callback(struct notifier_block *nb,
|
|
unsigned long val, void *data)
|
|
{
|
|
struct module *mod = data;
|
|
tracepoint_ptr_t *iter, *begin, *end;
|
|
struct tracepoint *tp;
|
|
int i;
|
|
|
|
if (val !=MODULE_STATE_LIVE)
|
|
return NOTIFY_DONE;
|
|
|
|
for (i = 0; i < ARRAY_SIZE(interests); i++) {
|
|
if ((interests[i].module == true) && (interests[i].init == false)) {
|
|
begin = mod->tracepoints_ptrs;
|
|
end = mod->tracepoints_ptrs + mod->num_tracepoints;
|
|
for (iter = begin; iter < end; iter++) {
|
|
tp = tracepoint_ptr_deref(iter);
|
|
if (strcmp(interests[i].name, tp->name) == 0) {
|
|
interests[i].tp = tp;
|
|
tracepoint_probe_register(interests[i].tp, interests[i].func, NULL);
|
|
interests[i].init = true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return NOTIFY_OK;
|
|
}
|
|
|
|
static struct notifier_block slog_module_nb = {
|
|
.notifier_call = slog_module_callback,
|
|
};
|
|
#endif
|
|
|
|
static int slog_threshold_proc_show(struct seq_file *m, void *v)
|
|
{
|
|
seq_printf(m, "slog threshold: %d\n", threshold);
|
|
return 0;
|
|
}
|
|
|
|
static char *_copy_from_user_for_proc(const char __user *buffer, size_t count)
|
|
{
|
|
char *buf = (char *)__get_free_page(GFP_USER);
|
|
|
|
if (!buf)
|
|
return NULL;
|
|
|
|
if (count >= PAGE_SIZE)
|
|
goto out;
|
|
|
|
if (copy_from_user(buf, buffer, count))
|
|
goto out;
|
|
|
|
buf[count] = '\0';
|
|
|
|
return buf;
|
|
|
|
out:
|
|
free_page((unsigned long)buf);
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static ssize_t slog_threshold_proc_write(struct file *file,
|
|
const char __user *buffer, size_t count, loff_t *pos)
|
|
{
|
|
unsigned int num;
|
|
int rc;
|
|
|
|
char *buf = _copy_from_user_for_proc(buffer, count);
|
|
|
|
if (!buf)
|
|
return -EINVAL;
|
|
|
|
rc = kstrtouint(buf, 10, &num);
|
|
|
|
if (rc < 0)
|
|
pr_info("transfer string to int error\n");
|
|
else
|
|
threshold = num;
|
|
|
|
free_page((unsigned long)buf);
|
|
|
|
pr_info("set threshold to %d\n", threshold);
|
|
return count;
|
|
}
|
|
|
|
#define PROC_FOPS_RW(name) \
|
|
static int name ## _proc_open(struct inode *inode, struct file *file) \
|
|
{ \
|
|
return single_open(file, name ## _proc_show, PDE_DATA(inode)); \
|
|
} \
|
|
static const struct proc_ops name ## _proc_fops = { \
|
|
.proc_open = name ## _proc_open, \
|
|
.proc_read = seq_read, \
|
|
.proc_lseek = seq_lseek, \
|
|
.proc_release = single_release, \
|
|
.proc_write = name ## _proc_write, \
|
|
}
|
|
|
|
#define PROC_ENTRY(name) {__stringify(name), &name ## _proc_fops}
|
|
|
|
PROC_FOPS_RW(slog_threshold);
|
|
|
|
int slog_procfs_init(void)
|
|
{
|
|
int i = 0;
|
|
|
|
struct pentry {
|
|
const char *name;
|
|
const struct proc_ops *fops;
|
|
};
|
|
|
|
const struct pentry entries[] = {
|
|
PROC_ENTRY(slog_threshold),
|
|
};
|
|
|
|
dir = proc_mkdir("slog", NULL);
|
|
|
|
if (!dir) {
|
|
pr_info("mkdir /proc/slog failed\n");
|
|
return -ENOMEM;
|
|
}
|
|
|
|
for (i = 0; i < ARRAY_SIZE(entries); i++) {
|
|
if (!proc_create(entries[i].name, 0640, dir, entries[i].fops))
|
|
pr_info("create /proc/slog/%s failed\n", entries[i].name);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int mtk_slog_init(void)
|
|
{
|
|
#ifdef MODULE
|
|
register_module_notifier(&slog_module_nb);
|
|
#endif
|
|
for_each_kernel_tracepoint(lookup_tracepoints, NULL);
|
|
slog_procfs_init();
|
|
|
|
return 0;
|
|
}
|
|
|
|
void mtk_slog_exit(void)
|
|
{
|
|
#ifdef MODULE
|
|
unregister_module_notifier(&slog_module_nb);
|
|
#endif
|
|
remove_proc_entry("slog_threshold", dir);
|
|
cleanup();
|
|
}
|