#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>
-#define CONFIG_EXYNOS_SMC_LOGGING
+#include <asm/smp.h>
-#ifdef CONFIG_EXYNOS_SMC_LOGGING
-#define EXYNOS_SMC_LOG_SIZE 1024
+#ifdef CONFIG_DEBUG_SNAPSHOT_LOGGING_SMC
+#define EXYNOS_SMC_LOG_SIZE (1024)
-struct exynos_smc_log_entry {
- u64 cpu_clk;
- u32 cmd;
- u32 arg1;
- u32 arg2;
- u32 arg3;
+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 DEFINE_SPINLOCK(drm_smc_log_lock);
+static struct attribute_group esmc_sysfs_group = {
+ .attrs = esmc_sysfs_attrs,
+};
-struct exynos_smc_log_entry drm_smc_log[EXYNOS_SMC_LOG_SIZE];
+static const struct attribute_group *esmc_sysfs_groups[] = {
+ &esmc_sysfs_group,
+ NULL,
+};
-static unsigned int drm_smc_log_idx;
+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)
{
-#ifdef CONFIG_EXYNOS_SMC_LOGGING
- unsigned long flags;
+ 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
-#ifdef CONFIG_EXYNOS_SMC_LOGGING
- if ((uint32_t)cmd >= SMC_PROTECTION_SET && (uint32_t)cmd < MC_FC_SET_CFW_PROT) {
- pr_debug("%s: cmd: 0x%x, arg1: 0x%x, arg2: 0x%x, arg3: 0x%x\n",
- __func__, (u32)cmd, (u32)arg1, (u32)arg2, (u32)arg3);
- spin_lock_irqsave(&drm_smc_log_lock, flags);
- drm_smc_log[drm_smc_log_idx].cpu_clk = local_clock();
- drm_smc_log[drm_smc_log_idx].cmd = (u32)cmd;
- drm_smc_log[drm_smc_log_idx].arg1 = (u32)arg1;
- drm_smc_log[drm_smc_log_idx].arg2 = (u32)arg2;
- drm_smc_log[drm_smc_log_idx].arg3 = (u32)arg3;
- drm_smc_log_idx++;
- if (drm_smc_log_idx == EXYNOS_SMC_LOG_SIZE)
- drm_smc_log_idx = 0;
- spin_unlock_irqrestore(&drm_smc_log_lock, flags);
+ 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;
}
-#endif
- return __exynos_smc(cmd, arg1, arg2, arg3);
+ spin_unlock_irqrestore(&smc_log_lock, flags);
+#endif
+ return ret;
}