blob: aeb0b0db4dd94ee5cdd20e3f279317a4c2833843 [file] [log] [blame]
/*
* Copyright (c) 2015 Samsung Electronics Co., Ltd.
* http://www.samsung.com/
*
* EXYNOS SMC
*
* 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/sched.h>
#include <linux/spinlock.h>
#include <linux/smc.h>
#include <linux/sysfs.h>
#include <linux/kobject.h>
#include <linux/device.h>
#include <linux/sched/clock.h>
#include <asm/smp.h>
#ifdef CONFIG_DEBUG_SNAPSHOT_LOGGING_SMC
#define EXYNOS_SMC_LOG_SIZE (1024)
static DEFINE_SPINLOCK(smc_log_lock);
static struct bus_type esmc_subsys = {
.name = "exynos-smc",
.dev_name = "exynos-smc",
};
struct esmc_log {
unsigned long cpu_clk; /* cpu clock */
unsigned long long start_time; /* start time */
unsigned long sp; /* call stack */
unsigned long long end_time; /* end time */
unsigned long long latency; /* latency */
unsigned long cmd;
unsigned long arg1;
unsigned long arg2;
unsigned long arg3;
};
struct esmc_log smc_log[NR_CPUS][EXYNOS_SMC_LOG_SIZE];
static uint32_t smc_log_idx[NR_CPUS];
static unsigned int esmc_log_threshold =
CONFIG_EXYNOS_SMC_LOG_THRESHOLD;
static ssize_t esmc_log_threshold_show(struct kobject *kobj,
struct kobj_attribute *attr, char *buf)
{
ssize_t n;
n = scnprintf(buf, 46, "threshold : %12u us\n", esmc_log_threshold);
return n;
}
static ssize_t esmc_log_threshold_store(struct kobject *kobj,
struct kobj_attribute *attr,
const char *buf, size_t count)
{
unsigned long val;
int err;
err = kstrtoul(buf, 0, &val);
if (err != 0) {
pr_err("can't read threshold value with err 0x%x\n", err);
} else {
esmc_log_threshold = val;
pr_info("threshold value : %lu\n", val);
}
return count;
}
static struct kobj_attribute esmc_log_threshold_attr =
__ATTR(threshold_esmc_log, 0644, esmc_log_threshold_show,
esmc_log_threshold_store);
static struct attribute *esmc_sysfs_attrs[] = {
&esmc_log_threshold_attr.attr,
NULL,
};
static struct attribute_group esmc_sysfs_group = {
.attrs = esmc_sysfs_attrs,
};
static const struct attribute_group *esmc_sysfs_groups[] = {
&esmc_sysfs_group,
NULL,
};
static int __init esmc_sysfs_init(void)
{
int ret = 0;
ret = subsys_system_register(&esmc_subsys, esmc_sysfs_groups);
if (ret)
pr_err("fail to register exynos-smc subsys\n");
return ret;
}
late_initcall(esmc_sysfs_init);
#endif
int exynos_smc(unsigned long cmd, unsigned long arg1, unsigned long arg2, unsigned long arg3)
{
int32_t ret;
#ifdef CONFIG_DEBUG_SNAPSHOT_LOGGING_SMC
unsigned long flags, stime, etime, latency;
unsigned int cpu, idx;
cpu = raw_smp_processor_id();
stime = cpu_clock(cpu);
#endif
ret = __exynos_smc(cmd, arg1, arg2, arg3);
#ifdef CONFIG_DEBUG_SNAPSHOT_LOGGING_SMC
etime = cpu_clock(cpu);
latency = etime - stime;
spin_lock_irqsave(&smc_log_lock, flags);
if (latency > (esmc_log_threshold * 1000)) {
idx = smc_log_idx[cpu];
smc_log[cpu][idx].cpu_clk = local_clock();
smc_log[cpu][idx].latency = latency;
smc_log[cpu][idx].start_time = stime;
smc_log[cpu][idx].end_time = etime;
smc_log[cpu][idx].sp = (uint64_t)current_stack_pointer;
smc_log[cpu][idx].cmd = cmd;
smc_log[cpu][idx].arg1 = arg1;
smc_log[cpu][idx].arg2 = arg2;
smc_log[cpu][idx].arg3 = arg3;
smc_log_idx[cpu]++;
if (smc_log_idx[cpu] == EXYNOS_SMC_LOG_SIZE)
smc_log_idx[cpu] = 0;
}
spin_unlock_irqrestore(&smc_log_lock, flags);
#endif
return ret;
}