[Feature]add MT2731_MP2_MR2_SVN388 baseline version

Change-Id: Ief04314834b31e27effab435d3ca8ba33b499059
diff --git a/src/devtools/met-driver/4.19/mt2712/interface.c b/src/devtools/met-driver/4.19/mt2712/interface.c
new file mode 100644
index 0000000..6417fdd
--- /dev/null
+++ b/src/devtools/met-driver/4.19/mt2712/interface.c
@@ -0,0 +1,1372 @@
+/*
+ * Copyright (C) 2018 MediaTek Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/sched/clock.h>
+#include <linux/fs.h>
+#include <linux/sched.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/miscdevice.h>
+#include <linux/kallsyms.h>
+#include <linux/syscore_ops.h>
+#include <linux/dma-mapping.h>
+#include "interface.h"
+#include "sampler.h"
+#include "util.h"
+
+#include "ondiemet.h"
+
+#include "met_drv.h"
+#include "met_tag.h"
+#include "met_kernel_symbol.h"
+#include "met_api_tbl.h"
+#include "version.h"
+
+extern int enable_met_backlight_tag(void);
+extern int output_met_backlight_tag(int level);
+
+static int run = -1;
+static int sample_rate = 1000;	/* Default: 1000 Hz */
+static int met_suspend_compensation_mode;
+static int met_suspend_compensation_flag;
+
+/*
+ * met_cpu_pmu_method:
+ *   0: MET pmu driver
+ *   1: perf APIs
+ */
+unsigned int met_cpu_pmu_method = 1;
+
+int met_hrtimer_expire;		/* in ns */
+int met_timer_expire;		/* in jiffies */
+unsigned int ctrl_flags;
+int met_mode;
+EXPORT_SYMBOL(met_mode);
+
+DEFINE_PER_CPU(char[MET_STRBUF_SIZE], met_strbuf_nmi);
+EXPORT_SYMBOL(met_strbuf_nmi);
+
+DEFINE_PER_CPU(char[MET_STRBUF_SIZE], met_strbuf_irq);
+EXPORT_SYMBOL(met_strbuf_irq);
+
+DEFINE_PER_CPU(char[MET_STRBUF_SIZE], met_strbuf_sirq);
+EXPORT_SYMBOL(met_strbuf_sirq);
+
+DEFINE_PER_CPU(char[MET_STRBUF_SIZE], met_strbuf);
+EXPORT_SYMBOL(met_strbuf);
+
+static void calc_timer_value(int rate)
+{
+	sample_rate = rate;
+
+	if (rate == 0) {
+		met_hrtimer_expire = 0;
+		met_timer_expire = 0;
+		return;
+	}
+
+	met_hrtimer_expire = 1000000000 / rate;
+
+	/* Case 1: hrtimer < 1 OS tick, met_timer_expire = 1 OS tick */
+	if (rate > HZ)
+		met_timer_expire = 1;
+	/* Case 2: hrtimer > 1 OS tick, met_timer_expire is hrtimer + 1 OS tick */
+	else
+		met_timer_expire = (HZ / rate) + 1;
+
+	/* pr_debug("JBK HZ=%d, met_hrtimer_expire=%d ns, met_timer_expire=%d ticks\n", */
+	/* HZ, met_hrtimer_expire, met_timer_expire); */
+}
+
+int met_parse_num(const char *str, unsigned int *value, int len)
+{
+	int ret;
+
+	if (len <= 0)
+		return -1;
+
+	if ((len > 2) &&
+		((str[0] == '0') &&
+		((str[1] == 'x') || (str[1] == 'X')))) {
+		ret = kstrtoint(str, 16, value);
+	} else {
+		ret = kstrtoint(str, 10, value);
+	}
+
+	if (ret != 0)
+		return -1;
+
+	return 0;
+}
+
+void met_set_suspend_notify(int flag)
+{
+	if (met_suspend_compensation_mode == 1)
+		met_suspend_compensation_flag = flag;
+	else
+		met_suspend_compensation_flag = 0;
+}
+
+LIST_HEAD(met_list);
+static struct kobject *kobj_misc;
+static struct kobject *kobj_pmu;
+static struct kobject *kobj_bus;
+
+static ssize_t ver_show(struct device *dev, struct device_attribute *attr, char *buf);
+static DEVICE_ATTR(ver, 0444, ver_show, NULL);
+
+static ssize_t plf_show(struct device *dev, struct device_attribute *attr, char *buf);
+static DEVICE_ATTR(plf, 0444, plf_show, NULL);
+
+static ssize_t core_topology_show(struct device *dev, struct device_attribute *attr, char *buf);
+static DEVICE_ATTR(core_topology, 0444, core_topology_show, NULL);
+
+static ssize_t devices_show(struct device *dev, struct device_attribute *attr, char *buf);
+static DEVICE_ATTR(devices, 0444, devices_show, NULL);
+
+static ssize_t ctrl_show(struct device *dev, struct device_attribute *attr, char *buf);
+static ssize_t ctrl_store(struct device *dev, struct device_attribute *attr, const char *buf,
+			  size_t count);
+static DEVICE_ATTR(ctrl, 0664, ctrl_show, ctrl_store);
+
+static ssize_t spr_show(struct device *dev, struct device_attribute *attr, char *buf);
+static ssize_t spr_store(struct device *dev, struct device_attribute *attr, const char *buf,
+			 size_t count);
+static DEVICE_ATTR(sample_rate, 0664, spr_show, spr_store);
+
+static ssize_t run_show(struct device *dev, struct device_attribute *attr, char *buf);
+static ssize_t run_store(struct device *dev, struct device_attribute *attr, const char *buf,
+			 size_t count);
+static DEVICE_ATTR(run, 0664, run_show, run_store);
+
+static ssize_t ksym_show(struct device *dev, struct device_attribute *attr, char *buf);
+static ssize_t ksym_store(struct device *dev, struct device_attribute *attr, const char *buf,
+			 size_t count);
+static DEVICE_ATTR(ksym, 0664, ksym_show, ksym_store);
+
+static ssize_t cpu_pmu_method_show(struct device *dev, struct device_attribute *attr, char *buf);
+static ssize_t cpu_pmu_method_store(struct device *dev, struct device_attribute *attr, const char *buf,
+			  size_t count);
+static DEVICE_ATTR(cpu_pmu_method, 0664, cpu_pmu_method_show, cpu_pmu_method_store);
+
+#ifdef PR_CPU_NOTIFY
+int met_cpu_notify;
+static ssize_t cpu_notify_show(struct device *dev, struct device_attribute *attr, char *buf);
+static ssize_t cpu_notify_store(struct device *dev, struct device_attribute *attr, const char *buf,
+			  size_t count);
+static DEVICE_ATTR(cpu_notify, 0664, cpu_notify_show, cpu_notify_store);
+#endif
+
+#ifdef CONFIG_CPU_FREQ
+static ssize_t dvfs_show(struct device *dev, struct device_attribute *attr, char *buf);
+static ssize_t dvfs_store(struct device *dev, struct device_attribute *attr, const char *buf,
+			  size_t count);
+static DEVICE_ATTR(dvfs, 0664, dvfs_show, dvfs_store);
+#endif
+
+static ssize_t suspend_compensation_enable_show(struct device *dev, struct device_attribute *attr, char *buf);
+static ssize_t suspend_compensation_enable_store(struct device *dev, struct device_attribute *attr,
+							const char *buf, size_t count);
+static DEVICE_ATTR(suspend_compensation_enable, 0664, suspend_compensation_enable_show,
+			suspend_compensation_enable_store);
+
+static ssize_t suspend_compensation_flag_show(struct device *dev, struct device_attribute *attr, char *buf);
+static DEVICE_ATTR(suspend_compensation_flag, 0444, suspend_compensation_flag_show, NULL);
+
+static ssize_t ipi_test_store(struct device *dev, struct device_attribute *attr, const char *buf,
+			 size_t count);
+static DEVICE_ATTR(ipi_test, 0220, NULL, ipi_test_store);
+
+static const struct file_operations met_file_ops = {
+	.owner = THIS_MODULE
+};
+
+struct miscdevice met_device = {
+	.minor = MISC_DYNAMIC_MINOR,
+	.name = "met",
+	.mode = 0664,
+	.fops = &met_file_ops
+};
+EXPORT_SYMBOL(met_device);
+
+static int met_run(void)
+{
+	sampler_start();
+#ifdef MET_USER_EVENT_SUPPORT
+	bltab.flag &= (~MET_CLASS_ALL);
+#endif
+	ondiemet_start();
+	return 0;
+}
+
+static void met_stop(void)
+{
+#ifdef MET_USER_EVENT_SUPPORT
+	bltab.flag |= MET_CLASS_ALL;
+#endif
+	sampler_stop();
+	/* the met.ko will be use by script "cat ...", release it */
+	if ((ondiemet_module[ONDIEMET_SSPM] == 0) || (sspm_buffer_size == -1))
+		ondiemet_log_manager_stop();
+	ondiemet_stop();
+	ondiemet_extract();
+}
+
+static ssize_t ver_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+	int i;
+
+	mutex_lock(&dev->mutex);
+	i = snprintf(buf, PAGE_SIZE, "%s\n", MET_BACKEND_VERSION);
+	mutex_unlock(&dev->mutex);
+	return i;
+}
+
+static ssize_t devices_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+	int len, total_len = 0;
+	struct metdevice *c = NULL;
+
+	mutex_lock(&dev->mutex);
+	list_for_each_entry(c, &met_list, list) {
+		len = 0;
+		if (c->type == MET_TYPE_PMU)
+			len = snprintf(buf, PAGE_SIZE - total_len, "pmu/%s:0\n", c->name);
+		else if (c->type == MET_TYPE_BUS)
+			len = snprintf(buf, PAGE_SIZE - total_len, "bus/%s:0\n", c->name);
+		else if (c->type == MET_TYPE_MISC)
+			len = snprintf(buf, PAGE_SIZE - total_len, "misc/%s:0\n", c->name);
+
+		if (c->ondiemet_mode == 0) {
+			if (c->process_argument)
+				buf[len - 2]++;
+		} else if (c->ondiemet_mode == 1) {
+			if (c->ondiemet_process_argument)
+				buf[len - 2]++;
+		} else if (c->ondiemet_mode == 2) {
+			if (c->process_argument)
+				buf[len - 2]++;
+			if (c->ondiemet_process_argument)
+				buf[len - 2]++;
+		}
+
+		buf += len;
+		total_len += len;
+	}
+
+	mutex_unlock(&dev->mutex);
+	return total_len;
+}
+
+static char met_platform[16] = "none";
+static ssize_t plf_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+	int i;
+
+	mutex_lock(&dev->mutex);
+	i = snprintf(buf, PAGE_SIZE, "%s\n", met_platform);
+	mutex_unlock(&dev->mutex);
+	return i;
+}
+
+static char met_topology[64] = "none";
+static ssize_t core_topology_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+	int i;
+
+	mutex_lock(&dev->mutex);
+	i = snprintf(buf, PAGE_SIZE, "%s\n", met_topology);
+	mutex_unlock(&dev->mutex);
+	return i;
+}
+
+static ssize_t ctrl_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+	return snprintf(buf, PAGE_SIZE, "%d\n", ctrl_flags);
+}
+
+static ssize_t ctrl_store(struct device *dev, struct device_attribute *attr, const char *buf,
+			  size_t count)
+{
+	unsigned int value = 0;
+
+	if (met_parse_num(buf, &value, count) < 0)
+		return -EINVAL;
+
+	ctrl_flags = value;
+	return count;
+}
+
+static ssize_t cpu_pmu_method_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+	return snprintf(buf, PAGE_SIZE, "%d\n", met_cpu_pmu_method);
+}
+
+static ssize_t cpu_pmu_method_store(struct device *dev, struct device_attribute *attr, const char *buf,
+			  size_t count)
+{
+	unsigned int value;
+
+	if (met_parse_num(buf, &value, count) < 0)
+		return -EINVAL;
+
+	met_cpu_pmu_method = value;
+	return count;
+}
+
+
+static void _test_trace_ipi_raise(void *info)
+{
+	unsigned int *cpu = (unsigned int *)info;
+	void (*arch_send_call_function_single_ipi_sym)(int cpu) = NULL;
+
+	arch_send_call_function_single_ipi_sym = (void *)symbol_get(met_arch_send_call_function_single_ipi);
+	if (arch_send_call_function_single_ipi_sym)
+		arch_send_call_function_single_ipi_sym(*cpu);
+}
+
+
+static ssize_t ipi_test_store(struct device *dev, struct device_attribute *attr, const char *buf,
+			 size_t count)
+{
+	int this_cpu = smp_processor_id();
+	unsigned int cpu = 0;
+	unsigned int value;
+
+	if (met_parse_num(buf, &value, count) < 0)
+		return -EINVAL;
+
+	cpu = value;
+	if (cpu == this_cpu)
+		_test_trace_ipi_raise(&cpu);
+	else
+		met_smp_call_function_single_symbol(cpu, _test_trace_ipi_raise, &cpu, 1);
+
+	return count;
+}
+
+
+#if	defined(MET_BOOT_MSG)
+char met_boot_msg_tmp[256];
+char met_boot_msg[PAGE_SIZE];
+int met_boot_msg_idx;
+
+int pr_bootmsg(int str_len, char *str)
+{
+	if (met_boot_msg_idx+str_len+1 > PAGE_SIZE)
+		return -1;
+	memcpy(met_boot_msg+met_boot_msg_idx, str, str_len);
+	met_boot_msg_idx += str_len;
+	return 0;
+}
+
+static ssize_t bootmsg_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+	int	i;
+
+	mutex_lock(&dev->mutex);
+	i = snprintf(buf, PAGE_SIZE, "%s\n", met_boot_msg);
+	mutex_unlock(&dev->mutex);
+	return i;
+}
+
+static DEVICE_ATTR_RO(bootmsg);
+EXPORT_SYMBOL(met_boot_msg_tmp);
+EXPORT_SYMBOL(pr_bootmsg);
+#endif
+
+static ssize_t spr_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+	int i;
+
+	mutex_lock(&dev->mutex);
+	i = snprintf(buf, PAGE_SIZE, "%d\n", sample_rate);
+	mutex_unlock(&dev->mutex);
+	return i;
+}
+
+static ssize_t spr_store(struct device *dev, struct device_attribute *attr, const char *buf,
+			 size_t count)
+{
+	int value;
+	struct metdevice *c = NULL;
+
+	mutex_lock(&dev->mutex);
+
+	if ((run == 1) || (count == 0) || (buf == NULL)) {
+		mutex_unlock(&dev->mutex);
+		return -EINVAL;
+	}
+	if (kstrtoint(buf, 0, &value) != 0) {
+		mutex_unlock(&dev->mutex);
+		return -EINVAL;
+	}
+
+	if ((value < 0) || (value > 10000)) {
+		mutex_unlock(&dev->mutex);
+		return -EINVAL;
+	}
+
+	calc_timer_value(value);
+
+	list_for_each_entry(c, &met_list, list) {
+		if (c->polling_interval > 0)
+			c->polling_count_reload = ((c->polling_interval * sample_rate) - 1) / 1000;
+		else
+			c->polling_count_reload = 0;
+	}
+
+	mutex_unlock(&dev->mutex);
+
+	return count;
+}
+
+static ssize_t run_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+	int i;
+
+	mutex_lock(&dev->mutex);
+	i = snprintf(buf, PAGE_SIZE, "%d\n", run);
+	mutex_unlock(&dev->mutex);
+	return i;
+}
+
+static ssize_t run_store(struct device *dev, struct device_attribute *attr, const char *buf,
+			 size_t count)
+{
+	int value;
+
+	mutex_lock(&dev->mutex);
+
+	if ((count == 0) || (buf == NULL)) {
+		mutex_unlock(&dev->mutex);
+		return -EINVAL;
+	}
+	if (kstrtoint(buf, 0, &value) != 0) {
+		mutex_unlock(&dev->mutex);
+		return -EINVAL;
+	}
+
+	switch (value) {
+	case 1:
+		if (run != 1) {
+			run = 1;
+			met_run();
+		}
+		break;
+	case 0:
+		if (run != 0) {
+			if (run == 1) {
+				met_stop();
+#ifdef MET_USER_EVENT_SUPPORT
+#ifdef CONFIG_MET_MODULE
+//				met_save_dump_buffer_real("/data/trace.dump");
+#else
+				met_save_dump_buffer("/data/trace.dump");
+#endif
+#endif
+				run = 0;
+			} else
+				/* run == -1 */
+				run = 0;
+		}
+		break;
+	case -1:
+		if (run != -1) {
+			if (run == 1)
+				met_stop();
+
+			run = -1;
+		}
+		break;
+	default:
+		mutex_unlock(&dev->mutex);
+		return -EINVAL;
+	}
+
+	mutex_unlock(&dev->mutex);
+
+	return count;
+}
+
+static unsigned int met_ksym_addr;
+static char met_func_name[512];
+static ssize_t ksym_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+	int i;
+	int len = 0;
+	int idx = 0;
+
+	mutex_lock(&dev->mutex);
+	if (met_ksym_addr != 0)
+		len = sprint_symbol_no_offset(met_func_name, met_ksym_addr);
+	if (len != 0) {
+		for (idx = 0; idx < 512; idx++)
+			if (met_func_name[idx] == ' ')
+				met_func_name[idx] = '\0';
+		i = snprintf(buf, PAGE_SIZE, "%s\n", met_func_name);
+	} else
+		i = snprintf(buf, PAGE_SIZE, "ksymlookup fail(%x)\n", met_ksym_addr);
+
+	mutex_unlock(&dev->mutex);
+	return i;
+}
+
+static ssize_t ksym_store(struct device *dev, struct device_attribute *attr, const char *buf,
+			 size_t count)
+{
+	mutex_lock(&dev->mutex);
+
+	if ((count == 0) || (buf == NULL)) {
+		mutex_unlock(&dev->mutex);
+		return -EINVAL;
+	}
+	if (kstrtoint(buf, 16, &met_ksym_addr) != 0) {
+		mutex_unlock(&dev->mutex);
+		return -EINVAL;
+	}
+
+	mutex_unlock(&dev->mutex);
+
+	return count;
+}
+
+#if	defined(PR_CPU_NOTIFY)
+static ssize_t cpu_notify_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+	int i;
+
+	i = snprintf(buf, PAGE_SIZE, "%d\n", met_cpu_notify);
+	return i;
+}
+
+static ssize_t cpu_notify_store(struct device *dev, struct device_attribute *attr, const char *buf,
+			  size_t count)
+{
+	if ((count == 0) || (buf == NULL))
+		return -EINVAL;
+
+	if (kstrtoint(buf, 0, &met_cpu_notify) != 0)
+		return -EINVAL;
+
+	return count;
+}
+#endif
+
+#ifdef CONFIG_CPU_FREQ
+static ssize_t dvfs_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+	int i;
+
+	i = snprintf(buf, PAGE_SIZE, "%d\n", 0);
+	return i;
+}
+
+static ssize_t dvfs_store(struct device *dev, struct device_attribute *attr, const char *buf,
+			  size_t count)
+{
+	return count;
+}
+#endif
+
+static ssize_t suspend_compensation_enable_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+	int ret;
+
+	ret = snprintf(buf, PAGE_SIZE, "%d\n", met_suspend_compensation_mode);
+
+	return ret;
+}
+
+static ssize_t suspend_compensation_enable_store(struct device *dev, struct device_attribute *attr,
+			const char *buf, size_t count)
+{
+	int value;
+
+	if ((count == 0) || (buf == NULL))
+		return -EINVAL;
+
+	if (kstrtoint(buf, 0, &value) != 0)
+		return -EINVAL;
+
+	if (value < 0)
+		return -EINVAL;
+
+	met_suspend_compensation_mode = value;
+
+	return count;
+}
+
+static ssize_t suspend_compensation_flag_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+	int ret;
+
+	ret = snprintf(buf, PAGE_SIZE, "%d\n", met_suspend_compensation_flag);
+
+	return ret;
+}
+
+static ssize_t hash_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+	return 0;
+}
+
+static ssize_t hash_store(struct device *dev, struct device_attribute *attr, const char *buf,
+			 size_t count)
+{
+	return 0;
+}
+
+static DEVICE_ATTR(hash, 0664, hash_show, hash_store);
+
+static ssize_t mode_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
+{
+	struct metdevice *c = NULL;
+
+	list_for_each_entry(c, &met_list, list) {
+		if (c->kobj == kobj)
+			break;
+	}
+	if (c == NULL)
+		return -ENOENT;
+
+	return snprintf(buf, PAGE_SIZE, "%d\n", c->mode);
+}
+
+static ssize_t mode_store(struct kobject *kobj, struct kobj_attribute *attr, const char *buf,
+			  size_t n)
+{
+	struct metdevice *c = NULL;
+
+	list_for_each_entry(c, &met_list, list) {
+		if (c->kobj == kobj)
+			break;
+	}
+	if (c == NULL)
+		return -ENOENT;
+
+	if (kstrtoint(buf, 0, &(c->mode)) != 0)
+		return -EINVAL;
+
+	return n;
+}
+
+static struct kobj_attribute mode_attr = __ATTR(mode, 0664, mode_show, mode_store);
+
+static ssize_t ondiemet_mode_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
+{
+	struct metdevice *c = NULL;
+
+	list_for_each_entry(c, &met_list, list) {
+		if (c->kobj == kobj)
+			break;
+	}
+	if (c == NULL)
+		return -ENOENT;
+
+	return snprintf(buf, PAGE_SIZE, "%d\n", c->ondiemet_mode);
+}
+
+static ssize_t ondiemet_mode_store(struct kobject *kobj, struct kobj_attribute *attr, const char *buf,
+			  size_t n)
+{
+	struct metdevice *c = NULL;
+
+	list_for_each_entry(c, &met_list, list) {
+		if (c->kobj == kobj)
+			break;
+	}
+	if (c == NULL)
+		return -ENOENT;
+
+	if (kstrtoint(buf, 0, &(c->ondiemet_mode)) != 0)
+		return -EINVAL;
+
+	return n;
+}
+
+static struct kobj_attribute ondiemet_mode_attr = __ATTR(ondiemet_mode, 0664, ondiemet_mode_show, ondiemet_mode_store);
+
+static ssize_t polling_interval_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
+{
+	int interval = 1;
+	struct metdevice *c = NULL;
+
+	list_for_each_entry(c, &met_list, list) {
+		if (c->kobj == kobj)
+			break;
+	}
+	if (c == NULL)
+		return -ENOENT;
+
+	if (c->polling_interval)
+		interval = c->polling_interval;
+
+	return snprintf(buf, PAGE_SIZE, "%d\n", interval);
+}
+
+static ssize_t polling_interval_store(struct kobject *kobj, struct kobj_attribute *attr,
+				      const char *buf, size_t n)
+{
+	struct metdevice *c = NULL;
+
+	list_for_each_entry(c, &met_list, list) {
+		if (c->kobj == kobj)
+			break;
+	}
+	if (c == NULL)
+		return -ENOENT;
+
+	if (kstrtoint(buf, 0, &(c->polling_interval)) != 0)
+		return -EINVAL;
+
+	if (c->polling_interval > 0)
+		c->polling_count_reload = ((c->polling_interval * sample_rate) - 1) / 1000;
+	else
+		c->polling_count_reload = 0;
+
+	return n;
+}
+
+static struct kobj_attribute polling_interval_attr =
+__ATTR(polling_ms, 0664, polling_interval_show, polling_interval_store);
+
+static ssize_t header_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
+{
+	struct metdevice *c = NULL;
+	ssize_t count = 0;
+
+	list_for_each_entry(c, &met_list, list) {
+		if (c->kobj == kobj)
+			break;
+	}
+	if (c == NULL)
+		return -ENOENT;
+
+	if (c->ondiemet_mode == 0) {
+		if ((c->mode) && (c->print_header))
+			return c->print_header(buf, PAGE_SIZE);
+	} else if (c->ondiemet_mode == 1) {
+		if ((c->mode) && (c->ondiemet_print_header))
+			return c->ondiemet_print_header(buf, PAGE_SIZE);
+	} else if (c->ondiemet_mode == 2) {
+		if ((c->mode) && (c->print_header))
+			count = c->print_header(buf, PAGE_SIZE);
+		if (count < PAGE_SIZE) {
+			if ((c->mode) && (c->ondiemet_print_header))
+				count += c->ondiemet_print_header(buf+count, PAGE_SIZE - count);
+		}
+		return count;
+	}
+
+	return 0;
+}
+
+static struct kobj_attribute header_attr = __ATTR(header, 0444, header_show, NULL);
+
+static ssize_t help_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
+{
+	struct metdevice *c = NULL;
+	ssize_t count = 0;
+
+	list_for_each_entry(c, &met_list, list) {
+		if (c->kobj == kobj)
+			break;
+	}
+	if (c == NULL)
+		return -ENOENT;
+
+	if (c->ondiemet_mode == 0) {
+		if (c->print_help)
+			return c->print_help(buf, PAGE_SIZE);
+	} else if (c->ondiemet_mode == 1) {
+		if (c->ondiemet_print_help)
+			return c->ondiemet_print_help(buf, PAGE_SIZE);
+	} else if (c->ondiemet_mode == 2) {
+		if (c->print_help)
+			count = c->print_help(buf, PAGE_SIZE);
+		if (count < PAGE_SIZE) {
+			if (c->ondiemet_print_help)
+				count += c->ondiemet_print_help(buf+count, PAGE_SIZE - count);
+		}
+		return count;
+	}
+
+	return 0;
+}
+
+static struct kobj_attribute help_attr = __ATTR(help, 0444, help_show, NULL);
+
+static int argu_status = -1;
+static ssize_t argu_store(struct kobject *kobj, struct kobj_attribute *attr, const char *buf,
+			  size_t n)
+{
+	int ret = 0;
+	struct metdevice *c = NULL;
+
+	argu_status = -1;
+
+	list_for_each_entry(c, &met_list, list) {
+		if (c->kobj == kobj)
+			break;
+	}
+	if (c == NULL)
+		return -ENOENT;
+
+	if (c->ondiemet_mode == 0) {
+		if (c->process_argument)
+			ret = c->process_argument(buf, (int)n);
+	} else if (c->ondiemet_mode == 1) {
+		if (c->ondiemet_process_argument)
+			ret = c->ondiemet_process_argument(buf, (int)n);
+	} else if (c->ondiemet_mode == 2) {
+		if (c->process_argument)
+			ret = c->process_argument(buf, (int)n);
+		if (c->ondiemet_process_argument)
+			ret = c->ondiemet_process_argument(buf, (int)n);
+	}
+
+	if (ret != 0)
+		return -EINVAL;
+
+	argu_status = 0;
+	return n;
+}
+
+static ssize_t argu_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
+{
+	return snprintf(buf, PAGE_SIZE, "%d\n", argu_status);
+}
+
+static struct kobj_attribute argu_attr = __ATTR(argu, 0664, argu_show, argu_store);
+
+static ssize_t reset_store(struct kobject *kobj,
+			struct kobj_attribute *attr,
+			const char *buf,
+			size_t n)
+{
+	int ret = 0;
+	struct metdevice *c = NULL;
+
+	list_for_each_entry(c, &met_list, list) {
+		if (c->kobj == kobj)
+			break;
+	}
+	if (c == NULL)
+		return -ENOENT;
+
+	if (c->ondiemet_mode == 0) {
+		if (c->reset)
+			ret = c->reset();
+		else
+			c->mode = 0;
+	} else if (c->ondiemet_mode == 1) {
+		if (c->ondiemet_reset)
+			ret = c->ondiemet_reset();
+	} else if (c->ondiemet_mode == 2) {
+		if (c->reset)
+			ret = c->reset();
+		else
+			c->mode = 0;
+		if (c->ondiemet_reset)
+			ret = c->ondiemet_reset();
+	}
+
+	if (ret != 0)
+		return -EINVAL;
+
+	return n;
+}
+
+static struct kobj_attribute reset_attr = __ATTR(reset, 0220, NULL, reset_store);
+
+static ssize_t header_read_again_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
+{
+	struct metdevice *c = NULL;
+
+	list_for_each_entry(c, &met_list, list) {
+		if (c->kobj == kobj)
+			break;
+	}
+	if (c == NULL)
+		return -ENOENT;
+
+	return snprintf(buf, PAGE_SIZE, "%d\n", c->header_read_again);
+}
+
+static struct kobj_attribute header_read_again_attr = __ATTR(header_read_again, 0664, header_read_again_show, NULL);
+
+
+int met_register(struct metdevice *met)
+{
+	int ret, cpu;
+	struct metdevice *c;
+
+	list_for_each_entry(c, &met_list, list) {
+		if (!strcmp(c->name, met->name))
+			return -EEXIST;
+	}
+
+	PR_BOOTMSG("met_register %s ...\n", met->name);
+
+	INIT_LIST_HEAD(&met->list);
+
+	/* Allocate timer count for per CPU */
+	met->polling_count = alloc_percpu(int);
+	if (met->polling_count == NULL)
+		return -EINVAL;
+
+	for_each_possible_cpu(cpu)
+		*(per_cpu_ptr(met->polling_count, cpu)) = 0;
+
+	if (met->polling_interval > 0) {
+		ret = ((met->polling_interval * sample_rate) - 1) / 1000;
+		met->polling_count_reload = ret;
+	} else
+		met->polling_count_reload = 0;
+
+	met->kobj = NULL;
+
+	if (met->type == MET_TYPE_BUS)
+		met->kobj = kobject_create_and_add(met->name, kobj_bus);
+	else if (met->type == MET_TYPE_PMU)
+		met->kobj = kobject_create_and_add(met->name, kobj_pmu);
+	else if (met->type == MET_TYPE_MISC)
+		met->kobj = kobject_create_and_add(met->name, kobj_misc);
+	else {
+		ret = -EINVAL;
+		goto err_out;
+	}
+
+	if (met->kobj == NULL) {
+		ret = -EINVAL;
+		goto err_out;
+	}
+
+	if (met->create_subfs) {
+		ret = met->create_subfs(met->kobj);
+		if (ret)
+			goto err_out;
+	}
+
+	ret = sysfs_create_file(met->kobj, &mode_attr.attr);
+	if (ret)
+		goto err_out;
+
+
+	ret = sysfs_create_file(met->kobj, &ondiemet_mode_attr.attr);
+	if (ret)
+		goto err_out;
+
+	ret = sysfs_create_file(met->kobj, &polling_interval_attr.attr);
+	if (ret)
+		goto err_out;
+
+	ret = sysfs_create_file(met->kobj, &header_read_again_attr.attr);
+	if (ret)
+		goto err_out;
+
+	if (met->print_header || met->ondiemet_print_header) {
+		ret = sysfs_create_file(met->kobj, &header_attr.attr);
+		if (ret)
+			goto err_out;
+	}
+
+	if (met->print_help || met->ondiemet_print_help) {
+		ret = sysfs_create_file(met->kobj, &help_attr.attr);
+		if (ret)
+			goto err_out;
+	}
+
+	if (met->process_argument || met->ondiemet_process_argument) {
+		ret = sysfs_create_file(met->kobj, &argu_attr.attr);
+		if (ret)
+			goto err_out;
+	}
+
+	if (met->reset) {
+		ret = sysfs_create_file(met->kobj, &reset_attr.attr);
+		if (ret)
+			goto err_out;
+	}
+
+	spin_lock_init(&met->my_lock);
+
+	list_add(&met->list, &met_list);
+	return 0;
+
+ err_out:
+
+	if (met->polling_count)
+		free_percpu(met->polling_count);
+
+	if (met->kobj) {
+		kobject_del(met->kobj);
+		kobject_put(met->kobj);
+		met->kobj = NULL;
+	}
+
+	return ret;
+}
+EXPORT_SYMBOL(met_register);
+
+int met_deregister(struct metdevice *met)
+{
+	struct metdevice *c = NULL;
+
+	list_for_each_entry(c, &met_list, list) {
+		if (c == met)
+			break;
+	}
+	if (c != met)
+		return -ENOENT;
+
+	if (met->print_header || met->ondiemet_print_header)
+		sysfs_remove_file(met->kobj, &header_attr.attr);
+
+	if (met->print_help || met->ondiemet_print_help)
+		sysfs_remove_file(met->kobj, &help_attr.attr);
+
+	if (met->process_argument || met->ondiemet_process_argument)
+		sysfs_remove_file(met->kobj, &argu_attr.attr);
+
+	sysfs_remove_file(met->kobj, &reset_attr.attr);
+	sysfs_remove_file(met->kobj, &header_read_again_attr.attr);
+	sysfs_remove_file(met->kobj, &polling_interval_attr.attr);
+	sysfs_remove_file(met->kobj, &mode_attr.attr);
+	sysfs_remove_file(met->kobj, &ondiemet_mode_attr.attr);
+
+	if (met->delete_subfs)
+		met->delete_subfs();
+
+	kobject_del(met->kobj);
+	kobject_put(met->kobj);
+	met->kobj = NULL;
+
+	if (met->polling_count)
+		free_percpu(met->polling_count);
+
+	list_del(&met->list);
+	return 0;
+}
+EXPORT_SYMBOL(met_deregister);
+
+int met_set_platform(const char *plf_name, int flag)
+{
+	strncpy(met_platform, plf_name, sizeof(met_platform) - 1);
+#if 0
+	int ret;
+
+	if (flag) {
+		ret = device_create_file(met_device.this_device, &dev_attr_plf);
+		if (ret != 0) {
+			pr_debug("can not create device file: plf\n");
+			return ret;
+		}
+		strncpy(met_platform, plf_name, sizeof(met_platform) - 1);
+	} else
+		device_remove_file(met_device.this_device, &dev_attr_plf);
+
+#endif
+	return 0;
+}
+EXPORT_SYMBOL(met_set_platform);
+
+int met_set_topology(const char *topology_name, int flag)
+{
+	strncpy(met_topology, topology_name, sizeof(met_topology) - 1);
+#if 0
+	int ret;
+
+	if (flag) {
+		ret = device_create_file(met_device.this_device, &dev_attr_core_topology);
+		if (ret != 0) {
+			pr_debug("can not create device file: topology\n");
+			return ret;
+		}
+		strncpy(met_topology, topology_name, sizeof(met_topology) - 1);
+	} else {
+		device_remove_file(met_device.this_device, &dev_attr_core_topology);
+	}
+#endif
+	return 0;
+}
+EXPORT_SYMBOL(met_set_topology);
+
+#include "met_struct.h"
+
+void force_sample(void *unused)
+{
+	int cpu;
+	unsigned long long stamp;
+	struct metdevice *c;
+	struct met_cpu_struct *met_cpu_ptr;
+
+	if ((run != 1) || (sample_rate == 0))
+		return;
+
+	/* to avoid met tag is coming after __met_hrtimer_stop and before run=-1 */
+	met_cpu_ptr = this_cpu_ptr(&met_cpu);
+	if (met_cpu_ptr->work_enabled == 0)
+		return;
+
+	cpu = smp_processor_id();
+
+	stamp = cpu_clock(cpu);
+
+	list_for_each_entry(c, &met_list, list) {
+		if (c->ondiemet_mode == 0) {
+			if ((c->mode != 0) && (c->tagged_polling != NULL))
+				c->tagged_polling(stamp, 0);
+		} else if (c->ondiemet_mode == 1) {
+			if ((c->mode != 0) && (c->ondiemet_tagged_polling != NULL))
+				c->ondiemet_tagged_polling(stamp, 0);
+		} else if (c->ondiemet_mode == 2) {
+			if ((c->mode != 0) && (c->tagged_polling != NULL))
+				c->tagged_polling(stamp, 0);
+			if ((c->mode != 0) && (c->ondiemet_tagged_polling != NULL))
+				c->ondiemet_tagged_polling(stamp, 0);
+		}
+	}
+}
+
+#define MET_SUSPEND_HAND
+#ifdef MET_SUSPEND_HAND
+static struct syscore_ops met_hrtimer_ops = {
+	.suspend = met_hrtimer_suspend,
+	.resume = met_hrtimer_resume,
+};
+#endif
+
+int fs_reg(void)
+{
+	int ret = 0;
+
+	ctrl_flags = 0;
+	met_mode = 0;
+
+#ifdef MET_SUSPEND_HAND
+	/* suspend/resume function handle register */
+	register_syscore_ops(&met_hrtimer_ops);
+#endif
+
+	calc_timer_value(sample_rate);
+
+	ret = misc_register(&met_device);
+	if (ret != 0) {
+		pr_debug("misc register failed\n");
+		return ret;
+	}
+
+	/* dma map config */
+	/* arch_setup_dma_ops(met_device.this_device, 0, 0, NULL, false); */
+	met_arch_setup_dma_ops_symbol(met_device.this_device);
+
+	ret = device_create_file(met_device.this_device, &dev_attr_ksym);
+	if (ret != 0) {
+		pr_debug("can not create device file: ksym\n");
+		return ret;
+	}
+
+	ret = device_create_file(met_device.this_device, &dev_attr_run);
+	if (ret != 0) {
+		pr_debug("can not create device file: run\n");
+		return ret;
+	}
+
+#if	defined(PR_CPU_NOTIFY)
+	ret = device_create_file(met_device.this_device, &dev_attr_cpu_notify);
+	if (ret != 0) {
+		pr_debug("can not create device file: cpu_notify\n");
+		return ret;
+	}
+#endif
+
+#ifdef CONFIG_CPU_FREQ
+	ret = device_create_file(met_device.this_device, &dev_attr_dvfs);
+	if (ret != 0) {
+		pr_debug("can not create device file: dvfs\n");
+		return ret;
+	}
+#endif
+
+	ret = device_create_file(met_device.this_device, &dev_attr_suspend_compensation_enable);
+	if (ret != 0) {
+		pr_debug("can not create device file: suspend_compensation_enable\n");
+		return ret;
+	}
+
+	ret = device_create_file(met_device.this_device, &dev_attr_suspend_compensation_flag);
+	if (ret != 0) {
+		pr_debug("can not create device file: suspend_compensation_enable\n");
+		return ret;
+	}
+
+	ret = device_create_file(met_device.this_device, &dev_attr_ver);
+	if (ret != 0) {
+		pr_debug("can not create device file: ver\n");
+		return ret;
+	}
+
+	ret = device_create_file(met_device.this_device, &dev_attr_devices);
+	if (ret != 0) {
+		pr_debug("can not create device file: devices\n");
+		return ret;
+	}
+
+	ret = device_create_file(met_device.this_device, &dev_attr_ctrl);
+	if (ret != 0) {
+		pr_debug("can not create device file: ctrl\n");
+		return ret;
+	}
+
+	ret = device_create_file(met_device.this_device, &dev_attr_cpu_pmu_method);
+	if (ret != 0) {
+		pr_debug("can not create device file: cpu_pmu_method\n");
+		return ret;
+	}
+
+#if	defined(MET_BOOT_MSG)
+	ret = device_create_file(met_device.this_device, &dev_attr_bootmsg);
+	if (ret != 0) {
+		pr_debug("can not create device file: bootmsg\n");
+		return ret;
+	}
+#endif
+
+	ret = device_create_file(met_device.this_device, &dev_attr_sample_rate);
+	if (ret != 0) {
+		pr_debug("can not create device file: sample_rate\n");
+		return ret;
+	}
+
+	ret = device_create_file(met_device.this_device, &dev_attr_core_topology);
+	if (ret != 0) {
+		pr_debug("can not create device file: topology\n");
+		return ret;
+	}
+
+	ret = device_create_file(met_device.this_device, &dev_attr_plf);
+	if (ret != 0) {
+		pr_debug("can not create device file: plf\n");
+		return ret;
+	}
+
+	ret = device_create_file(met_device.this_device, &dev_attr_hash);
+	if (ret != 0) {
+		pr_debug("can not create device file: hash\n");
+		return ret;
+	}
+
+	ret = device_create_file(met_device.this_device, &dev_attr_ipi_test);
+	if (ret != 0) {
+		pr_debug("can not create device file: ipi_test\n");
+		return ret;
+	}
+
+	kobj_misc = kobject_create_and_add("misc", &met_device.this_device->kobj);
+	if (kobj_misc == NULL) {
+		pr_debug("can not create kobject: kobj_misc\n");
+		return ret;
+	}
+
+	kobj_pmu = kobject_create_and_add("pmu", &met_device.this_device->kobj);
+	if (kobj_pmu == NULL) {
+		pr_debug("can not create kobject: kobj_pmu\n");
+		return ret;
+	}
+
+	kobj_bus = kobject_create_and_add("bus", &met_device.this_device->kobj);
+	if (kobj_bus == NULL) {
+		pr_debug("can not create kobject: kobj_bus\n");
+		return ret;
+	}
+
+	met_register(&met_cookie);
+	met_register(&met_cpupmu);
+#ifdef MET_SUPPORT_CPUPMU_V2
+	met_register(&met_cpupmu_v2);
+#endif
+#ifdef MET_USER_EVENT_SUPPORT
+	tag_reg((struct file_operations * const) met_device.fops, &met_device.this_device->kobj);
+#endif
+
+	ondiemet_log_manager_init(met_device.this_device);
+	ondiemet_attr_init(met_device.this_device);
+
+	return ret;
+}
+
+void fs_unreg(void)
+{
+	if (run == 1)
+		met_stop();
+
+	run = -1;
+
+#ifdef MET_USER_EVENT_SUPPORT
+	tag_unreg();
+#endif
+	met_deregister(&met_cpupmu);
+#ifdef MET_SUPPORT_CPUPMU_V2
+	met_deregister(&met_cpupmu_v2);
+#endif
+
+	kobject_del(kobj_misc);
+	kobject_put(kobj_misc);
+	kobj_misc = NULL;
+	kobject_del(kobj_pmu);
+	kobject_put(kobj_pmu);
+	kobj_pmu = NULL;
+	kobject_del(kobj_bus);
+	kobject_put(kobj_bus);
+	kobj_bus = NULL;
+
+	device_remove_file(met_device.this_device, &dev_attr_ksym);
+
+	device_remove_file(met_device.this_device, &dev_attr_run);
+#ifdef PR_CPU_NOTIFY
+	device_remove_file(met_device.this_device, &dev_attr_cpu_notify);
+#endif
+#ifdef CONFIG_CPU_FREQ
+	device_remove_file(met_device.this_device, &dev_attr_dvfs);
+#endif
+	device_remove_file(met_device.this_device, &dev_attr_suspend_compensation_enable);
+	device_remove_file(met_device.this_device, &dev_attr_suspend_compensation_flag);
+
+	device_remove_file(met_device.this_device, &dev_attr_ver);
+	device_remove_file(met_device.this_device, &dev_attr_devices);
+	device_remove_file(met_device.this_device, &dev_attr_sample_rate);
+
+	device_remove_file(met_device.this_device, &dev_attr_ctrl);
+	device_remove_file(met_device.this_device, &dev_attr_cpu_pmu_method);
+
+	device_remove_file(met_device.this_device, &dev_attr_core_topology);
+	device_remove_file(met_device.this_device, &dev_attr_plf);
+	device_remove_file(met_device.this_device, &dev_attr_hash);
+	device_remove_file(met_device.this_device, &dev_attr_ipi_test);
+
+	ondiemet_log_manager_uninit(met_device.this_device);
+	ondiemet_attr_uninit(met_device.this_device);
+
+	misc_deregister(&met_device);
+#ifdef MET_SUSPEND_HAND
+	/* suspend/resume function handle register */
+	unregister_syscore_ops(&met_hrtimer_ops);
+#endif
+}
+
+unsigned int get_ctrl_flags(void)
+{
+	return ctrl_flags;
+}