[Feature]add MT2731_MP2_MR2_SVN388 baseline version

Change-Id: Ief04314834b31e27effab435d3ca8ba33b499059
diff --git a/src/devtools/met-driver/4.19/mt2712/Kbuild b/src/devtools/met-driver/4.19/mt2712/Kbuild
new file mode 100644
index 0000000..be6faad
--- /dev/null
+++ b/src/devtools/met-driver/4.19/mt2712/Kbuild
@@ -0,0 +1,64 @@
+MET_PLF := $(MTK_PLATFORM)
+
+#ccflags-y += -DMET_CHIP_USE
+
+met-y := $(MET_PLF)/met_main.o \
+         $(MET_PLF)/interface.o \
+         $(MET_PLF)/sampler.o \
+         $(MET_PLF)/util.o \
+         $(MET_PLF)/core_plf_init.o \
+         $(MET_PLF)/core_plf_trace.o \
+         $(MET_PLF)/cookie.o \
+         $(MET_PLF)/ondiemet.o \
+         $(MET_PLF)/ondiemet_log.o \
+         $(MET_PLF)/sspm/ondiemet_sspm.o
+
+CFLAGS_interface.o :=
+#CFLAGS_interface.o += -DMET_USER_EVENT_SUPPORT
+#CFLAGS_met_tag_ex.o += -DMET_USER_EVENT_SUPPORT
+
+$(info ARCH = $(ARCH))
+ifeq ($(ARCH), mips)
+met-y += $(MET_PLF)/mips_pmu_hw.o
+endif #ifeq ($(ARCH), mips)
+
+ifeq ($(ARCH), arm)
+ccflags-y += -DCONFIG_MET_ARM_32BIT
+met-y += $(MET_PLF)/cpu_pmu.o
+met-y += $(MET_PLF)/v7_pmu_hw.o
+met-y += $(MET_PLF)/v6_pmu_hw.o
+endif #ifeq ($(ARCH), arm)
+
+ifeq ($(ARCH), arm64)
+ccflags-y += -DMET_SUPPORT_CPUPMU_V2
+met-y += $(MET_PLF)/cpu_pmu.o
+met-y += $(MET_PLF)/v8_pmu_hw.o
+met-y += $(MET_PLF)/cpu_pmu_v2.o
+met-y += $(MET_PLF)/v8_pmu_hw_v2.o
+endif
+
+$(info CONFIG_CPU_FREQ = $(CONFIG_CPU_FREQ))
+ifeq ($(CONFIG_CPU_FREQ),y)
+    met-y += $(MET_PLF)/power.o
+endif
+
+
+################################################################################
+# MET_AP_EMI
+################################################################################
+FEATURE_AP_EMI := $(if $(FEATURE_AP_EMI),$(FEATURE_AP_EMI),y)
+$(info FEATURE_AP_EMI = $(FEATURE_AP_EMI))
+ifneq ($(FEATURE_AP_EMI), n)
+    MET_AP_EMI := y
+else
+    MET_AP_EMI := n
+endif
+
+met-$(MET_AP_EMI) += $(MET_PLF)/met_emi.o $(MET_PLF)/mtk_emi_bm.o
+
+################################################################################
+# MET_GPU
+################################################################################
+MET_GPU := y
+met-y += $(MET_PLF)/mtk_gpu_metmonitor.o
+
diff --git a/src/devtools/met-driver/4.19/mt2712/Kbuild.platform.h b/src/devtools/met-driver/4.19/mt2712/Kbuild.platform.h
new file mode 100644
index 0000000..73845f1
--- /dev/null
+++ b/src/devtools/met-driver/4.19/mt2712/Kbuild.platform.h
@@ -0,0 +1,34 @@
+################################################################################
+# Include Path
+################################################################################
+MET_VCOREDVFS_INC := $(KERNEL_SRC)/drivers/misc/mediatek/base/power/include/vcorefs_v3
+MET_PTPOD_INC := $(KERNEL_SRC)/drivers/misc/mediatek/base/power/cpufreq_v1/src/mach/$(MTK_PLATFORM)/
+
+
+################################################################################
+# Feature Spec
+# CPUPMU_VERSION: V8_0/V8_2
+# EMI_SEDA_VERSION: SEDA2/SEDA3/SEDA3_5
+# EMI_DRAMC_VERSION: V1/V2
+################################################################################
+CPUPMU_LEGACY := y
+CPUPMU_VERSION := V8_0
+EMI_SEDA_VERSION := SEDA3_5
+EMI_DRAMC_VERSION := V2
+
+
+################################################################################
+# Feature On/Off
+################################################################################
+FEATURE_SPMTWAM := n
+FEATURE_CPUDSU := n
+FEATURE_SSPM_EMI := n
+FEATURE_AP_EMI := y
+FEATURE_GPU := n
+FEATURE_VCOREDVFS := n
+FEATURE_PTPOD := n
+FEATURE_WALLTIME := n
+FEATURE_SSPM_SMI := n
+FEATURE_EVENT_POWER := n
+FEATURE_ONDIEMET := n
+
diff --git a/src/devtools/met-driver/4.19/mt2712/Kbuild.yocto b/src/devtools/met-driver/4.19/mt2712/Kbuild.yocto
new file mode 100644
index 0000000..be6faad
--- /dev/null
+++ b/src/devtools/met-driver/4.19/mt2712/Kbuild.yocto
@@ -0,0 +1,64 @@
+MET_PLF := $(MTK_PLATFORM)
+
+#ccflags-y += -DMET_CHIP_USE
+
+met-y := $(MET_PLF)/met_main.o \
+         $(MET_PLF)/interface.o \
+         $(MET_PLF)/sampler.o \
+         $(MET_PLF)/util.o \
+         $(MET_PLF)/core_plf_init.o \
+         $(MET_PLF)/core_plf_trace.o \
+         $(MET_PLF)/cookie.o \
+         $(MET_PLF)/ondiemet.o \
+         $(MET_PLF)/ondiemet_log.o \
+         $(MET_PLF)/sspm/ondiemet_sspm.o
+
+CFLAGS_interface.o :=
+#CFLAGS_interface.o += -DMET_USER_EVENT_SUPPORT
+#CFLAGS_met_tag_ex.o += -DMET_USER_EVENT_SUPPORT
+
+$(info ARCH = $(ARCH))
+ifeq ($(ARCH), mips)
+met-y += $(MET_PLF)/mips_pmu_hw.o
+endif #ifeq ($(ARCH), mips)
+
+ifeq ($(ARCH), arm)
+ccflags-y += -DCONFIG_MET_ARM_32BIT
+met-y += $(MET_PLF)/cpu_pmu.o
+met-y += $(MET_PLF)/v7_pmu_hw.o
+met-y += $(MET_PLF)/v6_pmu_hw.o
+endif #ifeq ($(ARCH), arm)
+
+ifeq ($(ARCH), arm64)
+ccflags-y += -DMET_SUPPORT_CPUPMU_V2
+met-y += $(MET_PLF)/cpu_pmu.o
+met-y += $(MET_PLF)/v8_pmu_hw.o
+met-y += $(MET_PLF)/cpu_pmu_v2.o
+met-y += $(MET_PLF)/v8_pmu_hw_v2.o
+endif
+
+$(info CONFIG_CPU_FREQ = $(CONFIG_CPU_FREQ))
+ifeq ($(CONFIG_CPU_FREQ),y)
+    met-y += $(MET_PLF)/power.o
+endif
+
+
+################################################################################
+# MET_AP_EMI
+################################################################################
+FEATURE_AP_EMI := $(if $(FEATURE_AP_EMI),$(FEATURE_AP_EMI),y)
+$(info FEATURE_AP_EMI = $(FEATURE_AP_EMI))
+ifneq ($(FEATURE_AP_EMI), n)
+    MET_AP_EMI := y
+else
+    MET_AP_EMI := n
+endif
+
+met-$(MET_AP_EMI) += $(MET_PLF)/met_emi.o $(MET_PLF)/mtk_emi_bm.o
+
+################################################################################
+# MET_GPU
+################################################################################
+MET_GPU := y
+met-y += $(MET_PLF)/mtk_gpu_metmonitor.o
+
diff --git a/src/devtools/met-driver/4.19/mt2712/cookie.c b/src/devtools/met-driver/4.19/mt2712/cookie.c
new file mode 100644
index 0000000..2bdb711
--- /dev/null
+++ b/src/devtools/met-driver/4.19/mt2712/cookie.c
@@ -0,0 +1,259 @@
+#include <linux/mm.h>
+#include <linux/slab.h>
+#include <linux/sched.h>
+#include <linux/cpu.h>
+#include <linux/module.h>
+#include <linux/fs.h>
+#include <asm/irq_regs.h>
+#include <asm/stacktrace.h>
+#include <linux/stacktrace.h>
+#include "interface.h"
+#include "met_drv.h"
+
+#define LINE_SIZE	256
+
+struct cookie_info {
+	int depth;
+	int strlen;
+	char strbuf[LINE_SIZE];
+};
+
+static unsigned int back_trace_depth;
+static DEFINE_PER_CPU(struct cookie_info, info);
+
+static int reset_driver_stat(void)
+{
+	back_trace_depth = 0;
+	met_cookie.mode = 0;
+	return 0;
+}
+
+
+noinline void cookie(char *strbuf)
+{
+	MET_TRACE("%s\n", strbuf);
+}
+
+noinline void cookie2(char *strbuf)
+{
+	MET_TRACE("%s\n", strbuf);
+}
+
+static void get_kernel_cookie(unsigned long pc, struct cookie_info *pinfo)
+{
+	int ret;
+
+#ifdef CONFIG_MODULES
+	off_t off;
+	struct module *mod = __module_address(pc);
+
+	if (mod) {
+		if (within_module_core(pc, mod)) {
+			off = pc - (unsigned long)mod->core_layout.base;
+			ret = snprintf(pinfo->strbuf + pinfo->strlen, LINE_SIZE - pinfo->strlen,
+					",%s,%lx", mod->name, off);
+			pinfo->strlen += ret;
+			/* cookie(current->comm, pc, mod->name, off, 1); */
+		} else
+			MET_TRACE("Error: pc (0x%lx) is not in module range\n", pc);
+	} else
+#endif
+	{
+		ret =
+		    snprintf(pinfo->strbuf + pinfo->strlen, LINE_SIZE - pinfo->strlen,
+			     ",vmlinux,%lx", pc);
+		pinfo->strlen += ret;
+		/* cookie(current->comm, pc, "vmlinux", pc, 0); */
+	}
+}
+
+#if defined(__arm__)
+static int report_trace(struct stackframe *frame, void *d)
+{
+	struct cookie_info *pinfo = d;
+	unsigned long pc = frame->pc;
+
+	if (pinfo->depth > 0) {
+		get_kernel_cookie(pc, pinfo);
+		pinfo->depth--;
+		return 0;
+	}
+	return 1;
+}
+#endif
+
+static void kernel_backtrace(struct pt_regs *const regs, struct cookie_info *pinfo)
+{
+#if defined(__arm__)
+	struct stackframe frame;
+
+	frame.fp = regs->ARM_fp;
+	frame.sp = regs->ARM_sp;
+	frame.lr = regs->ARM_lr;
+	frame.pc = regs->ARM_pc;
+	walk_stackframe(&frame, report_trace, pinfo);
+#else
+	return;
+#endif
+}
+
+
+void met_cookie_polling(unsigned long long stamp, int cpu)
+{
+	struct pt_regs *regs;
+	struct cookie_info *pinfo;
+	unsigned long pc;
+	int ret, outflag = 0;
+	off_t off;
+
+	regs = get_irq_regs();
+
+	if (regs == 0)
+		return;
+
+	pc = profile_pc(regs);
+
+	pinfo = &(per_cpu(info, cpu));
+	pinfo->strlen = snprintf(pinfo->strbuf, LINE_SIZE, "%s,%lx", current->comm, pc);
+
+	if (user_mode(regs)) {
+		struct mm_struct *mm;
+		struct vm_area_struct *vma;
+		struct path *ppath;
+
+		mm = current->mm;
+		for (vma = find_vma(mm, pc); vma; vma = vma->vm_next) {
+
+			if (pc < vma->vm_start || pc >= vma->vm_end)
+				continue;
+
+			if (vma->vm_file) {
+				ppath = &(vma->vm_file->f_path);
+
+				if (vma->vm_flags & VM_DENYWRITE)
+					off = pc;
+				else
+					off = (vma->vm_pgoff << PAGE_SHIFT) + pc - vma->vm_start;
+
+				ret =
+				    snprintf(pinfo->strbuf + pinfo->strlen,
+					     LINE_SIZE - pinfo->strlen, ",%s,%lx",
+					     (char *)(ppath->dentry->d_name.name), off);
+				pinfo->strlen += ret;
+				outflag = 1;
+			} else {
+				/* must be an anonymous map */
+				ret =
+				    snprintf(pinfo->strbuf + pinfo->strlen,
+					     LINE_SIZE - pinfo->strlen, ",nofile,%lx", pc);
+				pinfo->strlen += ret;
+				outflag = 1;
+			}
+			break;
+		}
+	} else {
+		/* kernel mode code */
+		if (back_trace_depth > 0) {
+			pinfo->depth = back_trace_depth + 1;
+			kernel_backtrace(regs, pinfo);
+		} else
+			get_kernel_cookie(pc, pinfo);
+		outflag = 1;
+	}
+
+	/* check task is resolvable */
+	if (outflag == 0)
+		return;
+
+	if (back_trace_depth == 0)
+		cookie(pinfo->strbuf);
+	else
+		cookie2(pinfo->strbuf);
+}
+
+
+static void met_cookie_start(void)
+{
+	/* return; */
+}
+
+static void met_cookie_stop(void)
+{
+	/* return; */
+}
+
+
+static int met_cookie_process_argument(const char *arg, int len)
+{
+	unsigned int value;
+
+	if (met_parse_num(arg, &value, len) < 0) {
+		met_cookie.mode = 0;
+		return -EINVAL;
+	}
+
+	back_trace_depth = value;
+	met_cookie.mode = 1;
+
+	return 0;
+}
+
+static const char help[] =
+"  --cookie                              enable sampling task and PC\n"
+"  --cookie=N                            enable back trace (depth is N)\n";
+
+static int met_cookie_print_help(char *buf, int len)
+{
+	len = snprintf(buf, PAGE_SIZE, help);
+	return len;
+}
+
+
+static const char header[] =
+"# cookie: task_name,PC,cookie_name,offset\n"
+"met-info [000] 0.0: cookie_header: task_name,PC,cookie_name,offset\n";
+
+static const char header2_1[] = "# cookie2: task_name,PC,cookie,offset";
+static const char header2_2[] = "met-info [000] 0.0: cookie2_header: task_name,PC,cookie,offset";
+
+static int met_cookie_print_header(char *buf, int len)
+{
+	int i, ret;
+
+	if (back_trace_depth == 0) {
+		len = snprintf(buf, PAGE_SIZE, header);
+	} else {
+		len = snprintf(buf, PAGE_SIZE, header2_1);
+		for (i = 0; i < back_trace_depth; i++) {
+			ret = snprintf(buf + len, PAGE_SIZE, ",cookie%d,offset%d", i + 1, i + 1);
+			len += ret;
+		}
+		ret = snprintf(buf + len, PAGE_SIZE, "\n");
+		len += ret;
+
+		ret = snprintf(buf + len, PAGE_SIZE, header2_2);
+		len += ret;
+		for (i = 0; i < back_trace_depth; i++) {
+			ret = snprintf(buf + len, PAGE_SIZE, ",cookie%d,offset%d", i + 1, i + 1);
+			len += ret;
+		}
+		ret = snprintf(buf + len, PAGE_SIZE, "\n");
+		len += ret;
+	}
+
+	return len;
+}
+
+struct metdevice met_cookie = {
+	.name = "cookie",
+	.type = MET_TYPE_PMU,
+	.cpu_related = 1,
+	.start = met_cookie_start,
+	.stop = met_cookie_stop,
+	.reset = reset_driver_stat,
+	.polling_interval = 1,
+	.timed_polling = met_cookie_polling,
+	.process_argument = met_cookie_process_argument,
+	.print_help = met_cookie_print_help,
+	.print_header = met_cookie_print_header,
+};
diff --git a/src/devtools/met-driver/4.19/mt2712/core_plf_init.c b/src/devtools/met-driver/4.19/mt2712/core_plf_init.c
new file mode 100644
index 0000000..a6f86ca
--- /dev/null
+++ b/src/devtools/met-driver/4.19/mt2712/core_plf_init.c
@@ -0,0 +1,168 @@
+/*
+ * 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/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/kallsyms.h>
+#include "met_drv.h"
+#include "met_api_tbl.h"
+#include "interface.h"
+#include "core_plf_init.h"
+
+#undef	DEBUG
+
+#ifdef MET_AP_EMI
+/*
+ *   EMI Monitor
+ */
+void *(*mt_cen_emi_base_get_symbol)(void);
+void *(*mt_chn_emi_base_get_symbol)(int chn);
+unsigned int (*get_dram_data_rate_symbol)(void);
+#if 0 /* New APIs for mt_dramc_nao base get */
+void *(*mt_dramc_nao_cha_base_get_symbol)(void);
+void *(*mt_dramc_nao_chb_base_get_symbol)(void);
+void *(*mt_dramc_nao_chc_base_get_symbol)(void);
+void *(*mt_dramc_nao_chd_base_get_symbol)(void);
+void *(*mt_ddrphy_cha_base_get_symbol)(void);
+void *(*mt_dramc_cha_base_get_symbol)(void);
+#else
+void *(*mt_dramc_nao_chn_base_get_symbol)(int channel);
+void *(*mt_ddrphy_chn_base_get_symbol)(int channel);
+void *(*mt_dramc_chn_base_get_symbol)(int channel);
+#endif
+
+/*
+ *   DRAM
+ */
+void (*mt_dramfreq_setfreq_registerCB_symbol)(dram_sampler_func pCB);
+#endif /* MET_AP_EMI */
+
+#ifdef MET_GPU
+/*
+ *   GPU
+ */
+int (*mtk_get_gpu_loading_symbol)(unsigned int *pLoading);
+int (*mtk_get_gpu_freq_symbol)(unsigned int *pFreq);
+unsigned int (*mtk_get_gpu_memory_usage_symbol)(unsigned int *pMemUsage);
+#endif /* MET_GPU */
+
+static int met_symbol_get(void)
+{
+#define _MET_SYMBOL_GET(_func_name_) \
+	do { \
+		_func_name_##_symbol = (void *)symbol_get(_func_name_); \
+		if (_func_name_##_symbol == NULL) { \
+			pr_debug("MET ext. symbol : %s is not found!\n", #_func_name_); \
+			PR_BOOTMSG_ONCE("MET ext. symbol : %s is not found!\n", #_func_name_); \
+		} \
+	} while (0)
+
+#ifdef MET_AP_EMI
+    _MET_SYMBOL_GET(mt_cen_emi_base_get);
+    _MET_SYMBOL_GET(mt_chn_emi_base_get);
+    _MET_SYMBOL_GET(get_dram_data_rate);
+#if 0 /* New APIs for mt_dramc_nao base get */
+    _MET_SYMBOL_GET(mt_dramc_nao_cha_base_get);
+    _MET_SYMBOL_GET(mt_dramc_nao_chb_base_get);
+    _MET_SYMBOL_GET(mt_dramc_nao_chc_base_get);
+    _MET_SYMBOL_GET(mt_dramc_nao_chd_base_get);
+    _MET_SYMBOL_GET(mt_ddrphy_cha_base_get);
+    _MET_SYMBOL_GET(mt_dramc_cha_base_get);
+#else
+    _MET_SYMBOL_GET(mt_dramc_nao_chn_base_get);
+    _MET_SYMBOL_GET(mt_ddrphy_chn_base_get);
+    _MET_SYMBOL_GET(mt_dramc_chn_base_get);
+#endif
+    _MET_SYMBOL_GET(mt_dramfreq_setfreq_registerCB);
+#endif /* MET_AP_EMI */
+
+#ifdef MET_GPU
+	_MET_SYMBOL_GET(mtk_get_gpu_loading);
+	_MET_SYMBOL_GET(mtk_get_gpu_freq);
+	_MET_SYMBOL_GET(mtk_get_gpu_memory_usage);
+#endif /* MET_GPU */
+
+	return 0;
+}
+
+static int met_symbol_put(void)
+{
+#define _MET_SYMBOL_PUT(_func_name_) { \
+		if (_func_name_##_symbol) { \
+			symbol_put(_func_name_); \
+			_func_name_##_symbol = NULL; \
+		} \
+	}
+
+#ifdef MET_AP_EMI
+    _MET_SYMBOL_PUT(mt_cen_emi_base_get);
+    _MET_SYMBOL_PUT(mt_chn_emi_base_get);
+    _MET_SYMBOL_PUT(get_dram_data_rate);
+#if 0 /* New APIs for mt_dramc_nao base get */
+    _MET_SYMBOL_PUT(mt_dramc_nao_cha_base_get);
+    _MET_SYMBOL_PUT(mt_dramc_nao_chb_base_get);
+    _MET_SYMBOL_PUT(mt_dramc_nao_chc_base_get);
+    _MET_SYMBOL_PUT(mt_dramc_nao_chd_base_get);
+    _MET_SYMBOL_PUT(mt_ddrphy_cha_base_get);
+    _MET_SYMBOL_PUT(mt_dramc_cha_base_get);
+#else
+    _MET_SYMBOL_PUT(mt_dramc_nao_chn_base_get);
+    _MET_SYMBOL_PUT(mt_ddrphy_chn_base_get);
+    _MET_SYMBOL_PUT(mt_dramc_chn_base_get);
+#endif
+    _MET_SYMBOL_PUT(mt_dramfreq_setfreq_registerCB);
+#endif /* MET_AP_EMI */
+
+#ifdef MET_GPU
+	_MET_SYMBOL_PUT(mtk_get_gpu_loading);
+	_MET_SYMBOL_PUT(mtk_get_gpu_freq);
+	_MET_SYMBOL_PUT(mtk_get_gpu_memory_usage);
+#endif /* MET_GPU */
+
+	return 0;
+}
+
+int core_plf_init(void)
+{
+	/*initial met external symbol*/
+	met_symbol_get();
+
+#ifdef MET_AP_EMI
+    met_register(&met_emi);
+#endif
+
+#ifdef MET_GPU
+	met_register(&met_gpu);
+	met_register(&met_gpudvfs);
+	met_register(&met_gpumem);
+#endif
+
+	return 0;
+}
+
+void core_plf_exit(void)
+{
+	/*release met external symbol*/
+	met_symbol_put();
+
+#ifdef MET_AP_EMI
+    met_deregister(&met_emi);
+#endif
+
+#ifdef MET_GPU
+	met_deregister(&met_gpu);
+	met_deregister(&met_gpudvfs);
+	met_deregister(&met_gpumem);
+#endif
+}
diff --git a/src/devtools/met-driver/4.19/mt2712/core_plf_init.h b/src/devtools/met-driver/4.19/mt2712/core_plf_init.h
new file mode 100644
index 0000000..678ce75
--- /dev/null
+++ b/src/devtools/met-driver/4.19/mt2712/core_plf_init.h
@@ -0,0 +1,92 @@
+/*
+ * 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.
+ */
+
+#ifndef __CORE_PLF_INIT_H__
+#define __CORE_PLF_INIT_H__
+
+extern struct miscdevice met_device;
+
+/*
+ *   MET External Symbol
+ */
+
+#ifdef MET_AP_EMI
+/*
+ *   EMI Monitor
+ */
+extern unsigned int get_dram_data_rate(void);       /* in Mhz */
+extern void *mt_cen_emi_base_get(void);
+extern void *mt_chn_emi_base_get(void);
+
+#if 0 /* New APIs for mt_dramc_nao base get */
+extern void *mt_dramc_nao_cha_base_get(void);
+extern void *mt_dramc_nao_chb_base_get(void);
+extern void *mt_dramc_nao_chc_base_get(void);
+extern void *mt_dramc_nao_chd_base_get(void);
+extern void *mt_ddrphy_cha_base_get(void);
+extern void *mt_dramc_cha_base_get(void);
+#else
+extern void *mt_dramc_nao_chn_base_get(int channel);
+extern void *mt_ddrphy_chn_base_get(int channel);
+extern void *mt_dramc_chn_base_get(int channel);
+#endif
+
+extern unsigned int (*get_dram_data_rate_symbol)(void); /* in Mhz */
+extern void *(*mt_cen_emi_base_get_symbol)(void);
+extern void *(*mt_chn_emi_base_get_symbol)(int chn);
+#if 0 /* New APIs for mt_dramc_nao base get */
+extern void *(*mt_dramc_nao_cha_base_get_symbol)(void);
+extern void *(*mt_dramc_nao_chb_base_get_symbol)(void);
+extern void *(*mt_dramc_nao_chc_base_get_symbol)(void);
+extern void *(*mt_dramc_nao_chd_base_get_symbol)(void);
+extern void *(*mt_ddrphy_cha_base_get_symbol)(void);
+extern void *(*mt_dramc_cha_base_get_symbol)(void);
+#else
+extern void *(*mt_dramc_nao_chn_base_get_symbol)(int channel);
+extern void *(*mt_ddrphy_chn_base_get_symbol)(int channel);
+extern void *(*mt_dramc_chn_base_get_symbol)(int channel);
+#endif
+
+/*
+ *   DRAM
+ */
+typedef void (*dram_sampler_func) (unsigned int);
+extern void (*mt_dramfreq_setfreq_registerCB_symbol)(dram_sampler_func pCB);
+
+/*mt_dramc.c do not have header file, declare here */
+extern void mt_dramfreq_setfreq_registerCB(dram_sampler_func pCB);
+
+extern struct metdevice met_emi;
+#endif
+
+#ifdef MET_GPU
+/*
+ *   GPU
+ */
+//#include <mtk_gpu_utility.h>
+//#include <mtk_gpufreq.h>
+
+extern int mtk_get_gpu_loading(unsigned int *pLoading);
+extern int mtk_get_gpu_freq(unsigned int *pFreq);
+extern unsigned int mtk_get_gpu_memory_usage(unsigned int *pMemUsage);
+
+extern int (*mtk_get_gpu_loading_symbol)(unsigned int *pLoading);
+extern int (*mtk_get_gpu_freq_symbol)(unsigned int *pFreq);
+extern unsigned int (*mtk_get_gpu_memory_usage_symbol)(unsigned int *pMemUsage);
+
+extern struct metdevice met_gpu;
+extern struct metdevice met_gpudvfs;
+extern struct metdevice met_gpumem;
+#endif /* MET_GPU */
+
+#endif /*__CORE_PLF_INIT_H__*/
diff --git a/src/devtools/met-driver/4.19/mt2712/core_plf_trace.c b/src/devtools/met-driver/4.19/mt2712/core_plf_trace.c
new file mode 100644
index 0000000..937f63b
--- /dev/null
+++ b/src/devtools/met-driver/4.19/mt2712/core_plf_trace.c
@@ -0,0 +1,376 @@
+/*
+ * 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/kernel.h>
+#include <linux/module.h>
+
+#include "met_drv.h"
+#include "interface.h"
+#include "trace.h"
+
+char *ms_formatH(char *__restrict__ buf, unsigned char cnt, unsigned int *__restrict__ value)
+{
+	char *s = buf;
+	int len;
+
+	if (cnt == 0) {
+		buf[0] = '\0';
+		return buf;
+	}
+
+	switch (cnt % 4) {
+	case 1:
+		len = sprintf(s, "%x", value[0]);
+		s += len;
+		value += 1;
+		cnt -= 1;
+		break;
+	case 2:
+		len = sprintf(s, "%x,%x", value[0], value[1]);
+		s += len;
+		value += 2;
+		cnt -= 2;
+		break;
+	case 3:
+		len = sprintf(s, "%x,%x,%x", value[0], value[1], value[2]);
+		s += len;
+		value += 3;
+		cnt -= 3;
+		break;
+	case 0:
+		len = sprintf(s, "%x,%x,%x,%x", value[0], value[1], value[2], value[3]);
+		s += len;
+		value += 4;
+		cnt -= 4;
+		break;
+	}
+
+	while (cnt) {
+		len = sprintf(s, ",%x,%x,%x,%x", value[0], value[1], value[2], value[3]);
+		s += len;
+		value += 4;
+		cnt -= 4;
+	}
+
+	s[0] = '\0';
+
+	return s;
+}
+EXPORT_SYMBOL(ms_formatH);
+
+char *ms_formatD(char *__restrict__ buf, unsigned char cnt, unsigned int *__restrict__ value)
+{
+	char *s = buf;
+	int len;
+
+	if (cnt == 0) {
+		buf[0] = '\0';
+		return buf;
+	}
+
+	switch (cnt % 4) {
+	case 1:
+		len = sprintf(s, "%u", value[0]);
+		s += len;
+		value += 1;
+		cnt -= 1;
+		break;
+	case 2:
+		len = sprintf(s, "%u,%u", value[0], value[1]);
+		s += len;
+		value += 2;
+		cnt -= 2;
+		break;
+	case 3:
+		len = sprintf(s, "%u,%u,%u", value[0], value[1], value[2]);
+		s += len;
+		value += 3;
+		cnt -= 3;
+		break;
+	case 0:
+		len = sprintf(s, "%u,%u,%u,%u", value[0], value[1], value[2], value[3]);
+		s += len;
+		value += 4;
+		cnt -= 4;
+		break;
+	}
+
+	while (cnt) {
+		len = sprintf(s, ",%u,%u,%u,%u", value[0], value[1], value[2], value[3]);
+		s += len;
+		value += 4;
+		cnt -= 4;
+	}
+
+	s[0] = '\0';
+
+	return s;
+}
+EXPORT_SYMBOL(ms_formatD);
+
+char *ms_formatH_ulong(char *__restrict__ buf, unsigned char cnt, unsigned long *__restrict__ value)
+{
+	char *s = buf;
+	int len;
+
+	if (cnt == 0) {
+		buf[0] = '\0';
+		return buf;
+	}
+
+	switch (cnt % 4) {
+	case 1:
+		len = sprintf(s, "%lx", value[0]);
+		s += len;
+		value += 1;
+		cnt -= 1;
+		break;
+	case 2:
+		len = sprintf(s, "%lx,%lx", value[0], value[1]);
+		s += len;
+		value += 2;
+		cnt -= 2;
+		break;
+	case 3:
+		len = sprintf(s, "%lx,%lx,%lx", value[0], value[1], value[2]);
+		s += len;
+		value += 3;
+		cnt -= 3;
+		break;
+	case 0:
+		len = sprintf(s, "%lx,%lx,%lx,%lx", value[0], value[1], value[2], value[3]);
+		s += len;
+		value += 4;
+		cnt -= 4;
+		break;
+	}
+
+	while (cnt) {
+		len = sprintf(s, ",%lx,%lx,%lx,%lx", value[0], value[1], value[2], value[3]);
+		s += len;
+		value += 4;
+		cnt -= 4;
+	}
+
+	s[0] = '\0';
+
+	return buf;
+}
+EXPORT_SYMBOL(ms_formatH_ulong);
+
+char *ms_formatD_ulong(char *__restrict__ buf, unsigned char cnt, unsigned long *__restrict__ value)
+{
+	char *s = buf;
+	int len;
+
+	if (cnt == 0) {
+		buf[0] = '\0';
+		return buf;
+	}
+
+	switch (cnt % 4) {
+	case 1:
+		len = sprintf(s, "%lu", value[0]);
+		s += len;
+		value += 1;
+		cnt -= 1;
+		break;
+	case 2:
+		len = sprintf(s, "%lu,%lu", value[0], value[1]);
+		s += len;
+		value += 2;
+		cnt -= 2;
+		break;
+	case 3:
+		len = sprintf(s, "%lu,%lu,%lu", value[0], value[1], value[2]);
+		s += len;
+		value += 3;
+		cnt -= 3;
+		break;
+	case 0:
+		len = sprintf(s, "%lu,%lu,%lu,%lu", value[0], value[1], value[2], value[3]);
+		s += len;
+		value += 4;
+		cnt -= 4;
+		break;
+	}
+
+	while (cnt) {
+		len = sprintf(s, ",%lu,%lu,%lu,%lu", value[0], value[1], value[2], value[3]);
+		s += len;
+		value += 4;
+		cnt -= 4;
+	}
+
+	s[0] = '\0';
+
+	return buf;
+}
+EXPORT_SYMBOL(ms_formatD_ulong);
+
+char *ms_formatH_EOL(char *__restrict__ buf, unsigned char cnt, unsigned int *__restrict__ value)
+{
+	char *s = buf;
+	int len;
+
+	if (cnt == 0) {
+		buf[0] = '\0';
+		return buf;
+	}
+
+	switch (cnt % 4) {
+	case 1:
+		len = sprintf(s, "%x", value[0]);
+		s += len;
+		value += 1;
+		cnt -= 1;
+		break;
+	case 2:
+		len = sprintf(s, "%x,%x", value[0], value[1]);
+		s += len;
+		value += 2;
+		cnt -= 2;
+		break;
+	case 3:
+		len = sprintf(s, "%x,%x,%x", value[0], value[1], value[2]);
+		s += len;
+		value += 3;
+		cnt -= 3;
+		break;
+	case 0:
+		len = sprintf(s, "%x,%x,%x,%x", value[0], value[1], value[2], value[3]);
+		s += len;
+		value += 4;
+		cnt -= 4;
+		break;
+	}
+
+	while (cnt) {
+		len = sprintf(s, ",%x,%x,%x,%x", value[0], value[1], value[2], value[3]);
+		s += len;
+		value += 4;
+		cnt -= 4;
+	}
+
+	s[0] = '\n';
+	s[1] = '\0';
+
+	return s + 1;
+}
+EXPORT_SYMBOL(ms_formatH_EOL);
+
+char *ms_formatD_EOL(char *__restrict__ buf, unsigned char cnt, unsigned int *__restrict__ value)
+{
+	char *s = buf;
+	int len;
+
+	if (cnt == 0) {
+		buf[0] = '\0';
+		return buf;
+	}
+
+	switch (cnt % 4) {
+	case 1:
+		len = sprintf(s, "%u", value[0]);
+		s += len;
+		value += 1;
+		cnt -= 1;
+		break;
+	case 2:
+		len = sprintf(s, "%u,%u", value[0], value[1]);
+		s += len;
+		value += 2;
+		cnt -= 2;
+		break;
+	case 3:
+		len = sprintf(s, "%u,%u,%u", value[0], value[1], value[2]);
+		s += len;
+		value += 3;
+		cnt -= 3;
+		break;
+	case 0:
+		len = sprintf(s, "%u,%u,%u,%u", value[0], value[1], value[2], value[3]);
+		s += len;
+		value += 4;
+		cnt -= 4;
+		break;
+	}
+
+	while (cnt) {
+		len = sprintf(s, ",%u,%u,%u,%u", value[0], value[1], value[2], value[3]);
+		s += len;
+		value += 4;
+		cnt -= 4;
+	}
+
+	s[0] = '\n';
+	s[1] = '\0';
+
+	return s + 1;
+}
+EXPORT_SYMBOL(ms_formatD_EOL);
+
+noinline void ms_bw_limiter(unsigned char cnt, unsigned int *value)
+{
+    char *SOB, *EOB;
+
+    MET_TRACE_GETBUF(&SOB, &EOB);
+    EOB = ms_formatH_EOL(EOB, cnt, value);
+    MET_TRACE_PUTBUF(SOB, EOB);
+}
+
+noinline void ms_emi(unsigned char cnt, unsigned int *value)
+{
+    char *SOB, *EOB;
+
+    MET_TRACE_GETBUF(&SOB, &EOB);
+    EOB = ms_formatH_EOL(EOB, cnt, value);
+    MET_TRACE_PUTBUF(SOB, EOB);
+}
+
+noinline void ms_emi_tsct(unsigned char cnt, unsigned int *value)
+{
+    char *SOB, *EOB;
+
+    MET_TRACE_GETBUF(&SOB, &EOB);
+    EOB = ms_formatH_EOL(EOB, cnt, value);
+    MET_TRACE_PUTBUF(SOB, EOB);
+}
+
+noinline void ms_emi_mdct(unsigned char cnt, unsigned int *value)
+{
+    char *SOB, *EOB;
+
+    MET_TRACE_GETBUF(&SOB, &EOB);
+    EOB = ms_formatH_EOL(EOB, cnt, value);
+    MET_TRACE_PUTBUF(SOB, EOB);
+}
+
+noinline void ms_ttype(unsigned char cnt, unsigned int *value)
+{
+    char *SOB, *EOB;
+
+    MET_TRACE_GETBUF(&SOB, &EOB);
+    EOB = ms_formatH_EOL(EOB, cnt, value);
+    MET_TRACE_PUTBUF(SOB, EOB);
+}
+
+noinline void ms_dramc(unsigned char cnt, unsigned int *value)
+{
+    char *SOB, *EOB;
+
+    MET_TRACE_GETBUF(&SOB, &EOB);
+    EOB = ms_formatH_EOL(EOB, cnt, value);
+    MET_TRACE_PUTBUF(SOB, EOB);
+}
+
diff --git a/src/devtools/met-driver/4.19/mt2712/core_plf_trace.h b/src/devtools/met-driver/4.19/mt2712/core_plf_trace.h
new file mode 100644
index 0000000..8dc1e6c
--- /dev/null
+++ b/src/devtools/met-driver/4.19/mt2712/core_plf_trace.h
@@ -0,0 +1,38 @@
+/*
+ * 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.
+ */
+
+#ifndef _CORE_PLF_TRACE_H_
+#define _CORE_PLF_TRACE_H_
+
+#define	HVALUE_SIZE	9	/* 8 chars (max value ffffffff) + 1 char (',' or NULL) */
+#define	DVALUE_SIZE	12	/* 10 chars (max value 4,294,967,295) + 1 char (',' or NULL) */
+
+char *ms_formatH(char *__restrict__ buf, unsigned char cnt, unsigned int *__restrict__ value);
+char *core_ms_formatD(char *__restrict__ buf, unsigned char cnt, unsigned int *__restrict__ value);
+char *ms_formatH_ulong(char *__restrict__ buf, unsigned char cnt,
+		       unsigned long *__restrict__ value);
+char *ms_formatD_ulong(char *__restrict__ buf, unsigned char cnt,
+		       unsigned long *__restrict__ value);
+char *ms_formatH_EOL(char *__restrict__ buf, unsigned char cnt, unsigned int *__restrict__ value);
+char *ms_formatD_EOL(char *__restrict__ buf, unsigned char cnt, unsigned int *__restrict__ value);
+
+void ms_emi(unsigned char cnt, unsigned int *value);
+void ms_emi_tsct(unsigned char cnt, unsigned int *value);
+void ms_emi_mdct(unsigned char cnt, unsigned int *value);
+
+void ms_ttype(unsigned char cnt, unsigned int *value);
+void ms_bw_limiter(unsigned char cnt, unsigned int *value);
+
+void ms_dramc(unsigned char cnt, unsigned int *value);
+
+#endif	/* _CORE_PLF_TRACE_H_ */
diff --git a/src/devtools/met-driver/4.19/mt2712/cpu_pmu.c b/src/devtools/met-driver/4.19/mt2712/cpu_pmu.c
new file mode 100644
index 0000000..f0ec38d
--- /dev/null
+++ b/src/devtools/met-driver/4.19/mt2712/cpu_pmu.c
@@ -0,0 +1,620 @@
+/*
+ * 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 <asm/page.h> */
+#include <linux/slab.h>
+#include <linux/version.h>
+
+#include "interface.h"
+#include "trace.h"
+#include "cpu_pmu.h"
+#include "met_drv.h"
+
+#define MET_USER_EVENT_SUPPORT
+
+#include <linux/kthread.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/wait.h>
+#include <linux/signal.h>
+#include <linux/workqueue.h>
+#include <linux/perf_event.h>
+
+#include "met_kernel_symbol.h"
+#include "interface.h"
+
+static int counter_cnt;
+
+struct metdevice met_cpupmu;
+struct cpu_pmu_hw *cpu_pmu;
+static int nr_counters;
+
+static struct kobject *kobj_cpu;
+static struct met_pmu *pmu;
+static int nr_arg;
+
+
+#define CNTMAX 8
+static DEFINE_PER_CPU(unsigned long long[CNTMAX], perfCurr);
+static DEFINE_PER_CPU(unsigned long long[CNTMAX], perfPrev);
+static DEFINE_PER_CPU(int[CNTMAX], perfCntFirst);
+static DEFINE_PER_CPU(struct perf_event * [CNTMAX], pevent);
+static DEFINE_PER_CPU(struct perf_event_attr [CNTMAX], pevent_attr);
+static DEFINE_PER_CPU(int, perfSet);
+static DEFINE_PER_CPU(unsigned int, perf_task_init_done);
+static DEFINE_PER_CPU(unsigned int, perf_cpuid);
+
+static DEFINE_PER_CPU(struct delayed_work, cpu_pmu_dwork);
+static DEFINE_PER_CPU(struct delayed_work *, perf_delayed_work_setup);
+
+static inline int reset_driver_stat(int counters)
+{
+	int i;
+
+	nr_arg = 0;
+	counter_cnt = 0;
+	met_cpupmu.mode = 0;
+	for (i = 0; i < counters; i++) {
+		pmu[i].mode = MODE_DISABLED;
+		pmu[i].event = 0;
+		pmu[i].freq = 0;
+	}
+
+	return 0;
+}
+
+static inline struct met_pmu *lookup_pmu(struct kobject *kobj)
+{
+	int i;
+
+	for (i = 0; i < nr_counters; i++) {
+		if (pmu[i].kobj_cpu_pmu == kobj)
+			return &pmu[i];
+	}
+	return NULL;
+}
+
+static ssize_t count_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
+{
+	return snprintf(buf, PAGE_SIZE, "%d\n", nr_counters - 1);
+}
+
+static ssize_t count_store(struct kobject *kobj, struct kobj_attribute *attr, const char *buf, size_t n)
+{
+	return -EINVAL;
+}
+
+static ssize_t event_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
+{
+	struct met_pmu *p = lookup_pmu(kobj);
+
+	if (p != NULL)
+		return snprintf(buf, PAGE_SIZE, "0x%hx\n", p->event);
+
+	return -EINVAL;
+}
+
+static ssize_t event_store(struct kobject *kobj, struct kobj_attribute *attr, const char *buf, size_t n)
+{
+	struct met_pmu *p = lookup_pmu(kobj);
+	unsigned short event;
+
+	if (p != NULL) {
+		if (sscanf(buf, "0x%hx", &event) != 1)
+			return -EINVAL;
+
+		if (p == &(pmu[nr_counters - 1])) {	/* cycle counter */
+			if (event != 0xff)
+				return -EINVAL;
+		} else {
+			if (cpu_pmu->check_event(pmu, nr_arg, event) < 0)
+				return -EINVAL;
+		}
+
+		p->event = event;
+		return n;
+	}
+	return -EINVAL;
+}
+
+static ssize_t mode_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
+{
+	struct met_pmu *p = lookup_pmu(kobj);
+
+	if (p != NULL) {
+		switch (p->mode) {
+		case 0:
+			return snprintf(buf, PAGE_SIZE, "%hhd (disabled)\n", p->mode);
+		case 1:
+			return snprintf(buf, PAGE_SIZE, "%hhd (interrupt)\n", p->mode);
+		case 2:
+			return snprintf(buf, PAGE_SIZE, "%hhd (polling)\n", p->mode);
+		}
+	}
+	return -EINVAL;
+}
+
+static ssize_t mode_store(struct kobject *kobj, struct kobj_attribute *attr, const char *buf, size_t n)
+{
+	unsigned int mode;
+	struct met_pmu *p = lookup_pmu(kobj);
+
+	if (p != NULL) {
+		if (kstrtouint(buf, 0, &mode) != 0)
+			return -EINVAL;
+
+		if (mode <= 2) {
+			p->mode = (unsigned char)mode;
+			if (mode > 0)
+				met_cpupmu.mode = 1;
+			return n;
+		}
+	}
+	return -EINVAL;
+}
+
+static ssize_t freq_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
+{
+	struct met_pmu *p = lookup_pmu(kobj);
+
+	if (p != NULL)
+		return snprintf(buf, PAGE_SIZE, "%ld\n", p->freq);
+
+	return -EINVAL;
+}
+
+static ssize_t freq_store(struct kobject *kobj, struct kobj_attribute *attr, const char *buf, size_t n)
+{
+	struct met_pmu *p = lookup_pmu(kobj);
+
+	if (p != NULL) {
+		if (kstrtoul(buf, 0, &(p->freq)) != 0)
+			return -EINVAL;
+
+		return n;
+	}
+	return -EINVAL;
+}
+
+static struct kobj_attribute count_attr = __ATTR(count, 0664, count_show, count_store);
+static struct kobj_attribute event_attr = __ATTR(event, 0664, event_show, event_store);
+static struct kobj_attribute mode_attr = __ATTR(mode, 0664, mode_show, mode_store);
+static struct kobj_attribute freq_attr = __ATTR(freq, 0664, freq_show, freq_store);
+
+static int cpupmu_create_subfs(struct kobject *parent)
+{
+	int ret = 0;
+	int i;
+	char buf[16];
+
+	cpu_pmu = cpu_pmu_hw_init();
+	if (cpu_pmu == NULL) {
+		PR_BOOTMSG("Failed to init CPU PMU HW!!\n");
+		return -ENODEV;
+	}
+	nr_counters = cpu_pmu->nr_cnt;
+
+	pmu = kmalloc_array(nr_counters, sizeof(struct met_pmu), GFP_KERNEL);
+	if (pmu == NULL)
+		return -ENOMEM;
+
+	memset(pmu, 0, sizeof(struct met_pmu) * nr_counters);
+	cpu_pmu->pmu = pmu;
+	kobj_cpu = parent;
+
+	ret = sysfs_create_file(kobj_cpu, &count_attr.attr);
+	if (ret != 0) {
+		PR_BOOTMSG("Failed to create count in sysfs\n");
+		goto out;
+	}
+
+	for (i = 0; i < nr_counters; i++) {
+		snprintf(buf, sizeof(buf), "%d", i);
+		pmu[i].kobj_cpu_pmu = kobject_create_and_add(buf, kobj_cpu);
+
+		ret = sysfs_create_file(pmu[i].kobj_cpu_pmu, &event_attr.attr);
+		if (ret != 0) {
+			PR_BOOTMSG("Failed to create event in sysfs\n");
+			goto out;
+		}
+
+		ret = sysfs_create_file(pmu[i].kobj_cpu_pmu, &mode_attr.attr);
+		if (ret != 0) {
+			PR_BOOTMSG("Failed to create mode in sysfs\n");
+			goto out;
+		}
+
+		ret = sysfs_create_file(pmu[i].kobj_cpu_pmu, &freq_attr.attr);
+		if (ret != 0) {
+			PR_BOOTMSG("Failed to create freq in sysfs\n");
+			goto out;
+		}
+	}
+
+ out:
+	if (ret != 0) {
+		if (pmu != NULL) {
+			kfree(pmu);
+			pmu = NULL;
+		}
+	}
+	return ret;
+}
+
+static void cpupmu_delete_subfs(void)
+{
+	int i;
+
+	if (kobj_cpu != NULL) {
+		for (i = 0; i < nr_counters; i++) {
+			sysfs_remove_file(pmu[i].kobj_cpu_pmu, &event_attr.attr);
+			sysfs_remove_file(pmu[i].kobj_cpu_pmu, &mode_attr.attr);
+			sysfs_remove_file(pmu[i].kobj_cpu_pmu, &freq_attr.attr);
+			kobject_del(pmu[i].kobj_cpu_pmu);
+			kobject_put(pmu[i].kobj_cpu_pmu);
+			pmu[i].kobj_cpu_pmu = NULL;
+		}
+		sysfs_remove_file(kobj_cpu, &count_attr.attr);
+		kobj_cpu = NULL;
+	}
+
+	if (pmu != NULL) {
+		kfree(pmu);
+		pmu = NULL;
+	}
+
+	cpu_pmu  = NULL;
+}
+
+noinline void mp_cpu(unsigned char cnt, unsigned int *value)
+{
+	MET_GENERAL_PRINT(MET_TRACE, cnt, value);
+}
+
+static void dummy_handler(struct perf_event *event, struct perf_sample_data *data,
+			  struct pt_regs *regs)
+{
+/* Required as perf_event_create_kernel_counter() requires an overflow handler, even though all we do is poll */
+}
+
+void perf_cpupmu_polling(unsigned long long stamp, int cpu)
+{
+	int i, count;
+	long long int delta;
+	struct perf_event *ev;
+	unsigned int pmu_value[MXNR_CPU];
+	u64 value;
+
+	MET_TRACE("counter_cnt = %d\n", counter_cnt);
+	if (per_cpu(perfSet, cpu) == 0)
+		return;
+
+	memset(pmu_value, 0, sizeof(pmu_value));
+	count = 0;
+	for (i = 0; i < nr_counters; i++) {
+		if (pmu[i].mode == 0)
+			continue;
+
+		ev = per_cpu(pevent, cpu)[i];
+		if ((ev != NULL) && (ev->state == PERF_EVENT_STATE_ACTIVE)) {
+			met_perf_event_read_local_symbol(ev, &value, NULL, NULL);
+			per_cpu(perfCurr, cpu)[i] = value;
+			delta = (per_cpu(perfCurr, cpu)[i] - per_cpu(perfPrev, cpu)[i]);
+			per_cpu(perfPrev, cpu)[i] = per_cpu(perfCurr, cpu)[i];
+			if (per_cpu(perfCntFirst, cpu)[i] == 1) {
+				/* we shall omit delta counter when we get first counter */
+				per_cpu(perfCntFirst, cpu)[i] = 0;
+				continue;
+			}
+			pmu_value[i] = (unsigned int) delta;
+			count++;
+		}
+	}
+
+	MET_TRACE("count = %d, counter_cnt = %d\n", count, counter_cnt);
+
+	if (count == counter_cnt)
+		mp_cpu(count, pmu_value);
+}
+
+static int perf_thread_set_perf_events(unsigned int cpu)
+{
+	int i, size;
+	struct perf_event *ev;
+
+	size = sizeof(struct perf_event_attr);
+	if (per_cpu(perfSet, cpu) == 0) {
+		for (i = 0; i < nr_counters; i++) {
+			per_cpu(pevent, cpu)[i] = NULL;
+			if (!pmu[i].mode)	/* Skip disabled counters */
+				continue;
+
+			per_cpu(perfPrev, cpu)[i] = 0;
+			per_cpu(perfCurr, cpu)[i] = 0;
+			memset(&per_cpu(pevent_attr, cpu)[i], 0, size);
+			per_cpu(pevent_attr, cpu)[i].config = pmu[i].event;
+			per_cpu(pevent_attr, cpu)[i].type = PERF_TYPE_RAW;
+			per_cpu(pevent_attr, cpu)[i].size = size;
+			per_cpu(pevent_attr, cpu)[i].sample_period = 0;
+			per_cpu(pevent_attr, cpu)[i].pinned = 1;
+			if (pmu[i].event == 0xff) {
+				per_cpu(pevent_attr, cpu)[i].type = PERF_TYPE_HARDWARE;
+				per_cpu(pevent_attr, cpu)[i].config = PERF_COUNT_HW_CPU_CYCLES;
+			}
+
+			per_cpu(pevent, cpu)[i] =
+			    perf_event_create_kernel_counter(&per_cpu(pevent_attr, cpu)[i], cpu, NULL,
+				     dummy_handler, NULL);
+			if (IS_ERR(per_cpu(pevent, cpu)[i])) {
+				per_cpu(pevent, cpu)[i] = NULL;
+				PR_BOOTMSG("CPU=%d, %s:%d\n", cpu, __FUNCTION__, __LINE__);
+				continue;
+			}
+
+			if (per_cpu(pevent, cpu)[i]->state != PERF_EVENT_STATE_ACTIVE) {
+				perf_event_release_kernel(per_cpu(pevent, cpu)[i]);
+				per_cpu(pevent, cpu)[i] = NULL;
+				PR_BOOTMSG("CPU=%d, %s:%d\n", cpu, __FUNCTION__, __LINE__);
+				continue;
+			}
+
+			ev = per_cpu(pevent, cpu)[i];
+			if (ev != NULL)
+				perf_event_enable(ev);
+			per_cpu(perfCntFirst, cpu)[i] = 1;
+		} /* for all PMU counter */
+		per_cpu(perfSet, cpu) = 1;
+	} /* for perfSet */
+	return 0;
+}
+
+
+static void perf_thread_setup(struct work_struct *work)
+{
+	unsigned int cpu;
+	struct delayed_work *dwork = to_delayed_work(work);
+
+	cpu = dwork->cpu;
+	if (per_cpu(perf_task_init_done, cpu) == 0) {
+		per_cpu(perf_task_init_done, cpu) = 1;
+		perf_thread_set_perf_events(cpu);
+	}
+
+	return;
+}
+
+void met_perf_cpupmu_online(unsigned int cpu)
+{
+	if (met_cpupmu.mode == 0)
+		return;
+
+	per_cpu(perf_cpuid, cpu) = cpu;
+	if (per_cpu(perf_delayed_work_setup, cpu) == NULL) {
+		struct delayed_work *dwork;
+
+		dwork = &per_cpu(cpu_pmu_dwork, cpu);
+		dwork->cpu = cpu;
+		INIT_DELAYED_WORK(dwork, perf_thread_setup);
+		schedule_delayed_work(dwork, 0);
+		per_cpu(perf_delayed_work_setup, cpu) = dwork;
+	}
+}
+
+
+void met_perf_cpupmu_down(void *data)
+{
+	unsigned int cpu;
+	unsigned int i;
+	struct perf_event *ev;
+
+	cpu = *((unsigned int *)data);
+	if (met_cpupmu.mode == 0)
+		return;
+	if (per_cpu(perfSet, cpu) == 0)
+		return;
+	per_cpu(perfSet, cpu) = 0;
+	for (i = 0; i < nr_counters; i++) {
+		if (!pmu[i].mode)
+			continue;
+		ev = per_cpu(pevent, cpu)[i];
+		if ((ev != NULL) && (ev->state == PERF_EVENT_STATE_ACTIVE)) {
+			perf_event_disable(ev);
+			perf_event_release_kernel(ev);
+		}
+	}
+	per_cpu(perf_task_init_done, cpu) = 0;
+	per_cpu(perf_delayed_work_setup, cpu) = NULL;
+}
+
+void met_perf_cpupmu_stop(void)
+{
+	unsigned int cpu;
+
+	for_each_online_cpu(cpu) {
+		per_cpu(perf_cpuid, cpu) = cpu;
+		met_perf_cpupmu_down((void *)&per_cpu(perf_cpuid, cpu));
+	}
+}
+
+void met_perf_cpupmu_start(void)
+{
+	unsigned int cpu;
+
+	for_each_online_cpu(cpu) {
+		met_perf_cpupmu_online(cpu);
+	}
+}
+
+void cpupmu_polling(unsigned long long stamp, int cpu)
+{
+	int count;
+	unsigned int pmu_value[MXNR_CPU];
+
+	if (met_cpu_pmu_method == 0) {
+		count = cpu_pmu->polling(pmu, nr_counters, pmu_value);
+		mp_cpu(count, pmu_value);
+	} else {
+		perf_cpupmu_polling(stamp, cpu);
+	}
+}
+
+static void cpupmu_start(void)
+{
+	if (met_cpu_pmu_method == 0) {
+		nr_arg = 0;
+		cpu_pmu->start(pmu, nr_counters);
+	}
+}
+
+static void cpupmu_stop(void)
+{
+	if (met_cpu_pmu_method == 0)
+		cpu_pmu->stop(nr_counters);
+}
+
+static const char cache_line_header[] =
+	"met-info [000] 0.0: met_cpu_cache_line_size: %d\n";
+static const char header_n[] =
+	"# mp_cpu: pmu_value1, ...\n"
+	"met-info [000] 0.0: met_cpu_header: 0x%x:%s";
+static const char header[] =
+	"# mp_cpu: pmu_value1, ...\n"
+	"met-info [000] 0.0: met_cpu_header: 0x%x";
+
+static const char help[] =
+	"  --pmu-cpu-evt=EVENT                   select CPU-PMU events. in %s,\n"
+	"                                        you can enable at most \"%d general purpose events\"\n"
+	"                                        plus \"one special 0xff (CPU_CYCLE) event\"\n";
+
+static int cpupmu_print_help(char *buf, int len)
+{
+	return snprintf(buf, PAGE_SIZE, help, cpu_pmu->cpu_name, nr_counters - 1);
+}
+
+static int cpupmu_print_header(char *buf, int len)
+{
+	int i, ret, first;
+	char name[32];
+
+	first = 1;
+	ret = 0;
+
+	/*append CPU PMU access method*/
+	if (met_cpu_pmu_method == 0)
+		ret += snprintf(buf + ret, PAGE_SIZE,
+			"met-info [000] 0.0: CPU_PMU_method: PMU registers\n");
+	else
+		ret += snprintf(buf + ret, PAGE_SIZE,
+			"met-info [000] 0.0: CPU_PMU_method: perf APIs\n");
+
+	/*append cache line size*/
+	ret += snprintf(buf + ret, PAGE_SIZE - ret, cache_line_header, cache_line_size());
+
+	for (i = 0; i < nr_counters; i++) {
+		if (pmu[i].mode == 0)
+			continue;
+		if (cpu_pmu->get_event_desc && 0 == cpu_pmu->get_event_desc(i, pmu[i].event, name)) {
+			if (first) {
+				ret += snprintf(buf + ret, PAGE_SIZE - ret, header_n, pmu[i].event, name);
+				first = 0;
+			} else {
+				ret += snprintf(buf + ret, PAGE_SIZE - ret, ",0x%x:%s", pmu[i].event, name);
+			}
+		} else {
+			if (first) {
+				ret += snprintf(buf + ret, PAGE_SIZE - ret, header, pmu[i].event);
+				first = 0;
+			} else {
+				ret += snprintf(buf + ret, PAGE_SIZE - ret, ",0x%x", pmu[i].event);
+			}
+		}
+		pmu[i].mode = 0;
+
+	}
+
+	ret += snprintf(buf + ret, PAGE_SIZE - ret, "\n");
+	met_cpupmu.mode = 0;
+	reset_driver_stat(nr_counters);
+	nr_arg = 0;
+	return ret;
+}
+
+/*
+ * "met-cmd --start --pmu_cpu_evt=0x3"
+ */
+static int cpupmu_process_argument(const char *arg, int len)
+{
+	unsigned int value;
+
+	if (met_cpu_pmu_method == 0)
+		nr_counters = cpu_pmu->nr_cnt;
+	else
+		nr_counters = perf_num_counters();
+
+	if (nr_counters == 0)
+		goto arg_out;
+
+	if (met_parse_num(arg, &value, len) < 0)
+		goto arg_out;
+
+	if (cpu_pmu->check_event(pmu, nr_arg, value) < 0)
+		goto arg_out;
+
+	if (value == 0xff) {
+		if (met_cpu_pmu_method == 0) {
+			pmu[nr_counters - 1].mode = MODE_POLLING;
+			pmu[nr_counters - 1].event = 0xff;
+			pmu[nr_counters - 1].freq = 0;
+		} else {
+			if (nr_arg > (nr_counters - 1))
+				goto arg_out;
+
+			pmu[nr_arg].mode = MODE_POLLING;
+			pmu[nr_arg].event = value;
+			pmu[nr_arg].freq = 0;
+			nr_arg++;
+		}
+	} else {
+
+		if (nr_arg >= (nr_counters - 1))
+			goto arg_out;
+
+		pmu[nr_arg].mode = MODE_POLLING;
+		pmu[nr_arg].event = value;
+		pmu[nr_arg].freq = 0;
+		nr_arg++;
+	}
+	counter_cnt++;
+
+	met_cpupmu.mode = 1;
+	return 0;
+
+arg_out:
+	reset_driver_stat(nr_counters);
+	return -EINVAL;
+}
+
+struct metdevice met_cpupmu = {
+	.name = "cpu",
+	.type = MET_TYPE_PMU,
+	.cpu_related = 1,
+	.create_subfs = cpupmu_create_subfs,
+	.delete_subfs = cpupmu_delete_subfs,
+	.start = cpupmu_start,
+	.stop = cpupmu_stop,
+	.polling_interval = 1,
+	.timed_polling = cpupmu_polling,
+	.print_help = cpupmu_print_help,
+	.print_header = cpupmu_print_header,
+	.process_argument = cpupmu_process_argument
+};
diff --git a/src/devtools/met-driver/4.19/mt2712/cpu_pmu.h b/src/devtools/met-driver/4.19/mt2712/cpu_pmu.h
new file mode 100644
index 0000000..013a063
--- /dev/null
+++ b/src/devtools/met-driver/4.19/mt2712/cpu_pmu.h
@@ -0,0 +1,61 @@
+/*
+ * 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.
+ */
+
+#ifndef _PMU_H_
+#define _PMU_H_
+
+#include <linux/device.h>
+
+#define MODE_DISABLED	0
+#define MODE_INTERRUPT	1
+#define MODE_POLLING	2
+
+#define MXSIZE_PMU_DESC 32
+#define MXNR_CPU 16
+
+struct met_pmu {
+	unsigned char mode;
+	unsigned short event;
+	unsigned long freq;
+	struct kobject *kobj_cpu_pmu;
+};
+
+struct cpu_pmu_hw {
+	const char *name;
+	const char *cpu_name;
+	int nr_cnt;
+	int (*get_event_desc)(int idx, int event, char *event_desc);
+	int (*check_event)(struct met_pmu *pmu, int idx, int event);
+	void (*start)(struct met_pmu *pmu, int count);
+	void (*stop)(int count);
+	unsigned int (*polling)(struct met_pmu *pmu, int count, unsigned int *pmu_value);
+	struct met_pmu *pmu;
+};
+
+struct pmu_desc {
+	unsigned int event;
+	char name[MXSIZE_PMU_DESC];
+};
+
+struct cpu_pmu_hw *cpu_pmu_hw_init(void);
+
+extern struct cpu_pmu_hw *cpu_pmu;
+extern noinline void mp_cpu(unsigned char cnt, unsigned int *value);
+
+extern void met_perf_cpupmu_start(void);
+extern void met_perf_cpupmu_stop(void);
+extern void met_perf_cpupmu_online(unsigned int cpu);
+extern void met_perf_cpupmu_down(void *cpu);
+extern void cpupmu_polling(unsigned long long stamp, int cpu);
+
+#endif				/* _PMU_H_ */
diff --git a/src/devtools/met-driver/4.19/mt2712/cpu_pmu_v2.c b/src/devtools/met-driver/4.19/mt2712/cpu_pmu_v2.c
new file mode 100644
index 0000000..26dcbfa
--- /dev/null
+++ b/src/devtools/met-driver/4.19/mt2712/cpu_pmu_v2.c
@@ -0,0 +1,675 @@
+/*

+ * 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/slab.h>

+#include <linux/version.h>

+

+#include "interface.h"

+#include "trace.h"

+#include "cpu_pmu_v2.h"

+#include "v8_pmu_hw_v2.h"

+#include "met_drv.h"

+

+

+#define MET_USER_EVENT_SUPPORT

+

+#include <linux/kthread.h>

+#include <linux/kernel.h>

+#include <linux/sched.h>

+#include <linux/wait.h>

+#include <linux/signal.h>

+#include <linux/workqueue.h>

+#include <linux/perf_event.h>

+#include "met_kernel_symbol.h"

+

+

+/*******************************************************************************

+*				Type Define

+*******************************************************************************/

+#define CNTMAX 8

+

+

+/*******************************************************************************

+*				Fuction Pototypes

+*******************************************************************************/

+static inline struct met_pmu_v2 *get_met_pmu_by_cpu_id(const unsigned int cpu);

+static inline void set_met_pmu_by_cpu_id(const unsigned int cpu, struct met_pmu_v2 *met_pmu);

+

+static int reset_driver_stat(void);

+static struct met_pmu_v2 *lookup_pmu(struct kobject *kobj);

+

+static ssize_t mode_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf);

+

+static int cpupmu_create_subfs(struct kobject *parent);

+static void cpupmu_delete_subfs(void);

+static void _cpupmu_start(void *info);

+static void cpupmu_start(void);

+static void _cpupmu_stop(void *info);

+static void cpupmu_stop(void);

+static void cpupmu_polling(unsigned long long stamp, int cpu);

+extern void cpupmu_polling_v2(unsigned long long stamp, int cpu);

+static int cpupmu_print_help(char *buf, int len);

+static int cpupmu_print_header(char *buf, int len);

+static int cpupmu_process_argument(const char *arg, int len);

+

+

+/*******************************************************************************

+*				Globe Variables

+*******************************************************************************/

+static int module_status;

+

+struct cpu_pmu_hw_v2 *met_pmu_hw_v2;

+

+static unsigned int gPMU_CNT[2*MXNR_CPU_V2];

+static unsigned int gMAX_PMU_HW_CNT;

+

+static struct kobject *gKOBJ_CPU;

+static struct met_pmu_v2 *gMET_PMU[2*MXNR_CPU_V2];

+

+static struct kobj_attribute mode_attr = __ATTR(mode, 0444, mode_show, NULL);

+

+static const char cache_line_header[] =

+	"met-info [000] 0.0: met_cpu_cache_line_size: %d\n";

+static const char header[] =

+	"met-info [000] 0.0: met_cpu_header_v2: ";

+static const char help[] =

+	"  --cpu-pmu=CORE_ID:EVENT	     select CPU-PMU events. in %s,\n"

+	"				      you can enable at most \"%d general purpose events\"\n"

+	"				      plus \"one special 0xff (CPU_CYCLE) event\"\n";

+

+static DEFINE_PER_CPU(int[CNTMAX], perfCurr);

+static DEFINE_PER_CPU(int[CNTMAX], perfPrev);

+static DEFINE_PER_CPU(struct perf_event * [CNTMAX], pevent);

+static DEFINE_PER_CPU(struct perf_event_attr [CNTMAX], pevent_attr);

+static DEFINE_PER_CPU(int, perfSet);

+static DEFINE_PER_CPU(unsigned int, perf_task_init_done);

+static DEFINE_PER_CPU(unsigned int, perf_cpuid);

+

+static DEFINE_PER_CPU(struct delayed_work, cpu_pmu_dwork);

+static DEFINE_PER_CPU(struct delayed_work *, perf_delayed_work_setup);

+

+struct metdevice met_cpupmu_v2 = {

+	.name = "cpu-pmu",

+	.type = MET_TYPE_PMU,

+	.cpu_related = 1,

+	.create_subfs = cpupmu_create_subfs,

+	.delete_subfs = cpupmu_delete_subfs,

+	.start = cpupmu_start,

+	.stop = cpupmu_stop,

+	.polling_interval = 1,

+	.timed_polling = cpupmu_polling,

+	.print_help = cpupmu_print_help,

+	.print_header = cpupmu_print_header,

+	.process_argument = cpupmu_process_argument

+};

+

+

+/*******************************************************************************

+*				Iplement Start

+*******************************************************************************/

+static inline struct met_pmu_v2 *get_met_pmu_by_cpu_id(const unsigned int cpu)

+{

+	if (cpu < MXNR_CPU_V2)

+		return gMET_PMU[cpu];

+	else

+		return NULL;

+}

+

+

+static inline void set_met_pmu_by_cpu_id(const unsigned int cpu, struct met_pmu_v2 *met_pmu)

+{

+	if (cpu < MXNR_CPU_V2)

+		gMET_PMU[cpu] = met_pmu;

+}

+

+

+static int reset_driver_stat()

+{

+	int i;

+	int cpu;

+	struct met_pmu_v2 *met_pmu;

+

+	met_cpupmu_v2.mode = 0;

+	for_each_possible_cpu(cpu) {

+		met_pmu = get_met_pmu_by_cpu_id(cpu);

+		for (i = 0; i < gMAX_PMU_HW_CNT; i++) {

+			met_pmu[i].mode = MODE_DISABLED;

+			met_pmu[i].event = 0;

+		}

+		gPMU_CNT[cpu] = 0;

+	}

+	module_status = 0;

+	return 0;

+}

+

+

+static struct met_pmu_v2 *lookup_pmu(struct kobject *kobj)

+{

+	int i;

+	int cpu;

+	struct met_pmu_v2 *met_pmu;

+

+	for_each_possible_cpu(cpu) {

+		met_pmu = get_met_pmu_by_cpu_id(cpu);

+		for (i = 0; i < gMAX_PMU_HW_CNT; i++) {

+			if (met_pmu[i].kobj_cpu_pmu == kobj)

+				return &met_pmu[i];

+		}

+	}

+	return NULL;

+}

+

+

+static ssize_t mode_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)

+{

+	struct met_pmu_v2 *p = lookup_pmu(kobj);

+

+	if (p != NULL) {

+		switch (p->mode) {

+		case 0:

+			return snprintf(buf, PAGE_SIZE, "%hhd (disabled)\n", p->mode);

+		case 1:

+			return snprintf(buf, PAGE_SIZE, "%hhd (interrupt)\n", p->mode);

+		case 2:

+			return snprintf(buf, PAGE_SIZE, "%hhd (polling)\n", p->mode);

+		}

+	}

+	return -EINVAL;

+}

+

+

+static int cpupmu_create_subfs(struct kobject *parent)

+{

+	int ret = 0;

+	unsigned int i;

+	unsigned int cpu;

+	char buf[16];

+	struct met_pmu_v2 *met_pmu;

+

+	met_pmu_hw_v2 = cpu_pmu_hw_init_v2();

+	if (met_pmu_hw_v2 == NULL) {

+		PR_BOOTMSG("Failed to init CPU PMU HW!!\n");

+		return -ENODEV;

+	}

+	gMAX_PMU_HW_CNT = met_pmu_hw_v2->max_hw_count;

+

+	gKOBJ_CPU = parent;

+	for_each_possible_cpu(cpu) {

+		met_pmu = kmalloc_array(gMAX_PMU_HW_CNT, sizeof(struct met_pmu_v2), GFP_KERNEL);

+		if (met_pmu != NULL) {

+			memset(met_pmu, 0x0, gMAX_PMU_HW_CNT * sizeof(struct met_pmu_v2));

+			met_pmu_hw_v2->met_pmu[cpu] = met_pmu;

+			set_met_pmu_by_cpu_id(cpu, met_pmu);

+		} else

+			ret = -ENOMEM;

+

+		for (i = 0; i < gMAX_PMU_HW_CNT; i++) {

+			snprintf(buf, sizeof(buf), "CPU-%d-%d", cpu, i);

+			met_pmu[i].kobj_cpu_pmu = kobject_create_and_add(buf, gKOBJ_CPU);

+			if (met_pmu[i].kobj_cpu_pmu) {

+				ret = sysfs_create_file(met_pmu[i].kobj_cpu_pmu, &mode_attr.attr);

+				if (ret != 0) {

+					PR_BOOTMSG("Failed to create mode in sysfs\n");

+					goto out;

+				}

+			}

+		}

+	}

+ out:

+	if (ret != 0) {

+		for_each_possible_cpu(cpu) {

+			met_pmu = get_met_pmu_by_cpu_id(cpu);

+			if (met_pmu != NULL) {

+				kfree(met_pmu);

+				set_met_pmu_by_cpu_id(cpu, NULL);

+			}

+		}

+	}

+	return ret;

+}

+

+

+static void cpupmu_delete_subfs(void)

+{

+	unsigned int i;

+	unsigned int cpu;

+	struct met_pmu_v2 *met_pmu;

+

+	for_each_possible_cpu(cpu) {

+		met_pmu = get_met_pmu_by_cpu_id(cpu);

+		if (met_pmu != NULL) {

+			for (i = 0; i < gMAX_PMU_HW_CNT; i++) {

+				sysfs_remove_file(met_pmu[i].kobj_cpu_pmu, &mode_attr.attr);

+				kobject_del(met_pmu[i].kobj_cpu_pmu);

+				kobject_put(met_pmu[i].kobj_cpu_pmu);

+				met_pmu[i].kobj_cpu_pmu = NULL;

+			}

+			kfree(met_pmu);

+		}

+		set_met_pmu_by_cpu_id(cpu, NULL);

+	}

+

+	if (gKOBJ_CPU != NULL) {

+		gKOBJ_CPU = NULL;

+	}

+

+	met_pmu_hw_v2  = NULL;

+}

+

+

+noinline void mp_cpu_v2(unsigned char cnt, unsigned int *value)

+{

+        if (cnt < MXNR_CPU_V2)

+	        MET_GENERAL_PRINT(MET_TRACE, cnt, value);

+}

+

+

+static void dummy_handler(struct perf_event *event, struct perf_sample_data *data,

+			  struct pt_regs *regs)

+{

+/* Required as perf_event_create_kernel_counter() requires an overflow handler, even though all we do is poll */

+}

+

+

+void perf_cpupmu_polling_v2(unsigned long long stamp, int cpu)

+{

+	int i, count, delta;

+	struct perf_event *ev;

+	unsigned int pmu_value[MXNR_CPU_V2];

+	struct met_pmu_v2 *met_pmu;

+	u64 value;

+

+	if (per_cpu(perfSet, cpu) == 0)

+		return;

+

+	memset(pmu_value, 0, sizeof(pmu_value));

+	count = 0;

+	met_pmu = get_met_pmu_by_cpu_id(cpu);

+	for (i = 0; i < gMAX_PMU_HW_CNT; i++) {

+		if (met_pmu[i].mode == 0)

+			continue;

+

+		ev = per_cpu(pevent, cpu)[i];

+		if ((ev != NULL) && (ev->state == PERF_EVENT_STATE_ACTIVE)) {

+			if (per_cpu(perfPrev, cpu)[i] == 0) {

+				met_perf_event_read_local_symbol(ev, &value, NULL, NULL);

+				per_cpu(perfPrev, cpu)[i] = value;

+				continue;

+			}

+			met_perf_event_read_local_symbol(ev, &value, NULL, NULL);

+			per_cpu(perfCurr, cpu)[i] = value;

+			delta = per_cpu(perfCurr, cpu)[i] - per_cpu(perfPrev, cpu)[i];

+			per_cpu(perfPrev, cpu)[i] = per_cpu(perfCurr, cpu)[i];

+			if (delta < 0)

+				delta *= -1;

+			pmu_value[i] = delta;

+			count++;

+		}

+	}

+

+	if (count == gPMU_CNT[cpu])

+		mp_cpu_v2(count, pmu_value);

+}

+

+

+static int perf_thread_set_perf_events_v2(unsigned int cpu)

+{

+	int i, size;

+	struct perf_event *ev;

+	struct met_pmu_v2 *met_pmu;

+

+	size = sizeof(struct perf_event_attr);

+	if (per_cpu(perfSet, cpu) == 0) {

+		met_pmu = get_met_pmu_by_cpu_id(cpu);

+		for (i = 0; i < gMAX_PMU_HW_CNT; i++) {

+			per_cpu(pevent, cpu)[i] = NULL;

+			if (!met_pmu[i].mode) {/* Skip disabled counters */

+				continue;

+			}

+			per_cpu(perfPrev, cpu)[i] = 0;

+			per_cpu(perfCurr, cpu)[i] = 0;

+			memset(&per_cpu(pevent_attr, cpu)[i], 0, size);

+			per_cpu(pevent_attr, cpu)[i].config = met_pmu[i].event;

+			per_cpu(pevent_attr, cpu)[i].type = PERF_TYPE_RAW;

+			per_cpu(pevent_attr, cpu)[i].size = size;

+			per_cpu(pevent_attr, cpu)[i].sample_period = 0;

+			per_cpu(pevent_attr, cpu)[i].pinned = 1;

+			if (met_pmu[i].event == 0xff) {

+				per_cpu(pevent_attr, cpu)[i].type = PERF_TYPE_HARDWARE;

+				per_cpu(pevent_attr, cpu)[i].config = PERF_COUNT_HW_CPU_CYCLES;

+			}

+

+			per_cpu(pevent, cpu)[i] =

+			    perf_event_create_kernel_counter(&per_cpu(pevent_attr, cpu)[i], cpu, NULL,

+				     dummy_handler, NULL);

+			if (IS_ERR(per_cpu(pevent, cpu)[i])) {

+				per_cpu(pevent, cpu)[i] = NULL;

+				PR_BOOTMSG("CPU=%d, %s:%d\n", cpu, __FUNCTION__, __LINE__);

+				continue;

+			}

+

+			if (per_cpu(pevent, cpu)[i]->state != PERF_EVENT_STATE_ACTIVE) {

+				perf_event_release_kernel(per_cpu(pevent, cpu)[i]);

+				per_cpu(pevent, cpu)[i] = NULL;

+				PR_BOOTMSG("CPU=%d, %s:%d\n", cpu, __FUNCTION__, __LINE__);

+				continue;

+			}

+

+			ev = per_cpu(pevent, cpu)[i];

+			if (ev != NULL) {

+				perf_event_enable(ev);

+			}

+		} /* for all PMU counter */

+		per_cpu(perfSet, cpu) = 1;

+	} /* for perfSet */

+	return 0;

+}

+

+

+static void perf_thread_setup_v2(struct work_struct *work)

+{

+	unsigned int cpu;

+	struct delayed_work *dwork = to_delayed_work(work);

+

+	cpu = dwork->cpu;

+	if (per_cpu(perf_task_init_done, cpu) == 0) {

+		per_cpu(perf_task_init_done, cpu) = 1;

+		perf_thread_set_perf_events_v2(cpu);

+	}

+

+	return ;

+}

+

+

+void met_perf_cpupmu_online_v2(unsigned int cpu)

+{

+	if (met_cpupmu_v2.mode == 0) {

+		PR_BOOTMSG("CPU=%d, %s:%d\n", cpu, __FUNCTION__, __LINE__);

+		return;

+	}

+

+	per_cpu(perf_cpuid, cpu) = cpu;

+	if (per_cpu(perf_delayed_work_setup, cpu) == NULL) {

+		struct delayed_work *dwork;

+

+		dwork = &per_cpu(cpu_pmu_dwork, cpu);

+		dwork->cpu = cpu;

+		INIT_DELAYED_WORK(dwork, perf_thread_setup_v2);

+		schedule_delayed_work(dwork, 0);

+		per_cpu(perf_delayed_work_setup, cpu) = dwork;

+	}

+}

+

+

+void met_perf_cpupmu_down_v2(void *data)

+{

+	unsigned int cpu;

+	unsigned int i;

+	struct perf_event *ev;

+	struct met_pmu_v2 *met_pmu;

+

+	cpu = *((unsigned int *)data);

+	if (met_cpupmu_v2.mode == 0)

+		return;

+	if (per_cpu(perfSet, cpu) == 0)

+		return;

+

+	met_pmu = get_met_pmu_by_cpu_id(cpu);

+	per_cpu(perfSet, cpu) = 0;

+	for (i = 0; i < gMAX_PMU_HW_CNT; i++) {

+		if (!met_pmu[i].mode)

+			continue;

+		ev = per_cpu(pevent, cpu)[i];

+		if ((ev != NULL) && (ev->state == PERF_EVENT_STATE_ACTIVE)) {

+			perf_event_disable(ev);

+			perf_event_release_kernel(ev);

+		}

+	}

+	per_cpu(perf_task_init_done, cpu) = 0;

+	per_cpu(perf_delayed_work_setup, cpu) = NULL;

+}

+

+

+void met_perf_cpupmu_start_v2(void)

+{

+	unsigned int cpu;

+

+	for_each_online_cpu(cpu) {

+		met_perf_cpupmu_online_v2(cpu);

+	}

+}

+

+

+void met_perf_cpupmu_stop_v2(void)

+{

+	unsigned int cpu;

+

+	for_each_online_cpu(cpu) {

+		per_cpu(perf_cpuid, cpu) = cpu;

+		met_perf_cpupmu_down_v2((void *)&per_cpu(perf_cpuid, cpu));

+	}

+}

+

+

+static void cpupmu_polling(unsigned long long stamp, int cpu)

+{

+	int count;

+	struct met_pmu_v2 *met_pmu;

+	unsigned int pmu_value[MXNR_CPU_V2];

+

+	met_pmu = get_met_pmu_by_cpu_id(cpu);

+	if (met_cpu_pmu_method == 0) {

+		count = met_pmu_hw_v2->polling(met_pmu, gMAX_PMU_HW_CNT, pmu_value);

+		mp_cpu_v2(count, pmu_value);

+	} else

+		perf_cpupmu_polling_v2(stamp, cpu);

+}

+

+

+void cpupmu_polling_v2(unsigned long long stamp, int cpu)

+{

+	cpupmu_polling(stamp, cpu);

+}

+

+

+static void _cpupmu_start(void *info)

+{

+	unsigned int *cpu = (unsigned int *)info;

+	struct met_pmu_v2 *met_pmu;

+

+	met_pmu = get_met_pmu_by_cpu_id(*cpu);

+	met_pmu_hw_v2->start(met_pmu, gMAX_PMU_HW_CNT);

+}

+

+static void cpupmu_start(void)

+{

+	if (module_status == 1) {

+		PR_BOOTMSG("%s:%d\n", __FUNCTION__, __LINE__);

+		return;

+	}

+

+	if (met_cpu_pmu_method == 0) {

+		int this_cpu = smp_processor_id();

+		int cpu;

+

+		for_each_possible_cpu(cpu) {

+			if (cpu == this_cpu)

+				_cpupmu_start(&cpu);

+			else

+				met_smp_call_function_single_symbol(cpu, _cpupmu_start, &cpu, 1);

+		}

+	}

+	module_status = 1;

+}

+

+static void _cpupmu_stop(void *info)

+{

+	(void)info;

+

+	met_pmu_hw_v2->stop(gMAX_PMU_HW_CNT);

+}

+

+static void cpupmu_stop(void)

+{

+	if (module_status == 0) {

+		PR_BOOTMSG("%s:%d\n", __FUNCTION__, __LINE__);

+		return;

+	}

+

+	if (met_cpu_pmu_method == 0) {

+		int this_cpu = smp_processor_id();

+		int cpu;

+

+		for_each_possible_cpu(cpu) {

+			if (cpu == this_cpu)

+				_cpupmu_stop(&cpu);

+			else

+				met_smp_call_function_single_symbol(cpu, _cpupmu_stop, &cpu, 1);

+		}

+	}

+	module_status = 0;

+}

+

+static int cpupmu_print_help(char *buf, int len)

+{

+	return snprintf(buf, PAGE_SIZE, help, met_pmu_hw_v2->name, gMAX_PMU_HW_CNT - 1);

+}

+

+static int cpupmu_print_header(char *buf, int len)

+{

+	int i;

+	int ret = 0;

+	int pmu_cnt = 0;

+	char name[32];

+	unsigned int cpu;

+	struct met_pmu_v2 *met_pmu;

+

+	/*append CPU PMU access method*/

+	if (met_cpu_pmu_method == 0)

+		ret += snprintf(buf + ret, PAGE_SIZE,

+			"met-info [000] 0.0: CPU_PMU_method: PMU registers\n");

+	else

+		ret += snprintf(buf + ret, PAGE_SIZE,

+			"met-info [000] 0.0: CPU_PMU_method: perf APIs\n");

+

+	/*append cache line size*/

+	ret += snprintf(buf + ret, PAGE_SIZE - ret, cache_line_header, cache_line_size());

+	ret += snprintf(buf + ret, PAGE_SIZE - ret, header);

+	for_each_online_cpu(cpu) {

+		int cnt = 0;

+

+		pmu_cnt = gPMU_CNT[cpu];

+		met_pmu = get_met_pmu_by_cpu_id(cpu);

+		for (i = 0; i < pmu_cnt; i++) {

+			if (met_pmu[i].mode == 0)

+				continue;

+

+			if (met_pmu_hw_v2->get_event_desc && 0 == met_pmu_hw_v2->get_event_desc(met_pmu[i].event, name)) {

+				if (cnt == 0) {

+					ret += snprintf(buf + ret, PAGE_SIZE - ret, "CPU-%d=0x%x:%s", cpu, met_pmu[i].event, name);

+					cnt++;

+				} else

+					ret += snprintf(buf + ret, PAGE_SIZE - ret, ",0x%x:%s", met_pmu[i].event, name);

+			}

+			met_pmu[i].mode = 0;

+		}

+		if (cnt > 0 && cpu < MXNR_CPU_V2 - 1)

+			ret += snprintf(buf + ret, PAGE_SIZE - ret, ";");

+	}

+	ret += snprintf(buf + ret, PAGE_SIZE - ret, "\n");

+	met_cpupmu_v2.mode = 0;

+	reset_driver_stat();

+	return ret;

+}

+

+/*

+ * "met-cmd --start --pmu_core_evt=0:0x3,0x16,0x17"

+ */

+static int cpupmu_process_argument(const char *arg, int len)

+{

+	int ret;

+	unsigned int cpu;

+	unsigned int value;

+	unsigned int idx = 0;

+	char *str = NULL;

+	char *token = NULL;

+	struct met_pmu_v2 *met_pmu = NULL;

+

+	if (met_cpu_pmu_method == 0)

+		gMAX_PMU_HW_CNT = met_pmu_hw_v2->max_hw_count;

+	else

+		gMAX_PMU_HW_CNT = perf_num_counters();

+

+	if (gMAX_PMU_HW_CNT == 0) {

+		PR_BOOTMSG("%s:%d\n", __FUNCTION__, __LINE__);

+		goto arg_out;

+	}

+

+	str = kstrdup(arg, GFP_KERNEL);

+	token = strsep(&str, ":");

+	ret = met_parse_num(token, &cpu, strlen(token));

+	if (ret != 0) {

+		PR_BOOTMSG("%s:%d\n", __FUNCTION__, __LINE__);

+		goto arg_out;

+	}

+

+	met_pmu = get_met_pmu_by_cpu_id(cpu);

+	while (token && met_pmu && idx < gMAX_PMU_HW_CNT) {

+		token = strsep(&str, ",\r\n");

+		if (token) {

+			ret = met_parse_num(token, &value, strlen(token));

+			if (ret != 0) {

+				PR_BOOTMSG("%s:%d\n", __FUNCTION__, __LINE__);

+				goto arg_out;

+			}

+			if (value != 0xff) {

+				if (idx >= (gMAX_PMU_HW_CNT - 1)) {

+					PR_BOOTMSG("%s:%d\n", __FUNCTION__, __LINE__);

+					goto arg_out;

+				}

+				met_pmu[idx].mode = MODE_POLLING;

+				met_pmu[idx].event = value;

+				idx++;

+				gPMU_CNT[cpu]++;

+			} else {

+				if (met_cpu_pmu_method == 0) {

+					met_pmu[gMAX_PMU_HW_CNT - 1].mode = MODE_POLLING;

+					met_pmu[gMAX_PMU_HW_CNT - 1].event = 0xff;

+					gPMU_CNT[cpu]++;

+				} else {

+					if (idx > (gMAX_PMU_HW_CNT - 1)) {

+						PR_BOOTMSG("%s:%d\n", __FUNCTION__, __LINE__);

+						goto arg_out;

+					}

+					met_pmu[idx].mode = MODE_POLLING;

+					met_pmu[idx].event = 0xff;

+					idx++;

+					gPMU_CNT[cpu]++;

+				}

+			}

+			if (met_pmu_hw_v2->check_event(met_pmu, gPMU_CNT[cpu], value) < 0) {

+				PR_BOOTMSG("%s:%d\n", __FUNCTION__, __LINE__);

+				goto arg_out;

+			}

+		}

+	}

+	met_cpupmu_v2.mode = 1;

+	module_status = 0;

+	return 0;

+

+arg_out:

+	if (str)

+		kfree(str);

+	reset_driver_stat();

+	return -EINVAL;

+}

diff --git a/src/devtools/met-driver/4.19/mt2712/cpu_pmu_v2.h b/src/devtools/met-driver/4.19/mt2712/cpu_pmu_v2.h
new file mode 100644
index 0000000..6af56bb
--- /dev/null
+++ b/src/devtools/met-driver/4.19/mt2712/cpu_pmu_v2.h
@@ -0,0 +1,85 @@
+/*

+ * 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.

+ */

+

+#ifndef _PMU_V2_H_

+#define _PMU_V2_H_

+

+#include <linux/device.h>

+#include <linux/threads.h> /* NR_CPUS */

+

+#define MODE_DISABLED	0

+#define MODE_INTERRUPT	1

+#define MODE_POLLING	2

+#define MXSIZE_PMU_DESC 32

+#define MXNR_CPU_V2     NR_CPUS

+

+

+/*******************************************************************************

+*                                Type Define

+*******************************************************************************/

+struct met_pmu_v2 {

+	unsigned char mode;

+	unsigned short event;

+	struct kobject *kobj_cpu_pmu;

+        const char *cpu_name;

+};

+

+enum ARM_TYPE_v2 {

+	CORTEX_A53 = 0xD03,

+	CORTEX_A35 = 0xD04,

+	CORTEX_A57 = 0xD07,

+	CORTEX_A72 = 0xD08,

+	CORTEX_A73 = 0xD09,

+	CHIP_UNKNOWN = 0xFFF

+};

+

+struct pmu_desc_v2 {

+	unsigned int event;

+	char name[MXSIZE_PMU_DESC];

+};

+

+struct chip_pmu_v2 {

+	enum ARM_TYPE_v2 type;

+	struct pmu_desc_v2 *desc;

+        unsigned int pmu_count;

+	unsigned int hw_count;

+	const char *cpu_name;

+};

+

+struct cpu_pmu_hw_v2 {

+	const char *name;

+        int max_hw_count;

+	int (*get_event_desc)(int event, char *event_desc);

+	int (*check_event)(struct met_pmu_v2 *pmu, int idx, int event);

+	void (*start)(struct met_pmu_v2 *pmu, int count);

+	void (*stop)(int count);

+	unsigned int (*polling)(struct met_pmu_v2 *pmu, int count, unsigned int *pmu_value);

+	struct met_pmu_v2 *met_pmu[MXNR_CPU_V2];

+        struct chip_pmu_v2 *chip_pmu[MXNR_CPU_V2];

+};

+

+

+extern struct cpu_pmu_hw_v2 *cpu_pmu_hw_v2;

+

+

+/*******************************************************************************

+*                                Fuction Pototypes

+*******************************************************************************/

+extern noinline void mp_cpu_v2(unsigned char cnt, unsigned int *value);

+extern void met_perf_cpupmu_online_v2(unsigned int cpu);

+extern void met_perf_cpupmu_down_v2(void *cpu);

+extern void met_perf_cpupmu_start_v2(void);

+extern void met_perf_cpupmu_stop_v2(void);

+extern void cpupmu_polling_v2(unsigned long long stamp, int cpu);

+

+#endif /* _PMU_V2_H_ */

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;
+}
diff --git a/src/devtools/met-driver/4.19/mt2712/interface.h b/src/devtools/met-driver/4.19/mt2712/interface.h
new file mode 100644
index 0000000..00c26a6
--- /dev/null
+++ b/src/devtools/met-driver/4.19/mt2712/interface.h
@@ -0,0 +1,74 @@
+/*
+ * 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.
+ */
+
+#ifndef __INTERFACE_H__
+#define __INTERFACE_H__
+
+#include <linux/fs.h>
+
+#ifdef MET_USER_EVENT_SUPPORT
+extern int tag_reg(struct file_operations *const fops, struct kobject *kobj);
+extern int tag_unreg(void);
+#include "met_drv.h"
+#include "met_tag.h"
+extern struct bltable_t bltab;
+#endif
+
+extern struct metdevice met_stat;
+extern struct metdevice met_cpupmu;
+extern struct metdevice met_cpupmu_v2;
+extern struct metdevice met_cookie;
+extern struct metdevice met_memstat;
+extern struct metdevice met_switch;
+extern struct metdevice met_trace_event;
+extern struct metdevice met_dummy_header;
+extern struct metdevice met_backlight;
+
+/* This variable will decide which method to access the CPU PMU counter */
+/*     0: access registers directly */
+/*     others: via Linux perf driver */
+extern unsigned int met_cpu_pmu_method;
+
+extern int met_parse_num(const char *str, unsigned int *value, int len);
+extern void met_set_suspend_notify(int flag);
+
+#define	PR_CPU_NOTIFY
+#if	defined(PR_CPU_NOTIFY)
+extern int met_cpu_notify;
+#endif
+
+//#undef	MET_BOOT_MSG
+#define	MET_BOOT_MSG
+#if	defined(MET_BOOT_MSG)
+extern char met_boot_msg_tmp[256];
+extern int pr_bootmsg(int str_len, char *str);
+#define	PR_BOOTMSG(fmt, args...) { \
+	int str_len = snprintf(met_boot_msg_tmp, sizeof(met_boot_msg_tmp), \
+			       fmt, ##args); \
+	pr_bootmsg(str_len, met_boot_msg_tmp); }
+#define	PR_BOOTMSG_ONCE(fmt, args...) { \
+	static int once; \
+	if (!once) { \
+		int str_len = snprintf(met_boot_msg_tmp, \
+				       sizeof(met_boot_msg_tmp), \
+				       fmt, ##args); \
+		pr_bootmsg(str_len, met_boot_msg_tmp); \
+		once = 1; \
+	} }
+#else
+#define	pr_bootmsg(str_len, str)
+#define PR_BOOTMSG(fmt, args...)
+#define	PR_BOOTMSG_ONCE(fmt, args...)
+#endif
+
+#endif	/* __INTERFACE_H__ */
diff --git a/src/devtools/met-driver/4.19/mt2712/met_api_tbl.h b/src/devtools/met-driver/4.19/mt2712/met_api_tbl.h
new file mode 100644
index 0000000..f93ae5f
--- /dev/null
+++ b/src/devtools/met-driver/4.19/mt2712/met_api_tbl.h
@@ -0,0 +1,36 @@
+/*
+ * 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.
+ */
+
+struct met_api_tbl {
+	int (*met_tag_start)(unsigned int class_id, const char *name);
+	int (*met_tag_end)(unsigned int class_id, const char *name);
+	int (*met_tag_async_start)(unsigned int class_id, const char *name, unsigned int cookie);
+	int (*met_tag_async_end)(unsigned int class_id, const char *name, unsigned int cookie);
+	int (*met_tag_oneshot)(unsigned int class_id, const char *name, unsigned int value);
+	int (*met_tag_userdata)(char *pData);
+	int (*met_tag_dump)(unsigned int class_id, const char *name, void *data, unsigned int length);
+	int (*met_tag_disable)(unsigned int class_id);
+	int (*met_tag_enable)(unsigned int class_id);
+	int (*met_set_dump_buffer)(int size);
+	int (*met_save_dump_buffer)(const char *pathname);
+	int (*met_save_log)(const char *pathname);
+	int (*met_show_bw_limiter)(void);
+	int (*met_reg_bw_limiter)(void *fp);
+	int (*met_show_clk_tree)(const char *name, unsigned int addr, unsigned int status);
+	int (*met_reg_clk_tree)(void *fp);
+	void (*met_sched_switch)(struct task_struct *prev, struct task_struct *next);
+	int (*enable_met_backlight_tag)(void);
+	int (*output_met_backlight_tag)(int level);
+};
+
+extern struct met_api_tbl met_ext_api;
diff --git a/src/devtools/met-driver/4.19/mt2712/met_drv.h b/src/devtools/met-driver/4.19/mt2712/met_drv.h
new file mode 100644
index 0000000..99204e0
--- /dev/null
+++ b/src/devtools/met-driver/4.19/mt2712/met_drv.h
@@ -0,0 +1,273 @@
+/*
+ * 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.
+ */
+
+
+#ifndef MET_DRV
+#define MET_DRV
+
+#include <linux/version.h>
+#include <linux/preempt.h>
+#include <linux/device.h>
+#include <linux/percpu.h>
+#include <linux/hardirq.h>
+#include <linux/clk.h>
+
+extern int met_mode;
+extern int core_plf_init(void);
+extern void core_plf_exit(void);
+
+#define MET_MODE_TRACE_CMD_OFFSET	(1)
+#define MET_MODE_TRACE_CMD			(1<<MET_MODE_TRACE_CMD_OFFSET)
+
+#ifdef CONFIG_MET_MODULE
+#define my_preempt_enable() preempt_enable()
+#else
+#define my_preempt_enable() preempt_enable_no_resched()
+#endif
+
+#define MET_STRBUF_SIZE		1024
+DECLARE_PER_CPU(char[MET_STRBUF_SIZE], met_strbuf_nmi);
+DECLARE_PER_CPU(char[MET_STRBUF_SIZE], met_strbuf_irq);
+DECLARE_PER_CPU(char[MET_STRBUF_SIZE], met_strbuf_sirq);
+DECLARE_PER_CPU(char[MET_STRBUF_SIZE], met_strbuf);
+
+#ifdef CONFIG_TRACING
+#define TRACE_PUTS(p) \
+	do { \
+		trace_puts(p);; \
+	} while (0)
+#else
+#define TRACE_PUTS(p) do {} while (0)
+#endif
+
+#define GET_MET_TRACE_BUFFER_ENTER_CRITICAL() \
+	({ \
+		char *pmet_strbuf; \
+		preempt_disable(); \
+		if (in_nmi()) \
+			pmet_strbuf = per_cpu(met_strbuf_nmi, smp_processor_id()); \
+		else if (in_irq()) \
+			pmet_strbuf = per_cpu(met_strbuf_irq, smp_processor_id()); \
+		else if (in_softirq()) \
+			pmet_strbuf = per_cpu(met_strbuf_sirq, smp_processor_id()); \
+		else \
+			pmet_strbuf = per_cpu(met_strbuf, smp_processor_id()); \
+		pmet_strbuf;\
+	})
+
+#define PUT_MET_TRACE_BUFFER_EXIT_CRITICAL(pmet_strbuf) \
+	do {\
+		if (pmet_strbuf)\
+			TRACE_PUTS(pmet_strbuf); \
+		my_preempt_enable(); \
+	} while (0)
+
+#define MET_TRACE(FORMAT, args...) \
+	do { \
+		char *pmet_strbuf; \
+		preempt_disable(); \
+		if (in_nmi()) \
+			pmet_strbuf = per_cpu(met_strbuf_nmi, smp_processor_id()); \
+		else if (in_irq()) \
+			pmet_strbuf = per_cpu(met_strbuf_irq, smp_processor_id()); \
+		else if (in_softirq()) \
+			pmet_strbuf = per_cpu(met_strbuf_sirq, smp_processor_id()); \
+		else \
+			pmet_strbuf = per_cpu(met_strbuf, smp_processor_id()); \
+		if (met_mode & MET_MODE_TRACE_CMD) \
+			snprintf(pmet_strbuf, MET_STRBUF_SIZE, "%s: " FORMAT, __func__, ##args); \
+		else \
+			snprintf(pmet_strbuf, MET_STRBUF_SIZE, FORMAT, ##args); \
+		TRACE_PUTS(pmet_strbuf); \
+		my_preempt_enable(); \
+	} while (0)
+
+/*
+ * SOB: start of buf
+ * EOB: end of buf
+ */
+#define MET_TRACE_GETBUF(pSOB, pEOB) \
+	({ \
+		preempt_disable(); \
+		if (in_nmi()) \
+			*pSOB = per_cpu(met_strbuf_nmi, smp_processor_id()); \
+		else if (in_irq()) \
+			*pSOB = per_cpu(met_strbuf_irq, smp_processor_id()); \
+		else if (in_softirq()) \
+			*pSOB = per_cpu(met_strbuf_sirq, smp_processor_id()); \
+		else \
+			*pSOB = per_cpu(met_strbuf, smp_processor_id()); \
+		*pEOB = *pSOB; \
+		if (met_mode & MET_MODE_TRACE_CMD) \
+			*pEOB += snprintf(*pEOB, MET_STRBUF_SIZE, "%s: ", __func__); \
+	})
+
+#define MET_TRACE_PUTBUF(SOB, EOB) \
+	({ \
+		__trace_puts(_THIS_IP_, (SOB), (uintptr_t)((EOB)-(SOB))); \
+		my_preempt_enable(); \
+	})
+
+#define MET_FTRACE_DUMP(TRACE_NAME, args...)			\
+	do {							\
+		trace_##TRACE_NAME(args);;			\
+	} while (0)
+
+
+#define MET_TYPE_PMU	1
+#define MET_TYPE_BUS	2
+#define MET_TYPE_MISC	3
+
+enum met_action {
+	MET_CPU_ONLINE,
+	MET_CPU_OFFLINE,
+
+	NR_MET_ACTION,
+};
+
+struct metdevice {
+	struct list_head list;
+	int type;
+	const char *name;
+	struct module *owner;
+	struct kobject *kobj;
+
+	int (*create_subfs)(struct kobject *parent);
+	void (*delete_subfs)(void);
+	int mode;
+	int ondiemet_mode;	/* new for ondiemet; 1: call ondiemet functions */
+	int cpu_related;
+	int polling_interval;
+	int polling_count_reload;
+	int __percpu *polling_count;
+	int header_read_again;	/*for header size > 1 page */
+	void (*start)(void);
+	void (*stop)(void);
+	int (*reset)(void);
+	void (*timed_polling)(unsigned long long stamp, int cpu);
+	void (*tagged_polling)(unsigned long long stamp, int cpu);
+	int (*print_help)(char *buf, int len);
+	int (*print_header)(char *buf, int len);
+	int (*process_argument)(const char *arg, int len);
+
+	void (*ondiemet_start)(void);
+	void (*ondiemet_stop)(void);
+	int (*ondiemet_reset)(void);
+	int (*ondiemet_print_help)(char *buf, int len);
+	int (*ondiemet_print_header)(char *buf, int len);
+	int (*ondiemet_process_argument)(const char *arg, int len);
+	void (*ondiemet_timed_polling)(unsigned long long stamp, int cpu);
+	void (*ondiemet_tagged_polling)(unsigned long long stamp, int cpu);
+
+	struct list_head exlist;	/* for linked list before register */
+	void (*suspend)(void);
+	void (*resume)(void);
+
+	unsigned long long prev_stamp;
+	spinlock_t my_lock;
+	void *reversed1;
+};
+
+int met_register(struct metdevice *met);
+int met_deregister(struct metdevice *met);
+int met_set_platform(const char *plf_name, int flag);
+int met_set_topology(const char *topology_name, int flag);
+int met_devlink_add(struct metdevice *met);
+int met_devlink_del(struct metdevice *met);
+int met_devlink_register_all(void);
+int met_devlink_deregister_all(void);
+
+int fs_reg(void);
+void fs_unreg(void);
+
+/******************************************************************************
+ * Tracepoints
+ ******************************************************************************/
+#define MET_DEFINE_PROBE(probe_name, proto) \
+		static void probe_##probe_name(void *data, PARAMS(proto))
+#define MET_REGISTER_TRACE(probe_name) \
+		register_trace_##probe_name(probe_##probe_name, NULL)
+#define MET_UNREGISTER_TRACE(probe_name) \
+		unregister_trace_##probe_name(probe_##probe_name, NULL)
+
+
+/* ====================== Tagging API ================================ */
+
+#define MAX_EVENT_CLASS	31
+#define MAX_TAGNAME_LEN	128
+#define MET_CLASS_ALL	0x80000000
+
+/* IOCTL commands of MET tagging */
+struct mtag_cmd_t {
+	unsigned int class_id;
+	unsigned int value;
+	unsigned int slen;
+	char tname[MAX_TAGNAME_LEN];
+	void *data;
+	unsigned int size;
+};
+
+#define TYPE_START		1
+#define TYPE_END		2
+#define TYPE_ONESHOT		3
+#define TYPE_ENABLE		4
+#define TYPE_DISABLE		5
+#define TYPE_REC_SET		6
+#define TYPE_DUMP		7
+#define TYPE_DUMP_SIZE		8
+#define TYPE_DUMP_SAVE		9
+#define TYPE_USRDATA		10
+#define TYPE_DUMP_AGAIN		11
+#define TYPE_ASYNC_START	12
+#define TYPE_ASYNC_END		13
+#define TYPE_MET_SUSPEND	15
+#define TYPE_MET_RESUME		16
+
+/* Use 'm' as magic number */
+#define MTAG_IOC_MAGIC  'm'
+/* Please use a different 8-bit number in your code */
+#define MTAG_CMD_START		_IOW(MTAG_IOC_MAGIC, TYPE_START, struct mtag_cmd_t)
+#define MTAG_CMD_END		_IOW(MTAG_IOC_MAGIC, TYPE_END, struct mtag_cmd_t)
+#define MTAG_CMD_ONESHOT	_IOW(MTAG_IOC_MAGIC, TYPE_ONESHOT, struct mtag_cmd_t)
+#define MTAG_CMD_ENABLE		_IOW(MTAG_IOC_MAGIC, TYPE_ENABLE, int)
+#define MTAG_CMD_DISABLE	_IOW(MTAG_IOC_MAGIC, TYPE_DISABLE, int)
+#define MTAG_CMD_REC_SET	_IOW(MTAG_IOC_MAGIC, TYPE_REC_SET, int)
+#define MTAG_CMD_DUMP		_IOW(MTAG_IOC_MAGIC, TYPE_DUMP, struct mtag_cmd_t)
+#define MTAG_CMD_DUMP_SIZE	_IOWR(MTAG_IOC_MAGIC, TYPE_DUMP_SIZE, int)
+#define MTAG_CMD_DUMP_SAVE	_IOW(MTAG_IOC_MAGIC, TYPE_DUMP_SAVE, struct mtag_cmd_t)
+#define MTAG_CMD_USRDATA	_IOW(MTAG_IOC_MAGIC, TYPE_USRDATA, struct mtag_cmd_t)
+#define MTAG_CMD_DUMP_AGAIN	_IOW(MTAG_IOC_MAGIC, TYPE_DUMP_AGAIN, void *)
+#define MTAG_CMD_ASYNC_START	_IOW(MTAG_IOC_MAGIC, TYPE_ASYNC_START, struct mtag_cmd_t)
+#define MTAG_CMD_ASYNC_END	_IOW(MTAG_IOC_MAGIC, TYPE_ASYNC_END, struct mtag_cmd_t)
+
+/* include file */
+extern int met_tag_start_real(unsigned int class_id, const char *name);
+extern int met_tag_end_real(unsigned int class_id, const char *name);
+extern int met_tag_async_start_real(unsigned int class_id, const char *name, unsigned int cookie);
+extern int met_tag_async_end_real(unsigned int class_id, const char *name, unsigned int cookie);
+extern int met_tag_oneshot_real(unsigned int class_id, const char *name, unsigned int value);
+extern int met_tag_userdata_real(char *pData);
+extern int met_tag_dump_real(unsigned int class_id, const char *name, void *data, unsigned int length);
+extern int met_tag_disable_real(unsigned int class_id);
+extern int met_tag_enable_real(unsigned int class_id);
+extern int met_set_dump_buffer_real(int size);
+extern int met_save_dump_buffer_real(const char *pathname);
+extern int met_save_log_real(const char *pathname);
+extern int met_show_bw_limiter_real(void);
+extern int met_reg_bw_limiter_real(void *fp);
+extern int met_show_clk_tree_real(const char *name, unsigned int addr, unsigned int status);
+extern int met_reg_clk_tree_real(void *fp);
+extern int enable_met_backlight_tag(void);
+extern int output_met_backlight_tag(int level);
+
+#endif	/* MET_DRV */
diff --git a/src/devtools/met-driver/4.19/mt2712/met_emi.c b/src/devtools/met-driver/4.19/mt2712/met_emi.c
new file mode 100644
index 0000000..7f18f4a
--- /dev/null
+++ b/src/devtools/met-driver/4.19/mt2712/met_emi.c
@@ -0,0 +1,1107 @@
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/device.h>
+#include <linux/miscdevice.h>
+#include <linux/module.h>
+#include <linux/dma-mapping.h>
+#include <linux/string.h>
+
+#define MET_USER_EVENT_SUPPORT
+#include "met_drv.h"
+#include "trace.h"
+
+#include "mtk_typedefs.h"
+#include "core_plf_init.h"
+#include "core_plf_trace.h"
+#include "mtk_emi_bm.h"
+
+extern struct miscdevice met_device;
+
+/*======================================================================*/
+/*	Global variable definitions					*/
+/*======================================================================*/
+/*ondiemet emi sampling interval in us */
+int ondiemet_emi_polling_200us;
+int emi_tsct_enable;
+int emi_mdct_enable;
+
+
+int emi_use_ondiemet;
+int metemi_func_opt;
+
+int met_emi_regdump;
+/*WSCT/TSCT id selection enable*/
+int emi_wsct_tsct_id_selection[4];
+/* Dynamic MonitorCounter selection !!!EXPERIMENT!!! */
+static int msel_enable;
+static unsigned int msel_group1 = BM_Master_GP_1_Default;
+static unsigned int msel_group2 = BM_Master_GP_2_Default;
+static unsigned int msel_group3 = BM_Master_GP_3_Default;
+
+/* CVS Added changeable buffer for testing */
+static int mdmcu_sel_enable;
+static unsigned int rd_mdmcu_rsv_num = 0x5;
+
+/* Global variables */
+static struct kobject *kobj_emi;
+static int rwtype = BM_BOTH_READ_WRITE;
+
+/* BW Limiter */
+/*#define CNT_COUNTDOWN	(1000-1)*/		/* 1000 * 1ms = 1sec */
+#define CNT_COUNTDOWN	(0)			/* 1ms */
+static int countdown;
+static int bw_limiter_enable = BM_BW_LIMITER_ENABLE;
+
+/* TTYPE counter */
+static int ttype1_16_en = BM_TTYPE1_16_DISABLE;
+static int ttype17_21_en = BM_TTYPE17_21_DISABLE;
+
+unsigned int fmem_divider_freq_1;
+unsigned int fmem_divider_freq_2;
+
+static int dramc_pdir_enable;
+static int dram_chann_num = 1;
+
+/*======================================================================*/
+/*	EMI Test Operations						*/
+/*======================================================================*/
+static int times;
+
+static ssize_t test_apmcu_store(struct kobject *kobj,
+		struct kobj_attribute *attr,
+		const char *buf,
+		size_t n)
+{
+	int		i;
+	unsigned int	*src_addr_v;
+	dma_addr_t	src_addr_p;
+
+	if ((n == 0) || (buf == NULL))
+		return -EINVAL;
+	if (sscanf(buf, "%d", &times) != 1)
+		return -EINVAL;
+	if (times < 0)
+		return -EINVAL;
+
+	if (times > 5000)	/* Less than 20MB */
+		return -EINVAL;
+
+	/* dma_alloc */
+	src_addr_v = dma_alloc_coherent(met_device.this_device,
+			PAGE_SIZE,
+			&src_addr_p,
+			GFP_KERNEL);
+	if (src_addr_v == NULL) {
+	/*	met_tag_oneshot(0, "test_apmcu dma alloc fail", PAGE_SIZE); */
+		return -ENOMEM;
+	}
+	/* testing */
+	preempt_disable();
+	/* met_tag_start(0, "TEST_EMI_APMCU"); */
+	for (i = 0; i < times; i++) {
+		memset(src_addr_v, 2*i, PAGE_SIZE);
+		/* met_tag_oneshot(0, "TEST_EMI_APMCU", PAGE_SIZE); */
+	}
+	/* met_tag_end(0, "TEST_EMI_APMCU"); */
+	preempt_enable();
+
+	/* dma_free */
+	if (src_addr_v != NULL)
+		dma_free_coherent(met_device.this_device,
+				PAGE_SIZE,
+				src_addr_v,
+				src_addr_p);
+	return n;
+}
+
+/*======================================================================*/
+/*	KOBJ Declarations						*/
+/*======================================================================*/
+DECLARE_KOBJ_ATTR_INT(ondiemet_emi_polling_200us, ondiemet_emi_polling_200us)
+DECLARE_KOBJ_ATTR_INT(emi_tsct_enable, emi_tsct_enable)
+DECLARE_KOBJ_ATTR_INT(emi_mdct_enable, emi_mdct_enable)
+DECLARE_KOBJ_ATTR_INT(metemi_func_opt, metemi_func_opt)
+DECLARE_KOBJ_ATTR_INT(emi_regdump, met_emi_regdump)
+DECLARE_KOBJ_ATTR_INT(emi_wsct_tsct_id_selection1, emi_wsct_tsct_id_selection[0])
+DECLARE_KOBJ_ATTR_INT(emi_wsct_tsct_id_selection2, emi_wsct_tsct_id_selection[1])
+DECLARE_KOBJ_ATTR_INT(emi_wsct_tsct_id_selection3, emi_wsct_tsct_id_selection[2])
+DECLARE_KOBJ_ATTR_INT(emi_wsct_tsct_id_selection4, emi_wsct_tsct_id_selection[3])
+/* KOBJ: Dynamic MonitorCounter selection !!!EXPERIMENT!!! */
+DECLARE_KOBJ_ATTR_INT(msel_enable, msel_enable)
+DECLARE_KOBJ_ATTR_HEX_CHECK(msel_group1, msel_group1, msel_group1 > 0 && msel_group1 <= BM_MASTER_ALL)
+DECLARE_KOBJ_ATTR_HEX_CHECK(msel_group2, msel_group2, msel_group2 > 0 && msel_group2 <= BM_MASTER_ALL)
+DECLARE_KOBJ_ATTR_HEX_CHECK(msel_group3, msel_group3, msel_group3 > 0 && msel_group3 <= BM_MASTER_ALL)
+DECLARE_KOBJ_ATTR_INT(mdmcu_sel_enable, mdmcu_sel_enable)
+DECLARE_KOBJ_ATTR_INT(rd_mdmcu_rsv_num, rd_mdmcu_rsv_num)
+
+
+/* KOBJ: rwtype */
+DECLARE_KOBJ_ATTR_INT_CHECK(rwtype, rwtype, rwtype >= 0 && rwtype <= BM_WRITE_ONLY)
+
+static unsigned int get_emi_clock_rate(unsigned int dram_data_rate_MHz)
+{
+	return dram_data_rate_MHz/DRAM_EMI_BASECLOCK_RATE/DRAM_DATARATE;
+}
+
+/* KOBJ: emi_clock_rate */
+static ssize_t emi_clock_rate_show(struct kobject *kobj,
+		struct kobj_attribute *attr,
+		char *buf)
+{
+	unsigned int	dram_data_rate_MHz;
+
+	dram_data_rate_MHz = get_dram_data_rate();
+	return snprintf(buf, PAGE_SIZE, "%d\n",
+			get_emi_clock_rate(dram_data_rate_MHz));
+}
+
+DECLARE_KOBJ_ATTR_RO(emi_clock_rate)
+
+static ssize_t dram_data_rate_show(struct kobject *kobj,
+		struct kobj_attribute *attr,
+		char *buf)
+{
+	unsigned int	dram_data_rate_MHz;
+
+	dram_data_rate_MHz = get_dram_data_rate();
+	return snprintf(buf, PAGE_SIZE, "%d\n", dram_data_rate_MHz);
+}
+
+DECLARE_KOBJ_ATTR_RO(dram_data_rate)
+
+/* KOBJ: ttype1_16_en */
+DECLARE_KOBJ_ATTR_STR_LIST_ITEM(
+		ttype1_16_en,
+		KOBJ_ITEM_LIST(
+			{BM_TTYPE1_16_ENABLE,	"ENABLE"},
+			{BM_TTYPE1_16_DISABLE,	"DISABLE"}
+			)
+		)
+DECLARE_KOBJ_ATTR_STR_LIST(ttype1_16_en, ttype1_16_en, ttype1_16_en)
+
+/* KOBJ: ttype17_21_en */
+DECLARE_KOBJ_ATTR_STR_LIST_ITEM(
+		ttype17_21_en,
+		KOBJ_ITEM_LIST(
+			{BM_TTYPE17_21_ENABLE,	"ENABLE"},
+			{BM_TTYPE17_21_DISABLE,	"DISABLE"}
+			)
+		)
+DECLARE_KOBJ_ATTR_STR_LIST(ttype17_21_en, ttype17_21_en, ttype17_21_en)
+
+/* KOBJ: bw_limiter_enable */
+DECLARE_KOBJ_ATTR_STR_LIST_ITEM(
+		bw_limiter_enable,
+		KOBJ_ITEM_LIST(
+			{BM_BW_LIMITER_ENABLE,	"ENABLE"},
+			{BM_BW_LIMITER_DISABLE,	"DISABLE"}
+			)
+		)
+
+DECLARE_KOBJ_ATTR_STR_LIST(bw_limiter_enable, bw_limiter_enable, bw_limiter_enable)
+
+/* KOBJ: ttype_master */
+DECLARE_KOBJ_ATTR_STR_LIST_ITEM(
+		ttype_master,
+		KOBJ_ITEM_LIST(
+			{BM_MASTER_M0,	"M0"},
+			{BM_MASTER_M1,	"M1"},
+			{BM_MASTER_M2,	"M2"},
+			{BM_MASTER_M3,	"M3"},
+			{BM_MASTER_M4,	"M4"},
+			{BM_MASTER_M5,	"M5"},
+			{BM_MASTER_M6,	"M6"},
+			{BM_MASTER_M7,	"M7"}
+			)
+		)
+
+
+/* KOBJ: ttypeX_nbeat, ttypeX_nbyte, ttypeX_burst */
+DECLARE_KOBJ_ATTR_INT_LIST_ITEM(
+		ttype_nbeat,
+		KOBJ_ITEM_LIST(
+			{BM_TRANS_TYPE_1BEAT,	1},
+			{BM_TRANS_TYPE_2BEAT,	2},
+			{BM_TRANS_TYPE_3BEAT,	3},
+			{BM_TRANS_TYPE_4BEAT,	4},
+			{BM_TRANS_TYPE_5BEAT,	5},
+			{BM_TRANS_TYPE_6BEAT,	6},
+			{BM_TRANS_TYPE_7BEAT,	7},
+			{BM_TRANS_TYPE_8BEAT,	8},
+			{BM_TRANS_TYPE_9BEAT,	9},
+			{BM_TRANS_TYPE_10BEAT,	10},
+			{BM_TRANS_TYPE_11BEAT,	11},
+			{BM_TRANS_TYPE_12BEAT,	12},
+			{BM_TRANS_TYPE_13BEAT,	13},
+			{BM_TRANS_TYPE_14BEAT,	14},
+			{BM_TRANS_TYPE_15BEAT,	15},
+			{BM_TRANS_TYPE_16BEAT,	16}
+			)
+		)
+DECLARE_KOBJ_ATTR_INT_LIST_ITEM(
+		ttype_nbyte,
+		KOBJ_ITEM_LIST(
+			{BM_TRANS_TYPE_1Byte,	1},
+			{BM_TRANS_TYPE_2Byte,	2},
+			{BM_TRANS_TYPE_4Byte,	4},
+			{BM_TRANS_TYPE_8Byte,	8},
+			{BM_TRANS_TYPE_16Byte,	16},
+			{BM_TRANS_TYPE_32Byte,	32}
+			)
+		)
+DECLARE_KOBJ_ATTR_STR_LIST_ITEM(
+		ttype_burst,
+		KOBJ_ITEM_LIST(
+			{BM_TRANS_TYPE_BURST_INCR,	"INCR"},
+			{BM_TRANS_TYPE_BURST_WRAP,	"WRAP"}
+			)
+		)
+
+DECLARE_KOBJ_ATTR_STR_LIST_ITEM(
+		ttype_rw,
+		KOBJ_ITEM_LIST(
+			{BM_TRANS_RW_DEFAULT,	"DEFAULT"},
+			{BM_TRANS_RW_READONLY,	"R"},
+			{BM_TRANS_RW_WRITEONLY,	"W"},
+			{BM_TRANS_RW_RWBOTH,	"BOTH"}
+			)
+		)
+
+/* KOBJ: test_apmcu */
+DECLARE_KOBJ_ATTR_SHOW_INT(test_apmcu, times)
+/* please refer to session: "EMI Test Operations" for store operation */
+DECLARE_KOBJ_ATTR(test_apmcu)
+
+DECLARE_KOBJ_ATTR_INT(dramc_pdir_enable, dramc_pdir_enable)
+
+/*enable high priority filter*/
+static int high_priority_filter;
+DECLARE_KOBJ_ATTR_HEX(high_priority_filter, high_priority_filter)
+
+
+/**/
+static int ttype_master_val[21];
+static int ttype_busid_val[21];
+static int ttype_nbeat_val[21];
+static int ttype_nbyte_val[21];
+static int ttype_burst_val[21];
+static int ttype_rw_val[21];
+
+#define DECLARE_KOBJ_SERIAL_FNODE(nr) \
+	DECLARE_KOBJ_ATTR_STR_LIST(ttype##nr##_master, ttype_master_val[nr-1], ttype_master) \
+	DECLARE_KOBJ_ATTR_HEX(ttype##nr##_busid, ttype_busid_val[nr-1]) \
+	DECLARE_KOBJ_ATTR_INT_LIST(ttype##nr##_nbeat, ttype_nbeat_val[nr-1], ttype_nbeat) \
+	DECLARE_KOBJ_ATTR_INT_LIST(ttype##nr##_nbyte, ttype_nbyte_val[nr-1], ttype_nbyte) \
+	DECLARE_KOBJ_ATTR_STR_LIST(ttype##nr##_burst, ttype_burst_val[nr-1], ttype_burst) \
+	DECLARE_KOBJ_ATTR_STR_LIST(ttype##nr##_rw, ttype_rw_val[nr-1], ttype_rw)
+
+DECLARE_KOBJ_SERIAL_FNODE(1)
+DECLARE_KOBJ_SERIAL_FNODE(2)
+DECLARE_KOBJ_SERIAL_FNODE(3)
+DECLARE_KOBJ_SERIAL_FNODE(4)
+DECLARE_KOBJ_SERIAL_FNODE(5)
+DECLARE_KOBJ_SERIAL_FNODE(6)
+DECLARE_KOBJ_SERIAL_FNODE(7)
+DECLARE_KOBJ_SERIAL_FNODE(8)
+DECLARE_KOBJ_SERIAL_FNODE(9)
+DECLARE_KOBJ_SERIAL_FNODE(10)
+DECLARE_KOBJ_SERIAL_FNODE(11)
+DECLARE_KOBJ_SERIAL_FNODE(12)
+DECLARE_KOBJ_SERIAL_FNODE(13)
+DECLARE_KOBJ_SERIAL_FNODE(14)
+DECLARE_KOBJ_SERIAL_FNODE(15)
+DECLARE_KOBJ_SERIAL_FNODE(16)
+DECLARE_KOBJ_SERIAL_FNODE(17)
+DECLARE_KOBJ_SERIAL_FNODE(18)
+DECLARE_KOBJ_SERIAL_FNODE(19)
+DECLARE_KOBJ_SERIAL_FNODE(20)
+DECLARE_KOBJ_SERIAL_FNODE(21)
+
+	/**/
+#define KOBJ_ATTR_ITEM_SERIAL_FNODE(nr) \
+	KOBJ_ATTR_ITEM(ttype##nr##_master) \
+	KOBJ_ATTR_ITEM(ttype##nr##_nbeat) \
+	KOBJ_ATTR_ITEM(ttype##nr##_nbyte) \
+	KOBJ_ATTR_ITEM(ttype##nr##_burst) \
+	KOBJ_ATTR_ITEM(ttype##nr##_busid) \
+	KOBJ_ATTR_ITEM(ttype##nr##_rw) \
+
+#define KOBJ_ATTR_LIST \
+	KOBJ_ATTR_ITEM(high_priority_filter) \
+	KOBJ_ATTR_ITEM(metemi_func_opt) \
+	KOBJ_ATTR_ITEM(emi_tsct_enable) \
+	KOBJ_ATTR_ITEM(emi_mdct_enable) \
+	KOBJ_ATTR_ITEM(ondiemet_emi_polling_200us) \
+	KOBJ_ATTR_ITEM(emi_regdump) \
+	KOBJ_ATTR_ITEM(emi_wsct_tsct_id_selection1) \
+	KOBJ_ATTR_ITEM(emi_wsct_tsct_id_selection2) \
+	KOBJ_ATTR_ITEM(emi_wsct_tsct_id_selection3) \
+	KOBJ_ATTR_ITEM(emi_wsct_tsct_id_selection4) \
+	KOBJ_ATTR_ITEM(msel_enable) \
+	KOBJ_ATTR_ITEM(msel_group1) \
+	KOBJ_ATTR_ITEM(msel_group2) \
+	KOBJ_ATTR_ITEM(msel_group3) \
+	KOBJ_ATTR_ITEM(emi_clock_rate) \
+	KOBJ_ATTR_ITEM(dram_data_rate) \
+	KOBJ_ATTR_ITEM(rwtype) \
+	KOBJ_ATTR_ITEM(ttype17_21_en) \
+	KOBJ_ATTR_ITEM(ttype1_16_en) \
+	KOBJ_ATTR_ITEM_SERIAL_FNODE(1) \
+	KOBJ_ATTR_ITEM_SERIAL_FNODE(2) \
+	KOBJ_ATTR_ITEM_SERIAL_FNODE(3) \
+	KOBJ_ATTR_ITEM_SERIAL_FNODE(4) \
+	KOBJ_ATTR_ITEM_SERIAL_FNODE(5) \
+	KOBJ_ATTR_ITEM_SERIAL_FNODE(6) \
+	KOBJ_ATTR_ITEM_SERIAL_FNODE(7) \
+	KOBJ_ATTR_ITEM_SERIAL_FNODE(8) \
+	KOBJ_ATTR_ITEM_SERIAL_FNODE(9) \
+	KOBJ_ATTR_ITEM_SERIAL_FNODE(10) \
+	KOBJ_ATTR_ITEM_SERIAL_FNODE(11) \
+	KOBJ_ATTR_ITEM_SERIAL_FNODE(12) \
+	KOBJ_ATTR_ITEM_SERIAL_FNODE(13) \
+	KOBJ_ATTR_ITEM_SERIAL_FNODE(14) \
+	KOBJ_ATTR_ITEM_SERIAL_FNODE(15) \
+	KOBJ_ATTR_ITEM_SERIAL_FNODE(16) \
+	KOBJ_ATTR_ITEM_SERIAL_FNODE(17) \
+	KOBJ_ATTR_ITEM_SERIAL_FNODE(18) \
+	KOBJ_ATTR_ITEM_SERIAL_FNODE(19) \
+	KOBJ_ATTR_ITEM_SERIAL_FNODE(20) \
+	KOBJ_ATTR_ITEM_SERIAL_FNODE(21) \
+	KOBJ_ATTR_ITEM(test_apmcu) \
+	KOBJ_ATTR_ITEM(bw_limiter_enable) \
+	KOBJ_ATTR_ITEM(dramc_pdir_enable) \
+	KOBJ_ATTR_ITEM(mdmcu_sel_enable) \
+	KOBJ_ATTR_ITEM(rd_mdmcu_rsv_num) \
+
+/*======================================================================*/
+/*	EMI Operations							*/
+/*======================================================================*/
+static void emi_init(void)
+{
+	unsigned int bmrw0_val, bmrw1_val, i, enable;
+	unsigned int msel_group1_val, msel_group2_val, msel_group3_val;
+
+	/* get dram channel number */
+	dram_chann_num = MET_EMI_GetDramChannNum();
+
+	/* Init. EMI bus monitor */
+	MET_BM_SetReadWriteType(rwtype);
+
+	/* MSEL1: ALL */
+	MET_BM_SetMonitorCounter(1,
+		BM_MASTER_ALL,
+		BM_TRANS_TYPE_4BEAT |
+		BM_TRANS_TYPE_8Byte |
+		BM_TRANS_TYPE_BURST_WRAP);
+	if (msel_enable) {
+		msel_group1_val = msel_group1;
+		msel_group2_val = msel_group2;
+		msel_group3_val = msel_group3;
+	} else {
+		msel_group1_val = BM_Master_GP_1_Default;
+		msel_group2_val = BM_Master_GP_2_Default;
+		msel_group3_val = BM_Master_GP_3_Default;
+	}
+
+	/* MSEL2: msel_group1 */
+	MET_BM_SetMonitorCounter(2,
+		msel_group1_val & BM_MASTER_ALL,
+		BM_TRANS_TYPE_4BEAT |
+		BM_TRANS_TYPE_8Byte |
+		BM_TRANS_TYPE_BURST_WRAP);
+	/* MSEL3: msel_group2 */
+	MET_BM_SetMonitorCounter(3,
+		msel_group2_val & BM_MASTER_ALL,
+		BM_TRANS_TYPE_4BEAT |
+		BM_TRANS_TYPE_8Byte |
+		BM_TRANS_TYPE_BURST_WRAP);
+	/* MSEL4: msel_group3 */
+	MET_BM_SetMonitorCounter(4,
+		msel_group3_val & BM_MASTER_ALL,
+		BM_TRANS_TYPE_4BEAT |
+		BM_TRANS_TYPE_8Byte |
+		BM_TRANS_TYPE_BURST_WRAP);
+
+
+	if (ttype1_16_en == BM_TTYPE1_16_ENABLE)	{
+		MET_BM_SetLatencyCounter(0);
+
+		for (i = 1; i <= 16; i++) {
+			MET_BM_SetMonitorCounter(i,
+					ttype_master_val[i - 1],
+					ttype_nbeat_val[i - 1] |
+					ttype_nbyte_val[i - 1] |
+					ttype_burst_val[i - 1]);
+			MET_BM_SetIDSelect(i, ttype_busid_val[i - 1], (ttype_busid_val[i - 1] >= 0xffff) ? 0 : 1);
+		}
+	} else {
+		MET_BM_SetLatencyCounter(1);
+	}
+
+	if (ttype17_21_en == BM_TTYPE17_21_ENABLE) {
+		for (i = 17; i <= 21; i++) {
+			MET_BM_SetMonitorCounter(i,
+					ttype_master_val[i - 1],
+					ttype_nbeat_val[i - 1] |
+					ttype_nbyte_val[i - 1] |
+					ttype_burst_val[i - 1]);
+			MET_BM_SetIDSelect(i, ttype_busid_val[i - 1], (ttype_busid_val[i - 1] >= 0xffff) ? 0 : 1);
+		}
+	}
+
+	bmrw0_val = (
+			(ttype_rw_val[0] << 0) | (ttype_rw_val[1] << 2) |
+			(ttype_rw_val[2] << 4) | (ttype_rw_val[3] << 6) |
+			(ttype_rw_val[4] << 8) | (ttype_rw_val[5] << 10) |
+			(ttype_rw_val[6] << 12) | (ttype_rw_val[7] << 14) |
+			(ttype_rw_val[8] << 16) | (ttype_rw_val[9] << 18) |
+			(ttype_rw_val[10] << 20) | (ttype_rw_val[11] << 22) |
+			(ttype_rw_val[12] << 24) | (ttype_rw_val[13] << 26) |
+			(ttype_rw_val[14] << 28) | (ttype_rw_val[15] << 30));
+
+	bmrw1_val = (
+			(ttype_rw_val[16] << 0) | (ttype_rw_val[17] << 2) |
+			(ttype_rw_val[18] << 4) | (ttype_rw_val[19] << 6) |
+			(ttype_rw_val[20] << 8));
+
+	MET_BM_SetTtypeCounterRW(bmrw0_val, bmrw1_val);
+
+	for (i = 0; i < 4; i++)
+		MET_BM_Set_WsctTsct_id_sel(i, emi_wsct_tsct_id_selection[i]);
+
+	for (i = 0; i < BM_COUNTER_MAX; i++) {
+		if ((high_priority_filter & (1<<i)) == 0)
+			enable = 0;
+		else
+			enable = 1;
+
+		MET_BM_SetUltraHighFilter(i+1, enable);
+	}
+
+	if (met_emi_regdump == 1)
+		emi_dump_reg();
+}
+
+static void emi_uninit(void)
+{
+}
+
+static inline void emi_start(void)
+{
+	MET_BM_Enable(1);
+}
+
+static inline void emi_stop(void)
+{
+	MET_BM_Enable(0);
+}
+
+static inline int do_emi(void)
+{
+	return met_emi.mode;
+}
+
+noinline void DRAM_DVFS(unsigned int dram_data_rate_MHz)
+{
+	MET_TRACE("%u\n", dram_data_rate_MHz);
+}
+
+static unsigned int emi_bw_limiter(unsigned int *__restrict__ array)
+{
+	int		idx = 0;
+	unsigned int	dram_data_rate_MHz;
+
+	dram_data_rate_MHz = get_dram_data_rate();
+
+	/* print dram data rate */
+	DRAM_DVFS(dram_data_rate_MHz);
+
+	/* get correct dram_clock_rate */
+	array[idx++] = dram_data_rate_MHz;
+
+	/* get correct ARB A->LAST */
+	array[idx++] = MET_EMI_GetARBA();
+	array[idx++] = MET_EMI_GetARBB();
+	array[idx++] = MET_EMI_GetARBC();
+	array[idx++] = MET_EMI_GetARBD();
+	array[idx++] = MET_EMI_GetARBE();
+	array[idx++] = MET_EMI_GetARBF();
+	array[idx++] = MET_EMI_GetARBG();
+	array[idx++] = MET_EMI_GetARBH();
+	/* EMI Total BW Thresholds */
+	array[idx++] = MET_EMI_GetBWCT0();
+	array[idx++] = MET_EMI_GetBWCT1();
+	array[idx++] = MET_EMI_GetBWCT2();
+	array[idx++] = MET_EMI_GetBWCT3();
+	array[idx++] = MET_EMI_GetBWCT4();
+	array[idx++] = MET_EMI_GetBWST0();
+	array[idx++] = MET_EMI_GetBWST1();
+	/* EMI C+G BW Thresholds */
+	array[idx++] = MET_EMI_GetBWCT0_2ND();
+	array[idx++] = MET_EMI_GetBWCT1_2ND();
+	array[idx++] = MET_EMI_GetBWST_2ND();
+
+	return idx;
+}
+
+
+static void _ms_dramc(unsigned int *__restrict__ dramc_pdir_value, int dram_chann_num)
+{
+	MET_DRAMC_GetDebugCounter(dramc_pdir_value, dram_chann_num);
+}
+
+static unsigned int emi_polling(unsigned int *__restrict__ emi_value, unsigned int *__restrict__ emi_tsct,
+	unsigned int *__restrict__ emi_ttype_value, unsigned int *__restrict__ dramc_pdir_value,
+	unsigned int *__restrict__ emi_mdct_value)
+{
+	int	j = 4;		/* skip 4 WSCTs */
+	int	i = 0;		/* ttype start at 0 */
+	int	k = 0;		/* tsct start at 0 */
+	int n;
+
+	MET_BM_Pause();
+
+	if (ttype1_16_en != BM_TTYPE1_16_ENABLE) {	/*1~21 NOT for ttype*/
+
+		/* Get Word Count */
+
+		emi_value[0] = MET_BM_GetWordCount(1);	/* All */
+		emi_value[1] = MET_BM_GetWordCount(2);	/* Group 1 */
+		emi_value[2] = MET_BM_GetWordCount(3);	/* Group 2 */
+		emi_value[3] = MET_BM_GetWordCount(4);	/* Group 3 */
+
+
+		/* Get Latency */
+		emi_value[j++] = MET_BM_GetLatencyCycle(1);
+		emi_value[j++] = MET_BM_GetLatencyCycle(2);
+		emi_value[j++] = MET_BM_GetLatencyCycle(3);
+		emi_value[j++] = MET_BM_GetLatencyCycle(4);
+		emi_value[j++] = MET_BM_GetLatencyCycle(5);
+		emi_value[j++] = MET_BM_GetLatencyCycle(6);
+		emi_value[j++] = MET_BM_GetLatencyCycle(7);
+		emi_value[j++] = MET_BM_GetLatencyCycle(8);
+
+		/* Get Trans. */
+		emi_value[j++] = MET_BM_GetLatencyCycle(9);
+		emi_value[j++] = MET_BM_GetLatencyCycle(10);
+		emi_value[j++] = MET_BM_GetLatencyCycle(11);
+		emi_value[j++] = MET_BM_GetLatencyCycle(12);
+		emi_value[j++] = MET_BM_GetLatencyCycle(13);
+		emi_value[j++] = MET_BM_GetLatencyCycle(14);
+		emi_value[j++] = MET_BM_GetLatencyCycle(15);
+		emi_value[j++] = MET_BM_GetLatencyCycle(16);
+	} else {
+		for (n = 0; n < 20; n++)
+			emi_value[n] = 0;
+		j = 20;
+
+		emi_ttype_value[i++] = MET_BM_GetLatencyCycle(1);
+		emi_ttype_value[i++] = MET_BM_GetLatencyCycle(2);
+		emi_ttype_value[i++] = MET_BM_GetLatencyCycle(3);
+		emi_ttype_value[i++] = MET_BM_GetLatencyCycle(4);
+		emi_ttype_value[i++] = MET_BM_GetLatencyCycle(5);
+		emi_ttype_value[i++] = MET_BM_GetLatencyCycle(6);
+		emi_ttype_value[i++] = MET_BM_GetLatencyCycle(7);
+		emi_ttype_value[i++] = MET_BM_GetLatencyCycle(8);
+
+		/* Get Trans. */
+		emi_ttype_value[i++] = MET_BM_GetLatencyCycle(9);
+		emi_ttype_value[i++] = MET_BM_GetLatencyCycle(10);
+		emi_ttype_value[i++] = MET_BM_GetLatencyCycle(11);
+		emi_ttype_value[i++] = MET_BM_GetLatencyCycle(12);
+		emi_ttype_value[i++] = MET_BM_GetLatencyCycle(13);
+		emi_ttype_value[i++] = MET_BM_GetLatencyCycle(14);
+		emi_ttype_value[i++] = MET_BM_GetLatencyCycle(15);
+		emi_ttype_value[i++] = MET_BM_GetLatencyCycle(16);
+	}
+
+	/* Get BACT/BSCT/BCNT */
+	emi_value[j++] = MET_BM_GetBandwidthWordCount();
+	emi_value[j++] = MET_BM_GetOverheadWordCount();
+	emi_value[j++] = MET_BM_GetBusCycCount();
+	/* Get PageHist/PageMiss/InterBank/Idle */
+	for (n = 0; n < dram_chann_num; n++) {
+#if 1
+		/* TBD */
+		emi_value[j++] = MET_DRAMC_GetPageHitCount(DRAMC_ALL, n);
+		emi_value[j++] = MET_DRAMC_GetPageMissCount(DRAMC_ALL, n);
+		emi_value[j++] = MET_DRAMC_GetInterbankCount(DRAMC_ALL, n);
+		emi_value[j++] = MET_DRAMC_GetIdleCount(n);
+		emi_value[j++] = ((MET_DRAMC_Misc_Status(n) >> 8) & 0x7);
+		emi_value[j++] = MET_DRAMC_RefPop(n);
+		emi_value[j++] = MET_DRAMC_Free26M(n);
+		emi_value[j++] = MET_DRAMC_RByte(n);
+		emi_value[j++] = MET_DRAMC_WByte(n);
+#else
+		emi_value[j++] = 0;
+		emi_value[j++] = 0;
+		emi_value[j++] = 0;
+		emi_value[j++] = 0;
+		emi_value[j++] = 0;
+		emi_value[j++] = 0;
+		emi_value[j++] = 0;
+		emi_value[j++] = 0;
+		emi_value[j++] = 0;
+#endif
+	}
+	/* TTYPE */
+	if (ttype17_21_en == BM_TTYPE17_21_ENABLE) {	/*17~21 for ttype*/
+		emi_ttype_value[16] = MET_BM_GetLatencyCycle(17);
+		emi_ttype_value[17] = MET_BM_GetLatencyCycle(18);
+		emi_ttype_value[18] = MET_BM_GetLatencyCycle(19);
+		emi_ttype_value[19] = MET_BM_GetLatencyCycle(20);
+		emi_ttype_value[20] = MET_BM_GetLatencyCycle(21);
+	}
+
+	/* Get tsct */
+	if (emi_tsct_enable == 1) {
+		emi_tsct[k++] = MET_BM_GetTransCount(1);
+		emi_tsct[k++] = MET_BM_GetTransCount(2);
+		emi_tsct[k++] = MET_BM_GetTransCount(3);
+	}
+	/*get mdct rsv buffer*/
+	if (emi_mdct_enable == 1) {
+		emi_mdct_value[0] = (MET_BM_GetMDCT() >> 16) & 0x7;
+		emi_mdct_value[1] = (MET_BM_GetMDCT_2() & 0x7);
+	}
+
+	if (dramc_pdir_enable == 1)
+		_ms_dramc(dramc_pdir_value, dram_chann_num);
+
+	MET_BM_Continue();
+	MET_BM_Enable(0);
+	MET_BM_Enable(1);
+
+	return j;
+}
+
+/*======================================================================*/
+/*	MET Device Operations						*/
+/*======================================================================*/
+static int emi_inited;
+
+static int met_emi_create(struct kobject *parent)
+{
+	int ret = 0;
+	int i;
+
+	for (i = 0; i < 21; i++) {
+		ttype_master_val[i] = BM_MASTER_M0;
+		ttype_nbeat_val[i] = BM_TRANS_TYPE_1BEAT;
+		ttype_nbyte_val[i] = BM_TRANS_TYPE_8Byte;
+		ttype_burst_val[i] = BM_TRANS_TYPE_BURST_INCR;
+		ttype_busid_val[i] = 0xffff;
+		ttype_rw_val[i] =  BM_TRANS_RW_DEFAULT;
+	}
+
+	ret = MET_BM_Init();
+	if (ret != 0) {
+		pr_err("MET_BM_Init failed!!!\n");
+		ret = 0;	/* will retry later */
+	} else {
+		emi_inited = 1;
+	}
+
+	kobj_emi = parent;
+
+#define	KOBJ_ATTR_ITEM(attr_name) \
+	ret = sysfs_create_file(kobj_emi, &attr_name##_attr.attr); \
+	if (ret != 0) { \
+		pr_err("Failed to create " #attr_name " in sysfs\n"); \
+		return ret; \
+	}
+	KOBJ_ATTR_LIST
+#undef	KOBJ_ATTR_ITEM
+
+	return ret;
+}
+
+static void met_emi_delete(void)
+{
+#define	KOBJ_ATTR_ITEM(attr_name) \
+	sysfs_remove_file(kobj_emi, &attr_name##_attr.attr);
+	if (kobj_emi != NULL) {
+		KOBJ_ATTR_LIST
+			kobj_emi = NULL;
+	}
+#undef	KOBJ_ATTR_ITEM
+
+	if (emi_inited)
+		MET_BM_DeInit();
+}
+
+static void met_emi_start(void)
+{
+	unsigned int bw_limiter[NIDX_BL];
+
+	if (!emi_inited) {
+		if (MET_BM_Init() != 0) {
+			met_emi.mode = 0;
+			pr_err("MET_BM_Init failed!!!\n");
+			return;
+		}
+		emi_inited = 1;
+	}
+
+	if (do_emi()) {
+		emi_init();
+		emi_stop();
+		emi_start();
+
+		/* Draw the first BW Limiter point */
+		if (bw_limiter_enable == BM_BW_LIMITER_ENABLE) {
+			emi_bw_limiter(bw_limiter);
+			ms_bw_limiter(NIDX_BL, bw_limiter);
+			/* init countdown value */
+			countdown = CNT_COUNTDOWN;
+		}
+	}
+}
+
+static void met_emi_stop(void)
+{
+	unsigned int	bw_limiter[NIDX_BL];
+
+	if (!emi_inited)
+		return;
+
+	if (met_emi_regdump == 1)
+		emi_dump_reg();
+
+	if (do_emi()) {
+		/* Draw the last BW Limiter point */
+		if (bw_limiter_enable == BM_BW_LIMITER_ENABLE) {
+			/*
+			 * Skip drawing when we just draw
+			 * the point at last polling.
+			 */
+			if (countdown < CNT_COUNTDOWN) {
+				emi_bw_limiter(bw_limiter);
+				ms_bw_limiter(NIDX_BL, bw_limiter);
+			}
+		}
+
+		emi_stop();
+		emi_uninit();
+	}
+}
+
+static void met_emi_polling(unsigned long long stamp, int cpu)
+{
+	unsigned int	emi_value[NIDX];
+	unsigned int	emi_tsct[3];
+	unsigned int	emi_ttype_value[21];
+	unsigned int	dramc_pdir_value[DRAMC_Debug_MAX_CNT*NCH];
+	unsigned int	emi_mdct_value[2];
+
+	if (!do_emi())
+		return;
+
+	/* get emi & dramc counters */
+	emi_value[0] = 0;	/* 0: pure linux MET , 0xa5: OnDieMET*/
+	emi_value[1] = 0;	/* EBM pause duration (ns)*/
+	emi_polling(emi_value+2, emi_tsct, emi_ttype_value, dramc_pdir_value, emi_mdct_value);
+
+	/* get and output BW Limiter */
+	if (bw_limiter_enable == BM_BW_LIMITER_ENABLE) {
+		unsigned int bw_limiter[NIDX_BL];
+
+		if (countdown > 0) {
+			countdown--;
+		} else {
+			emi_bw_limiter(bw_limiter);
+			ms_bw_limiter(NIDX_BL, bw_limiter);
+			/* reload countdown value */
+			countdown = CNT_COUNTDOWN;
+		}
+	}
+
+	/* output emi */
+	ms_emi(NIDX_EMI-NTTYPE + (NCNT*dram_chann_num), emi_value);
+	/* output tsct*/
+
+
+
+	if (emi_tsct_enable == 1)
+		ms_emi_tsct(3, emi_tsct);
+
+	/* output mdct*/
+	if (emi_mdct_enable == 1)
+		ms_emi_mdct(2, emi_mdct_value);
+
+	/* output dramc*/
+	if (dramc_pdir_enable == 1)
+		ms_dramc(DRAMC_Debug_MAX_CNT*dram_chann_num, dramc_pdir_value);
+
+	/* output ms_ttype */
+	if ((ttype1_16_en == BM_TTYPE1_16_ENABLE) && (ttype17_21_en == BM_TTYPE17_21_ENABLE))
+		ms_ttype(21, emi_ttype_value);
+	else if (ttype17_21_en == BM_TTYPE17_21_ENABLE)
+		ms_ttype(5, (emi_ttype_value + 16));
+
+	/* adjust MDMCU buffer */
+	if (mdmcu_sel_enable == 1)
+		MET_BM_SetMDCT_MDMCU(rd_mdmcu_rsv_num);
+}
+
+static char help[] = "  --emi                                 monitor EMI banwidth\n";
+static int emi_print_help(char *buf, int len)
+{
+	return snprintf(buf, PAGE_SIZE, help);
+}
+
+
+#define TTYPE_NAME_STR_LEN  64
+static char ttype_name[21][TTYPE_NAME_STR_LEN];
+static int emi_print_header(char *buf, int len)
+{
+	int	ret = 0;
+	int	ret_m[21];
+	int	i = 0;
+	unsigned int	dram_data_rate_MHz;
+
+	/*ttype header info*/
+	for (i = 0; i < 21; i++) {
+
+		int k;
+
+		/*busid >= 0xffff    not specific bus id , show all on specificmaster*/
+		if (ttype_busid_val[i] >= 0xffff) {
+
+			int j;
+
+			for (j = 0; j < ARRAY_SIZE(ttype_master_list_item); j++) {
+				if (ttype_master_val[i] == ttype_master_list_item[j].key) {
+					ret_m[i] = snprintf(ttype_name[i], TTYPE_NAME_STR_LEN, "ttyp%d_%s",
+						i+1, ttype_master_list_item[j].val);/*master*/
+					break;
+				}
+			}
+			if (j == ARRAY_SIZE(ttype_master_list_item))
+				ret_m[i] = snprintf(ttype_name[i], TTYPE_NAME_STR_LEN, "ttyp%d_%s",
+					i+1, "unknown");
+		} else {
+			ret_m[i] = snprintf(ttype_name[i], TTYPE_NAME_STR_LEN, "ttyp%d_%x",
+				i+1, ttype_busid_val[i]);/*busID*/
+		}
+
+		/*show beat type*/
+		for (k = 0; k < ARRAY_SIZE(ttype_nbeat_list_item); k++) {
+
+			if (ttype_nbeat_val[i] == ttype_nbeat_list_item[k].key)
+				ret_m[i] += snprintf(ttype_name[i]+ret_m[i], TTYPE_NAME_STR_LEN-ret_m[i], "_%d",
+					ttype_nbeat_list_item[k].val);/*beat*/
+		}
+
+		/*show byte type*/
+		for (k = 0; k < ARRAY_SIZE(ttype_nbyte_list_item); k++) {
+
+			if (ttype_nbyte_val[i] == ttype_nbyte_list_item[k].key)
+				ret_m[i] += snprintf(ttype_name[i]+ret_m[i], TTYPE_NAME_STR_LEN-ret_m[i], "x%d",
+					ttype_nbyte_list_item[k].val);/*byte*/
+		}
+
+		/*show burst type*/
+		for (k = 0; k < ARRAY_SIZE(ttype_burst_list_item); k++) {
+
+			if (ttype_burst_val[i] == ttype_burst_list_item[k].key)
+				ret_m[i] += snprintf(ttype_name[i]+ret_m[i], TTYPE_NAME_STR_LEN-ret_m[i], "_%s",
+					ttype_burst_list_item[k].val);/*burst*/
+		}
+
+		/*show rw type*/
+		for (k = 0; k < ARRAY_SIZE(ttype_rw_list_item); k++) {
+
+			if (ttype_rw_val[i] == ttype_rw_list_item[k].key)
+				ret_m[i] += snprintf(ttype_name[i]+ret_m[i], TTYPE_NAME_STR_LEN-ret_m[i], "_%s",
+					ttype_rw_list_item[k].val);/*rw*/
+		}
+	}
+
+	dram_chann_num = MET_EMI_GetDramChannNum();
+	/* met_dram_chann_num_header */
+	/* TBD: what is VID */
+	ret = snprintf(buf, PAGE_SIZE, "met-info [000] 0.0: met_dram_chann_num_header: %d,%d,%d,%d,%d\n",
+			dram_chann_num, DRAM_EMI_BASECLOCK_RATE, DRAM_IO_BUS_WIDTH, DRAM_DATARATE, 0);
+
+	/* metemi_func_opt for middleware */
+	ret += snprintf(buf+ret, PAGE_SIZE-ret, "met-info [000] 0.0: metemi_func_opt_header: %d\n",
+			metemi_func_opt);
+
+	dram_data_rate_MHz = get_dram_data_rate();
+	ret += snprintf(buf+ret, PAGE_SIZE-ret,
+			"met-info [000] 0.0: met_dram_clockrate: %u\n",
+			dram_data_rate_MHz);
+
+	/*master port mapping*/
+	ret += snprintf(buf+ret, PAGE_SIZE-ret,
+			"met-info [000] 0.0: met_emi_mport_map: %s,%s,%s,%s,%s,%s,%s,%s\n",
+			BM_Master_M0_name, BM_Master_M1_name, BM_Master_M2_name, BM_Master_M3_name,
+			BM_Master_M4_name, BM_Master_M5_name, BM_Master_M6_name, BM_Master_M7_name);
+
+	/* not to change by default master port sequency */
+	ret += snprintf(buf+ret, PAGE_SIZE-ret,
+			"met-info [000] 0.0: met_emi_mgroup_map: %x,%x,%x,%x\n",
+			BM_Master_GP_AP, BM_Master_GP_MM, BM_Master_GP_GPU, BM_Master_GP_PERI);
+
+	/* 1 : by ondiemet, 0: by pure linux */
+	ret += snprintf(buf+ret, PAGE_SIZE-ret,
+		       "met-info [000] 0.0: emi_use_ondiemet: %u\n",
+		       emi_use_ondiemet);
+
+	/* msel header */
+	if (msel_enable) {
+		ret += snprintf(buf+ret, PAGE_SIZE-ret,
+				"met-info [000] 0.0: met_emi_msel: %x,%x,%x\n",
+				msel_group1 & BM_MASTER_ALL,
+				msel_group2 & BM_MASTER_ALL,
+				msel_group3 & BM_MASTER_ALL);
+	} else {
+		ret += snprintf(buf+ret, PAGE_SIZE-ret,
+				"met-info [000] 0.0: met_emi_msel: %x,%x,%x\n",
+				BM_Master_GP_1_Default & BM_MASTER_ALL,
+				BM_Master_GP_2_Default & BM_MASTER_ALL,
+				BM_Master_GP_3_Default & BM_MASTER_ALL);
+	}
+
+	/* ms_emi */
+	ret += snprintf(buf+ret, PAGE_SIZE-ret,
+			"# ms_emi: TS0,TS1,GP0_WSCT,GP1_WSCT,GP2_WSCT,GP3_WSCT,");
+	ret += snprintf(buf+ret, PAGE_SIZE-ret,
+			"M0_LATENCY,M1_LATENCY,M2_LATENCY,M3_LATENCY,M4_LATENCY,M5_LATENCY,M6_LATENCY,M7_LATENCY,");
+	ret += snprintf(buf+ret, PAGE_SIZE-ret,
+			"M0_TRANS,M1_TRANS,M2_TRANS,M3_TRANS,M4_TRANS,M5_TRANS,M6_TRANS,M7_TRANS,");
+	ret += snprintf(buf+ret, PAGE_SIZE-ret,
+			"BACT,BSCT,BCNT,");
+
+	for (i = 0; i < dram_chann_num; i++) {
+		if (i != 0)
+			ret += snprintf(buf+ret, PAGE_SIZE-ret,
+				",");
+		ret += snprintf(buf+ret, PAGE_SIZE-ret,
+				"PageHit_%d,PageMiss_%d,InterBank_%d,Idle_%d,", i, i, i, i);
+		ret += snprintf(buf+ret, PAGE_SIZE-ret,
+				"mr4_%d,refresh_pop_%d,freerun_26m_%d,", i, i, i);
+		ret += snprintf(buf+ret, PAGE_SIZE-ret,
+				"read_bytes_%d,write_bytes_%d", i, i);
+	}
+	ret += snprintf(buf+ret, PAGE_SIZE-ret,
+			"\n");
+
+
+	ret += snprintf(buf+ret, PAGE_SIZE-ret,
+			"met-info [000] 0.0: met_emi_header: TS0,TS1,GP0_WSCT,GP1_WSCT,GP2_WSCT,GP3_WSCT,");
+	ret += snprintf(buf+ret, PAGE_SIZE-ret,
+			"M0_LATENCY,M1_LATENCY,M2_LATENCY,M3_LATENCY,M4_LATENCY,M5_LATENCY,M6_LATENCY,M7_LATENCY,");
+	ret += snprintf(buf+ret, PAGE_SIZE-ret,
+			"M0_TRANS,M1_TRANS,M2_TRANS,M3_TRANS,M4_TRANS,M5_TRANS,M6_TRANS,M7_TRANS,");
+	ret += snprintf(buf+ret, PAGE_SIZE-ret,
+			"BACT,BSCT,BCNT,");
+
+	for (i = 0; i < dram_chann_num; i++) {
+		if (i != 0)
+			ret += snprintf(buf+ret, PAGE_SIZE-ret,
+				",");
+		ret += snprintf(buf+ret, PAGE_SIZE-ret,
+				"PageHit_%d,PageMiss_%d,InterBank_%d,Idle_%d,", i, i, i, i);
+		ret += snprintf(buf+ret, PAGE_SIZE-ret,
+				"mr4_%d,refresh_pop_%d,freerun_26m_%d,", i, i, i);
+		ret += snprintf(buf+ret, PAGE_SIZE-ret,
+				"read_bytes_%d,write_bytes_%d", i, i);
+	}
+	ret += snprintf(buf+ret, PAGE_SIZE-ret,
+			"\n");
+
+
+	/*TSCT header*/
+	if (emi_tsct_enable == 1) {
+		ret += snprintf(buf+ret, PAGE_SIZE-ret,
+				"met-info [000] 0.0: ms_ud_sys_header: ms_emi_tsct,");
+		ret += snprintf(buf+ret, PAGE_SIZE-ret,
+				"tsct1,tsct2,tsct3,x,x,x\n");
+	}
+
+
+	/*MDCT header*/
+	if (emi_mdct_enable == 1) {
+		ret += snprintf(buf+ret, PAGE_SIZE-ret,
+				"met-info [000] 0.0: ms_ud_sys_header: ms_emi_mdct,");
+		ret += snprintf(buf+ret, PAGE_SIZE-ret,
+				"RD_ULTRA,RD_MDMCU,d,d\n");
+		ret += snprintf(buf+ret, PAGE_SIZE-ret,
+				"met-info [000] 0.0: ms_ud_sys_description: ms_emi_mdct:");
+		ret += snprintf(buf+ret, PAGE_SIZE-ret,
+				"CHART_TYPE=histogram_edge;STATISTICS_METHOD=histogram_edge;CHART_RESAMPLE_METHOD=del_dup\n");
+	}
+
+	/*ttype header*/
+	if ((ttype1_16_en == BM_TTYPE1_16_ENABLE) && (ttype17_21_en == BM_TTYPE17_21_ENABLE)) {
+		/*header = ttype1~21t*/
+		int i;
+
+		ret += snprintf(buf+ret, PAGE_SIZE-ret,	"met-info [000] 0.0: ms_ud_sys_header: ms_ttype,");
+		for (i = 0; i < 21; i++)
+			ret += snprintf(buf+ret, PAGE_SIZE-ret, "%s,", ttype_name[i]);
+
+		ret += snprintf(buf+ret, PAGE_SIZE-ret, "x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x\n");
+
+	} else if (ttype17_21_en == BM_TTYPE17_21_ENABLE) {
+		/*header = ttype17~21t*/
+		int i;
+
+		ret += snprintf(buf+ret, PAGE_SIZE-ret, "met-info [000] 0.0: ms_ud_sys_header: ms_ttype,");
+
+		for (i = 16; i < 21; i++)
+			ret += snprintf(buf+ret, PAGE_SIZE-ret, "%s,", ttype_name[i]);
+
+		ret += snprintf(buf+ret, PAGE_SIZE-ret, "x,x,x,x,x\n");
+	}
+
+	/* met_bw_limiter_header */
+	if (bw_limiter_enable == BM_BW_LIMITER_ENABLE) {
+		ret += snprintf(buf+ret, PAGE_SIZE-ret,
+				"met-info [000] 0.0: met_bw_limiter_header: CLK,");
+		ret += snprintf(buf+ret, PAGE_SIZE-ret,
+				"ARBA,ARBB,ARBC,ARBD,ARBE,ARBF,ARBG,ARBH,BWCT0,BWCT1,BWCT2,BWCT3,BWCT4,BWST0,BWST1,BWCT0_2ND,BWCT1_2ND,BWST_2ND\n");
+	}
+	/* DRAM DVFS */
+	ret += snprintf(buf+ret, PAGE_SIZE-ret,
+			"met-info [000] 0.0: ms_ud_sys_header: DRAM_DVFS,datarate(MHz),d\n");
+	ret += snprintf(buf+ret, PAGE_SIZE-ret,
+			"met-info [000] 0.0: ms_ud_sys_description: DRAM_DVFS:");
+	ret += snprintf(buf+ret, PAGE_SIZE-ret,
+			"CHART_TYPE=histogram_edge;STATISTICS_METHOD=histogram_edge;CHART_RESAMPLE_METHOD=del_dup\n");
+
+	/*PDIR met_dramc_header*/
+	if (dramc_pdir_enable == 1) {
+		ret += snprintf(buf+ret, PAGE_SIZE-ret,
+			"met-info [000] 0.0: met_dramc_header: ");
+		for (i = 0; i < dram_chann_num; i++) {
+			if (i != 0)
+				ret += snprintf(buf+ret, PAGE_SIZE-ret,
+					",");
+			ret += snprintf(buf+ret, PAGE_SIZE-ret,
+				"rk0_pre_sb_%d,rk0_pre_pd_%d,rk0_act_sb_%d,rk0_act_pd_%d,", i, i, i, i);
+			ret += snprintf(buf+ret, PAGE_SIZE-ret,
+				"rk1_pre_sb_%d,rk1_pre_pd_%d,rk1_act_sb_%d,rk1_act_pd_%d,", i, i, i, i);
+			ret += snprintf(buf+ret, PAGE_SIZE-ret,
+				"rk2_pre_sb_%d,rk2_pre_pd_%d,rk2_act_sb_%d,rk2_act_pd_%d", i, i, i, i);
+		}
+		ret += snprintf(buf+ret, PAGE_SIZE-ret, "\n");
+	}
+	return ret;
+}
+
+struct metdevice met_emi = {
+	.name			= "emi",
+	.owner			= THIS_MODULE,
+	.type			= MET_TYPE_BUS,
+	.create_subfs	= met_emi_create,
+	.delete_subfs	= met_emi_delete,
+	.cpu_related	= 0,
+	.start			= met_emi_start,
+	.stop			= met_emi_stop,
+	.timed_polling	= met_emi_polling,
+	.print_help		= emi_print_help,
+	.print_header	= emi_print_header,
+};
diff --git a/src/devtools/met-driver/4.19/mt2712/met_kernel_symbol.h b/src/devtools/met-driver/4.19/mt2712/met_kernel_symbol.h
new file mode 100644
index 0000000..10f70e1
--- /dev/null
+++ b/src/devtools/met-driver/4.19/mt2712/met_kernel_symbol.h
@@ -0,0 +1,53 @@
+/*
+ * 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.
+ */
+
+#ifndef MET_KERNEL_SYMBOL
+#define MET_KERNEL_SYMBOL
+
+/*lookup symbol*/
+#include <asm/cpu.h>
+#include <linux/kallsyms.h>
+#include <linux/perf_event.h>
+#include <linux/kthread.h>
+
+#if	defined(CONFIG_MET_ARM_32BIT)
+extern void met_get_cpuinfo(int cpu, struct cpuinfo_arm **cpuinfo);
+extern void (*met_get_cpuinfo_symbol)(int cpu, struct cpuinfo_arm **cpuinfo);
+#else
+extern void met_get_cpuinfo(int cpu, struct cpuinfo_arm64 **cpuinfo);
+extern void (*met_get_cpuinfo_symbol)(int cpu, struct cpuinfo_arm64 **cpuinfo);
+#endif
+
+extern void (*tracing_record_cmdline_symbol)(struct task_struct *tsk);
+extern void met_cpu_frequency(unsigned int frequency, unsigned int cpu_id);
+extern void (*met_cpu_frequency_symbol)(unsigned int frequency, unsigned int cpu_id);
+extern void (*met_arch_setup_dma_ops_symbol)(struct device *dev);
+extern int (*met_perf_event_read_local_symbol)(struct perf_event *ev, u64 *value, u64 *enable, u64 *running);
+extern struct task_struct *(*met_kthread_create_on_cpu_symbol)(int (*threadfn)(void *data),
+				void *data, unsigned int cpu,
+				const char *namefmt);
+extern int (*met_smp_call_function_single_symbol)(int cpu, smp_call_func_t func, void *info, int wait);
+
+extern void met_tracing_record_cmdline(struct task_struct *tsk);
+extern int met_reg_switch(void);
+extern int (*met_reg_switch_symbol)(void);
+extern void met_unreg_switch(void);
+extern void (*met_unreg_switch_symbol)(void);
+extern void met_arch_setup_dma_ops(struct device *dev);
+extern int met_perf_event_read_local(struct perf_event *ev, u64 *value, u64 *enable, u64 *running);
+extern struct task_struct *met_kthread_create_on_cpu(int (*threadfn)(void *data),
+				void *data, unsigned int cpu,
+				const char *namefmt);
+extern int met_smp_call_function_single(int cpu, smp_call_func_t func, void *info, int wait);
+extern void met_arch_send_call_function_single_ipi(int cpu);
+#endif	/* MET_KERNEL_SYMBOL */
diff --git a/src/devtools/met-driver/4.19/mt2712/met_main.c b/src/devtools/met-driver/4.19/mt2712/met_main.c
new file mode 100644
index 0000000..883a918
--- /dev/null
+++ b/src/devtools/met-driver/4.19/mt2712/met_main.c
@@ -0,0 +1,293 @@
+/*
+ * 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/init.h>
+#include <linux/module.h>
+#include <linux/hrtimer.h>
+#include <linux/cpu.h>
+#include <linux/mm.h>
+#include <linux/slab.h>
+#include <linux/profile.h>
+#include <linux/dcache.h>
+#include <linux/types.h>
+#include <linux/dcookies.h>
+#include <linux/sched.h>
+#include <linux/fs.h>
+
+#include <asm/irq_regs.h>
+
+#include "met_struct.h"
+#include "met_drv.h"
+#include "met_kernel_symbol.h"
+#include "interface.h"
+#include <linux/of.h>
+
+
+extern struct device_node *of_root;
+static const char *platform_name;
+
+struct cpu_type_name {
+	char full_name[32];
+	char abbr_name[8];
+};
+
+static struct cpu_type_name met_known_cpu_type[] = {
+	{"arm,cortex-a35", "CA35"},
+	{"arm,cortex-a53", "CA53"},
+	{"arm,cortex-a72", "CA72"},
+	{"arm,cortex-a73", "CA73"},
+};
+#define MET_KNOWN_CPU_TYPE_COUNT \
+	(sizeof(met_known_cpu_type)/sizeof(struct cpu_type_name))
+
+static char met_cpu_topology[64];
+
+#if	defined(CONFIG_MET_ARM_32BIT)
+void (*met_get_cpuinfo_symbol)(int cpu, struct cpuinfo_arm **cpuinfo);
+#else
+void (*met_get_cpuinfo_symbol)(int cpu, struct cpuinfo_arm64 **cpuinfo);
+#endif
+
+void (*tracing_record_cmdline_symbol)(struct task_struct *tsk);
+void (*met_cpu_frequency_symbol)(unsigned int frequency, unsigned int cpu_id);
+int (*met_reg_switch_symbol)(void);
+void (*met_unreg_switch_symbol)(void);
+
+void (*met_arch_setup_dma_ops_symbol)(struct device *dev);
+int (*met_perf_event_read_local_symbol)(struct perf_event *ev, u64 *value, u64 *enable, u64 *running);
+struct task_struct *(*met_kthread_create_on_cpu_symbol)(int (*threadfn)(void *data),
+				void *data, unsigned int cpu,
+				const char *namefmt);
+int (*met_smp_call_function_single_symbol)(int cpu, smp_call_func_t func, void *info, int wait);
+
+
+const char *met_get_platform_name(void)
+{
+	return platform_name;
+}
+EXPORT_SYMBOL(met_get_platform_name);
+
+static void get_cpu_type_name(const char *compatible, char *cpu_type)
+{
+	int i;
+
+	for (i = 0; i < MET_KNOWN_CPU_TYPE_COUNT; i++) {
+		if (!strncmp(compatible, met_known_cpu_type[i].full_name,
+					strlen(met_known_cpu_type[i].full_name)))
+			strncpy(cpu_type, met_known_cpu_type[i].abbr_name,
+					strlen(met_known_cpu_type[i].abbr_name) + 1);
+	}
+}
+
+static void met_set_cpu_topology(int core_id, int cluster_core_num)
+{
+	int i, buf_len = strlen(met_cpu_topology);
+	struct device_node *node = NULL;
+	const char *prev_cptb = NULL;
+	const char *cptb;
+	char cpu_type[16];
+
+	for (i = 0; i < cluster_core_num; i++) {
+		node = of_get_cpu_node(core_id + i, NULL);
+		if (node) {
+			cptb = of_get_property(node, "compatible", NULL);
+			if (cptb) {
+				get_cpu_type_name(cptb, cpu_type);
+				if (prev_cptb == NULL)
+					/* first write:  write core_type & core_number */
+					buf_len += snprintf(met_cpu_topology + buf_len,
+								sizeof(met_cpu_topology) - buf_len,
+								"%s:%d", cpu_type, core_id + i);
+				else if (!strncmp(prev_cptb, cptb, strlen(prev_cptb)))
+					/* cpu type is the same with before */
+					/* write core_number */
+					buf_len += snprintf(met_cpu_topology + buf_len,
+								sizeof(met_cpu_topology) - buf_len,
+								",%d", core_id + i);
+				else
+					/* cpu type is different with before */
+					/* write core_type & core_number */
+					buf_len += snprintf(met_cpu_topology + buf_len,
+								sizeof(met_cpu_topology) - buf_len,
+								"|%s:%d", cpu_type, core_id + i);
+
+				prev_cptb = cptb;
+			}
+		}
+	}
+}
+
+static int met_create_cpu_topology(void)
+{
+	int i, len;
+	struct device_node *node = NULL;
+	int start_core_id = 0;
+	int cluster_num = 0, cluster_core_num = 0;
+	char cluster_name[16];
+
+	node = of_find_node_by_name(NULL, "cpu-map");
+	if (node) {
+		cluster_num = of_get_child_count(node);
+
+		for (i = 0; i < cluster_num; i++) {
+			snprintf(cluster_name, sizeof(cluster_name), "cluster%d", i);
+			node = of_find_node_by_name(NULL, cluster_name);
+			if (node) {
+				cluster_core_num = of_get_child_count(node);
+
+				/* "|" use to separate different cluster */
+				if (i > 0) {
+					len = strlen(met_cpu_topology);
+					snprintf(met_cpu_topology + len, sizeof(met_cpu_topology) - len, "|");
+				}
+
+				met_set_cpu_topology(start_core_id, cluster_core_num);
+				start_core_id = cluster_core_num;
+			}
+		}
+	}
+
+	return strlen(met_cpu_topology);
+}
+
+static int met_kernel_symbol_get(void)
+{
+	int ret = 0;
+
+	if (met_get_cpuinfo_symbol == NULL)
+		met_get_cpuinfo_symbol = (void *)symbol_get(met_get_cpuinfo);
+	if (met_get_cpuinfo_symbol == NULL)
+		return -2;
+
+	if (tracing_record_cmdline_symbol == NULL)
+		tracing_record_cmdline_symbol = (void *)symbol_get(met_tracing_record_cmdline);
+	if (tracing_record_cmdline_symbol == NULL)
+		ret = -3;
+
+	if (met_cpu_frequency_symbol == NULL)
+		met_cpu_frequency_symbol = (void *)symbol_get(met_cpu_frequency);
+	if (met_cpu_frequency_symbol == NULL)
+		ret = -4;
+
+	if (met_reg_switch_symbol == NULL)
+		met_reg_switch_symbol = (void *)symbol_get(met_reg_switch);
+	if (met_reg_switch_symbol == NULL)
+		ret = -5;
+
+	if (met_unreg_switch_symbol == NULL)
+		met_unreg_switch_symbol = (void *)symbol_get(met_unreg_switch);
+	if (met_unreg_switch_symbol == NULL)
+		ret = -6;
+
+	if (met_arch_setup_dma_ops_symbol == NULL)
+		met_arch_setup_dma_ops_symbol = (void *)symbol_get(met_arch_setup_dma_ops);
+	if (met_arch_setup_dma_ops_symbol == NULL)
+		ret = -7;
+
+	if (met_perf_event_read_local_symbol == NULL)
+		met_perf_event_read_local_symbol = (void *)symbol_get(met_perf_event_read_local);
+	if (met_perf_event_read_local_symbol == NULL)
+		ret = -8;
+
+	if (met_kthread_create_on_cpu_symbol == NULL)
+		met_kthread_create_on_cpu_symbol = (void *)symbol_get(met_kthread_create_on_cpu);
+	if (met_kthread_create_on_cpu_symbol == NULL)
+		ret = -9;
+
+	if (met_smp_call_function_single_symbol == NULL)
+		met_smp_call_function_single_symbol = (void *)symbol_get(met_smp_call_function_single);
+	if (met_smp_call_function_single_symbol == NULL)
+		ret = -10;
+	return ret;
+}
+
+DEFINE_PER_CPU(struct met_cpu_struct, met_cpu);
+
+static int __init met_drv_init(void)
+{
+	int cpu;
+	int ret;
+	int cpu_topology_len;
+	struct met_cpu_struct *met_cpu_ptr;
+
+	for_each_possible_cpu(cpu) {
+		met_cpu_ptr = &per_cpu(met_cpu, cpu);
+		/* snprintf(&(met_cpu_ptr->name[0]), sizeof(met_cpu_ptr->name), "met%02d", cpu); */
+		met_cpu_ptr->cpu = cpu;
+	}
+
+	ret = met_kernel_symbol_get();
+	if (ret) {
+		pr_notice("[MET] met_kernel_symbol_get fail, ret = %d\n", ret);
+		return ret;
+	}
+	fs_reg();
+
+	if (of_root){
+		/*
+			mt6765.dts
+			model = "MT6765";
+			compatible = "mediatek,MT6765";
+			interrupt-parent = <&sysirq>;
+		*/
+		if (of_root->properties) {
+			of_property_read_string(of_root, of_root->properties->name, &platform_name);
+			PR_BOOTMSG("platform_name=%s\n", platform_name);
+		}
+	}
+	if (platform_name) {
+		char buf[7];
+
+		memset(buf, 0x0, 7);
+		buf[0] = 'm';
+		buf[1] = 't';
+		strncpy(&buf[2], &platform_name[11], 4);
+		met_set_platform(buf, 1);
+		PR_BOOTMSG("buf=%s\n", buf);
+	}
+
+	cpu_topology_len = met_create_cpu_topology();
+	if (cpu_topology_len)
+		met_set_topology(met_cpu_topology, 1);
+
+#ifdef MET_PLF_USE
+	core_plf_init();
+#endif
+	return 0;
+}
+
+static void __exit met_drv_exit(void)
+{
+	if (met_cpu_frequency_symbol)
+		symbol_put(met_cpu_frequency);
+	if (met_reg_switch_symbol)
+		symbol_put(met_reg_switch);
+	if (met_unreg_switch_symbol)
+		symbol_put(met_unreg_switch);
+	if (tracing_record_cmdline_symbol)
+		symbol_put(met_tracing_record_cmdline);
+	if (met_get_cpuinfo_symbol)
+		symbol_put(met_get_cpuinfo);
+
+#ifdef MET_PLF_USE
+	core_plf_exit();
+#endif
+	fs_unreg();
+
+}
+module_init(met_drv_init);
+module_exit(met_drv_exit);
+
+MODULE_AUTHOR("DT_DM5");
+MODULE_DESCRIPTION("MET_CORE");
+MODULE_LICENSE("GPL");
diff --git a/src/devtools/met-driver/4.19/mt2712/met_struct.h b/src/devtools/met-driver/4.19/mt2712/met_struct.h
new file mode 100644
index 0000000..a74ff78
--- /dev/null
+++ b/src/devtools/met-driver/4.19/mt2712/met_struct.h
@@ -0,0 +1,34 @@
+/*
+ * 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.
+ */
+
+#ifndef _MET_STRUCT_H_
+#define _MET_STRUCT_H_
+
+#include <linux/hrtimer.h>
+
+struct met_cpu_struct {
+	struct hrtimer hrtimer;
+	struct delayed_work dwork;
+/* struct kmem_cache *cachep; */
+/* struct list_head sample_head; */
+/* spinlock_t list_lock; */
+/* struct mutex list_sync_lock; */
+	int work_enabled;
+	int cpu;
+	int hrtimer_online_check;
+/* char name[16]; */
+};
+
+DECLARE_PER_CPU(struct met_cpu_struct, met_cpu);
+
+#endif				/* _MET_STRUCT_H_ */
diff --git a/src/devtools/met-driver/4.19/mt2712/met_tag.h b/src/devtools/met-driver/4.19/mt2712/met_tag.h
new file mode 100644
index 0000000..04b5e09
--- /dev/null
+++ b/src/devtools/met-driver/4.19/mt2712/met_tag.h
@@ -0,0 +1,37 @@
+/*
+ * 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.
+ */
+
+#ifndef __MET_TAG_EX_H__
+#define __MET_TAG_EX_H__
+
+#ifdef BUILD_WITH_MET
+void force_sample(void *unused);
+#else
+#include <linux/string.h>
+#endif
+
+/* Black List Table */
+struct bltable_t {
+	struct mutex mlock;
+	/* flag - Bit31: Global ON/OFF; Bit0~30: ON/OF slot map of class_id */
+	unsigned int flag;
+	int class_id[MAX_EVENT_CLASS];
+};
+
+extern void met_sched_switch(struct task_struct *prev, struct task_struct *next);
+
+extern int tracing_mark_write(int type, unsigned int class_id,
+		const char *name, unsigned int value,
+		unsigned int value2, unsigned int value3);
+
+#endif				/* __MET_TAG_EX_H__ */
diff --git a/src/devtools/met-driver/4.19/mt2712/mips_pmu_hw.c b/src/devtools/met-driver/4.19/mt2712/mips_pmu_hw.c
new file mode 100644
index 0000000..19e836b
--- /dev/null
+++ b/src/devtools/met-driver/4.19/mt2712/mips_pmu_hw.c
@@ -0,0 +1,438 @@
+/*
+ * 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 <asm/system.h>
+#include <linux/smp.h>
+
+#include "cpu_pmu.h"
+#include "mips_pmu_name.h"
+
+struct chip_pmu {
+	enum cpu_type_enum type;
+	struct pmu_desc **desc;
+	void *refptr;
+	const char *cpu_name;
+	unsigned int pmu_desc_size;
+	unsigned int max_hw_events;
+	unsigned int max_reg_count;
+};
+
+struct pmu_desc *mips_pmu_desc[MIPS_MAX_HWEVENTS];
+
+static struct chip_pmu chips[] = {
+	{CPU_1004K, mips_pmu_desc, (void *)mips_1004k_pmu_desc, "MIPS_1004K",
+	 MIPS_1004K_PMU_DESC_SIZE, MIPS_1004K_PMU_DESC_COUNT, PMU_1004K_MAX_HW_REGS},
+};
+
+static struct chip_pmu chip_unknown = { CPU_UNKNOWN, NULL, NULL, "Unknown CPU", 0, 0, 0 };
+
+#define CHIP_PMU_COUNT (sizeof(chips) / sizeof(struct chip_pmu))
+static struct chip_pmu *chip;
+#define M_CONFIG1_PC    (1 << 4)
+
+#define M_PERFCTL_EXL           (1  <<  0)
+#define M_PERFCTL_KERNEL        (1  <<  1)
+#define M_PERFCTL_SUPERVISOR        (1  <<  2)
+#define M_PERFCTL_USER          (1  <<  3)
+#define M_PERFCTL_INTERRUPT_ENABLE  (1  <<  4)
+#define M_PERFCTL_EVENT(event)      (((event) & 0x3ff)  << 5)
+#define M_PERFCTL_VPEID(vpe)        ((vpe)    << 16)
+
+#ifdef CONFIG_CPU_BMIPS5000
+#define M_PERFCTL_MT_EN(filter)     0
+#else				/* !CONFIG_CPU_BMIPS5000 */
+#define M_PERFCTL_MT_EN(filter)     ((filter) << 20)
+#endif				/* CONFIG_CPU_BMIPS5000 */
+
+#define    M_TC_EN_ALL          M_PERFCTL_MT_EN(0)
+#define    M_TC_EN_VPE          M_PERFCTL_MT_EN(1)
+#define    M_TC_EN_TC           M_PERFCTL_MT_EN(2)
+#define M_PERFCTL_TCID(tcid)        ((tcid)   << 22)
+#define M_PERFCTL_WIDE          (1  << 30)
+#define M_PERFCTL_MORE          (1  << 31)
+#define M_PERFCTL_TC            (1  << 30)
+
+#define M_PERFCTL_COUNT_EVENT_WHENEVER  (M_PERFCTL_EXL |        \
+		M_PERFCTL_KERNEL |      \
+		M_PERFCTL_USER |        \
+		M_PERFCTL_SUPERVISOR |      \
+		M_PERFCTL_INTERRUPT_ENABLE)
+
+#ifdef CONFIG_MIPS_MT_SMP
+#define M_PERFCTL_CONFIG_MASK       0x3fff801f
+#else
+#define M_PERFCTL_CONFIG_MASK       0x1f
+#endif
+#define M_PERFCTL_EVENT_MASK        0xfe0
+
+#define vpe_id()    0
+
+/* To get current TCID*/
+#define read_c0_tcbind() __read_32bit_c0_register($2, 2)
+
+struct cpu_hw_events {
+	unsigned int config_base[MIPS_MAX_HWEVENTS];
+	unsigned int saved_ctrl[MIPS_MAX_HWEVENTS];
+};
+
+DEFINE_PER_CPU(struct cpu_hw_events, cpu_hw_events) = {
+	.config_base = {
+	0, 0, 0, 0}, .saved_ctrl = {
+0, 0, 0, 0},};
+
+static enum cpu_type_enum mips_get_ic(void)
+{
+	unsigned int value = current_cpu_type();
+
+	/* pr_debug("ic value: %X\n", value); */
+	return value;
+}
+
+static int __n_counters(void)
+{
+	if (!(read_c0_config1() & M_CONFIG1_PC))
+		return 0;
+	if (!(read_c0_perfctrl0() & M_PERFCTL_MORE))
+		return 1;
+	if (!(read_c0_perfctrl1() & M_PERFCTL_MORE))
+		return 2;
+	if (!(read_c0_perfctrl2() & M_PERFCTL_MORE))
+		return 3;
+
+	return 4;
+}
+
+static int n_counters(void)
+{
+	int counters;
+
+	switch (current_cpu_type()) {
+	case CPU_R10000:
+		counters = 2;
+		break;
+	case CPU_R12000:
+	case CPU_R14000:
+		counters = 4;
+		break;
+	default:
+		counters = __n_counters();
+		break;
+	}
+
+	return counters;
+}
+
+static int mips_pmu_hw_get_counters(void)
+{
+	int count = n_counters();
+
+	/* pr_debug("pmu hw event nr: %d\n", count); */
+	return count;
+}
+
+static unsigned int mipsxx_pmu_swizzle_perf_idx(unsigned int idx)
+{
+	if (vpe_id() == 1)
+		idx = (idx + 2) & 3;
+	return idx;
+}
+
+static void mipsxx_pmu_write_counter(unsigned int idx, u64 val)
+{
+	idx = mipsxx_pmu_swizzle_perf_idx(idx);
+
+	switch (idx) {
+	case 0:
+		write_c0_perfcntr0(val);
+		return;
+	case 1:
+		write_c0_perfcntr1(val);
+		return;
+	case 2:
+		write_c0_perfcntr2(val);
+		return;
+	case 3:
+		write_c0_perfcntr3(val);
+		return;
+	}
+}
+
+static u64 mipsxx_pmu_read_counter(unsigned int idx)
+{
+	idx = mipsxx_pmu_swizzle_perf_idx(idx);
+
+	switch (idx) {
+	case 0:
+		/*
+		 * The counters are unsigned, we must cast to truncate
+		 * off the high bits.
+		 */
+		return (u32) read_c0_perfcntr0();
+	case 1:
+		return (u32) read_c0_perfcntr1();
+	case 2:
+		return (u32) read_c0_perfcntr2();
+	case 3:
+		return (u32) read_c0_perfcntr3();
+	default:
+		WARN_ONCE(1, "Invalid performance counter number (%d)\n", idx);
+		return 0;
+	}
+}
+
+
+static unsigned int mipsxx_pmu_read_control(unsigned int idx)
+{
+	idx = mipsxx_pmu_swizzle_perf_idx(idx);
+
+	switch (idx) {
+	case 0:
+		return read_c0_perfctrl0();
+	case 1:
+		return read_c0_perfctrl1();
+	case 2:
+		return read_c0_perfctrl2();
+	case 3:
+		return read_c0_perfctrl3();
+	default:
+		WARN_ONCE(1, "Invalid performance counter number (%d)\n", idx);
+		return 0;
+	}
+}
+
+static void mipsxx_pmu_write_control(unsigned int idx, unsigned int val)
+{
+	idx = mipsxx_pmu_swizzle_perf_idx(idx);
+
+	switch (idx) {
+	case 0:
+		write_c0_perfctrl0(val);
+		return;
+	case 1:
+		write_c0_perfctrl1(val);
+		return;
+	case 2:
+		write_c0_perfctrl2(val);
+		return;
+	case 3:
+		write_c0_perfctrl3(val);
+		return;
+	}
+}
+
+static int mipsxx_pmu_get_vpeid(void)
+{
+	return read_c0_tcbind() & 0xF;
+}
+
+static void mipsxx_pmu_reset_counters(int idx)
+{
+	switch (idx) {
+	case 3:
+		mipsxx_pmu_write_control(3, 0);
+		mipsxx_pmu_write_counter(3, 0);
+		break;
+	case 2:
+		mipsxx_pmu_write_control(2, 0);
+		mipsxx_pmu_write_counter(2, 0);
+		break;
+	case 1:
+		mipsxx_pmu_write_control(1, 0);
+		mipsxx_pmu_write_counter(1, 0);
+		break;
+	case 0:
+		mipsxx_pmu_write_control(0, 0);
+		mipsxx_pmu_write_counter(0, 0);
+		break;
+	}
+}
+
+static void mipsxx_pmu_enable_event(int idx, int event)
+{
+	struct cpu_hw_events *cpuc = this_cpu_ptr(&cpu_hw_events);
+	unsigned long flags;
+
+	WARN_ON(idx < 0 || idx >= chip->max_hw_events);
+	cpuc->saved_ctrl[idx] = M_PERFCTL_EVENT(event & 0xff) |
+	    M_PERFCTL_VPEID(mipsxx_pmu_get_vpeid()) |
+	    (cpuc->config_base[idx] & M_PERFCTL_CONFIG_MASK);
+#ifdef CONFIG_CPU_BMIPS5000
+	/* if (IS_ENABLED(CONFIG_CPU_BMIPS5000)) */
+	/* enable the counter for the calling thread */
+	cpuc->saved_ctrl[idx] |= (1 << (12 + vpe_id())) | M_PERFCTL_TC;
+#endif
+	/*
+	 * To enable pmu count
+	 */
+	local_irq_save(flags);
+	mipsxx_pmu_write_control(idx, cpuc->saved_ctrl[idx]);
+	local_irq_restore(flags);
+}
+
+static void mipsxx_pmu_disable_event(int idx)
+{
+	struct cpu_hw_events *cpuc = this_cpu_ptr(&cpu_hw_events);
+	unsigned long flags;
+
+	/* WARN_ON(idx < 0 || idx >= mipspmu.num_counters); */
+	WARN_ON(idx < 0 || idx >= chip->max_hw_events);
+
+	local_irq_save(flags);
+	cpuc->saved_ctrl[idx] = mipsxx_pmu_read_control(idx) & ~M_PERFCTL_COUNT_EVENT_WHENEVER;
+	mipsxx_pmu_write_control(idx, cpuc->saved_ctrl[idx]);
+	local_irq_restore(flags);
+}
+
+static int mips_pmu_hw_get_event_desc(int idx, int event, char *event_desc)
+{
+	int i;
+
+	if (event_desc == NULL) {
+		pr_debug("event_desc is NULL\n");
+		return -1;
+	}
+
+	for (i = 0; i < chip->max_reg_count; i++) {
+		if (chip->desc[idx][i].event == event) {
+			strncpy(event_desc, chip->desc[idx][i].name, MXSIZE_PMU_DESC - 1);
+			break;
+		}
+	}
+	if (i == chip->max_reg_count)
+		return -1;
+
+	return 0;
+}
+
+
+static int mips_pmu_hw_check_event(struct met_pmu *pmu, int idx, int event)
+{
+	int i;
+
+	/* to check index over run */
+	if (!chip)
+		return -1;
+
+	if (idx >= chip->max_hw_events)
+		return -1;
+
+	for (i = 0; i < chip->max_reg_count; i++) {
+		if (chip->desc[idx][i].event == event)
+			break;
+	}
+	if (i == chip->max_reg_count)
+		return -1;
+
+	return 0;
+}
+
+static void mips_pmu_hw_start(struct met_pmu *pmu, int count)
+{
+	int i;
+	int generic = count - 1;
+	struct cpu_hw_events *cpuc = this_cpu_ptr(&cpu_hw_events);
+
+	/* pr_debug("hw_start generic: %d\n", generic); */
+	for (i = 0; i < generic; i++) {
+		/* init config */
+		cpuc->config_base[i] = 0;
+		cpuc->config_base[i] |= M_TC_EN_VPE;
+		cpuc->config_base[i] |= M_PERFCTL_USER;
+		cpuc->config_base[i] |= M_PERFCTL_KERNEL;
+		cpuc->config_base[i] |= M_PERFCTL_EXL;
+		cpuc->config_base[i] |= M_PERFCTL_SUPERVISOR;
+		cpuc->config_base[i] &= M_PERFCTL_CONFIG_MASK;
+		 /**/ mipsxx_pmu_reset_counters(i);
+		if (pmu[i].mode == MODE_POLLING)
+			mipsxx_pmu_enable_event(i, pmu[i].event);
+	}
+	if (pmu[count - 1].mode == MODE_POLLING)
+		pr_debug("%s %d BUG!!! index over run!!\n", __func__, __LINE__);
+}
+
+static void mips_pmu_hw_stop(int count)
+{
+	int idx = 0;
+	int generic = count - 1;
+	/* pr_debug("reset %d\n", generic); */
+	for (idx = 0; idx < generic; idx++) {
+		mipsxx_pmu_reset_counters(idx);
+		mipsxx_pmu_disable_event(idx);
+	}
+}
+
+
+static unsigned int mips_pmu_hw_polling(struct met_pmu *pmu, int count, unsigned int *pmu_value)
+{
+	int i, cnt = 0;
+	int generic = count - 1;
+
+	for (i = 0; i < generic; i++) {
+		if (pmu[i].mode == MODE_POLLING) {
+			pmu_value[cnt] = mipsxx_pmu_read_counter(i);
+			cnt++;
+			mipsxx_pmu_reset_counters(i);
+			mipsxx_pmu_enable_event(i, pmu[i].event);
+		}
+	}
+	if (pmu[count - 1].mode == MODE_POLLING) {
+		pr_debug("%s %d BUG!!! index over run!!\n", __func__, __LINE__);
+		pmu_value[cnt] = 0xFFFF;
+		cnt++;
+	}
+
+	return cnt;
+}
+
+
+
+struct cpu_pmu_hw mips_pmu = {
+	.name = "mips_pmu",
+	.get_event_desc = mips_pmu_hw_get_event_desc,
+	.check_event = mips_pmu_hw_check_event,
+	.start = mips_pmu_hw_start,
+	.stop = mips_pmu_hw_stop,
+	.polling = mips_pmu_hw_polling,
+};
+
+struct cpu_pmu_hw *cpu_pmu_hw_init(void)
+{
+	int i = 0;
+	enum cpu_type_enum type;
+	int pmu_hw_count = 0;
+
+	type = mips_get_ic();
+
+	if (CPU_UNKNOWN == type || CPU_LAST == type) {
+		chip = &chip_unknown;
+		return NULL;
+	}
+	for (i = 0; i < CHIP_PMU_COUNT; i++) {
+		if (chips[i].type == type) {
+			chip = &(chips[i]);
+			break;
+		}
+	}
+	if (i == CHIP_PMU_COUNT) {
+		chip = &chip_unknown;
+		return NULL;
+	}
+
+	pmu_hw_count = mips_pmu_hw_get_counters();
+	for (i = 0; i < pmu_hw_count; i++)
+		chip->desc[i] = chip->refptr + (chip->pmu_desc_size * i);
+
+	mips_pmu.nr_cnt = pmu_hw_count + 1;
+	mips_pmu.cpu_name = chip->cpu_name;
+	return &mips_pmu;
+}
diff --git a/src/devtools/met-driver/4.19/mt2712/mips_pmu_name.h b/src/devtools/met-driver/4.19/mt2712/mips_pmu_name.h
new file mode 100644
index 0000000..d7ea26a
--- /dev/null
+++ b/src/devtools/met-driver/4.19/mt2712/mips_pmu_name.h
@@ -0,0 +1,173 @@
+/*
+ * 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.
+ */
+
+
+#ifndef _MIPS_PMU_NAME_H_
+#define _MIPS_PMU_NAME_H_
+
+/* MIPS 1004K */
+#define MIPS_MAX_HWEVENTS		(4)
+
+#define PMU_1004K_MAX_HW_REGS	(128)
+struct pmu_desc mips_1004k_pmu_desc[][PMU_1004K_MAX_HW_REGS] = {
+	/* COUNT 0 */
+	{
+	 {0, "CPU_CYCLES"},
+	 {1, "CPU_INST"},
+	 {2, "BRANCH_INSNS"},
+	 {3, "JR_31_INSNS"},
+	 {4, "JR_NON_31_INSNS"},
+	 {5, "ITLB_ACCESSES"},
+	 {6, "DTLB_ACCESSES"},
+	 {7, "JTLB_INSN_ACCESSES"},
+	 {8, "JTLB_DATA_ACCESSES"},
+	 {9, "ICACHE_ACCESSES"},
+	 {10, "DCACHE_ACCESSES"},
+	 {11, "DCACHE_MISSES"},
+	 {12, "RESERVED"},
+	 {13, "STORE_MISS_INSNS"},
+	 {14, "INTEGER_INSNS"},
+	 {15, "LOAD_INSNS"},
+	 {16, "J_JAL_INSNS"},
+	 {17, "NO_OPS_INSNS"},
+	 {18, "ALL_STALLS"},
+	 {19, "SC_INSNS"},
+	 {20, "PREFETCH_INSNS"},
+	 {21, "L2_CACHE_WRITEBACKS"},
+	 {22, "L2_CACHE_MISSES"},
+	 {23, "EXCEPTIONS_TAKEN"},
+	 {24, "CACHE_FIXUP_CYCLES"},
+	 {25, "IFU_STALLS"},
+	 {26, "DSP_INSNS"},
+	 {27, "RESERVED"},
+	 {28, "POLICY_EVENTS"},
+	 {29, "ISPRAM_EVENTS"},
+	 {30, "COREEXTEND_EVENTS"},
+	 {31, "YIELD_EVENTS"},
+	 {32, "ITC_LOADS"},
+	 {33, "UNCACHED_LOAD_INSNS"},
+	 {34, "FORK_INSNS"},
+	 {35, "CP2_ARITH_INSNS"},
+	 {36, "INTERVENTION_STALLS"},
+	 {37, "ICACHE_MISS_STALLS"},
+	 {38, "RESERVED"},
+	 {39, "DCACHE_MISS_CYCLES"},
+	 {40, "UNCACHED_STALLS"},
+	 {41, "MDU_STALLS"},
+	 {42, "CP2_STALLS"},
+	 {43, "ISPRAM_STALLS"},
+	 {44, "CACHE_INSN_STALLS"},
+	 {45, "LOAD_USE_STALLS"},
+	 {46, "INTERLOCK_STALLS"},
+	 {47, "RELAX_STALLS"},
+	 {48, "IFU_FB_FULL_REFETCHES"},
+	 {49, "EJTAG_INSN_TRIGGERS"},
+	 {50, "FSB_LESS_25_FULL"},
+	 {51, "FSB_OVER_50_FULL"},
+	 {52, "LDQ_LESS_25_FULL"},
+	 {53, "LDQ_OVER_50_FULL"},
+	 {54, "WBB_LESS_25_FULL"},
+	 {55, "WBB_OVER_50_FULL"},
+	 {56, "INTERVENTION_HIT_COUNT"},
+	 {57, "INVALIDATE_INTERVENTION_COUNT"},
+	 {58, "EVICTION_COUNT"},
+	 {59, "MESI_INVAL_COUNT"},
+	 {60, "MESI_MODIFIED_COUNT"},
+	 {61, "SELF_INTERVENTION_LATENCY"},
+	 {62, "READ_RESPONSE_LATENCY"},
+	 {63, "RESERVED"},
+	 {64, "SI_PCEVENT1"},
+	 {65, "SI_PCEVENT3"},
+	 {66, "SI_PCEVENT5"},
+	 {67, "SI_PCEVENT7"},
+	 {-1, "RESERVED"},
+	 /* 68 - 127 for Reserved */
+	 },
+	/* COUNT 1 */
+	{
+	 {0, "CPU_CYCLES"},
+	 {1, "CPU_INST"},
+	 {2, "MISPREDICTED_BRANCH_INSNS"},
+	 {3, "JR_31_MISPREDICTIONS"},
+	 {4, "JR_31_NO_PREDICTIONS"},
+	 {5, "ITLB_MISSES"},
+	 {6, "DTLB_MISSES"},
+	 {7, "JTLB_INSN_MISSES"},
+	 {8, "JTLB_DATA_MISSES"},
+	 {9, "ICACHE_MISSES"},
+	 {10, "DCACHE_WRITEBACKS"},
+	 {11, "DCACHE_MISSES"},
+	 {12, "RESERVED"},
+	 {13, "LOAD_MISS_INSNS"},
+	 {14, "FPU_INSNS"},
+	 {15, "STORE_INSNS"},
+	 {16, "MIPS16_INSNS"},
+	 {17, "INT_MUL_DIV_INSNS"},
+	 {18, "REPLAYED_INSNS"},
+	 {19, "SC_INSNS_FAILED"},
+	 {20, "CACHE_HIT_PREFETCH_INSNS"},
+	 {21, "L2_CACHE_ACCESSES"},
+	 {22, "L2_CACHE_SINGLE_BIT_ERRORS"},
+	 {23, "SINGLE_THREADED_CYCLES"},
+	 {24, "REFETCHED_INSNS"},
+	 {25, "ALU_STALLS"},
+	 {26, "ALU_DSP_SATURATION_INSNS"},
+	 {27, "MDU_DSP_SATURATION_INSNS"},
+	 {28, "CP2_EVENTS"},
+	 {29, "DSPRAM_EVENTS"},
+	 {30, "RESERVED"},
+	 {31, "ITC_EVENT"},
+	 {33, "UNCACHED_STORE_INSNS"},
+	 {34, "YIELD_IN_COMP"},
+	 {35, "CP2_TO_FROM_INSNS"},
+	 {36, "INTERVENTION_MISS_STALLS"},
+	 {37, "DCACHE_MISS_STALLS"},
+	 {38, "RESERVED"},
+	 /* 38 was listed in OPROFILE web page, but not listed 1004k mips spec */
+	 /* {38, "FSB_INDEX_CONFLICT_STALLS"}, */
+	 {39, "L2_CACHE_MISS_CYCLES"},
+	 {40, "ITC_STALLS"},
+	 {41, "FPU_STALLS"},
+	 {42, "COREEXTEND_STALLS"},
+	 {43, "DSPRAM_STALLS"},
+	 {45, "ALU_TO_AGEN_STALLS"},
+	 {46, "MISPREDICTION_STALLS"},
+	 {47, "RESERVED"},
+	 {48, "FB_ENTRY_ALLOCATED_CYCLES"},
+	 {49, "EJTAG_DATA_TRIGGERS"},
+	 {50, "FSB_25_50_FULL"},
+	 {51, "FSB_FULL_STALLS"},
+	 {52, "LDQ_25_50_FULL"},
+	 {53, "LDQ_FULL_STALLS"},
+	 {54, "WBB_25_50_FULL"},
+	 {55, "WBB_FULL_STALLS"},
+	 {56, "INTERVENTION_COUNT"},
+	 {57, "INVALID_INTERVENT_HIT_CNT"},
+	 {58, "WRITEBACK_COUNT"},
+	 {59, "MESI_EXCLUSIVE_COUNT"},
+	 {60, "MESI_SHARED_COUNT"},
+	 {61, "SELF_INTERVENTION_COUNT"},
+	 {62, "READ_RESPONSE_COUNT"},
+	 {63, "RESERVED"},
+	 {64, "SI_PCEVENT0"},
+	 {65, "SI_PCEVENT2"},
+	 {66, "SI_PCEVENT4"},
+	 {67, "SI_PCEVENT6"},
+	 {-1, "RESERVED"},
+	 },
+};
+
+#define MIPS_1004K_PMU_DESC_SIZE (sizeof(mips_1004k_pmu_desc[0]))
+#define MIPS_1004K_PMU_DESC_COUNT (sizeof(mips_1004k_pmu_desc) / MIPS_1004K_PMU_DESC_SIZE)
+
+#endif				/* _V8_PMU_NAME_H_ */
diff --git a/src/devtools/met-driver/4.19/mt2712/mtk_dramc.c b/src/devtools/met-driver/4.19/mt2712/mtk_dramc.c
new file mode 100644
index 0000000..22de7af
--- /dev/null
+++ b/src/devtools/met-driver/4.19/mt2712/mtk_dramc.c
@@ -0,0 +1,511 @@
+/* Copyright Statement:
+ *
+ * This software/firmware and related documentation ("MediaTek Software") are
+ * protected under relevant copyright laws. The information contained herein
+ * is confidential and proprietary to MediaTek Inc. and/or its licensors.
+ * Without the prior written permission of MediaTek inc. and/or its licensors,
+ * any reproduction, modification, use or disclosure of MediaTek Software,
+ * and information contained herein, in whole or in part, shall be strictly prohibited.
+ */
+/* MediaTek Inc. (C) 2012. All rights reserved.
+ *
+ * BY OPENING THIS FILE, RECEIVER HEREBY UNEQUIVOCALLY ACKNOWLEDGES AND AGREES
+ * THAT THE SOFTWARE/FIRMWARE AND ITS DOCUMENTATIONS ("MEDIATEK SOFTWARE")
+ * RECEIVED FROM MEDIATEK AND/OR ITS REPRESENTATIVES ARE PROVIDED TO RECEIVER ON
+ * AN "AS-IS" BASIS ONLY. MEDIATEK EXPRESSLY DISCLAIMS ANY AND ALL WARRANTIES,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE OR NONINFRINGEMENT.
+ * NEITHER DOES MEDIATEK PROVIDE ANY WARRANTY WHATSOEVER WITH RESPECT TO THE
+ * SOFTWARE OF ANY THIRD PARTY WHICH MAY BE USED BY, INCORPORATED IN, OR
+ * SUPPLIED WITH THE MEDIATEK SOFTWARE, AND RECEIVER AGREES TO LOOK ONLY TO SUCH
+ * THIRD PARTY FOR ANY WARRANTY CLAIM RELATING THERETO. RECEIVER EXPRESSLY ACKNOWLEDGES
+ * THAT IT IS RECEIVER'S SOLE RESPONSIBILITY TO OBTAIN FROM ANY THIRD PARTY ALL PROPER LICENSES
+ * CONTAINED IN MEDIATEK SOFTWARE. MEDIATEK SHALL ALSO NOT BE RESPONSIBLE FOR ANY MEDIATEK
+ * SOFTWARE RELEASES MADE TO RECEIVER'S SPECIFICATION OR TO CONFORM TO A PARTICULAR
+ * STANDARD OR OPEN FORUM. RECEIVER'S SOLE AND EXCLUSIVE REMEDY AND MEDIATEK'S ENTIRE AND
+ * CUMULATIVE LIABILITY WITH RESPECT TO THE MEDIATEK SOFTWARE RELEASED HEREUNDER WILL BE,
+ * AT MEDIATEK'S OPTION, TO REVISE OR REPLACE THE MEDIATEK SOFTWARE AT ISSUE,
+ * OR REFUND ANY SOFTWARE LICENSE FEES OR SERVICE CHARGE PAID BY RECEIVER TO
+ * MEDIATEK FOR SUCH MEDIATEK SOFTWARE AT ISSUE.
+ *
+ * The following software/firmware and/or related documentation ("MediaTek Software")
+ * have been modified by MediaTek Inc. All revisions are subject to any receiver's
+ * applicable license agreements with MediaTek Inc.
+ */
+
+#include <linux/io.h>
+#include <asm/cacheflush.h>
+#include <asm/uaccess.h>
+/* #include <asm/system.h> */
+#include "mtk_dramc.h"
+//#include "x_hal_io.h"
+
+/* #define DRAMC_DEBUG */
+#ifndef DRAMC_DEBUG
+#define DRAMC_LOG(fmt, ...)
+#else
+#define DRAMC_LOG(fmt, arg...) printk("%s:%d:"fmt, __FILE__, __LINE__,##arg);
+#endif
+
+/* #define TEST_AGENT */
+
+
+#if 1
+struct dramc_desc_t dramc_desc[DRAMC_MX_CHANNUM][DRAMC_MX_GRPNUM][DRAMC_MX_NUM_IN_AGRP] = DRAMC_AGENT_TABLE;
+#else
+struct dramc_desc_t dramc_desc[DRAMC_MX_CHANNUM][DRAMC_MX_GRPNUM][DRAMC_MX_NUM_IN_AGRP] =
+{
+/* 	/////////////////////////////////////////////////////////
+	define channel A releationship between group and name
+	///////////////////////////////////////////////////////// */
+	{
+		{
+			/* GRP 1 */
+			{0, 	"00_audio"},
+			{1, 	"01_demux/gcpu/ddi"},
+			{2, 	"02_vbi/3d/tve/dolby"},
+			{3, 	"03_xpscaler_ip/tddc"},
+			{4, 	"04_mib"},
+			{5, 	"05_b2r"},
+			{6, 	"06_cpu"},
+			{7, 	"07_scpos"},
+			{8,	"08_vdec_vld"},
+			{9, 	"09_audio_dsp1"},
+			{10, 	"10_3d_gpu"},
+			{11,	"11_2d_graph/jpgdec/osd/gfx_imgrsz/2d_graph_cmd/irt_dma/png"},
+			{12,	"12_ethernet/demod_isdbt/ci_spi/rs232/usb2/usb3/usb3_d"},
+			{13,	"13_osd"},
+			{14,	"14_venc/vp8_encoder"},
+			{15,	"15_test0/audio_dsp0"},
+			{-1,	""}
+		},
+		{
+			/* GRP 2 */
+			{16,	"16_vdec_lat"},
+			{17,	"17_mmu"},
+			{18,	"18_memc"},
+			{19,	"19_video_imgrsz0/video_imgrsz2"},
+			{20,	"20_arm11"},
+			{21,	"21_msdc/emmc/gdma"},
+			{22,	"22_vdec_mc"},
+			{30,	"30_agent_30"},
+			{-1,	""}
+		},
+		{
+			/* GRP 3 */
+			{23,	"23_cpu_bim_read"},
+			{24,	"24_demux/gcpu/ddi "},
+			{25,	"25_nfi_dma/sfalsh_dma/lzhs/ci_spi"},
+			{26,	"26_rs232"},
+			{27,	"27_agent_27"},
+			{28,	"28_agent_28"},
+			{29,	"29_agent_29"},
+			{31,	"31_agent_31"},
+			{-1,	""}
+		}
+	},
+
+/* 	///////////////////////////////////////////////////////////
+	define channel B releationship between group and name
+	///////////////////////////////////////////////////////// */
+	{
+		{
+			/* GRP 1 */
+			{0, 	"00_audio"},
+			{1, 	"01_demux/gcpu/ddi"},
+			{2, 	"02_vbi/3d/tve/dolby"},
+			{3, 	"03_xpscaler_ip/tddc"},
+			{4, 	"04_mib"},
+			{5, 	"05_b2r"},
+			{6, 	"06_cpu"},
+			{7, 	"07_scpos"},
+			{8,	"08_vdec_vld"},
+			{9, 	"09_audio_dsp1"},
+			{10, 	"10_3d_gpu"},
+			{11,	"11_2d_graph/jpgdec/osd/gfx_imgrsz/2d_graph_cmd/irt_dma/png"},
+			{12,	"12_ethernet/demod_isdbt/ci_spi/rs232/usb2/usb3/usb3_d"},
+			{13,	"13_osd"},
+			{14,	"14_venc/vp8_encoder"},
+			{15,	"15_test0/audio_dsp0"},
+			{-1,	""}
+		},
+		{
+			/* GRP 2 */
+			{16,	"16_vdec_lat"},
+			{17,	"17_mmu"},
+			{18,	"18_memc"},
+			{19,	"19_video_imgrsz0/video_imgrsz2"},
+			{20,	"20_arm11"},
+			{21,	"21_msdc/emmc/gdma"},
+			{22,	"22_vdec_mc"},
+			{30,	"30_agent_30"},
+			{-1,	""}
+		},
+		{
+			/* GRP 3 */
+			{23,	"23_cpu_bim_read"},
+			{24,	"24_demux/gcpu/ddi "},
+			{25,	"25_nfi_dma/sfalsh_dma/lzhs/ci_spi"},
+			{26,	"26_rs232"},
+			{27,	"27_agent_27"},
+			{28,	"28_agent_28"},
+			{29,	"29_agent_29"},
+			{31,	"31_agent_31"},
+			{-1,	""}
+		}
+	},
+
+/* 	///////////////////////////////////////////////////////////
+	define channel C releationship between group and name
+	/////////////////////////////////////////////////////////// */
+	{
+		{
+			/* GRP 1 */
+			{0, 	"00_audio"},
+			{1, 	"01_demux/gcpu/ddi"},
+			{2, 	"02_vbi/3d/tve/dolby"},
+			{3, 	"03_xpscaler_ip/tddc"},
+			{4, 	"04_mib"},
+			{5, 	"05_b2r"},
+			{6, 	"06_cpu"},
+			{7, 	"07_scpos"},
+			{8,	"08_vdec_vld"},
+			{9, 	"09_audio_dsp1"},
+			{10, 	"10_3d_gpu"},
+			{11,	"11_2d_graph/jpgdec/osd/gfx_imgrsz/2d_graph_cmd/irt_dma/png"},
+			{12,	"12_ethernet/demod_isdbt/ci_spi/rs232/usb2/usb3/usb3_d"},
+			{13,	"13_osd"},
+			{14,	"14_venc/vp8_encoder"},
+			{15,	"15_test0/audio_dsp0"},
+			{-1,	""}
+		},
+		{
+			/* GRP 2 */
+			{16,	"16_vdec_lat"},
+			{17,	"17_mmu"},
+			{18,	"18_memc"},
+			{19,	"19_video_imgrsz0/video_imgrsz2"},
+			{20,	"20_arm11"},
+			{21,	"21_msdc/emmc/gdma"},
+			{22,	"22_vdec_mc"},
+			{30,	"30_agent_30"},
+			{-1,	""}
+		},
+		{
+			/* GRP 3 */
+			{23,	"23_cpu_bim_read"},
+			{24,	"24_demux/gcpu/ddi "},
+			{25,	"25_nfi_dma/sfalsh_dma/lzhs/ci_spi"},
+			{26,	"26_rs232"},
+			{27,	"27_agent_27"},
+			{28,	"28_agent_28"},
+			{29,	"29_agent_29"},
+			{31,	"31_agent_31"},
+			{-1,	""}
+		}
+	}
+
+};
+#endif
+
+static unsigned long dramc_base[DRAMC_MX_CHANNUM] =
+{
+	MET_DRAMC0_BASE,
+};
+
+/*
+force to 32 bit means current is 16 bit
+not force to 32 bit means current is 32 bit
+*/
+static unsigned long dramc_ext32_off[DRAMC_MX_CHANNUM] =
+{
+	DRAM_CHA_FORCE32,
+};
+
+
+static inline unsigned int dramc_reg_read(unsigned long addr)
+{
+#if 0
+#ifdef CONFIG_64BIT
+	unsigned int value = 0;
+	value = HAL_READ32((void*)addr);
+#else
+	unsigned int value = readl((void*)addr);
+#endif
+#endif
+
+	unsigned int value = readl((void*)addr);
+
+	mb();
+	return value;
+}
+
+static inline void dramc_reg_write(unsigned long addr, unsigned int value)
+{
+#if 0
+	/* make sure writel() be completed before outer_sync() */
+#ifdef CONFIG_64BIT
+	HAL_WRITE32((void*)addr, value);
+#else
+	writel(value, (void*)addr);
+#endif
+#endif
+
+	writel(value, (void*)addr);
+	mb();
+}
+
+#define DRAMC_SET_VALUE(target, value, shift, bit) \
+do { \
+	volatile u32 temp = dramc_reg_read(target); \
+	u32 mask1 = (~(((0xFFFFFFFF >> (32 - bit))<< shift))); \
+	u32 mask2 = ((0xFFFFFFFF >> (32 - bit))<< shift); \
+	dramc_reg_write(target,(temp & mask1) | ((value << shift) & mask2)); \
+} while (0)
+
+void DRAMC_Init(int chan)
+{
+	unsigned long base = dramc_base[chan];
+	volatile u32 temp_reg = 0;
+
+	DRAMC_LOG("%s chann: %d ext_32_value: %x ext_32bit: %d\n", __FUNCTION__, chan, dramc_ext32_off[chan], DRMAC_IS_FORCE32(dramc_ext32_off[chan]));
+	/* determine the number of dram for the corresponding dramc */
+	DRAMC_SET_VALUE(DRAMC_MON_BM(base),  DRMAC_IS_FORCE32(dramc_ext32_off[chan]), DRAMC_BM_DMBW32B_SHIFT, 1);
+
+	/* disable all group */
+	DRAMC_SET_VALUE(DRAMC_MON_BM(base), 0, DRAMC_BM_GROUP1_ENABLE_SHIFT, 1);
+	DRAMC_SET_VALUE(DRAMC_MON_BM(base), 0, DRAMC_BM_GROUP2_ENABLE_SHIFT, 1);
+	DRAMC_SET_VALUE(DRAMC_MON_BM(base), 0, DRAMC_BM_GROUP3_ENABLE_SHIFT, 1);
+	DRAMC_SET_VALUE(DRAMC_MON_BM(base), 0, DRAMC_BM_FREEZE_SHIFT, 1);
+	temp_reg = dramc_reg_read(DRAMC_MON_BM(base));
+	DRAMC_LOG("%s MON_BM: %X\n", __FUNCTION__, temp_reg);
+
+#ifdef TEST_AGENT
+	u32 reg;
+	reg = base + 0;
+	dramc_reg_write(reg, 0xC050EF00);
+
+	reg = base + 0x100;
+	if (0 == chan)
+		dramc_reg_write(reg, 0x3C553000);
+	else if (1 == chan)
+		dramc_reg_write(reg, 0x5FFD0000);
+	else if (2 == chan)
+		dramc_reg_write(reg, 0x85D20000);
+
+	reg = base + 0x104;
+	dramc_reg_write(reg, 0x8000);
+
+	reg = base + 0x118;
+	dramc_reg_write(reg, 0x0600110D);
+	dramc_reg_write(reg, 0x1600110D);
+
+	reg = base + 0x160;
+	temp_reg = dramc_reg_read(reg);
+#endif
+}
+
+void DRAMC_MaxLtcyMode(int chan, int enable)
+{
+	unsigned long base = dramc_base[chan];
+
+	if (1 == enable) {
+		DRAMC_SET_VALUE(DRAMC_MON_BM(base), 0, DRAMC_BM_PACNTEN, 1);
+		DRAMC_SET_VALUE(DRAMC_MON_BM(base), 1, DRAMC_BM_REQCNTEN, 1);
+	} else {
+		DRAMC_SET_VALUE(DRAMC_MON_BM(base), 0, DRAMC_BM_PACNTEN, 1);
+		DRAMC_SET_VALUE(DRAMC_MON_BM(base), 0, DRAMC_BM_REQCNTEN, 1);
+	}
+}
+
+void DRAMC_Enable(int chan, int group, int agent)
+{
+	unsigned long base = dramc_base[chan];
+	volatile u32 temp_reg = 0;
+
+	/* disable all group */
+	switch (group) {
+	case 1:
+		DRAMC_SET_VALUE(DRAMC_MON_BM(base), agent, DRAMC_BM_GROUP1_AGENT_ID_SHIFT, DRAMC_BM_GROUP1_AGENT_ID_LEN);
+		DRAMC_SET_VALUE(DRAMC_MON_BM(base), 1, DRAMC_BM_GROUP1_ENABLE_SHIFT, 1);
+		break;
+
+	case 2:
+		DRAMC_SET_VALUE(DRAMC_MON_BM(base), agent, DRAMC_BM_GROUP2_AGENT_ID_SHIFT, DRAMC_BM_GROUP2_AGENT_ID_LEN);
+		DRAMC_SET_VALUE(DRAMC_MON_BM(base), 1, DRAMC_BM_GROUP2_ENABLE_SHIFT, 1);
+		break;
+
+	case 3:
+		DRAMC_SET_VALUE(DRAMC_MON_BM(base), agent, DRAMC_BM_GROUP3_AGENT_ID_SHIFT, DRAMC_BM_GROUP3_AGENT_ID_LEN);
+		DRAMC_SET_VALUE(DRAMC_MON_BM(base), 1, DRAMC_BM_GROUP3_ENABLE_SHIFT, 1);
+		break;
+	}
+	temp_reg = dramc_reg_read(DRAMC_MON_BM(base));
+	DRAMC_LOG("%s MON_BM: %X\n", __FUNCTION__, temp_reg);
+}
+
+
+void DRAMC_Disable(int chan)
+{
+	unsigned long base = dramc_base[chan];
+	volatile u32 temp_reg = 0;
+
+	/* disable all group */
+	DRAMC_SET_VALUE(DRAMC_MON_BM(base), 0, DRAMC_BM_GROUP1_ENABLE_SHIFT, 1);
+	DRAMC_SET_VALUE(DRAMC_MON_BM(base), 0, DRAMC_BM_GROUP2_ENABLE_SHIFT, 1);
+	DRAMC_SET_VALUE(DRAMC_MON_BM(base), 0, DRAMC_BM_GROUP3_ENABLE_SHIFT, 1);
+
+	/* fire the BM function */
+	DRAMC_SET_VALUE(DRAMC_MON_BM(base), 0, DRAMC_BM_FREEZE_SHIFT, 1);
+	temp_reg = dramc_reg_read(DRAMC_MON_BM(base));
+	DRAMC_LOG("%s MON_BM: %X\n", __FUNCTION__, temp_reg);
+}
+
+void DRAMC_Freeze(int chan)
+{
+	unsigned long base = dramc_base[chan];
+	volatile u32 temp_reg = dramc_reg_read(DRAMC_MON_BM(base));
+
+	/* Freeze the BM function */
+	DRAMC_SET_VALUE(DRAMC_MON_BM(base), 1, DRAMC_BM_FREEZE_SHIFT, 1);
+	temp_reg = dramc_reg_read(DRAMC_MON_BM(base));
+	DRAMC_LOG("%s MON_BM: %X\n", __FUNCTION__, temp_reg);
+}
+
+void DRAMC_ConfigTargetCount(int chan, u32 count)
+{
+	unsigned long base = dramc_base[chan];
+	volatile u32 temp_reg = 0;
+
+	dramc_reg_write(DRAMC_MON_BMCYC(base), 0xFFFFFFFF);
+	temp_reg = dramc_reg_read(DRAMC_MON_BMCYC(base));
+	DRAMC_LOG("%s MON_BMCYC: %X\n", __FUNCTION__, temp_reg);
+	return;
+}
+
+u32 DRAMC_GetMaxLtcy(int chan)
+{
+	unsigned long base = dramc_base[chan];
+
+	return dramc_reg_read(DRAMC_MON_ROBM4(base));
+}
+
+u32 DRAMC_GetDramcFreq(void)
+{
+	unsigned long tcm_dramc_flags = 0;
+	tcm_dramc_flags = dramc_reg_read(TCM_DRAM_FLAGS_ADDR);
+	return TCMGET_DDR_CLK(tcm_dramc_flags);
+}
+
+
+u32 DRAMC_GetCycleCount(int chan, int group)
+{
+	unsigned long base = dramc_base[chan];
+
+	switch (group) {
+	case 1:
+		return dramc_reg_read(DRAMC_MON_ROBM0(base));
+
+	case 2:
+		return dramc_reg_read(DRAMC_MON_ROBM1(base));
+
+	case 3:
+		return dramc_reg_read(DRAMC_MON_ROBM2(base));
+	}
+	return 0;
+}
+
+u32 DRAMC_GetTotalCycleCount(int chan)
+{
+	unsigned long base = dramc_base[chan];
+
+	return dramc_reg_read(DRAMC_MON_ROBM3(base));
+}
+
+int DRAMC_CheckCntIsOverFlow(u32 count)
+{
+	if (0xFFFFFFFF == count) {
+		return 1;
+	}
+	return 0;
+}
+
+void DRAMC_WriteMode(int chan, int enable)
+{
+	unsigned long base = dramc_base[chan];
+
+	if (1 == enable) {
+		DRAMC_SET_VALUE(DRAMC_MON_BM(base), 1, DRAMC_BM_WREN_SHIFT, 1);
+	} else {
+		DRAMC_SET_VALUE(DRAMC_MON_BM(base), 0, DRAMC_BM_WREN_SHIFT, 1);
+	}
+}
+
+void DRAMC_ReadMode(int chan, int enable)
+{
+	unsigned long base = dramc_base[chan];
+
+	if (1 == enable) {
+		DRAMC_SET_VALUE(DRAMC_MON_BM(base), 1, DRAMC_BM_RDEN_SHIFT, 1);
+	} else {
+		DRAMC_SET_VALUE(DRAMC_MON_BM(base), 0, DRAMC_BM_RDEN_SHIFT, 1);
+	}
+}
+
+unsigned int DRAMC_GetEfuseValue(void)
+{
+	volatile u32 efuse_reg = 0;
+
+	efuse_reg = dramc_reg_read(DRAMC_EFUSE_BIT_ADDRESS);
+
+	return (efuse_reg & DRAMC_EFUSE_BIT_MASK);
+}
+
+void DRAMC_GetGroup1AgentCounter(int channel, unsigned int* counter)
+{
+	unsigned long base = dramc_base[channel];
+	unsigned long agent_base = 0;
+	int		   i = 0;
+
+	agent_base = DRAMC_MON_AGENT_BASE(base);
+	for (i=0; i<DRAMC_MON_AGENT_NUM; i++) {
+		counter[i] = dramc_reg_read(agent_base + i*sizeof(int));
+	}
+	smp_rmb();
+
+	return ;
+}
+
+void DRAMC_GetGroup1Latency(int channel, unsigned int* latency)
+{
+	unsigned long base = dramc_base[channel];
+	unsigned long lty_base = 0;
+	int		   i = 0;
+
+	lty_base = DRAMC_MON_LTY_AGENT_BASE(base);
+	for (i=0; i<DRAMC_MON_LTY_AGENT_NUM; i++) {
+		latency[i] = dramc_reg_read(lty_base + i*sizeof(int));
+	}
+	smp_rmb();
+
+	return ;
+}
+
+void DRAMC_GetGroup1MaxLatency(int channel, unsigned int* max_ltcy)
+{
+	unsigned long base = dramc_base[channel];
+	unsigned long max_lty_base = 0;
+	unsigned int  value = 0;
+	int		   i = 0;
+
+	/* max latency counter is 16 bit*/
+	max_lty_base = DRAMC_MON_MAX_LTY_AGENT_BASE(base);
+	for (i=0; i<DRAMC_MON_LTY_AGENT_NUM/sizeof(short); i++) {
+		value = dramc_reg_read(max_lty_base + i*sizeof(int));
+		max_ltcy[2*i] = value & 0x0000FFFF;
+		max_ltcy[2*i+1] = (value & 0xFFFF0000) >> 16;
+	}
+	smp_rmb();
+
+	return ;
+}
diff --git a/src/devtools/met-driver/4.19/mt2712/mtk_dramc.h b/src/devtools/met-driver/4.19/mt2712/mtk_dramc.h
new file mode 100644
index 0000000..1f2e8a1
--- /dev/null
+++ b/src/devtools/met-driver/4.19/mt2712/mtk_dramc.h
@@ -0,0 +1,300 @@
+/* Copyright Statement:
+ *
+ * This software/firmware and related documentation ("MediaTek Software") are
+ * protected under relevant copyright laws. The information contained herein
+ * is confidential and proprietary to MediaTek Inc. and/or its licensors.
+ * Without the prior written permission of MediaTek inc. and/or its licensors,
+ * any reproduction, modification, use or disclosure of MediaTek Software,
+ * and information contained herein, in whole or in part, shall be strictly prohibited.
+ */
+/* MediaTek Inc. (C) 2012. All rights reserved.
+ *
+ * BY OPENING THIS FILE, RECEIVER HEREBY UNEQUIVOCALLY ACKNOWLEDGES AND AGREES
+ * THAT THE SOFTWARE/FIRMWARE AND ITS DOCUMENTATIONS ("MEDIATEK SOFTWARE")
+ * RECEIVED FROM MEDIATEK AND/OR ITS REPRESENTATIVES ARE PROVIDED TO RECEIVER ON
+ * AN "AS-IS" BASIS ONLY. MEDIATEK EXPRESSLY DISCLAIMS ANY AND ALL WARRANTIES,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE OR NONINFRINGEMENT.
+ * NEITHER DOES MEDIATEK PROVIDE ANY WARRANTY WHATSOEVER WITH RESPECT TO THE
+ * SOFTWARE OF ANY THIRD PARTY WHICH MAY BE USED BY, INCORPORATED IN, OR
+ * SUPPLIED WITH THE MEDIATEK SOFTWARE, AND RECEIVER AGREES TO LOOK ONLY TO SUCH
+ * THIRD PARTY FOR ANY WARRANTY CLAIM RELATING THERETO. RECEIVER EXPRESSLY ACKNOWLEDGES
+ * THAT IT IS RECEIVER'S SOLE RESPONSIBILITY TO OBTAIN FROM ANY THIRD PARTY ALL PROPER LICENSES
+ * CONTAINED IN MEDIATEK SOFTWARE. MEDIATEK SHALL ALSO NOT BE RESPONSIBLE FOR ANY MEDIATEK
+ * SOFTWARE RELEASES MADE TO RECEIVER'S SPECIFICATION OR TO CONFORM TO A PARTICULAR
+ * STANDARD OR OPEN FORUM. RECEIVER'S SOLE AND EXCLUSIVE REMEDY AND MEDIATEK'S ENTIRE AND
+ * CUMULATIVE LIABILITY WITH RESPECT TO THE MEDIATEK SOFTWARE RELEASED HEREUNDER WILL BE,
+ * AT MEDIATEK'S OPTION, TO REVISE OR REPLACE THE MEDIATEK SOFTWARE AT ISSUE,
+ * OR REFUND ANY SOFTWARE LICENSE FEES OR SERVICE CHARGE PAID BY RECEIVER TO
+ * MEDIATEK FOR SUCH MEDIATEK SOFTWARE AT ISSUE.
+ *
+ * The following software/firmware and/or related documentation ("MediaTek Software")
+ * have been modified by MediaTek Inc. All revisions are subject to any receiver's
+ * applicable license agreements with MediaTek Inc.
+ */
+
+#ifndef __MT_DRAMC_H__
+#define __MT_DRAMC_H__
+#include <linux/types.h>
+//#include "x_hal_io.h"
+
+/* define BM function base address */
+#define MET_DRAMC0_BASE                         (0xF0006000)
+#define MET_DRAMC1_BASE                         (0xF0010000)
+#define MET_DRAMC2_BASE                         (0xF0011000)
+#define TCM_DRAM_FLAGS_ADDR                     (0xF00080F8)
+#define DRAM_CLOCK_MASK                         (0x00000FFF)
+
+#define BASE_DDR_CLK                            1000000
+#define TCMGET_DDR_CLK(p)                       ((p & DRAM_CLOCK_MASK) * BASE_DDR_CLK)
+
+/* define BM function offset */
+#define DRAMC_MON_BM(i)                         (i + 0x80)
+#define DRAMC_MON_BMCYC(i)                      (i + 0x8C) /* Bus monitor cycle number, BMCYC */
+#define DRAMC_MON_ROBM0(i)                      (i + 0x90) /* Bus monitor counter for group 1,BMLENGP1_CNT */
+#define DRAMC_MON_ROBM1(i)                      (i + 0x94) /* Bus monitor counter for group 2,BMLENGP2_CNT */
+#define DRAMC_MON_ROBM2(i)                      (i + 0x98) /* Bus monitor counter for group 3,BMLENGP3_CNT */
+#define DRAMC_MON_ROBM3(i)                      (i + 0x9C) /* bus monitor cycle counter,BM_CYC_CNT */
+#define DRAMC_MON_ROBM4(i)                      (i + 0xA0) /* Maximum burst length, 16'h0000, MAX_BSTCNT */
+
+/* define BM function shift */
+#define DRAMC_BM_PACNTEN                        (31) /* Bus monitor function select for number of precharge and active */
+#define DRAMC_BM_REQCNTEN                       (30) /* Bus monitor function select for number of REQ='1'&ALE='1' */
+#define DRAMC_BM_CHGPRIEN                       (29) /* On-line change agent priority enabling */
+#define DRAMC_BM_FREEZE_SHIFT                   (28) /* Bus monitor freeze */
+#define DRAMC_BM_DMBW32B_SHIFT                  (27) /* 1:External 32Bit data bus, 0:External 16Bit data bus */
+#define DRAMC_BM_WREN_SHIFT                     (25) /* Bus monitor only monitor write cycle enabling */
+#define DRAMC_BM_RDEN_SHIFT                     (24) /* Bus monitor only monitor read cycle enabling */
+#define DRAMC_BM_GROUP3_ENABLE_SHIFT            (23) /* Bus monitor for Group 3 agents enabling */
+#define DRAMC_BM_GROUP3_AGENT_ID_SHIFT          (20) /* Bus monitor for Group 3 agent-ID */
+#define DRAMC_BM_GROUP3_AGENT_ID_LEN            (3)
+#define DRAMC_BM_GROUP2_ENABLE_SHIFT            (19) /* Bus monitor for Group 2 agents enabling */
+#define DRAMC_BM_GROUP2_AGENT_ID_SHIFT          (16) /* Bus monitor for Group 2 agent-ID */
+#define DRAMC_BM_GROUP2_AGENT_ID_LEN            (3)
+#define DRAMC_BM_GROUP1_ENABLE_SHIFT            (15) /* Bus monitor for Group 1 agents enabling */
+#define DRAMC_BM_GROUP1_AGENT_ID_SHIFT          (8) /* Bus monitor for Group 1 agent-ID or */
+#define DRAMC_BM_GROUP1_AGENT_ID_LEN            (5)
+#define DRAMC_BM_BSTCNTEN                       (7) /* Monitor burst length enable */
+#define DRAMC_BM_R_DMBSTCNT_SEL                 (6) /* Latency mode select */
+#define DRAMC_BM_R_DMLATRECEN                   (5) /* Agent latency record enable */
+#define DRAMC_BM_BSTCNT                         (0) /* Agent-ID of the burst length monitored agent */
+#define DRAMC_BM_BSTCNT_LEN                     (4)
+
+/* define TCM function base address */
+#ifndef IO_VIRT
+#define IO_VIRT                                 (0xF0000000)
+#endif
+
+#define REG_RW_GPRB6                            (0x00f8) /* RISC Byte General Purpose Register 6 */
+#define TCM_SRAM_ADDR                           (IO_VIRT + 0x8000)
+
+#ifdef CONFIG_64BIT
+#define TCM_DRAM_FLAGS                          (*((volatile unsigned long*)(OFFSET_64BIT + TCM_SRAM_ADDR + REG_RW_GPRB6)))
+#else
+#define TCM_DRAM_FLAGS                          (*((volatile unsigned long*)(TCM_SRAM_ADDR + REG_RW_GPRB6)))
+#endif
+
+#define CHA_FORCE32_SHIFT                       (22)
+#define DRAM_CHA_FORCE32                        (1U << CHA_FORCE32_SHIFT)
+#define CHB_FORCE32_SHIFT                       (23)
+#define DRAM_CHB_FORCE32                        (1U << CHB_FORCE32_SHIFT)
+#define CHC_FORCE32_SHIFT                       (31)
+#define DRAM_CHC_FORCE32                        (1U << CHC_FORCE32_SHIFT)
+
+
+/* to check dramc is ext 32 bit or not*/
+#define DRMAC_IS_FORCE32(p)                     ((TCM_DRAM_FLAGS & p) ? 0 : 1)
+
+/* generate random cycle to return fake report */
+/* #define DRAMC_FAKE_REPORT */
+
+/* define dramc spec */
+#define DRAMC_MX_CHANNUM                        (1)
+#define DRAMC_MX_GRPNUM                         (3)
+#define DRAMC_MX_NUM_IN_AGRP                    (32)
+#define DRAMC_MX_AGENT_NAMEBUF                  (96)
+
+/* define all group id */
+#define DRAMC_ALL_GROUP_AGENT_ID                (0x20)
+#define DRAMC_MX_LATENCY                        (0x21)
+
+/* efuse bit[15:14]
+00:4G
+01:3G
+10:2G
+11:1G */
+#define DRAMC_EFUSE_BIT_ADDRESS                 (0xF0008664)
+#define DRAMC_EFUSE_BIT_MASK                    (0x0000C000)
+#define DRAMC_EFUSE_BIT_1G                      (0x0000C000)
+#define DRAMC_EFUSE_BIT_2G                      (0x00008000)
+#define DRAMC_EFUSE_BIT_3G                      (0x00004000)
+#define DRAMC_EFUSE_BIT_4G                      (0x00000000)
+
+#define DRAM_DDRPHY_CHA_BANK                    (IO_VIRT + 0x7A000)
+#define DRAM_DDRPHY_CHB_BANK                    (IO_VIRT + 0x7B000)
+#define DRAM_DDRPHY_CHC_BANK                    (IO_VIRT + 0x7C000)
+
+#define DRAM_BASE                               (IO_VIRT + 0x07000)
+#define DRAM_CHB_BASE                           (IO_VIRT + 0x0F000)
+#define DRAM_CHC_BASE                           (IO_VIRT + 0x15000)
+
+/* Bandwidth counter for agent0-15 */
+#define DRAMC_MON_AGENT_NUM                     (16)
+#define DRAMC_MON_AGENT_BASE(i)                 (i + 0x2C0)
+#define DRAMC_MON_AGENT0(i)                     (i + 0x2C0)
+#define DRAMC_MON_AGENT1(i)                     (i + 0x2C4)
+#define DRAMC_MON_AGENT2(i)                     (i + 0x2C8)
+#define DRAMC_MON_AGENT3(i)                     (i + 0x2CC)
+#define DRAMC_MON_AGENT4(i)                     (i + 0x2D0)
+#define DRAMC_MON_AGENT5(i)                     (i + 0x2D4)
+#define DRAMC_MON_AGENT6(i)                     (i + 0x2D8)
+#define DRAMC_MON_AGENT7(i)                     (i + 0x2DC)
+#define DRAMC_MON_AGENT8(i)                     (i + 0x2E0)
+#define DRAMC_MON_AGENT9(i)                     (i + 0x2E4)
+#define DRAMC_MON_AGENT10(i)                    (i + 0x2E8)
+#define DRAMC_MON_AGENT11(i)                    (i + 0x2EC)
+#define DRAMC_MON_AGENT12(i)                    (i + 0x2F0)
+#define DRAMC_MON_AGENT13(i)                    (i + 0x2F4)
+#define DRAMC_MON_AGENT14(i)                    (i + 0x2F8)
+#define DRAMC_MON_AGENT15(i)                    (i + 0x2FC)
+
+/* total latency counter for agent0-15 */
+#define DRAMC_MON_LTY_AGENT_NUM                 (16)
+#define DRAMC_MON_LTY_AGENT_BASE(i)             (i + 0x300)
+#define DRAMC_MON_LTY_AGENT0(i)                 (i + 0x300)
+#define DRAMC_MON_LTY_AGENT1(i)                 (i + 0x304)
+#define DRAMC_MON_LTY_AGENT2(i)                 (i + 0x308)
+#define DRAMC_MON_LTY_AGENT3(i)                 (i + 0x30C)
+#define DRAMC_MON_LTY_AGENT4(i)                 (i + 0x310)
+#define DRAMC_MON_LTY_AGENT5(i)                 (i + 0x314)
+#define DRAMC_MON_LTY_AGENT6(i)                 (i + 0x318)
+#define DRAMC_MON_LTY_AGENT7(i)                 (i + 0x31C)
+#define DRAMC_MON_LTY_AGENT8(i)                 (i + 0x320)
+#define DRAMC_MON_LTY_AGENT9(i)                 (i + 0x324)
+#define DRAMC_MON_LTY_AGENT10(i)                (i + 0x328)
+#define DRAMC_MON_LTY_AGENT11(i)                (i + 0x32C)
+#define DRAMC_MON_LTY_AGENT12(i)                (i + 0x330)
+#define DRAMC_MON_LTY_AGENT13(i)                (i + 0x334)
+#define DRAMC_MON_LTY_AGENT14(i)                (i + 0x338)
+#define DRAMC_MON_LTY_AGENT15(i)                (i + 0x33C)
+
+/* max latency counter for agent0-15 */
+#define DRAMC_MON_MAX_LTY_AGENT_NUM             (16)
+#define DRAMC_MON_MAX_LTY_AGENT_BASE(i)         (i + 0x340)
+#define DRAMC_MON_MAX_LTY_AGENT0(i)             (i + 0x340)
+#define DRAMC_MON_MAX_LTY_AGENT1(i)             (i + 0x340)
+#define DRAMC_MON_MAX_LTY_AGENT2(i)             (i + 0x344)
+#define DRAMC_MON_MAX_LTY_AGENT3(i)             (i + 0x344)
+#define DRAMC_MON_MAX_LTY_AGENT4(i)             (i + 0x348)
+#define DRAMC_MON_MAX_LTY_AGENT5(i)             (i + 0x348)
+#define DRAMC_MON_MAX_LTY_AGENT6(i)             (i + 0x34C)
+#define DRAMC_MON_MAX_LTY_AGENT7(i)             (i + 0x34C)
+#define DRAMC_MON_MAX_LTY_AGENT8(i)             (i + 0x350)
+#define DRAMC_MON_MAX_LTY_AGENT9(i)             (i + 0x350)
+#define DRAMC_MON_MAX_LTY_AGENT10(i)            (i + 0x354)
+#define DRAMC_MON_MAX_LTY_AGENT11(i)            (i + 0x354)
+#define DRAMC_MON_MAX_LTY_AGENT12(i)            (i + 0x358)
+#define DRAMC_MON_MAX_LTY_AGENT13(i)            (i + 0x358)
+#define DRAMC_MON_MAX_LTY_AGENT14(i)            (i + 0x35C)
+#define DRAMC_MON_MAX_LTY_AGENT15(i)            (i + 0x35C)
+
+struct dramc_desc_t {
+    int agent_id;
+    char name[DRAMC_MX_AGENT_NAMEBUF];
+};
+
+#define DRAMC_AGENT_TABLE \
+{ \
+	/* define channel A releationship between group and name */ \
+	{ \
+		{ \
+			/* GRP 1 */ \
+			{0, 	"00_audio"}, \
+			{1, 	"01_demux/gcpu/ddi"}, \
+			{2, 	"02_vbi/3d/tve"}, \
+			{3, 	"03_xpscaler_ip/tddc"}, \
+			{4, 	"04_none"}, \
+			{5, 	"05_audio_dsp_low1"}, \
+			{6, 	"06_2d_graph/2dgraph_cmd/irt_dma"}, \
+			{7, 	"07_ether/ci-spi/rs232"}, \
+			{8,	"08_demod_isdbt"}, \
+			{9, 	"09_usb2"}, \
+			{10, 	"10_usb3_d"}, \
+			{11,	"11_mmu"}, \
+			{12,	"12_arm11"}, \
+			{13,	"13_msdc/emmc"}, \
+			{14,	"14_gdma"}, \
+			{15,	"15_test0/audio_dsp0"}, \
+			{-1,	""} \
+		}, \
+		{ \
+			/* GRP 2 */ \
+			{16,	"16_nfi_dma/sfalsh_dma/lzhs/ci_spi"}, \
+			{17,	"17_ufozip"}, \
+			{18,	"18_usb3_c"}, \
+			{19,	"19_none"}, \
+			{20,	"20_none"}, \
+			{21,	"21_none"}, \
+			{22,	"22_none"}, \
+			{30,	"30_none"}, \
+			{-1,	""} \
+		}, \
+		{ \
+			/* GRP 3 */ \
+			{23,	"23_none"}, \
+			{24,	"24_none"}, \
+			{25,	"25_none"}, \
+			{26,	"26_none"}, \
+			{27,	"27_none"}, \
+			{28,	"28_none"}, \
+			{29,	"29_none"}, \
+			{31,	"31_none"}, \
+			{-1,	""} \
+		} \
+	} \
+}
+
+/* some marco and static inline function define */
+static inline int FIND_NEXT_AGENT(u64 mask, int agent)
+{
+    int idx = 0;
+    int mx_cnt = sizeof(u64)*8;
+    int temp_agent = agent + 1;
+
+    for (idx = 0 ; idx < mx_cnt; idx++,temp_agent++) {
+        if (mx_cnt == temp_agent)
+            temp_agent = 0;
+        if ((1ULL<<temp_agent) & mask)
+            break;
+    }
+    if (idx == mx_cnt)
+        return -1;
+    return temp_agent;
+}
+
+#define GROUP_ID(agent) ((agent <= 0xF)?1:((agent<=0x17)?2:3))
+#define GROUP1_MASK     (0xFFFF)
+
+
+/* function prototype */
+extern void DRAMC_Init(int chan);
+extern void DRAMC_MaxLtcyMode(int chan, int enable);
+extern void DRAMC_Enable(int chan, int group, int agent);
+extern void DRAMC_Disable(int chan);
+extern void DRAMC_Freeze(int chan);
+extern void DRAMC_ConfigTargetCount(int chan, u32 count);
+extern u32 DRAMC_GetMaxLtcy(int chan);
+extern u32 DRAMC_GetCycleCount(int chan, int group);
+extern u32 DRAMC_GetTotalCycleCount(int chan);
+extern u32 DRAMC_GetDramcFreq(void);
+extern int DRAMC_CheckCntIsOverFlow(u32 count);
+
+extern void DRAMC_WriteMode(int chan, int enable);
+extern void DRAMC_ReadMode(int chan, int enable);
+extern unsigned int DRAMC_GetEfuseValue(void);
+
+extern void DRAMC_GetGroup1AgentCounter(int chan, unsigned int* counter);
+extern void DRAMC_GetGroup1Latency(int chan, unsigned int* latency);
+extern void DRAMC_GetGroup1MaxLatency(int chan, unsigned int* max_ltcy);
+
+#endif
diff --git a/src/devtools/met-driver/4.19/mt2712/mtk_emi_bm.c b/src/devtools/met-driver/4.19/mt2712/mtk_emi_bm.c
new file mode 100644
index 0000000..d7ecb7c
--- /dev/null
+++ b/src/devtools/met-driver/4.19/mt2712/mtk_emi_bm.c
@@ -0,0 +1,1362 @@
+#include <linux/kernel.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/io.h>
+#include <asm/cacheflush.h>
+#include <asm/uaccess.h>
+#include <asm/io.h>
+//#include <mt-plat/sync_write.h>
+//#include <mt-plat/mtk_io.h>
+//#include "x_hal_io.h"
+//#include "x_hal_5381.h"
+#include "mtk_typedefs.h"
+#include "core_plf_init.h"
+#include "mtk_emi_bm.h"
+#include "mtk_dramc.h"
+#include "met_drv.h"
+#include "interface.h"
+
+#define IOMEM(x)	((void __force __iomem *)(x))
+
+#define mt_reg_sync_writel(v, a) \
+	do {    \
+		__raw_writel((v), (void __force __iomem *)((a)));   \
+		mb();  \
+	} while (0)
+
+#if 0
+#undef  MET_BOOT_MSG
+#if     defined(MET_BOOT_MSG)
+extern char met_boot_msg_tmp[256];
+extern int pr_bootmsg(int str_len, char *str);
+#define PR_BOOTMSG(fmt, args...) { \
+        int str_len = snprintf(met_boot_msg_tmp, sizeof(met_boot_msg_tmp), \
+                               fmt, ##args); \
+        pr_bootmsg(str_len, met_boot_msg_tmp); }
+#define PR_BOOTMSG_ONCE(fmt, args...) { \
+        static int once; \
+        if (!once) { \
+                int str_len = snprintf(met_boot_msg_tmp, \
+                                       sizeof(met_boot_msg_tmp), \
+                                       fmt, ##args); \
+                pr_bootmsg(str_len, met_boot_msg_tmp); \
+                once = 1; \
+        } }
+#else
+#define pr_bootmsg(str_len, str)
+#define PR_BOOTMSG(fmt, args...)
+#define PR_BOOTMSG_ONCE(fmt, args...)
+#endif
+#endif
+
+//static unsigned long gEMIBaseAddr = EMI_REG_BASE;
+
+
+/* MET internal define */
+#ifndef DRAMC0_NAO_BASE
+#define DRAMC0_NAO_BASE             (IO_VIRT + 0x204000)
+#endif
+#ifndef DRAMC1_NAO_BASE
+#define DRAMC1_NAO_BASE             (IO_VIRT + 0x20C000)
+#endif
+#ifndef DRAMC2_NAO_BASE
+#define DRAMC2_NAO_BASE             (IO_VIRT + 0x214000)
+#endif
+#ifndef DRAMC3_NAO_BASE
+#define DRAMC3_NAO_BASE             (IO_VIRT + 0x21C000)
+#endif
+static unsigned long gDRAMCBaseAddr[] =
+{
+	DRAMC0_NAO_BASE,
+	DRAMC1_NAO_BASE,
+	DRAMC2_NAO_BASE,
+	DRAMC3_NAO_BASE,
+};
+
+#define MX_DRAMC_CHAN_NR (sizeof(gDRAMCBaseAddr)/sizeof(gDRAMCBaseAddr[0]))
+
+/* GET EMI Base Address */
+#if 0
+unsigned long get_emi_base_addr(void)
+{
+	return gEMIBaseAddr;
+}
+#endif
+
+unsigned long get_dram_nao_base_addr(int idx)
+{
+	if (idx >= MX_DRAMC_CHAN_NR) {
+		METERROR("Invalid DRAMC idx: %d !\n", idx);
+		PR_BOOTMSG_ONCE("Invalid DRAMC idx: %d !\n", idx);
+		return 0;
+	}
+	return gDRAMCBaseAddr[idx];
+}
+
+#undef	DEBUG
+#undef	debug_reg
+
+#ifdef	debug_reg
+static inline unsigned int emi_readl(void __iomem *padr)
+{
+	unsigned int tmp;
+
+	tmp = readl(padr);
+	MET_TRACE("[MET_EMI] RD_Reg: %p: %08x\n", padr, tmp);
+	return tmp;
+}
+
+static inline void __emi_reg_sync_writel(unsigned int data, void __iomem *padr)
+{
+	unsigned int tmp;
+
+	mt_reg_sync_writel(data, padr);
+	tmp = readl(padr);
+	MET_TRACE("[MET_EMI] WR_Reg: %p: %08x, %08x\n", padr, data, tmp);
+}
+
+#define emi_reg_sync_writel(data, adr)	__emi_reg_sync_writel(data, IOMEM(adr))
+
+#else
+#define	emi_readl		readl
+#define	emi_reg_sync_writel	mt_reg_sync_writel
+#endif
+
+#define MASK_MASTER	0xFF
+#define MASK_TRANS_TYPE	0xFF
+
+static void __iomem *BaseAddrEMI;
+static void __iomem *BaseAddrDRAMC0;
+static void __iomem *BaseAddrDRAMC1;
+static void __iomem *BaseAddrDRAMC2;
+static void __iomem *BaseAddrDRAMC3;
+static void __iomem *AddrDramDatarate;
+
+/*
+*   MET_REG_BSET/MET_REG_BCLR:
+*   reading value before set and clear
+*/
+static inline void MET_REG_BSET(unsigned long reg, u32 shift)
+{
+	volatile unsigned int read_val = 0;
+
+	read_val = emi_readl(IOMEM(reg));
+	emi_reg_sync_writel(read_val | (1<<shift), reg);
+}
+
+static inline void MET_REG_BCLR(unsigned long reg, u32 shift)
+{
+	volatile unsigned int read_val = 0;
+
+	read_val = emi_readl(IOMEM(reg));
+	emi_reg_sync_writel(read_val & (~((1<<shift) & 0xFFFFFFFF)), reg);
+}
+
+#define GET_EMI_FROM_DTS
+//#undef GET_EMI_FROM_DTS
+
+int MET_BM_Init(void)
+{
+#ifdef GET_EMI_FROM_DTS
+	struct device_node *node;
+
+	/* Device Tree Path */
+	node = of_find_compatible_node(NULL, NULL, of_emi_desc);
+	if (!node) {
+		METINFO("node of_emi_desc not found\n");
+		PR_BOOTMSG_ONCE("node of_emi_desc not found\n");
+		return -1;
+	}
+
+	BaseAddrEMI = (void *)of_iomap(node, 0);
+#else
+	BaseAddrEMI = (void*)get_emi_base_addr();
+#endif
+	if (BaseAddrEMI == 0) {
+		METERROR("BaseAddrEMI = 0\n");
+		PR_BOOTMSG_ONCE("BaseAddrEMI = 0\n");
+		return -1;
+	}
+	METINFO("MET EMI: map emi to %p\n", BaseAddrEMI);
+	PR_BOOTMSG("MET EMI: map emi to %p\n", BaseAddrEMI);
+
+#ifdef GET_EMI_FROM_DTS
+	/* DRAMC TBD */
+	/* MAP NAO (NON-AO) DRAMC address */
+	node = of_find_compatible_node(NULL, NULL, of_dramc_desc);
+	if (!node) {
+		METINFO("node of_dramc_desc not found\n");
+		PR_BOOTMSG_ONCE("node of_dramc_desc not found\n");
+		return -1;
+	}
+	BaseAddrDRAMC0 = (void *)of_iomap(node, 0);
+	BaseAddrDRAMC1 = (void *)of_iomap(node, 1);
+	BaseAddrDRAMC2 = (void *)of_iomap(node, 2);
+	BaseAddrDRAMC3 = (void *)of_iomap(node, 3);
+#else
+	BaseAddrDRAMC0 = (void *) get_dram_nao_base_addr(0);
+	BaseAddrDRAMC1 = (void *) get_dram_nao_base_addr(1);
+	BaseAddrDRAMC2 = (void *) get_dram_nao_base_addr(2);
+	BaseAddrDRAMC3 = (void *) get_dram_nao_base_addr(3);
+#endif
+
+	AddrDramDatarate = ioremap_nocache(0x10100000, 0x1000);
+	if (AddrDramDatarate == NULL) {
+		pr_err("ioremap AddrDramDatarate fail ...\n");
+		return -1;
+	}
+
+	if (BaseAddrDRAMC0 == 0
+		|| BaseAddrDRAMC1 == 0
+		|| BaseAddrDRAMC2 == 0
+		|| BaseAddrDRAMC3 == 0
+	) {
+		METERROR("BaseAddrDRAMC0 = %p\n", BaseAddrDRAMC0);
+		METERROR("BaseAddrDRAMC1 = %p\n", BaseAddrDRAMC1);
+		METERROR("BaseAddrDRAMC2 = %p\n", BaseAddrDRAMC2);
+		METERROR("BaseAddrDRAMC3 = %p\n", BaseAddrDRAMC3);
+		PR_BOOTMSG_ONCE("BaseAddrDRAMC0 = %p\n", BaseAddrDRAMC0);
+		PR_BOOTMSG_ONCE("BaseAddrDRAMC1 = %p\n", BaseAddrDRAMC1);
+		PR_BOOTMSG_ONCE("BaseAddrDRAMC2 = %p\n", BaseAddrDRAMC2);
+		PR_BOOTMSG_ONCE("BaseAddrDRAMC3 = %p\n", BaseAddrDRAMC3);
+		return -1;
+	}
+	METINFO("MET EMI: map nao dramcA to %p\n", BaseAddrDRAMC0);
+	METINFO("MET EMI: map nao dramcB to %p\n", BaseAddrDRAMC1);
+	METINFO("MET EMI: map nao dramcC to %p\n", BaseAddrDRAMC2);
+	METINFO("MET EMI: map nao dramcD to %p\n", BaseAddrDRAMC3);
+	PR_BOOTMSG("MET EMI: map nao dramcA to %p\n", BaseAddrDRAMC0);
+	PR_BOOTMSG("MET EMI: map nao dramcB to %p\n", BaseAddrDRAMC1);
+	PR_BOOTMSG("MET EMI: map nao dramcC to %p\n", BaseAddrDRAMC2);
+	PR_BOOTMSG("MET EMI: map nao dramcD to %p\n", BaseAddrDRAMC3);
+	return 0;
+}
+
+void MET_BM_DeInit(void)
+{
+}
+
+void MET_BM_Enable(const unsigned int enable)
+{
+	volatile unsigned long int value_check;
+	int i = 0;
+
+	while (i < 100) {
+
+		if (enable == 0) {
+			/* SET BIT IDLE */
+			MET_REG_BSET(ADDR_EMI+EMI_BMEN, BUS_MON_IDLE_SHIFT);
+
+			/* CLR BIT EN */
+			MET_REG_BCLR(ADDR_EMI+EMI_BMEN, BUS_MON_EN_SHIFT);
+
+			/* CLR BIT IDLE */
+			MET_REG_BCLR(ADDR_EMI+EMI_BMEN, BUS_MON_IDLE_SHIFT);
+		} else {
+			/* CLR BIT IDLE */
+			MET_REG_BCLR(ADDR_EMI+EMI_BMEN, BUS_MON_IDLE_SHIFT);
+
+			/* SET BIT EN */
+			MET_REG_BSET(ADDR_EMI+EMI_BMEN, BUS_MON_EN_SHIFT);
+		}
+
+		value_check = emi_readl(IOMEM(ADDR_EMI+EMI_BMEN));
+
+		if (enable == 0) {
+			/* EN == 0, IDLE == 0 when EMI RESET*/
+			if (!test_bit(BUS_MON_EN_SHIFT, &value_check)
+					&& !test_bit(BUS_MON_IDLE_SHIFT, &value_check)) {
+				break;
+			}
+		} else {
+			/* EN == 1, IDLE == 0 when EMI START*/
+			if (test_bit(BUS_MON_EN_SHIFT, &value_check)
+					&& !test_bit(BUS_MON_IDLE_SHIFT, &value_check)) {
+				break;
+			}
+		}
+		i++;
+	}
+	for (i = 0; i < MX_DRAMC_CHAN_NR; i++) {
+		if (enable == 0)
+			/* Disable Dramc Bus Monitor */
+			MET_DRAMC_BMEnable(i, 0);
+		else
+			/* Enable Dramc Bus Monitor */
+			MET_DRAMC_BMEnable(i, 1);
+	}
+	/*MET_TRACE("[MET_BM_ENABLE] value_check: %lx, enable = %d\n", value_check, enable);*/
+
+}
+
+void MET_BM_Pause(void)
+{
+	int i = 0;
+	const unsigned int value = emi_readl(IOMEM(ADDR_EMI+EMI_BMEN));
+
+	emi_reg_sync_writel(value | (1<<BUS_MON_PAUSE_SHIFT), ADDR_EMI+EMI_BMEN);
+	for (i = 0; i < MX_DRAMC_CHAN_NR; i++) {
+		/* Pause Dramc Bus Monitor */
+		MET_DRAMC_BMPause(i, 1);
+	}
+}
+
+void MET_BM_Continue(void)
+{
+	int i = 0;
+	const unsigned int value = emi_readl(IOMEM(ADDR_EMI+EMI_BMEN));
+
+	emi_reg_sync_writel(value & (~(1<<BUS_MON_PAUSE_SHIFT)), ADDR_EMI+EMI_BMEN);
+	for (i = 0; i < MX_DRAMC_CHAN_NR; i++) {
+		/* Enable Dramc Bus Monitor */
+		MET_DRAMC_BMPause(i, 0);
+	}
+}
+
+unsigned int MET_BM_IsOverrun(void)
+{
+	/*
+	 * return 0 if EMI_BCNT(bus cycle counts) or
+	 * EMI_WACT(total word counts) is overrun,
+	 * otherwise return an !0 value
+	 */
+	const unsigned int value = emi_readl(IOMEM(ADDR_EMI+EMI_BMEN));
+
+	return (value & (1<<BC_OVERRUN_SHIFT));
+}
+
+unsigned int MET_BM_GetReadWriteType(void)
+{
+	const unsigned int value = emi_readl(IOMEM(ADDR_EMI+EMI_BMEN));
+
+	return ((value & 0xFFFFFFCF) >> 4);
+}
+
+void MET_BM_SetReadWriteType(const unsigned int ReadWriteType)
+{
+	const unsigned int value = emi_readl(IOMEM(ADDR_EMI+EMI_BMEN));
+
+	/*
+	 * ReadWriteType: 00/11 --> both R/W
+	 *                   01 --> only R
+	 *                   10 --> only W
+	 */
+	emi_reg_sync_writel((value & 0xFFFFFFCF) | (ReadWriteType << 4), ADDR_EMI+EMI_BMEN);
+}
+
+int MET_BM_GetBusCycCount(void)
+{
+	return MET_BM_IsOverrun() ? BM_ERR_OVERRUN : emi_readl(IOMEM(ADDR_EMI+EMI_BCNT));/*Bus cycle counter*/
+}
+
+unsigned int MET_BM_GetTransAllCount(void)
+{
+	return emi_readl(IOMEM(ADDR_EMI+EMI_TACT));
+}
+
+int MET_BM_GetTransCount(const unsigned int counter_num)
+{
+	unsigned int iCount;
+
+	switch (counter_num) {
+	case 1:
+		iCount = emi_readl(IOMEM(ADDR_EMI+EMI_TSCT));
+		break;
+
+	case 2:
+		iCount = emi_readl(IOMEM(ADDR_EMI+EMI_TSCT2));
+		break;
+
+	case 3:
+		iCount = emi_readl(IOMEM(ADDR_EMI+EMI_TSCT3));
+		break;
+
+	default:
+		return BM_ERR_WRONG_REQ;
+	}
+
+	return iCount;
+}
+
+int MET_BM_GetWordAllCount(void)
+{
+	return MET_BM_IsOverrun() ? BM_ERR_OVERRUN : emi_readl(IOMEM(ADDR_EMI+EMI_WACT));
+}
+
+int MET_BM_GetWordCount(const unsigned int counter_num)
+{
+	unsigned int iCount;
+
+	switch (counter_num) {
+	case 1:
+		iCount = emi_readl(IOMEM(ADDR_EMI+EMI_WSCT));
+		break;
+
+	case 2:
+		iCount = emi_readl(IOMEM(ADDR_EMI+EMI_WSCT2));
+		break;
+
+	case 3:
+		iCount = emi_readl(IOMEM(ADDR_EMI+EMI_WSCT3));
+		break;
+
+	case 4:
+		iCount = emi_readl(IOMEM(ADDR_EMI+EMI_WSCT4));
+		break;
+
+	default:
+		return BM_ERR_WRONG_REQ;
+	}
+
+	return iCount;
+}
+
+unsigned int MET_BM_GetBandwidthWordCount(void)
+{
+	return emi_readl(IOMEM(ADDR_EMI+EMI_BACT));/*Bandwidth counter for access*/
+}
+
+unsigned int MET_BM_GetOverheadWordCount(void)
+{
+	return emi_readl(IOMEM(ADDR_EMI+EMI_BSCT));/*Overhead counter*/
+}
+
+int MET_BM_GetTransTypeCount(const unsigned int counter_num)
+{
+	return (counter_num < 1 || counter_num > BM_COUNTER_MAX)
+		? BM_ERR_WRONG_REQ : emi_readl(IOMEM(ADDR_EMI+EMI_TTYPE1 + (counter_num - 1) * 8));
+}
+
+int MET_BM_GetMDCT(void)
+{
+	return emi_readl(IOMEM(ADDR_EMI+EMI_MDCT));
+}
+
+int MET_BM_GetMDCT_2(void)
+{
+	return emi_readl(IOMEM(ADDR_EMI+EMI_MDCT_2ND));
+}
+
+int MET_BM_SetMDCT_MDMCU(unsigned int mdmcu_rd_buf)
+{
+	volatile unsigned int value_origin;
+
+	value_origin = emi_readl(IOMEM(ADDR_EMI+EMI_MDCT_2ND));
+	MET_TRACE("[MET_BM_SetMDCT_MDMCU] value_origin: %x\n", value_origin);
+
+	value_origin = value_origin & ~(0x7);
+	value_origin = value_origin | ((mdmcu_rd_buf)&0x7);
+
+	emi_reg_sync_writel(value_origin, ADDR_EMI+EMI_MDCT_2ND);
+
+	return BM_REQ_OK;
+}
+
+
+int MET_BM_GetMonitorCounter(const unsigned int counter_num,
+		unsigned int *master,
+		unsigned int *trans_type)
+{
+	unsigned int value, addr;
+
+	if (counter_num < 1 || counter_num > BM_COUNTER_MAX)
+		return BM_ERR_WRONG_REQ;
+
+
+	if (counter_num == 1) {
+		addr = EMI_BMEN;
+		value = emi_readl(IOMEM(ADDR_EMI+addr));
+		*master = (value>>16) & MASK_MASTER;
+		*trans_type = (value>>24) & MASK_TRANS_TYPE;
+	} else {
+		addr = (counter_num <= 3) ?
+			EMI_MSEL : (EMI_MSEL2 + (counter_num / 2 - 2) * 8);
+		value = emi_readl(IOMEM(ADDR_EMI+addr)) >> ((counter_num % 2) * 16);
+		*master = value & MASK_MASTER;
+		*trans_type = (value>>8) & MASK_TRANS_TYPE;
+	}
+
+	return BM_REQ_OK;
+}
+
+int MET_BM_SetMonitorCounter(const unsigned int counter_num,
+		const unsigned int master,
+		const unsigned int trans_type)
+{
+	unsigned int value, addr;
+	const unsigned int iMask = (MASK_TRANS_TYPE << 8) | MASK_MASTER;
+
+	if (counter_num < 1 || counter_num > BM_COUNTER_MAX)
+		return BM_ERR_WRONG_REQ;
+
+
+	if (counter_num == 1) {
+		addr = EMI_BMEN;
+		value = (emi_readl(IOMEM(ADDR_EMI+addr)) & ~(iMask << 16)) |
+			((trans_type & MASK_TRANS_TYPE) << 24) |
+			((master & MASK_MASTER) << 16);
+	} else {
+		addr = (counter_num <= 3) ?
+			EMI_MSEL : (EMI_MSEL2 + (counter_num / 2 - 2) * 8);
+
+		/* clear master and transaction type fields */
+		value = emi_readl(IOMEM(ADDR_EMI+addr)) & ~(iMask << ((counter_num % 2) * 16));
+
+		/* set master and transaction type fields */
+		value |= (((trans_type & MASK_TRANS_TYPE) << 8) |
+				(master & MASK_MASTER)) <<
+			((counter_num % 2) * 16);
+	}
+
+	emi_reg_sync_writel(value, ADDR_EMI+addr);
+
+	return BM_REQ_OK;
+}
+
+int MET_BM_SetTtypeCounterRW(unsigned int bmrw0_val, unsigned int bmrw1_val)
+{
+
+	volatile unsigned int value_origin;
+
+	value_origin = emi_readl(IOMEM(ADDR_EMI+EMI_BMRW0));
+	MET_TRACE("[MET_EMI_settype1] value_origin: %x\n", value_origin);
+	if (value_origin != bmrw0_val) {
+		emi_reg_sync_writel(bmrw0_val, ADDR_EMI+EMI_BMRW0);
+		MET_TRACE("[MET_EMI_settype1] bmrw0_val: %x, value_origin: %x\n", bmrw0_val, value_origin);
+	}
+
+
+	value_origin = emi_readl(IOMEM(ADDR_EMI+EMI_BMRW1));
+	MET_TRACE("[MET_EMI_settype2] value_origin: %x\n", value_origin);
+	if (value_origin != bmrw1_val) {
+		emi_reg_sync_writel(bmrw1_val, ADDR_EMI+EMI_BMRW1);
+		MET_TRACE("[MET_EMI_settype2] bmrw0_val: %x, value_origin: %x\n", bmrw1_val, value_origin);
+
+	}
+	return BM_REQ_OK;
+}
+
+int MET_BM_Set_WsctTsct_id_sel(unsigned int counter_num, unsigned int enable)
+{
+	unsigned int value;
+
+	if (counter_num > 3)
+		return BM_ERR_WRONG_REQ;
+
+	value = ((emi_readl(IOMEM(ADDR_EMI+EMI_BMEN2)) & (~(1 << (28+counter_num)))) | (enable << (28+counter_num)));
+	emi_reg_sync_writel(value, ADDR_EMI+EMI_BMEN2);
+
+	return BM_REQ_OK;
+}
+
+int MET_BM_SetMaster(const unsigned int counter_num, const unsigned int master)
+{
+	unsigned int value, addr;
+	const unsigned int iMask = 0x7F;
+
+	if (counter_num < 1 || counter_num > BM_COUNTER_MAX)
+		return BM_ERR_WRONG_REQ;
+
+
+	if (counter_num == 1) {
+		addr = EMI_BMEN;
+		value = (emi_readl(IOMEM(ADDR_EMI+addr)) & ~(iMask << 16)) | ((master & iMask) << 16);
+	} else {
+		addr = (counter_num <= 3) ? EMI_MSEL : (EMI_MSEL2 + (counter_num / 2 - 2) * 8);
+
+		/* clear master and transaction type fields */
+		value = emi_readl(IOMEM(ADDR_EMI+addr)) & ~(iMask << ((counter_num % 2) * 16));
+
+		/* set master and transaction type fields */
+		value |= ((master & iMask) << ((counter_num % 2) * 16));
+	}
+
+	emi_reg_sync_writel(value, ADDR_EMI+addr);
+
+	return BM_REQ_OK;
+}
+
+int MET_BM_SetIDSelect(const unsigned int counter_num,
+		const unsigned int id,
+		const unsigned int enable)
+{
+	unsigned int value, addr, shift_num;
+
+	if (enable == 0) {
+
+		value = (emi_readl(IOMEM(ADDR_EMI+EMI_BMEN2))
+				& ~(1 << (counter_num - 1)));
+
+
+		emi_reg_sync_writel(value, ADDR_EMI+EMI_BMEN2);
+		return BM_REQ_OK;
+	}
+
+	if ((counter_num < 1 || counter_num > BM_COUNTER_MAX) || (id > EMI_BMID_MASK) || (enable > 1))
+		return BM_ERR_WRONG_REQ;
+
+
+	addr = EMI_BMID0 + (counter_num - 1) / 2 * 4;
+
+	/* field's offset in the target EMI_BMIDx register */
+	shift_num = ((counter_num - 1) % 2) * 16;
+
+	/* clear SELx_ID field */
+	value = emi_readl(IOMEM(ADDR_EMI+addr)) & ~(EMI_BMID_MASK << shift_num);
+
+	/* set SELx_ID field */
+	value |= id << shift_num;
+
+	emi_reg_sync_writel(value, ADDR_EMI+addr);
+
+	value = (emi_readl(IOMEM(ADDR_EMI+EMI_BMEN2))
+		& ~(1 << (counter_num - 1)))
+		| (enable << (counter_num - 1));
+
+	emi_reg_sync_writel(value, ADDR_EMI+EMI_BMEN2);
+
+	return BM_REQ_OK;
+}
+
+int MET_BM_SetUltraHighFilter(const unsigned int counter_num, const unsigned int enable)
+{
+	unsigned int value;
+
+	if ((counter_num < 1 || counter_num > BM_COUNTER_MAX) || (enable > 1))
+		return BM_ERR_WRONG_REQ;
+
+
+	value = (emi_readl(IOMEM(ADDR_EMI+EMI_BMEN1))
+		& ~(1 << (counter_num - 1)))
+		| (enable << (counter_num - 1));
+
+	emi_reg_sync_writel(value, ADDR_EMI+EMI_BMEN1);
+
+	return BM_REQ_OK;
+}
+
+int MET_BM_SetLatencyCounter(unsigned int enable)
+{
+	unsigned int value;
+
+	value = emi_readl(IOMEM(ADDR_EMI+EMI_BMEN2)) & ~(0x3 << 24);
+	/*
+	 * emi_ttype1 -- emi_ttype8 change as total latencies
+	 * for m0 -- m7,
+	 * and emi_ttype9 -- emi_ttype16 change as total transaction counts
+	 * for m0 -- m7
+	 */
+	if (enable == 1)
+		value |= (0x2 << 24);
+
+	emi_reg_sync_writel(value, ADDR_EMI+EMI_BMEN2);
+
+	return BM_REQ_OK;
+}
+
+int MET_BM_GetLatencyCycle(const unsigned int counter_num)
+{
+	unsigned int cycle_count;
+
+	switch (counter_num) {
+	case 1:
+		cycle_count = emi_readl(IOMEM(ADDR_EMI+EMI_TTYPE1));
+		break;
+	case 2:
+		cycle_count = emi_readl(IOMEM(ADDR_EMI+EMI_TTYPE2));
+		break;
+	case 3:
+		cycle_count = emi_readl(IOMEM(ADDR_EMI+EMI_TTYPE3));
+		break;
+	case 4:
+		cycle_count = emi_readl(IOMEM(ADDR_EMI+EMI_TTYPE4));
+		break;
+	case 5:
+		cycle_count = emi_readl(IOMEM(ADDR_EMI+EMI_TTYPE5));
+		break;
+	case 6:
+		cycle_count = emi_readl(IOMEM(ADDR_EMI+EMI_TTYPE6));
+		break;
+	case 7:
+		cycle_count = emi_readl(IOMEM(ADDR_EMI+EMI_TTYPE7));
+		break;
+	case 8:
+		cycle_count = emi_readl(IOMEM(ADDR_EMI+EMI_TTYPE8));
+		break;
+	case 9:
+		cycle_count = emi_readl(IOMEM(ADDR_EMI+EMI_TTYPE9));
+		break;
+	case 10:
+		cycle_count = emi_readl(IOMEM(ADDR_EMI+EMI_TTYPE10));
+		break;
+	case 11:
+		cycle_count = emi_readl(IOMEM(ADDR_EMI+EMI_TTYPE11));
+		break;
+	case 12:
+		cycle_count = emi_readl(IOMEM(ADDR_EMI+EMI_TTYPE12));
+		break;
+	case 13:
+		cycle_count = emi_readl(IOMEM(ADDR_EMI+EMI_TTYPE13));
+		break;
+	case 14:
+		cycle_count = emi_readl(IOMEM(ADDR_EMI+EMI_TTYPE14));
+		break;
+	case 15:
+		cycle_count = emi_readl(IOMEM(ADDR_EMI+EMI_TTYPE15));
+		break;
+	case 16:
+		cycle_count = emi_readl(IOMEM(ADDR_EMI+EMI_TTYPE16));
+		break;
+	case 17:
+		cycle_count = emi_readl(IOMEM(ADDR_EMI+EMI_TTYPE17));
+		break;
+	case 18:
+		cycle_count = emi_readl(IOMEM(ADDR_EMI+EMI_TTYPE18));
+		break;
+	case 19:
+		cycle_count = emi_readl(IOMEM(ADDR_EMI+EMI_TTYPE19));
+		break;
+	case 20:
+		cycle_count = emi_readl(IOMEM(ADDR_EMI+EMI_TTYPE20));
+		break;
+	case 21:
+		cycle_count = emi_readl(IOMEM(ADDR_EMI+EMI_TTYPE21));
+		break;
+	default:
+		return BM_ERR_WRONG_REQ;
+	}
+
+	return cycle_count;
+}
+
+unsigned int MET_BM_GetEmiDcm(void)
+{
+	return (emi_readl(IOMEM(ADDR_EMI+EMI_CONM))>>24);
+}
+
+int MET_BM_SetEmiDcm(const unsigned int setting)
+{
+	unsigned int value;
+
+	value = emi_readl(IOMEM(ADDR_EMI+EMI_CONM));
+	emi_reg_sync_writel((value & 0x00FFFFFF) | (setting << 24), ADDR_EMI+EMI_CONM);
+
+	return BM_REQ_OK;
+}
+
+/* DRAMC */
+unsigned int MET_DRAMC_GetPageHitCount(DRAMC_Cnt_Type CountType, int chann)
+{
+	unsigned int iCount;
+	unsigned long addr_base = ADDR_DRAMC0;
+
+	if (chann == 0)
+		addr_base = ADDR_DRAMC0;
+	else if (chann == 1)
+		addr_base = ADDR_DRAMC1;
+	else if (chann == 2)
+		addr_base = ADDR_DRAMC2;
+	else if (chann == 3)
+		addr_base = ADDR_DRAMC3;
+	else
+		return BM_ERR_WRONG_REQ;
+
+
+	switch (CountType) {
+	case DRAMC_R2R:
+		iCount = emi_readl(IOMEM(addr_base+DRAMC_R2R_PAGE_HIT));
+		break;
+	case DRAMC_R2W:
+		iCount = emi_readl(IOMEM(addr_base+DRAMC_R2W_PAGE_HIT));
+		break;
+	case DRAMC_W2R:
+		iCount = emi_readl(IOMEM(addr_base+DRAMC_W2R_PAGE_HIT));
+		break;
+	case DRAMC_W2W:
+		iCount = emi_readl(IOMEM(addr_base+DRAMC_W2W_PAGE_HIT));
+		break;
+	case DRAMC_ALL:
+		iCount = emi_readl(IOMEM(addr_base+DRAMC_R2R_PAGE_HIT)) +
+			 emi_readl(IOMEM(addr_base+DRAMC_R2W_PAGE_HIT)) +
+			 emi_readl(IOMEM(addr_base+DRAMC_W2R_PAGE_HIT)) +
+			 emi_readl(IOMEM(addr_base+DRAMC_W2W_PAGE_HIT));
+		break;
+	default:
+		return BM_ERR_WRONG_REQ;
+	}
+
+	return iCount;
+}
+
+unsigned int MET_DRAMC_GetPageMissCount(DRAMC_Cnt_Type CountType, int chann)
+{
+	unsigned int iCount;
+	unsigned long addr_base = ADDR_DRAMC0;
+
+	if (chann == 0)
+		addr_base = ADDR_DRAMC0;
+	else if (chann == 1)
+		addr_base = ADDR_DRAMC1;
+	else if (chann == 2)
+		addr_base = ADDR_DRAMC2;
+	else if (chann == 3)
+		addr_base = ADDR_DRAMC3;
+	else
+		return BM_ERR_WRONG_REQ;
+
+
+	switch (CountType) {
+	case DRAMC_R2R:
+		iCount = emi_readl(IOMEM(addr_base+DRAMC_R2R_PAGE_MISS));
+		break;
+	case DRAMC_R2W:
+		iCount = emi_readl(IOMEM(addr_base+DRAMC_R2W_PAGE_MISS));
+		break;
+	case DRAMC_W2R:
+		iCount = emi_readl(IOMEM(addr_base+DRAMC_W2R_PAGE_MISS));
+		break;
+	case DRAMC_W2W:
+		iCount = emi_readl(IOMEM(addr_base+DRAMC_W2W_PAGE_MISS));
+		break;
+	case DRAMC_ALL:
+		iCount = emi_readl(IOMEM(addr_base+DRAMC_R2R_PAGE_MISS)) +
+			 emi_readl(IOMEM(addr_base+DRAMC_R2W_PAGE_MISS)) +
+			 emi_readl(IOMEM(addr_base+DRAMC_W2R_PAGE_MISS)) +
+			 emi_readl(IOMEM(addr_base+DRAMC_W2W_PAGE_MISS));
+		break;
+	default:
+		return BM_ERR_WRONG_REQ;
+	}
+
+	return iCount;
+}
+
+unsigned int MET_DRAMC_GetInterbankCount(DRAMC_Cnt_Type CountType, int chann)
+{
+	unsigned int iCount;
+	unsigned long addr_base = ADDR_DRAMC0;
+
+	if (chann == 0)
+		addr_base = ADDR_DRAMC0;
+	else if (chann == 1)
+		addr_base = ADDR_DRAMC1;
+	else if (chann == 2)
+		addr_base = ADDR_DRAMC2;
+	else if (chann == 3)
+		addr_base = ADDR_DRAMC3;
+	else
+		return BM_ERR_WRONG_REQ;
+
+
+	switch (CountType) {
+	case DRAMC_R2R:
+		iCount = emi_readl(IOMEM(addr_base+DRAMC_R2R_INTERBANK));
+		break;
+	case DRAMC_R2W:
+		iCount = emi_readl(IOMEM(addr_base+DRAMC_R2W_INTERBANK));
+		break;
+	case DRAMC_W2R:
+		iCount = emi_readl(IOMEM(addr_base+DRAMC_W2R_INTERBANK));
+		break;
+	case DRAMC_W2W:
+		iCount = emi_readl(IOMEM(addr_base+DRAMC_W2W_INTERBANK));
+		break;
+	case DRAMC_ALL:
+		iCount = emi_readl(IOMEM(addr_base+DRAMC_R2R_INTERBANK)) +
+			 emi_readl(IOMEM(addr_base+DRAMC_R2W_INTERBANK)) +
+			 emi_readl(IOMEM(addr_base+DRAMC_W2R_INTERBANK)) +
+			 emi_readl(IOMEM(addr_base+DRAMC_W2W_INTERBANK));
+		break;
+	default:
+		return BM_ERR_WRONG_REQ;
+	}
+
+	return iCount;
+}
+
+unsigned int MET_DRAMC_GetIdleCount(int chann)
+{
+	unsigned long addr_base = ADDR_DRAMC0;
+
+	if (chann == 0)
+		addr_base = ADDR_DRAMC0;
+	else if (chann == 1)
+		addr_base = ADDR_DRAMC1;
+	else if (chann == 2)
+		addr_base = ADDR_DRAMC2;
+	else if (chann == 3)
+		addr_base = ADDR_DRAMC3;
+	else
+		return BM_ERR_WRONG_REQ;
+
+	return emi_readl(IOMEM(addr_base+DRAMC_IDLE_COUNT));
+}
+
+unsigned int MET_DRAMC_Misc_Status(int chann)
+{
+	unsigned long addr_base = ADDR_DRAMC0;
+
+	if (chann == 0)
+		addr_base = ADDR_DRAMC0;
+	else if (chann == 1)
+		addr_base = ADDR_DRAMC1;
+	else if (chann == 2)
+		addr_base = ADDR_DRAMC2;
+	else if (chann == 3)
+		addr_base = ADDR_DRAMC3;
+	else
+		return BM_ERR_WRONG_REQ;
+
+	return emi_readl(IOMEM(addr_base+DRAMC_MISC_STATUSA));
+}
+
+int MET_DRAMC_BMPause(int chann, int set)
+{
+	unsigned long addr_base = ADDR_DRAMC0;
+	unsigned int value;
+
+	if (chann == 0)
+		addr_base = ADDR_DRAMC0;
+	else if (chann == 1)
+		addr_base = ADDR_DRAMC1;
+	else if (chann == 2)
+		addr_base = ADDR_DRAMC2;
+	else if (chann == 3)
+		addr_base = ADDR_DRAMC3;
+	else
+		return BM_ERR_WRONG_REQ;
+
+	value = emi_readl(IOMEM(addr_base+DRAMC_DMMONITOR));
+	if (set == 0) {
+		/* Continue DRAMC Monitor*/
+		value = (value & 0xFFFFFFFB);
+	}
+	else if (set == 1) {
+		/* Pause DRAMC Monitor*/
+		value = (value | 0x00000004);
+	}
+	else
+		return BM_ERR_WRONG_REQ;
+	emi_reg_sync_writel(value, addr_base+DRAMC_DMMONITOR);
+
+	return BM_REQ_OK;
+}
+
+
+int MET_DRAMC_BMEnable(int chann, int set)
+{
+	unsigned long addr_base = ADDR_DRAMC0;
+	unsigned int value;
+
+	if (chann == 0)
+		addr_base = ADDR_DRAMC0;
+	else if (chann == 1)
+		addr_base = ADDR_DRAMC1;
+	else if (chann == 2)
+		addr_base = ADDR_DRAMC2;
+	else if (chann == 3)
+		addr_base = ADDR_DRAMC3;
+	else
+		return BM_ERR_WRONG_REQ;
+
+	value = emi_readl(IOMEM(addr_base+DRAMC_DMMONITOR));
+	if (set == 0) {
+		/* Disable DRAMC Monitor*/
+		value = (value & 0xFFFFFFF7);
+	}
+	else if (set == 1) {
+		/* Enable DRAMC Monitor*/
+		value = (value | 0x00000008);
+	}
+	else
+		return BM_ERR_WRONG_REQ;
+	emi_reg_sync_writel(value, addr_base+DRAMC_DMMONITOR);
+
+	return BM_REQ_OK;
+}
+
+unsigned int MET_DRAMC_RefPop(int chann)
+{
+	unsigned long addr_base = ADDR_DRAMC0;
+
+	if (chann == 0)
+		addr_base = ADDR_DRAMC0;
+	else if (chann == 1)
+		addr_base = ADDR_DRAMC1;
+	else if (chann == 2)
+		addr_base = ADDR_DRAMC2;
+	else if (chann == 3)
+		addr_base = ADDR_DRAMC3;
+	else
+		return BM_ERR_WRONG_REQ;
+
+	return emi_readl(IOMEM(addr_base+DRAMC_REFRESH_POP));
+}
+
+
+
+unsigned int MET_DRAMC_Free26M(int chann)
+{
+	unsigned long addr_base = ADDR_DRAMC0;
+
+	if (chann == 0)
+		addr_base = ADDR_DRAMC0;
+	else if (chann == 1)
+		addr_base = ADDR_DRAMC1;
+	else if (chann == 2)
+		addr_base = ADDR_DRAMC2;
+	else if (chann == 3)
+		addr_base = ADDR_DRAMC3;
+	else
+		return BM_ERR_WRONG_REQ;
+
+	return emi_readl(IOMEM(addr_base+DRAMC_FREERUN_26M));
+}
+
+unsigned int MET_DRAMC_RByte(int chann)
+{
+	unsigned long addr_base = ADDR_DRAMC0;
+
+	if (chann == 0)
+		addr_base = ADDR_DRAMC0;
+	else if (chann == 1)
+		addr_base = ADDR_DRAMC1;
+	else if (chann == 2)
+		addr_base = ADDR_DRAMC2;
+	else if (chann == 3)
+		addr_base = ADDR_DRAMC3;
+	else
+		return BM_ERR_WRONG_REQ;
+
+	return emi_readl(IOMEM(addr_base+DRAMC_READ_BYTES));
+}
+
+unsigned int MET_DRAMC_WByte(int chann)
+{
+	unsigned long addr_base = ADDR_DRAMC0;
+
+	if (chann == 0)
+		addr_base = ADDR_DRAMC0;
+	else if (chann == 1)
+		addr_base = ADDR_DRAMC1;
+	else if (chann == 2)
+		addr_base = ADDR_DRAMC2;
+	else if (chann == 3)
+		addr_base = ADDR_DRAMC3;
+	else
+		return BM_ERR_WRONG_REQ;
+
+	return emi_readl(IOMEM(addr_base+DRAMC_WRITE_BYTES));
+}
+
+unsigned int MET_EMI_GetMDCT(void)
+{
+	return emi_readl(IOMEM(ADDR_EMI+EMI_MDCT));
+}
+
+unsigned int MET_EMI_GetMDCT_2ND(void)
+{
+	return emi_readl(IOMEM(ADDR_EMI+EMI_MDCT_2ND));
+}
+
+
+unsigned int MET_EMI_GetARBA(void)
+{
+	/* EMI_ARBA EMI Bandwidth Filter Control M0/1 */
+	return emi_readl(IOMEM(ADDR_EMI+EMI_ARBA));
+}
+
+unsigned int MET_EMI_GetARBB(void)
+{
+	/* return emi_readl(IOMEM(ADDR_EMI+EMI_ARBB)); */
+	/* Note: Olympus Coda doesn't has this regiester, So we return 0 */
+	return 0;
+}
+
+unsigned int MET_EMI_GetARBC(void)
+{
+	return emi_readl(IOMEM(ADDR_EMI+EMI_ARBC));
+}
+
+unsigned int MET_EMI_GetARBD(void)
+{
+	return emi_readl(IOMEM(ADDR_EMI+EMI_ARBD));
+}
+
+unsigned int MET_EMI_GetARBE(void)
+{
+	return emi_readl(IOMEM(ADDR_EMI+EMI_ARBE));
+}
+
+unsigned int MET_EMI_GetARBF(void)
+{
+	return emi_readl(IOMEM(ADDR_EMI+EMI_ARBF));
+}
+
+unsigned int MET_EMI_GetARBG(void)
+{
+	return emi_readl(IOMEM(ADDR_EMI+EMI_ARBG));
+}
+
+unsigned int MET_EMI_GetARBH(void)
+{
+	return emi_readl(IOMEM(ADDR_EMI+EMI_ARBH));
+}
+
+/* Total BW status*/
+unsigned int MET_EMI_GetBWCT0(void)
+{
+	return emi_readl(IOMEM(ADDR_EMI+EMI_BWCT0));
+}
+
+/* Total BW status*/
+unsigned int MET_EMI_GetBWCT1(void)
+{
+	return emi_readl(IOMEM(ADDR_EMI+EMI_BWCT1));
+}
+
+/* Total BW status*/
+unsigned int MET_EMI_GetBWCT2(void)
+{
+	return emi_readl(IOMEM(ADDR_EMI+EMI_BWCT2));
+}
+
+/* Total BW status*/
+unsigned int MET_EMI_GetBWCT3(void)
+{
+	return emi_readl(IOMEM(ADDR_EMI+EMI_BWCT3));
+}
+
+/* Total BW status*/
+unsigned int MET_EMI_GetBWCT4(void)
+{
+	return emi_readl(IOMEM(ADDR_EMI+EMI_BWCT4));
+}
+
+/* Total BW status*/
+unsigned int MET_EMI_GetBWST0(void)
+{
+	return emi_readl(IOMEM(ADDR_EMI+EMI_BWST0));
+}
+
+/* Total BW status*/
+unsigned int MET_EMI_GetBWST1(void)
+{
+	return emi_readl(IOMEM(ADDR_EMI+EMI_BWST1));
+}
+
+
+/* C+G BW status*/
+unsigned int MET_EMI_GetBWCT0_2ND(void)
+{
+	return emi_readl(IOMEM(ADDR_EMI+EMI_BWCT0_2ND));
+}
+
+/* C+G BW status*/
+unsigned int MET_EMI_GetBWCT1_2ND(void)
+{
+	return emi_readl(IOMEM(ADDR_EMI+EMI_BWCT1_2ND));
+}
+
+/* C+G BW status*/
+unsigned int MET_EMI_GetBWST_2ND(void)
+{
+	return emi_readl(IOMEM(ADDR_EMI+EMI_BWST_2ND));
+}
+
+unsigned int MET_EMI_GetBMRW0(void)
+{
+	return readl(IOMEM(ADDR_EMI+EMI_BMRW0));
+}
+
+void emi_dump_reg(void)
+{
+	int i;
+
+	MET_TRACE("[emi_regdump]\n");
+	for (i = 0x400; i < 0x500; i = i+16)
+		MET_TRACE("%4x__ %8x %8x %8x %8x\n", i,  readl(IOMEM(ADDR_EMI+i)),
+			readl(IOMEM(ADDR_EMI+i+4)), readl(IOMEM(ADDR_EMI+i+8)), readl(IOMEM(ADDR_EMI+i+12)));
+}
+
+unsigned int MET_EMI_GetDramChannNum(void)
+{
+	int num = -1;
+
+	if (BaseAddrEMI) {
+		num = emi_readl(IOMEM(ADDR_EMI+EMI_CONA));
+		num = ((num >> 8) & 0x0000003);
+	} else {
+		return 1;
+	}
+
+	if (num == M0_DOUBLE_HALF_BW_1CH)
+		return 1;
+	else if (num == M0_DOUBLE_HALF_BW_2CH)
+		return 2;
+	else if (num == M0_DOUBLE_HALF_BW_4CH)
+		return 4;
+
+	else		/* default return single channel */
+		return 1;
+
+}
+
+void MET_DRAMC_GetDebugCounter(int *value, int chann)
+{
+	int i;
+	unsigned long addr_base = ADDR_DRAMC0;
+
+	for (i = 0; i < chann; i++)	{
+
+		if (i == 0)
+			addr_base = ADDR_DRAMC0;
+		else if (i == 1)
+			addr_base = ADDR_DRAMC1;
+		else if (i == 2)
+			addr_base = ADDR_DRAMC2;
+		else if (i == 3)
+			addr_base = ADDR_DRAMC3;
+		else
+			return;
+
+
+		value[DRAMC_Debug_MAX_CNT*i + BM_RK0_PRE_STANDBY] =
+			emi_readl(IOMEM(addr_base + DRAMC_RK0_PRE_STANDBY));
+		value[DRAMC_Debug_MAX_CNT*i + BM_RK0_PRE_POWERDOWN] =
+			emi_readl(IOMEM(addr_base + DRAMC_RK0_PRE_POWERDOWN));
+		value[DRAMC_Debug_MAX_CNT*i + BM_RK0_ACT_STANDBY] =
+			emi_readl(IOMEM(addr_base + DRAMC_RK0_ACT_STANDBY));
+		value[DRAMC_Debug_MAX_CNT*i + BM_RK0_ACT_POWERDOWN] =
+			emi_readl(IOMEM(addr_base + DRAMC_RK0_ACT_POWERDOWN));
+
+		value[DRAMC_Debug_MAX_CNT*i + BM_RK1_PRE_STANDBY] =
+			emi_readl(IOMEM(addr_base + DRAMC_RK1_PRE_STANDBY));
+		value[DRAMC_Debug_MAX_CNT*i + BM_RK1_PRE_POWERDOWN] =
+			emi_readl(IOMEM(addr_base + DRAMC_RK1_PRE_POWERDOWN));
+		value[DRAMC_Debug_MAX_CNT*i + BM_RK1_ACT_STANDBY] =
+			emi_readl(IOMEM(addr_base + DRAMC_RK1_ACT_STANDBY));
+		value[DRAMC_Debug_MAX_CNT*i + BM_RK1_ACT_POWERDOWN] =
+			emi_readl(IOMEM(addr_base + DRAMC_RK1_ACT_POWERDOWN));
+
+		value[DRAMC_Debug_MAX_CNT*i + BM_RK2_PRE_STANDBY] =
+			emi_readl(IOMEM(addr_base + DRAMC_RK2_PRE_STANDBY));
+		value[DRAMC_Debug_MAX_CNT*i + BM_RK2_PRE_POWERDOWN] =
+			emi_readl(IOMEM(addr_base + DRAMC_RK2_PRE_POWERDOWN));
+		value[DRAMC_Debug_MAX_CNT*i + BM_RK2_ACT_STANDBY] =
+			emi_readl(IOMEM(addr_base + DRAMC_RK2_ACT_STANDBY));
+		value[DRAMC_Debug_MAX_CNT*i + BM_RK2_ACT_POWERDOWN] =
+			emi_readl(IOMEM(addr_base + DRAMC_RK2_ACT_POWERDOWN));
+	}
+}
+#if 1
+static inline unsigned int dramc_reg_read(unsigned long addr)
+{
+#if 0
+#ifdef CONFIG_64BIT
+	unsigned int value = 0;
+	value = HAL_READ32((void*)addr);
+#else
+	unsigned int value = readl((void*)addr);
+#endif
+#endif
+
+	unsigned int value = readl((void*)addr);
+
+	mb();
+	return value;
+}
+
+#if 0
+u32 DRAMC_GetDramcFreq(void)
+{
+	unsigned long tcm_dramc_flags = 0;
+	tcm_dramc_flags = dramc_reg_read(TCM_DRAM_FLAGS_ADDR);
+	return TCMGET_DDR_CLK(tcm_dramc_flags);
+}
+
+unsigned int get_dram_data_rate(void)
+{
+	return DRAMC_GetDramcFreq()/1000000;
+}
+#else
+
+#define DRAM_DATARATE_REG	0x10100D9C
+
+u32 DRAMC_GetDramcFreq(void)
+{
+	unsigned long reg_val;
+	unsigned long dram_datarate = 0;
+
+	reg_val = dramc_reg_read((unsigned long)(AddrDramDatarate + 0xD9C));
+//	pr_err("dram datarate reg val = 0x%lx, reg_val[9-16] = %lu\n", reg_val, (reg_val >> 9) & 0xff);
+	dram_datarate = (((dramc_reg_read((unsigned long)(AddrDramDatarate + 0xD9C))) >> 16) & 0x1ffff) * 26 / 256;
+
+	return dram_datarate;
+}
+
+unsigned int get_dram_data_rate(void)
+{
+	return DRAMC_GetDramcFreq();
+}
+#endif
+
+
+#else
+#define DRAMC_AO_CHA_BASE_ADDR (DRAM_BASE)
+#define PDEF_DRAMC0_CHA_REG_0E4 IOMEM((DRAMC_AO_CHA_BASE_ADDR + 0x00e4))
+#define PDEF_DRAMC0_CHA_REG_010 IOMEM((DRAMC_AO_CHA_BASE_ADDR + 0x0010))
+#define DDRPHY_CHA_BASE_ADDR (DRAM_DDRPHY_CHA_BANK)
+unsigned int DRAM_TYPE = 0;
+enum DDRTYPE {
+        TYPE_LPDDR3 = 1,
+        TYPE_LPDDR4,
+        TYPE_LPDDR4X
+};
+
+static unsigned int get_shuffle_status(void)
+{
+	/* HPM = 0, LPM = 1, ULPM = 2; */
+	return (emi_readl(PDEF_DRAMC0_CHA_REG_0E4) & 0x6) >> 1;
+}
+
+unsigned int get_dram_data_rate(void)
+{
+	return DRAMC_GetDramcFreq();
+
+	unsigned long addr_base = DDRPHY_CHA_BASE_ADDR;
+	unsigned int u4ShuLevel, u4SDM_PCW, u4PREDIV, u4POSDIV, u4CKDIV4, u4VCOFreq, u4DataRate = 0;
+	if (DRAM_TYPE == 0)
+		DRAM_TYPE = (emi_readl(PDEF_DRAMC0_CHA_REG_010) & 0x1C00) >> 10;
+	u4ShuLevel = get_shuffle_status();
+
+	u4SDM_PCW = emi_readl(IOMEM(addr_base + 0xd94 + 0x500 * u4ShuLevel)) >> 16;
+	u4PREDIV = (emi_readl(IOMEM(addr_base + 0xda0 + 0x500 * u4ShuLevel)) & 0x000c0000) >> 18;
+	u4POSDIV = emi_readl(IOMEM(addr_base + 0xda0 + 0x500 * u4ShuLevel)) & 0x00000007;
+	u4CKDIV4 = (emi_readl(IOMEM(addr_base + 0xd18 + 0x500 * u4ShuLevel)) & 0x08000000) >> 27;
+
+	u4VCOFreq = ((52>>u4PREDIV)*(u4SDM_PCW>>8))>>u4POSDIV;
+
+	u4DataRate = u4VCOFreq>>u4CKDIV4;
+
+	/* pr_err("[DRAMC Driver] u4ShuLevel=%d, PCW=0x%X, u4PREDIV=%d, u4POSDIV=%d, CKDIV4=%d, DataRate=%d\n",
+	 * u4ShuLevel, u4SDM_PCW, u4PREDIV, u4POSDIV, u4CKDIV4, u4DataRate);
+	 */
+
+//	if (DRAM_TYPE == TYPE_LPDDR4X) {
+		if (u4DataRate == 3198)
+			u4DataRate = 3200;
+		else if (u4DataRate == 2652)
+			u4DataRate = 2667;
+		else if (u4DataRate == 1599)
+			u4DataRate = 1600;
+		else if (u4DataRate == 799)
+			u4DataRate = 800;
+		else
+			u4DataRate = 0;
+//	} else
+//		u4DataRate = 0;
+
+	return u4DataRate;
+}
+#endif
diff --git a/src/devtools/met-driver/4.19/mt2712/mtk_emi_bm.h b/src/devtools/met-driver/4.19/mt2712/mtk_emi_bm.h
new file mode 100644
index 0000000..921d80d
--- /dev/null
+++ b/src/devtools/met-driver/4.19/mt2712/mtk_emi_bm.h
@@ -0,0 +1,414 @@
+#ifndef __MT_MET_EMI_BM_H__
+#define __MT_MET_EMI_BM_H__
+
+
+
+
+#define DEF_BM_RW_TYPE		(BM_BOTH_READ_WRITE)
+#define NTS					2
+#define NWSCT				4
+#define NLATENCY			8
+#define NTRANS				8
+#define NALL					3
+#define NTTYPE				5
+#define NIDX_EMI			(NTS + NWSCT + NLATENCY + NTRANS + NALL + NTTYPE)
+
+#define NCNT					9
+#define NCH					4
+#define NIDX_DRAMC			(NCNT * NCH)
+#define NIDX					(NIDX_EMI + NIDX_DRAMC)
+
+#define NCLK					1
+#define NARB					8
+#define NBW					10
+#define NIDX_BL				(NCLK + NARB + NBW)
+
+/* 1000 To Khz and 4x freq & 2x data rate for LPDDR4 */
+/* 1000 To Khz and 2x freq & 2x data rate for LPDDR3*/
+/* TBD: calculate emi clock rate from DRAM DATA RATE */
+
+/*dram baseclock/EMI clock  :	LP4=4	LP3=2	*/
+#define DRAM_EMI_BASECLOCK_RATE	4
+/*dram io width  :	LP4=x16		LP3=x32 or x16	*/
+#define DRAM_IO_BUS_WIDTH		16
+/*dram datarate  :	DDR=double */
+#define DRAM_DATARATE	2
+
+#define	ADDR_EMI		((unsigned long) BaseAddrEMI)
+#define	ADDR_DRAMC0	((unsigned long) BaseAddrDRAMC0)
+#define	ADDR_DRAMC1	((unsigned long) BaseAddrDRAMC1)
+#define	ADDR_DRAMC2	((unsigned long) BaseAddrDRAMC2)
+#define	ADDR_DRAMC3	((unsigned long) BaseAddrDRAMC3)
+
+static const char of_emi_desc[] = "mediatek,mt2712-emi";
+static const char of_dramc_desc[] = "mediatek,mt2712-dramc";
+
+typedef enum {
+	DRAMC_DTS_DRAMC0_AO = 0x0,
+	DRAMC_DTS_DRAMC0_NAO = 0x4,
+	DRAMC_DTS_DRAMC1_NAO = 0x5,
+	DRAMC_DTS_DRAMC2_NAO = 0x6,
+	DRAMC_DTS_DRAMC3_NAO = 0x7,
+	DRAMC_DTS_DDRPHY0_AO = 0x8,
+}	BM_DRAMC_DTS_INDEX;
+
+#define BM_Master_M0_name	"m0_APMCU0"
+#define BM_Master_M1_name	"m1_APMCU1"
+#define BM_Master_M2_name	"m2_MM1_M1"
+#define BM_Master_M3_name	"m3_MM2_M1"
+#define BM_Master_M4_name	"m4_MM2_M0"
+#define BM_Master_M5_name	"m5_MM1_M0"
+#define BM_Master_M6_name	"m6_PERI"
+#define BM_Master_M7_name	"m7_GPU"
+
+#define BM_Master_GP_AP	(BM_MASTER_M0 | BM_MASTER_M1)
+#define BM_Master_GP_MM	(BM_MASTER_M2 | BM_MASTER_M3 | BM_MASTER_M4 | BM_MASTER_M5)
+#define BM_Master_GP_GPU	(BM_MASTER_M7)
+#define BM_Master_GP_PERI	(BM_MASTER_M6)
+
+
+/*Need no change by project*/
+#define BM_Master_GP_1_Default	BM_Master_GP_AP
+#define BM_Master_GP_2_Default	BM_Master_GP_MM
+#define BM_Master_GP_3_Default	BM_Master_GP_GPU
+
+#define BM_MASTER_M0		(0x01)
+#define BM_MASTER_M1		(0x02)
+#define BM_MASTER_M2		(0x04)
+#define BM_MASTER_M3		(0x08)
+#define BM_MASTER_M4		(0x10)
+#define BM_MASTER_M5		(0x20)
+#define BM_MASTER_M6		(0x40)
+#define BM_MASTER_M7		(0x80)
+#define BM_MASTER_ALL		(0xFF)
+
+typedef enum {
+	BM_RK0_PRE_STANDBY = 0x0,
+	BM_RK0_PRE_POWERDOWN,
+	BM_RK0_ACT_STANDBY,
+	BM_RK0_ACT_POWERDOWN,
+	BM_RK1_PRE_STANDBY,
+	BM_RK1_PRE_POWERDOWN,
+	BM_RK1_ACT_STANDBY,
+	BM_RK1_ACT_POWERDOWN,
+	BM_RK2_PRE_STANDBY,
+	BM_RK2_PRE_POWERDOWN,
+	BM_RK2_ACT_STANDBY,
+	BM_RK2_ACT_POWERDOWN,
+	DRAMC_Debug_MAX_CNT
+} DRAMC_Debug_Type;
+
+typedef enum {
+	DRAMC_R2R,
+	DRAMC_R2W,
+	DRAMC_W2R,
+	DRAMC_W2W,
+	DRAMC_ALL
+} DRAMC_Cnt_Type;
+
+typedef enum {
+	BM_BOTH_READ_WRITE,
+	BM_READ_ONLY,
+	BM_WRITE_ONLY
+} BM_RW_Type;
+
+enum {
+	BM_TRANS_TYPE_1BEAT = 0x0,
+	BM_TRANS_TYPE_2BEAT,
+	BM_TRANS_TYPE_3BEAT,
+	BM_TRANS_TYPE_4BEAT,
+	BM_TRANS_TYPE_5BEAT,
+	BM_TRANS_TYPE_6BEAT,
+	BM_TRANS_TYPE_7BEAT,
+	BM_TRANS_TYPE_8BEAT,
+	BM_TRANS_TYPE_9BEAT,
+	BM_TRANS_TYPE_10BEAT,
+	BM_TRANS_TYPE_11BEAT,
+	BM_TRANS_TYPE_12BEAT,
+	BM_TRANS_TYPE_13BEAT,
+	BM_TRANS_TYPE_14BEAT,
+	BM_TRANS_TYPE_15BEAT,
+	BM_TRANS_TYPE_16BEAT,
+	BM_TRANS_TYPE_1Byte = 0 << 4,
+	BM_TRANS_TYPE_2Byte = 1 << 4,
+	BM_TRANS_TYPE_4Byte = 2 << 4,
+	BM_TRANS_TYPE_8Byte = 3 << 4,
+	BM_TRANS_TYPE_16Byte = 4 << 4,
+	BM_TRANS_TYPE_32Byte = 5 << 4,
+	BM_TRANS_TYPE_BURST_WRAP = 0 << 7,
+	BM_TRANS_TYPE_BURST_INCR = 1 << 7
+};
+
+enum {
+	BM_TRANS_RW_DEFAULT = 0x0,
+	BM_TRANS_RW_READONLY,
+	BM_TRANS_RW_WRITEONLY,
+	BM_TRANS_RW_RWBOTH
+};
+
+
+/*coda busid 12bit, but HW support 16 bit*/
+#define EMI_BMID_MASK					(0xFFFF)
+#define BM_COUNTER_MAX				(21)
+
+/*
+*#define BUS_MON_EN		    (0x00000001)
+*#define BUS_MON_PAUSE		(0x00000002)
+*#define BUS_MON_IDLE		(0x00000008)
+*#define BC_OVERRUN		    (0x00000100)
+*/
+enum {
+	BUS_MON_EN_SHIFT = 0,
+	BUS_MON_PAUSE_SHIFT = 1,
+	BUS_MON_IDLE_SHIFT = 3,
+	BC_OVERRUN_SHIFT = 8,
+};
+
+#define BM_REQ_OK						(0)
+#define BM_ERR_WRONG_REQ				(-1)
+#define BM_ERR_OVERRUN					(-2)
+
+#define BM_WSCT_TSCT_IDSEL_ENABLE	(0)
+#define BM_WSCT_TSCT_IDSEL_DISABLE	(-1)
+#define BM_TTYPE1_16_ENABLE			(0)
+#define BM_TTYPE1_16_DISABLE			(-1)
+#define BM_TTYPE17_21_ENABLE			(0)
+#define BM_TTYPE17_21_DISABLE			(-1)
+#define BM_BW_LIMITER_ENABLE			(0)
+#define BM_BW_LIMITER_DISABLE			(-1)
+
+#define M0_DOUBLE_HALF_BW_1CH	(0x0)
+#define M0_DOUBLE_HALF_BW_2CH	(0x1)
+#define M0_DOUBLE_HALF_BW_4CH	(0x2)
+
+#ifdef CONFIG_MTK_TINYSYS_SSPM_SUPPORT
+/*ondiemet emi ipi command*/
+typedef enum {
+	SET_BASE_EMI = 0x0,
+	SET_BASE_DRAMC0,
+	SET_BASE_DRAMC1,
+	SET_BASE_DRAMC2,
+	SET_BASE_DRAMC3,
+	SET_BASE_DDRPHY0AO,
+	SET_BASE_DRAMC0_AO,
+	SET_EBM_CONFIGS,
+}	BM_EMI_IPI_Type;
+#endif
+
+#define	EMI_OFF			0x0000
+#define EMI_CONA		(0x000-EMI_OFF)
+#define EMI_CONH		(0x038-EMI_OFF)
+#define EMI_CONM		(0x060-EMI_OFF)
+#define EMI_CONO		(0x070-EMI_OFF)
+
+#define EMI_MDCT		(0x078-EMI_OFF)
+#define EMI_MDCT_2ND    (0x07C-EMI_OFF)
+
+#define EMI_ARBA		(0x100-EMI_OFF)
+#define EMI_ARBB		(0x108-EMI_OFF)
+#define EMI_ARBC		(0x110-EMI_OFF)
+#define EMI_ARBD		(0x118-EMI_OFF)
+#define EMI_ARBE		(0x120-EMI_OFF)
+#define EMI_ARBF		(0x128-EMI_OFF)
+#define EMI_ARBG		(0x130-EMI_OFF)
+#define EMI_ARBG_2ND    (0x134-EMI_OFF)
+#define EMI_ARBH		(0x138-EMI_OFF)
+
+#define EMI_BMEN		(0x400-EMI_OFF)
+#define EMI_BCNT		(0x408-EMI_OFF)
+#define EMI_TACT		(0x410-EMI_OFF)
+#define EMI_TSCT		(0x418-EMI_OFF)
+#define EMI_WACT		(0x420-EMI_OFF)
+#define EMI_WSCT		(0x428-EMI_OFF)
+#define EMI_BACT		(0x430-EMI_OFF)
+#define EMI_BSCT		(0x438-EMI_OFF)
+
+#define EMI_MSEL		(0x440-EMI_OFF)
+#define EMI_TSCT2		(0x448-EMI_OFF)
+#define EMI_TSCT3		(0x450-EMI_OFF)
+#define EMI_WSCT2		(0x458-EMI_OFF)
+#define EMI_WSCT3		(0x460-EMI_OFF)
+#define EMI_WSCT4		(0x464-EMI_OFF)
+#define EMI_MSEL2		(0x468-EMI_OFF)
+#define EMI_MSEL3		(0x470-EMI_OFF)
+#define EMI_MSEL4		(0x478-EMI_OFF)
+#define EMI_MSEL5		(0x480-EMI_OFF)
+#define EMI_MSEL6		(0x488-EMI_OFF)
+#define EMI_MSEL7		(0x490-EMI_OFF)
+#define EMI_MSEL8		(0x498-EMI_OFF)
+#define EMI_MSEL9		(0x4A0-EMI_OFF)
+#define EMI_MSEL10		(0x4A8-EMI_OFF)
+
+#define EMI_BMID0		(0x4B0-EMI_OFF)
+#define EMI_BMID1		(0x4B4-EMI_OFF)
+#define EMI_BMID2		(0x4B8-EMI_OFF)
+#define EMI_BMID3		(0x4BC-EMI_OFF)
+#define EMI_BMID4		(0x4C0-EMI_OFF)
+#define EMI_BMID5		(0x4C4-EMI_OFF)
+#define EMI_BMID6		(0x4C8-EMI_OFF)
+#define EMI_BMID7		(0x4CC-EMI_OFF)
+#define EMI_BMID8		(0x4D0-EMI_OFF)
+#define EMI_BMID9		(0x4D4-EMI_OFF)
+#define EMI_BMID10		(0x4D8-EMI_OFF)
+
+#define EMI_BMEN1		(0x4E0-EMI_OFF)
+#define EMI_BMEN2		(0x4E8-EMI_OFF)
+#define EMI_BMRW0		(0x4F8-EMI_OFF)
+#define EMI_BMRW1		(0x4FC-EMI_OFF)
+#define EMI_TTYPE1		(0x500-EMI_OFF)
+#define EMI_TTYPE2		(0x508-EMI_OFF)
+#define EMI_TTYPE3		(0x510-EMI_OFF)
+#define EMI_TTYPE4		(0x518-EMI_OFF)
+#define EMI_TTYPE5		(0x520-EMI_OFF)
+#define EMI_TTYPE6		(0x528-EMI_OFF)
+#define EMI_TTYPE7		(0x530-EMI_OFF)
+#define EMI_TTYPE8		(0x538-EMI_OFF)
+#define EMI_TTYPE9		(0x540-EMI_OFF)
+#define EMI_TTYPE10		(0x548-EMI_OFF)
+#define EMI_TTYPE11		(0x550-EMI_OFF)
+#define EMI_TTYPE12		(0x558-EMI_OFF)
+#define EMI_TTYPE13		(0x560-EMI_OFF)
+#define EMI_TTYPE14		(0x568-EMI_OFF)
+#define EMI_TTYPE15		(0x570-EMI_OFF)
+#define EMI_TTYPE16		(0x578-EMI_OFF)
+#define EMI_TTYPE17		(0x580-EMI_OFF)
+#define EMI_TTYPE18		(0x588-EMI_OFF)
+#define EMI_TTYPE19		(0x590-EMI_OFF)
+#define EMI_TTYPE20		(0x598-EMI_OFF)
+#define EMI_TTYPE21		(0x5A0-EMI_OFF)
+
+#define EMI_BWCT0		(0x5B0-EMI_OFF)
+#define EMI_BWCT1		(0x5B4-EMI_OFF)
+#define EMI_BWCT2		(0x5B8-EMI_OFF)
+#define EMI_BWCT3		(0x5BC-EMI_OFF)
+#define EMI_BWCT4		(0x5C0-EMI_OFF)
+#define EMI_BWST0		(0x5C4-EMI_OFF)
+#define EMI_BWST1		(0x5C8-EMI_OFF)
+
+#define EMI_BWCT0_2ND	(0x6A0-EMI_OFF)
+#define EMI_BWCT1_2ND	(0x6A4-EMI_OFF)
+#define EMI_BWST_2ND	(0x6A8-EMI_OFF)
+
+#define DRAMC_DMMONITOR		0x24
+#define DRAMC_MISC_STATUSA	0x80
+#define DRAMC_REFRESH_POP       0x300
+#define DRAMC_FREERUN_26M       0x304
+#define DRAMC_R2R_PAGE_HIT	0x30C
+#define DRAMC_R2R_PAGE_MISS	0x310
+#define DRAMC_R2R_INTERBANK	0x314
+#define DRAMC_R2W_PAGE_HIT	0x318
+#define DRAMC_R2W_PAGE_MISS	0x31C
+#define DRAMC_R2W_INTERBANK	0x320
+#define DRAMC_W2R_PAGE_HIT	0x324
+#define DRAMC_W2R_PAGE_MISS	0x328
+#define DRAMC_W2R_INTERBANK	0x32C
+#define DRAMC_W2W_PAGE_HIT	0x330
+#define DRAMC_W2W_PAGE_MISS	0x334
+#define DRAMC_W2W_INTERBANK	0x338
+#define DRAMC_IDLE_COUNT	0x308
+#define DRAMC_RK0_PRE_STANDBY   0x33c
+#define DRAMC_RK0_PRE_POWERDOWN 0x340
+#define DRAMC_RK0_ACT_STANDBY   0x344
+#define DRAMC_RK0_ACT_POWERDOWN 0x348
+#define DRAMC_RK1_PRE_STANDBY   0x34c
+#define DRAMC_RK1_PRE_POWERDOWN 0x350
+#define DRAMC_RK1_ACT_STANDBY   0x354
+#define DRAMC_RK1_ACT_POWERDOWN 0x358
+#define DRAMC_RK2_PRE_STANDBY   0x35c
+#define DRAMC_RK2_PRE_POWERDOWN 0x360
+#define DRAMC_RK2_ACT_STANDBY   0x364
+#define DRAMC_RK2_ACT_POWERDOWN 0x368
+#define DRAMC_READ_BYTES	0x38c
+#define DRAMC_WRITE_BYTES	0x390
+
+
+extern void emi_dump_reg(void);
+extern int MET_BM_Init(void);
+extern void MET_BM_DeInit(void);
+extern void MET_BM_Enable(const unsigned int enable);
+extern void MET_BM_Pause(void);
+extern void MET_BM_Continue(void);
+extern unsigned int MET_BM_IsOverrun(void);
+extern unsigned int MET_BM_GetReadWriteType(void);
+extern void MET_BM_SetReadWriteType(const unsigned int ReadWriteType);
+extern int MET_BM_GetBusCycCount(void);
+extern unsigned int MET_BM_GetTransAllCount(void);
+extern int MET_BM_GetTransCount(const unsigned int counter_num);
+extern int MET_BM_GetWordAllCount(void);
+extern int MET_BM_GetWordCount(const unsigned int counter_num);
+extern unsigned int MET_BM_GetBandwidthWordCount(void);
+extern unsigned int MET_BM_GetOverheadWordCount(void);
+extern int MET_BM_GetTransTypeCount(const unsigned int counter_num);
+extern int MET_BM_GetMDCT(void);
+extern int MET_BM_GetMDCT_2(void);
+extern int MET_BM_GetMonitorCounter(const unsigned int counter_num,
+		unsigned int *master,
+		unsigned int *trans_type);
+extern int MET_BM_SetMDCT_MDMCU(unsigned int mdmcu_rd_buf);
+extern int MET_BM_SetMonitorCounter(const unsigned int counter_num,
+		const unsigned int master,
+		const unsigned int trans_type);
+extern int MET_BM_SetTtypeCounterRW(unsigned int bmrw0_val, unsigned int bmrw1_val);
+extern int MET_BM_Set_WsctTsct_id_sel(unsigned int counter_num, unsigned int enable);
+extern int MET_BM_SetMaster(const unsigned int counter_num,
+		const unsigned int master);
+extern int MET_BM_SetIDSelect(const unsigned int counter_num,
+		const unsigned int id,
+		const unsigned int enable);
+extern int MET_BM_SetUltraHighFilter(const unsigned int counter_num,
+		const unsigned int enable);
+extern int MET_BM_SetLatencyCounter(unsigned int enable);
+extern int MET_BM_GetLatencyCycle(const unsigned int counter_num);
+extern unsigned int MET_BM_GetEmiDcm(void);
+extern int MET_BM_SetEmiDcm(const unsigned int setting);
+
+/* DRAMC */
+extern unsigned int MET_DRAMC_GetPageHitCount(DRAMC_Cnt_Type CountType, int chann);
+extern unsigned int MET_DRAMC_GetPageMissCount(DRAMC_Cnt_Type CountType, int chann);
+extern unsigned int MET_DRAMC_GetInterbankCount(DRAMC_Cnt_Type CountType, int chann);
+extern unsigned int MET_DRAMC_GetIdleCount(int chann);
+extern unsigned int MET_DRAMC_Misc_Status(int chann);
+extern unsigned int MET_DRAMC_RefPop(int chann);
+extern unsigned int MET_DRAMC_Free26M(int chann);
+extern unsigned int MET_DRAMC_RByte(int chann);
+extern unsigned int MET_DRAMC_WByte(int chann);
+extern int MET_DRAMC_BMEnable(int chann, int set);
+extern int MET_DRAMC_BMPause(int chann, int set);
+extern unsigned int get_dram_data_rate(void);
+
+/* Config */
+unsigned int MET_EMI_GetARBA(void);
+unsigned int MET_EMI_GetARBB(void);
+unsigned int MET_EMI_GetARBC(void);
+unsigned int MET_EMI_GetARBD(void);
+unsigned int MET_EMI_GetARBE(void);
+unsigned int MET_EMI_GetARBF(void);
+unsigned int MET_EMI_GetARBG(void);
+unsigned int MET_EMI_GetARBH(void);
+
+/* Total BW status */
+extern unsigned int MET_EMI_GetBWCT0(void);
+extern unsigned int MET_EMI_GetBWCT1(void);
+extern unsigned int MET_EMI_GetBWCT2(void);
+extern unsigned int MET_EMI_GetBWCT3(void);
+extern unsigned int MET_EMI_GetBWCT4(void);
+extern unsigned int MET_EMI_GetBWST0(void);
+extern unsigned int MET_EMI_GetBWST1(void);
+/* C+G BW */
+extern unsigned int MET_EMI_GetBWCT0_2ND(void);
+extern unsigned int MET_EMI_GetBWCT1_2ND(void);
+extern unsigned int MET_EMI_GetBWST_2ND(void);
+
+unsigned int MET_EMI_GetBMRW0(void);
+unsigned int MET_EMI_GetDramChannNum(void);
+
+/* Debug Counter status */
+void MET_DRAMC_GetDebugCounter(int *value, int chann);
+
+/* ondiemet*/
+void MET_BM_IPI_baseaddr(void);
+void met_emi_phyaddr_debug(void);
+
+
+
+#endif  /* !__MT_MET_EMI_BM_H__ */
diff --git a/src/devtools/met-driver/4.19/mt2712/mtk_gpu_metmonitor.c b/src/devtools/met-driver/4.19/mt2712/mtk_gpu_metmonitor.c
new file mode 100644
index 0000000..842825f
--- /dev/null
+++ b/src/devtools/met-driver/4.19/mt2712/mtk_gpu_metmonitor.c
@@ -0,0 +1,802 @@
+/*
+ * 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 <asm/page.h>
+#include <linux/device.h>
+#include <linux/module.h>
+#include <linux/string.h>
+#include <linux/fs.h>
+#include <linux/kernel.h>
+#include <linux/syscalls.h>
+#include <linux/mm.h>
+#include <linux/slab.h>
+#include <asm/uaccess.h>
+#include <linux/hrtimer.h>
+
+#include "met_drv.h"
+#include "trace.h"
+
+#include "mtk_gpu_metmonitor.h"
+#include "core_plf_init.h"
+#include "core_plf_trace.h"
+
+
+/*
+ * define if the hal implementation might re-schedule, cannot run inside softirq
+ * undefine this is better for sampling jitter if HAL support it
+ */
+#undef GPU_HAL_RUN_PREMPTIBLE
+
+#ifdef GPU_HAL_RUN_PREMPTIBLE
+static struct delayed_work gpu_dwork;
+//static struct delayed_work gpu_pwr_dwork;
+#endif
+
+/* the mt_gpufreq_get_thermal_limit_freq use mutex_lock to do its job */
+/* so, change the gpu-dvfs implementation to dwork */
+static struct delayed_work gpu_dvfs_dwork;
+
+/*
+ * GPU monitor HAL comes from alps\mediatek\kernel\include\linux\mtk_gpu_utility.h
+ *
+ * mtk_get_gpu_memory_usage(unsigned int* pMemUsage) in unit of bytes
+ *
+ * mtk_get_gpu_xxx_loading are in unit of %
+*/
+
+enum MET_GPU_PROFILE_INDEX {
+	eMET_GPU_LOADING = 0,
+	eMET_GPU_BLOCK_LOADING,	/* 1 */
+	eMET_GPU_IDLE_LOADING,	/* 2 */
+	eMET_GPU_PROFILE_CNT
+};
+
+static unsigned long g_u4AvailableInfo;
+
+noinline void GPU_Loading(unsigned char cnt, int *value)
+{
+	switch (cnt) {
+	case 1:
+		MET_TRACE("%d\n", value[0]);
+		break;
+	case 2:
+		MET_TRACE("%d,%d\n", value[0], value[1]);
+		break;
+	case 3:
+		MET_TRACE("%d,%d,%d\n", value[0], value[1], value[2]);
+		break;
+	case 4:
+		MET_TRACE("%d,%d,%d,%d\n", value[0], value[1], value[2], value[3]);
+		break;
+	default:
+		break;
+	}
+
+}
+
+noinline void GPU_Sub_Loading(unsigned int loading)
+{
+	MET_TRACE("%u\n", loading);
+}
+
+noinline void GPU_3D_Fences_Count(int count)
+{
+	MET_TRACE("%d\n", count);
+}
+
+#ifdef GPU_HAL_RUN_PREMPTIBLE
+static void gpu_GPULoading(struct work_struct *work)
+{
+	unsigned int	pu4Value[eMET_GPU_PROFILE_CNT];
+	unsigned long	u4Index = 0;
+	unsigned int	loading = 0;
+	int		count = 0;
+
+	memset(pu4Value, 0x00, sizeof(unsigned int)*eMET_GPU_PROFILE_CNT);
+	if ((1 << eMET_GPU_LOADING) & g_u4AvailableInfo) {
+		if (mtk_get_gpu_loading_symbol && mtk_get_gpu_loading_symbol(&pu4Value[u4Index]))
+			u4Index += 1;
+	}
+
+	if ((1 << eMET_GPU_BLOCK_LOADING) & g_u4AvailableInfo) {
+		if (mtk_get_gpu_block_symbol && mtk_get_gpu_block_symbol(&pu4Value[u4Index]))
+			u4Index += 1;
+	}
+
+	if ((1 << eMET_GPU_IDLE_LOADING) & g_u4AvailableInfo) {
+		if (mtk_get_gpu_idle_symbol && mtk_get_gpu_idle_symbol(&pu4Value[u4Index]))
+			u4Index += 1;
+	}
+
+	if (g_u4AvailableInfo)
+		GPU_Loading(u4Index, pu4Value);
+
+	if (mtk_get_gpu_sub_loading_symbol && mtk_get_gpu_sub_loading_symbol(&loading))
+		GPU_Sub_Loading(loading);
+
+	if (mtk_get_3D_fences_count_symbol && mtk_get_3D_fences_count_symbol(&count))
+		GPU_3D_Fences_Count(count);
+}
+#else
+static void gpu_GPULoading(unsigned long long stamp, int cpu)
+{
+	int	pu4Value[eMET_GPU_PROFILE_CNT];
+	unsigned long	u4Index = 0;
+	unsigned int	loading = 0;
+//	int		count = 0;
+
+	memset(pu4Value, 0x00, sizeof(int)*eMET_GPU_PROFILE_CNT);
+	if ((1 << eMET_GPU_LOADING) & g_u4AvailableInfo) {
+		if (mtk_get_gpu_loading_symbol) {
+			mtk_get_gpu_loading_symbol(&loading);
+			pu4Value[u4Index] = loading;
+		}
+		else
+			pu4Value[u4Index] = -1;
+
+		u4Index += 1;
+	}
+
+#if 0
+	if ((1 << eMET_GPU_BLOCK_LOADING) & g_u4AvailableInfo) {
+		if (mtk_get_gpu_block_symbol) {
+			mtk_get_gpu_block_symbol(&pu4Value[u4Index]);
+			u4Index += 1;
+		}
+	}
+
+	if ((1 << eMET_GPU_IDLE_LOADING) & g_u4AvailableInfo) {
+		if (mtk_get_gpu_idle_symbol) {
+			mtk_get_gpu_idle_symbol(&pu4Value[u4Index]);
+			u4Index += 1;
+		}
+	}
+#endif
+
+	if (g_u4AvailableInfo)
+		GPU_Loading(u4Index, pu4Value);
+
+#if 0
+	if (mtk_get_gpu_sub_loading_symbol) {
+		mtk_get_gpu_sub_loading_symbol(&loading);
+		GPU_Sub_Loading(loading);
+	}
+
+	if (mtk_get_3D_fences_count_symbol) {
+		mtk_get_3D_fences_count_symbol(&count);
+		GPU_3D_Fences_Count(count);
+	}
+#endif
+}
+#endif
+
+static void gpu_monitor_start(void)
+{
+	if (mtk_get_gpu_loading_symbol) {
+		g_u4AvailableInfo |= (1 << eMET_GPU_LOADING);
+	}
+#if 0
+	if (mtk_get_gpu_block_symbol)
+		g_u4AvailableInfo |= (1 << eMET_GPU_BLOCK_LOADING);
+	if (mtk_get_gpu_idle_symbol)
+		g_u4AvailableInfo |= (1 << eMET_GPU_IDLE_LOADING);
+#endif
+
+#ifdef GPU_HAL_RUN_PREMPTIBLE
+	INIT_DELAYED_WORK(&gpu_dwork, gpu_GPULoading);
+#endif
+}
+
+#ifdef GPU_HAL_RUN_PREMPTIBLE
+static void gpu_monitor_stop(void)
+{
+	cancel_delayed_work_sync(&gpu_dwork);
+}
+
+static void GPULoadingNotify(unsigned long long stamp, int cpu)
+{
+	schedule_delayed_work(&gpu_dwork, 0);
+}
+#endif
+
+static char help[] =
+	"  --gpu				monitor gpu status\n";
+static int gpu_status_print_help(char *buf, int len)
+{
+	return snprintf(buf, PAGE_SIZE, help);
+}
+
+static char g_pComGPUStatusHeader[] =
+	"met-info [000] 0.0: met_gpu_loading_header: ";
+static int gpu_status_print_header(char *buf, int len)
+{
+	int ret = 0;
+
+	ret = snprintf(buf, PAGE_SIZE, "%s", g_pComGPUStatusHeader);
+
+	if ((1 << eMET_GPU_LOADING) & g_u4AvailableInfo)
+		ret += snprintf(buf+ret, PAGE_SIZE-ret, "%s", "Loading\n");
+
+#if 0
+	if ((1 << eMET_GPU_BLOCK_LOADING) & g_u4AvailableInfo)
+		ret += snprintf(buf+ret, PAGE_SIZE-ret, "%s", "Blcok,");
+
+	if ((1 << eMET_GPU_IDLE_LOADING) & g_u4AvailableInfo)
+		ret += snprintf(buf+ret, PAGE_SIZE-ret, "%s", "Idle");
+
+	ret += snprintf(buf+ret, PAGE_SIZE-ret, "%s", "\n");
+
+	ret += snprintf(buf+ret, PAGE_SIZE-ret,
+			"met-info [000] 0.0: met_gpu_sub_loading_header: Loading\n");
+	ret += snprintf(buf+ret, PAGE_SIZE-ret,
+			"met-info [000] 0.0: met_gpu_3d_fences_count_header: Count\n");
+#endif
+
+	return ret;
+}
+
+struct metdevice met_gpu = {
+	.name			= "gpu",
+	.owner			= THIS_MODULE,
+	.type			= MET_TYPE_BUS,
+	.cpu_related		= 0,
+	.start			= gpu_monitor_start,
+	.mode			= 0,
+	.polling_interval	= 1,	/* ms */
+#ifdef GPU_HAL_RUN_PREMPTIBLE
+	.timed_polling		= GPULoadingNotify,
+	.stop			= gpu_monitor_stop,
+#else
+	.timed_polling		= gpu_GPULoading,
+#endif
+	.print_help		= gpu_status_print_help,
+	.print_header		= gpu_status_print_header,
+};
+
+/*
+ * GPU DVFS Monitor
+ */
+#if 0
+#define	MTK_GPU_DVFS_TYPE_ITEM(type)	#type,
+static char *gpu_dvfs_type_name[] = MTK_GPU_DVFS_TYPE_LIST;
+#undef	MTK_GPU_DVFS_TYPE_ITEM
+
+static MTK_GPU_DVFS_TYPE gpu_dvfs_type_prev;
+static unsigned long gpu_dvfs_type_freq_prev;
+static unsigned int gpu_dvfs_type_freq[ARRAY_SIZE(gpu_dvfs_type_name)];
+#endif
+
+#if 0
+noinline void GPU_DVFS(unsigned int Freq, unsigned int ThermalLimit,
+			unsigned long CustomBoost, unsigned long CustomUpbound)
+{
+	MET_TRACE("%u,%u,%lu,%lu\n", Freq, ThermalLimit, CustomBoost, CustomUpbound);
+}
+#else
+noinline void GPU_DVFS(int Freq)
+{
+	MET_TRACE("%d\n", Freq);
+}
+#endif
+
+#if 0
+noinline void GPU_DVFS_TYPE(void)
+{
+	char	*SOB, *EOB;
+
+	MET_TRACE_GETBUF(&SOB, &EOB);
+	EOB = ms_formatD_EOL(EOB, ARRAY_SIZE(gpu_dvfs_type_freq), gpu_dvfs_type_freq);
+	MET_TRACE_PUTBUF(SOB, EOB);
+}
+
+noinline void GPU_DVFS_VSYNC(unsigned long freq)
+{
+	MET_TRACE("%lu\n", freq);
+}
+
+noinline void GPU_VSYNC_OFFSET_STATUS(unsigned int event_status, unsigned int debug_status)
+{
+	MET_TRACE("%u,%u\n", event_status, debug_status);
+}
+#endif
+
+static void gpu_dvfs(void)
+{
+	unsigned int freq = 0;
+#if 0
+	unsigned int		ThermalLimit = 0;
+	MTK_GPU_DVFS_TYPE	peType;
+	unsigned long		pulFreq = 0;
+	unsigned long		CustomBoost = 0;
+	unsigned long		CustomUpbound = 0;
+	unsigned int		event_status = 0;
+	unsigned int		debug_status = 0;
+#endif
+
+	if (mtk_get_gpu_freq_symbol)
+		mtk_get_gpu_freq_symbol(&freq);
+	else
+		freq = 0;
+#if 0
+	ThermalLimit = mt_gpufreq_get_thermal_limit_freq_symbol ? mt_gpufreq_get_thermal_limit_freq_symbol() : 0;
+	if (mtk_get_custom_boost_gpu_freq_symbol)
+		mtk_get_custom_boost_gpu_freq_symbol(&CustomBoost);
+	if (mtk_get_custom_upbound_gpu_freq_symbol)
+		mtk_get_custom_upbound_gpu_freq_symbol(&CustomUpbound);
+#endif
+//	GPU_DVFS(freq, ThermalLimit, CustomBoost, CustomUpbound);
+	GPU_DVFS(freq);
+
+#if 0
+	/* gpu dvfs type */
+	if (mtk_get_gpu_dvfs_from_symbol && mtk_get_gpu_dvfs_from_symbol(&peType, &pulFreq)) {
+		if (gpu_dvfs_type_prev != peType || gpu_dvfs_type_freq_prev != pulFreq) {
+			gpu_dvfs_type_freq[gpu_dvfs_type_prev] = 0;
+			gpu_dvfs_type_prev = peType;
+			gpu_dvfs_type_freq_prev = pulFreq;
+			gpu_dvfs_type_freq[gpu_dvfs_type_prev] = gpu_dvfs_type_freq_prev;
+			GPU_DVFS_TYPE();
+		}
+	}
+
+	if (mtk_get_vsync_based_target_freq_symbol && mtk_get_vsync_based_target_freq_symbol(&pulFreq))
+		GPU_DVFS_VSYNC(pulFreq);
+
+	if (mtk_get_vsync_offset_event_status_symbol && mtk_get_vsync_offset_debug_status_symbol) {
+		if (mtk_get_vsync_offset_event_status_symbol(&event_status)
+		    && mtk_get_vsync_offset_debug_status_symbol(&debug_status)) {
+			GPU_VSYNC_OFFSET_STATUS(event_status, debug_status);
+		}
+	}
+#endif
+}
+
+static void gpu_dvfs_work(struct work_struct *work)
+{
+	gpu_dvfs();
+}
+
+static void gpu_dvfs_monitor_start(void)
+{
+	gpu_dvfs();
+	INIT_DELAYED_WORK(&gpu_dvfs_dwork, gpu_dvfs_work);
+}
+
+static void gpu_dvfs_monitor_stop(void)
+{
+	cancel_delayed_work_sync(&gpu_dvfs_dwork);
+	gpu_dvfs();
+}
+
+static void gpu_dvfs_monitor_polling(unsigned long long stamp, int cpu)
+{
+	schedule_delayed_work(&gpu_dvfs_dwork, 0);
+}
+
+static int gpu_dvfs_print_help(char *buf, int len)
+{
+	return snprintf(buf, PAGE_SIZE,
+			"  --gpu-dvfs				monitor gpu freq\n");
+}
+
+static int gpu_dvfs_print_header(char *buf, int len)
+{
+	int ret = 0;
+//	int i = 0;
+
+	ret = snprintf(buf, PAGE_SIZE,
+			"met-info [000] 0.0: met_gpu_dvfs_header: ");
+#if 0
+	ret += snprintf(buf+ret, PAGE_SIZE-ret,
+			"Freq(kHz),ThermalLimit(kHz),CustomBoost,CustomUpbound\n");
+#else
+	ret += snprintf(buf+ret, PAGE_SIZE-ret,
+			"Freq(kHz)\n");
+#endif
+
+#if 0
+	ret += snprintf(buf+ret, PAGE_SIZE-ret,
+			"met-info [000] 0.0: met_gpu_dvfs_type_header: %s", gpu_dvfs_type_name[0]);
+	for (i = 1; i < ARRAY_SIZE(gpu_dvfs_type_name); i++)
+		ret += snprintf(buf+ret, PAGE_SIZE-ret, ",%s", gpu_dvfs_type_name[i]);
+	ret += snprintf(buf+ret, PAGE_SIZE-ret, "%s", "\n");
+
+	ret += snprintf(buf+ret, PAGE_SIZE-ret,
+			"met-info [000] 0.0: met_gpu_dvfs_vsync_header: VSYNC Based Freq\n");
+	ret += snprintf(buf+ret, PAGE_SIZE-ret,
+			"met-info [000] 0.0: met_gpu_vsync_offset_status_header: Event Status,Debug Status\n");
+#endif
+
+	return ret;
+}
+
+struct metdevice met_gpudvfs = {
+	.name			= "gpu-dvfs",
+	.owner			= THIS_MODULE,
+	.type			= MET_TYPE_BUS,
+	.cpu_related		= 0,
+	.start			= gpu_dvfs_monitor_start,
+	.stop			= gpu_dvfs_monitor_stop,
+	.polling_interval	= 1,	/* ms */
+	.timed_polling		= gpu_dvfs_monitor_polling,
+	.print_help		= gpu_dvfs_print_help,
+	.print_header		= gpu_dvfs_print_header,
+	.ondiemet_mode		= 0,
+};
+
+/*
+ * GPU MEM monitor
+ */
+static unsigned long g_u4MemProfileIsOn;
+
+static void gpu_mem_monitor_start(void)
+{
+	if (!mtk_get_gpu_memory_usage_symbol)
+		return;
+
+	g_u4MemProfileIsOn = 1;
+}
+
+noinline void GPU_MEM(unsigned long long stamp, int cpu)
+{
+	unsigned int u4Value = 0;
+
+	if (!mtk_get_gpu_memory_usage_symbol)
+		return;
+
+	if (g_u4MemProfileIsOn == 1) {
+		mtk_get_gpu_memory_usage_symbol(&u4Value);
+		MET_TRACE("%d\n", u4Value);
+	}
+}
+
+static void gpu_mem_monitor_stop(void)
+{
+	g_u4MemProfileIsOn = 0;
+}
+
+static char help_mem[] =
+	"  --gpu-mem				monitor gpu memory status\n";
+static int gpu_mem_status_print_help(char *buf, int len)
+{
+	return snprintf(buf, PAGE_SIZE, help_mem);
+}
+
+static char g_pComGPUMemHeader[] =
+	"met-info [000] 0.0: met_gpu_mem_header: Usage(Byte)\n";
+static int gpu_mem_status_print_header(char *buf, int len)
+{
+	return snprintf(buf, PAGE_SIZE, g_pComGPUMemHeader);
+}
+
+struct metdevice met_gpumem = {
+	.name			= "gpu-mem",
+	.owner			= THIS_MODULE,
+	.type			= MET_TYPE_BUS,
+	.cpu_related		= 0,
+	.start			= gpu_mem_monitor_start,
+	.stop			= gpu_mem_monitor_stop,
+	.mode			= 0,
+	.polling_interval	= 1,	/* ms */
+	.timed_polling		= GPU_MEM,
+	.print_help		= gpu_mem_status_print_help,
+	.print_header		= gpu_mem_status_print_header,
+};
+
+#if 0
+
+/*
+ * GPU power monitor
+ */
+static unsigned long g_u4PowerProfileIsOn;
+
+#ifdef GPU_HAL_RUN_PREMPTIBLE
+noinline void GPU_Power(struct work_struct *work)
+{
+	unsigned int u4Value = 0;
+
+	if (!mtk_get_gpu_power_loading_symbol)
+		return;
+
+	mtk_get_gpu_power_loading_symbol(&u4Value);
+	MET_TRACE("%d\n", u4Value);
+}
+
+static void GPU_PowerNotify(unsigned long long stamp, int cpu)
+{
+	if (g_u4PowerProfileIsOn == 1)
+		schedule_delayed_work(&gpu_pwr_dwork, 0);
+}
+#else
+noinline void GPU_Power(unsigned long long stamp, int cpu)
+{
+	unsigned int u4Value = 0;
+
+	if (!mtk_get_gpu_power_loading_symbol)
+		return;
+
+	if (g_u4PowerProfileIsOn == 1) {
+		mtk_get_gpu_power_loading_symbol(&u4Value);
+		MET_TRACE("%d\n", u4Value);
+	}
+}
+#endif
+
+static void gpu_Power_monitor_start(void)
+{
+	if (!mtk_get_gpu_power_loading_symbol)
+		return;
+
+#if 0
+	if (mtk_get_gpu_power_loading_symbol(&u4Value))
+		g_u4PowerProfileIsOn = 1;
+#endif
+	g_u4PowerProfileIsOn = 1;
+#ifdef GPU_HAL_RUN_PREMPTIBLE
+	INIT_DELAYED_WORK(&gpu_pwr_dwork, GPU_Power);
+#endif
+}
+
+static void gpu_Power_monitor_stop(void)
+{
+	g_u4PowerProfileIsOn = 0;
+
+#ifdef GPU_HAL_RUN_PREMPTIBLE
+	cancel_delayed_work_sync(&gpu_pwr_dwork);
+#endif
+}
+
+static char help_pwr[] =
+	"  --gpu-pwr				monitor gpu power status\n";
+static int gpu_Power_status_print_help(char *buf, int len)
+{
+	return snprintf(buf, PAGE_SIZE, help_pwr);
+}
+
+static char g_pComGPUPowerHeader[] =
+	"met-info [000] 0.0: met_gpu_power_header: Loading\n";
+static int gpu_Power_status_print_header(char *buf, int len)
+{
+	return snprintf(buf, PAGE_SIZE, g_pComGPUPowerHeader);
+}
+
+struct metdevice met_gpupwr = {
+	.name			= "gpu-pwr",
+	.owner			= THIS_MODULE,
+	.type			= MET_TYPE_BUS,
+	.cpu_related		= 0,
+	.start			= gpu_Power_monitor_start,
+	.stop			= gpu_Power_monitor_stop,
+	.mode			= 0,
+	.polling_interval	= 1,	/* ms */
+#ifdef GPU_HAL_RUN_PREMPTIBLE
+	.timed_polling		= GPU_PowerNotify,
+#else
+	.timed_polling		= GPU_Power,
+#endif
+	.print_help		= gpu_Power_status_print_help,
+	.print_header		= gpu_Power_status_print_header,
+};
+
+
+/*
+ * GPU PMU
+ */
+#define UNUSE_ARG(arg) ((void)arg)
+
+#ifdef GPU_HAL_RUN_PREMPTIBLE
+static struct delayed_work gpu_pmu_dwork;
+#endif
+
+#define MAX_PMU_STR_LEN (1024 * 5)
+
+static const char help_pmu[] = "  --gpu-pmu				monitor gpu pmu status";
+static const char header_pmu[] = "met-info [000] 0.0: met_gpu_pmu_header: ";
+static char pmu_str[MAX_PMU_STR_LEN];
+static int pmu_cnt;
+static int gpu_pwr_status = 1;
+static GPU_PMU *pmu_list;
+
+
+noinline void GPU_PMU_RAW(
+	unsigned long long stamp,
+	int cpu)
+{
+	bool ret;
+	int i = 0;
+	char *SOB, *EOB;
+	unsigned int value[pmu_cnt];
+
+	if (stamp == 0 && cpu == 0) {
+		for (i = 0; i < pmu_cnt; i++)
+			value[i] = 0;
+
+		MET_TRACE_GETBUF(&SOB, &EOB);
+		EOB = ms_formatH(EOB, pmu_cnt, value);
+		MET_TRACE_PUTBUF(SOB, EOB);
+		return;
+	}
+
+	if (mtk_get_gpu_pmu_swapnreset_symbol) {
+		ret = mtk_get_gpu_pmu_swapnreset_symbol(pmu_list, pmu_cnt);
+		if (ret) {
+			for (i = 0; i < pmu_cnt; i++) {
+				if (pmu_list[i].overflow)
+					pmu_list[i].value = 0xFFFFFFFF;
+				value[i] = pmu_list[i].value;
+			}
+			MET_TRACE_GETBUF(&SOB, &EOB);
+			EOB = ms_formatH(EOB, pmu_cnt, value);
+			MET_TRACE_PUTBUF(SOB, EOB);
+		}
+	}
+}
+
+static int create_gpu_pmu_list(void)
+{
+	int ret = 0;
+	int len = 0;
+	int i = 0;
+
+	if (mtk_get_gpu_pmu_init_symbol) {
+		ret = mtk_get_gpu_pmu_init(NULL, 0, &pmu_cnt);
+		if (pmu_cnt == 0 || ret == 0)
+			return 0;
+	} else
+		return 0;
+
+	pmu_list = kmalloc_array(pmu_cnt, sizeof(GPU_PMU), GFP_KERNEL);
+	if (pmu_list) {
+		memset(pmu_list, 0x00, sizeof(GPU_PMU)*pmu_cnt);
+		ret = mtk_get_gpu_pmu_init(pmu_list, pmu_cnt, NULL);
+
+		memset(pmu_str, 0x00, MAX_PMU_STR_LEN);
+		len = snprintf(pmu_str, MAX_PMU_STR_LEN, "%s", pmu_list[0].name);
+		for (i = 1; i < pmu_cnt; i++)
+			len += snprintf(pmu_str + len, MAX_PMU_STR_LEN - len, ",%s", pmu_list[i].name);
+
+		/*
+		* dummy read in order to reset GPU PMU counter
+		*/
+		if (mtk_get_gpu_pmu_swapnreset_symbol)
+			mtk_get_gpu_pmu_swapnreset_symbol(pmu_list, pmu_cnt);
+	}
+
+	return ret;
+}
+
+static void delete_gpu_pmu_list(void)
+{
+	kfree(pmu_list);
+	pmu_list = NULL;
+	pmu_cnt = 0;
+}
+
+static void gpu_pwr_status_cb(int on)
+{
+	MET_TRACE("on = %d\n", on);
+
+	if (on == 1) {
+		/*
+		* dummy read in order to reset GPU PMU counter
+		*/
+		if (mtk_get_gpu_pmu_swapnreset_symbol)
+			mtk_get_gpu_pmu_swapnreset_symbol(pmu_list, pmu_cnt);
+
+	} else {
+		GPU_PMU_RAW(1, 0);
+		GPU_PMU_RAW(0, 0);
+	}
+
+	gpu_pwr_status = on;
+}
+
+static void gpu_pmu_monitor_start(void)
+{
+	int ret;
+
+	ret = create_gpu_pmu_list();
+	if (ret == 0)
+		return;
+
+	if (mtk_register_gpu_power_change_symbol)
+		mtk_register_gpu_power_change_symbol("met_gpu", gpu_pwr_status_cb);
+
+#ifdef GPU_HAL_RUN_PREMPTIBLE
+	INIT_DELAYED_WORK(&gpu_pmu_dwork, GPU_PMU_RAW);
+#endif
+}
+
+static void gpu_pmu_monitor_stop(void)
+{
+#ifdef GPU_HAL_RUN_PREMPTIBLE
+	cancel_delayed_work_sync(&gpu_pmu_dwork);
+#endif
+
+	if (mtk_unregister_gpu_power_change_symbol)
+		mtk_unregister_gpu_power_change_symbol("met_gpu");
+	delete_gpu_pmu_list();
+
+#if 0
+	/* stop polling counter */
+	if (mtk_get_gpu_pmu_swapnreset_stop_symbol)
+		mtk_get_gpu_pmu_swapnreset_stop_symbol();
+	/* release resource */
+	if (mtk_get_gpu_pmu_deinit_symbol)
+		mtk_get_gpu_pmu_deinit_symbol();
+#endif
+}
+
+#ifdef GPU_HAL_RUN_PREMPTIBLE
+static void gpu_pmu_timed_polling_notify(
+	unsigned long long stamp,
+	int cpu)
+{
+	UNUSE_ARG(stamp);
+	UNUSE_ARG(cpu);
+
+	if (gpu_pwr_status == 1)
+		schedule_delayed_work(&gpu_pmu_dwork, 0);
+}
+#else
+static void gpu_pmu_timed_polling(
+	unsigned long long stamp,
+	int cpu)
+{
+	UNUSE_ARG(stamp);
+	UNUSE_ARG(cpu);
+
+	if (gpu_pwr_status == 1)
+		GPU_PMU_RAW(stamp, cpu);
+}
+#endif
+
+static int gpu_pmu_print_help(
+	char *buf,
+	int len)
+{
+	UNUSE_ARG(len);
+	return snprintf(buf, PAGE_SIZE, "%s\n", help_pmu);
+}
+
+static int gpu_pmu_print_header(
+	char *buf,
+	int len)
+{
+	len = 0;
+
+	len = snprintf(buf, PAGE_SIZE, "%s", header_pmu);
+	len += snprintf(buf + len, PAGE_SIZE - len, "%s\n", pmu_str);
+
+	return len;
+}
+
+struct metdevice met_gpu_pmu = {
+	.name			= "gpu-pmu",
+	.owner			= THIS_MODULE,
+	.type			= MET_TYPE_PMU,
+	.cpu_related		= 0,
+	.start			= gpu_pmu_monitor_start,
+	.stop			= gpu_pmu_monitor_stop,
+	.mode			= 0,
+	.polling_interval	= 1,	/* ms */
+#ifdef GPU_HAL_RUN_PREMPTIBLE
+	.timed_polling		= gpu_pmu_timed_polling_notify,
+#else
+	.timed_polling		= gpu_pmu_timed_polling,
+#endif
+	.print_help		= gpu_pmu_print_help,
+	.print_header		= gpu_pmu_print_header,
+};
+
+#endif
diff --git a/src/devtools/met-driver/4.19/mt2712/mtk_gpu_metmonitor.h b/src/devtools/met-driver/4.19/mt2712/mtk_gpu_metmonitor.h
new file mode 100644
index 0000000..069c534
--- /dev/null
+++ b/src/devtools/met-driver/4.19/mt2712/mtk_gpu_metmonitor.h
@@ -0,0 +1,18 @@
+/*
+ * 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.
+ */
+
+#ifndef _MT_GPU_METMONITOR_H_
+
+#define _MT_GPU_METMONITOR_H_
+
+#endif				/* _MT_GPU_METMONITOR_H_ */
diff --git a/src/devtools/met-driver/4.19/mt2712/mtk_typedefs.h b/src/devtools/met-driver/4.19/mt2712/mtk_typedefs.h
new file mode 100644
index 0000000..2b7af2d
--- /dev/null
+++ b/src/devtools/met-driver/4.19/mt2712/mtk_typedefs.h
@@ -0,0 +1,318 @@
+/*
+ * 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.
+ */
+
+#ifndef _MT_TYPEDEFS_H__
+
+/*
+ *  KOBJ ATTR Manipulations Macros
+ */
+
+#define KOBJ_ITEM_LIST(args...)		args
+
+/*
+ * Declaring KOBJ attributes
+ */
+#define DECLARE_KOBJ_ATTR(attr_name) \
+	static struct kobj_attribute attr_name##_attr = \
+		__ATTR(attr_name, 0664, attr_name##_show, attr_name##_store);
+
+#define DECLARE_KOBJ_ATTR_RO(attr_name) \
+	static struct kobj_attribute attr_name##_attr = \
+		__ATTR_RO(attr_name);
+
+/*
+ * Declaring KOBJ attributes with integer variable
+ */
+/* normal version */
+#define DECLARE_KOBJ_ATTR_SHOW_INT(attr_name, var_name) \
+	static ssize_t attr_name##_show( \
+		struct kobject *kobj, \
+		struct kobj_attribute *attr, \
+		char *buf) \
+	{ \
+		return snprintf(buf, PAGE_SIZE, "%d\n", var_name); \
+	}
+#define DECLARE_KOBJ_ATTR_STORE_INT(attr_name, var_name) \
+	static ssize_t attr_name##_store( \
+		struct kobject *kobj, \
+		struct kobj_attribute *attr, \
+		const char *buf, \
+		size_t n) \
+	{ \
+		int	val; \
+		if (kstrtoint(buf, 0, &val) != 0) { \
+			return -EINVAL; \
+		} \
+		var_name = val; \
+		return n; \
+	}
+#define DECLARE_KOBJ_ATTR_INT(attr_name, var_name) \
+	DECLARE_KOBJ_ATTR_SHOW_INT(attr_name, var_name) \
+	DECLARE_KOBJ_ATTR_STORE_INT(attr_name, var_name) \
+	DECLARE_KOBJ_ATTR(attr_name)
+#define DECLARE_KOBJ_ATTR_RO_INT(attr_name, var_name) \
+	DECLARE_KOBJ_ATTR_SHOW_INT(attr_name, var_name) \
+	DECLARE_KOBJ_ATTR_RO(attr_name)
+
+/* cond-check version */
+#define DECLARE_KOBJ_ATTR_STORE_INT_CHECK(attr_name, var_name, cond) \
+	static ssize_t attr_name##_store( \
+		struct kobject *kobj, \
+		struct kobj_attribute *attr, \
+		const char *buf, \
+		size_t n) \
+	{ \
+		int	var_name##temp = var_name; \
+		if (kstrtoint(buf, 0, &var_name) != 0) { \
+			var_name = var_name##temp; \
+			return -EINVAL; \
+		} \
+		if (cond) { \
+			return n; \
+		} else { \
+			var_name = var_name##temp; \
+			return -EINVAL; \
+		} \
+	}
+/* Note: the name of val in cond can NOT be the same as var_name */
+#define DECLARE_KOBJ_ATTR_INT_CHECK(attr_name, var_name, cond) \
+	DECLARE_KOBJ_ATTR_SHOW_INT(attr_name, var_name) \
+	DECLARE_KOBJ_ATTR_STORE_INT_CHECK(attr_name, var_name, cond) \
+	DECLARE_KOBJ_ATTR(attr_name)
+
+/* helper procedure version */
+#define DECLARE_KOBJ_ATTR_SHOW_INT_PROC(attr_name, var_name, func) \
+	static ssize_t attr_name##_show( \
+		struct kobject *kobj, \
+		struct kobj_attribute *attr, \
+		char *buf) \
+	{ \
+		return func(kobj, attr, buf, var_name); \
+	}
+#define DECLARE_KOBJ_ATTR_STORE_INT_PROC(attr_name, var_name, func) \
+	static ssize_t attr_name##_store( \
+		struct kobject *kobj, \
+		struct kobj_attribute *attr, \
+		const char *buf, \
+		size_t n) \
+	{ \
+		return func(kobj, attr, buf, n, &(var_name)); \
+	}
+#define DECLARE_KOBJ_ATTR_INT_PROC(attr_name, var_name, show, store) \
+	DECLARE_KOBJ_ATTR_SHOW_INT_PROC(attr_name, var_name, show) \
+	DECLARE_KOBJ_ATTR_STORE_INT_PROC(attr_name, var_name, store) \
+	DECLARE_KOBJ_ATTR(attr_name)
+
+/*
+ * Declaring KOBJ attributes with integer(hex) variable
+ */
+/* normal version */
+#define DECLARE_KOBJ_ATTR_SHOW_HEX(attr_name, var_name) \
+	static ssize_t attr_name##_show( \
+		struct kobject *kobj, \
+		struct kobj_attribute *attr, \
+		char *buf) \
+	{ \
+		return snprintf(buf, PAGE_SIZE, "%x\n", var_name); \
+	}
+#define DECLARE_KOBJ_ATTR_STORE_HEX(attr_name, var_name) \
+	static ssize_t attr_name##_store( \
+		struct kobject *kobj, \
+		struct kobj_attribute *attr, \
+		const char *buf, \
+		size_t n) \
+	{ \
+		unsigned int	val; \
+		if (kstrtouint(buf, 0, &val) != 0) { \
+			return -EINVAL; \
+		} \
+		var_name = val; \
+		return n; \
+	}
+#define DECLARE_KOBJ_ATTR_HEX(attr_name, var_name) \
+	DECLARE_KOBJ_ATTR_SHOW_HEX(attr_name, var_name) \
+	DECLARE_KOBJ_ATTR_STORE_HEX(attr_name, var_name) \
+	DECLARE_KOBJ_ATTR(attr_name)
+#define DECLARE_KOBJ_ATTR_RO_HEX(attr_name, var_name) \
+	DECLARE_KOBJ_ATTR_SHOW_HEX(attr_name, var_name) \
+	DECLARE_KOBJ_ATTR_RO(attr_name)
+
+/* cond-check version */
+#define DECLARE_KOBJ_ATTR_STORE_HEX_CHECK(attr_name, var_name, cond) \
+	static ssize_t attr_name##_store( \
+		struct kobject *kobj, \
+		struct kobj_attribute *attr, \
+		const char *buf, \
+		size_t n) \
+	{ \
+		unsigned int	var_name##temp = var_name; \
+		if (kstrtouint(buf, 0, &var_name) != 0) { \
+			var_name = var_name##temp; \
+			return -EINVAL; \
+		} \
+		if (cond) { \
+			return n; \
+		} else { \
+			var_name = var_name##temp; \
+			return -EINVAL; \
+		} \
+	}
+/* Note: the name of val in cond can NOT be the same as var_name */
+#define DECLARE_KOBJ_ATTR_HEX_CHECK(attr_name, var_name, cond) \
+	DECLARE_KOBJ_ATTR_SHOW_HEX(attr_name, var_name) \
+	DECLARE_KOBJ_ATTR_STORE_HEX_CHECK(attr_name, var_name, cond) \
+	DECLARE_KOBJ_ATTR(attr_name)
+
+/* helper procedure version */
+#define DECLARE_KOBJ_ATTR_SHOW_HEX_PROC(attr_name, var_name, func) \
+	static ssize_t attr_name##_show( \
+		struct kobject *kobj, \
+		struct kobj_attribute *attr, \
+		char *buf) \
+	{ \
+		return func(kobj, attr, buf, var_name); \
+	}
+#define DECLARE_KOBJ_ATTR_STORE_HEX_PROC(attr_name, var_name, func) \
+	static ssize_t attr_name##_store( \
+		struct kobject *kobj, \
+		struct kobj_attribute *attr, \
+		const char *buf, \
+		size_t n) \
+	{ \
+		return func(kobj, attr, buf, n, &(var_name)); \
+	}
+#define DECLARE_KOBJ_ATTR_HEX_PROC(attr_name, var_name, show, store) \
+	DECLARE_KOBJ_ATTR_SHOW_HEX_PROC(attr_name, var_name, show) \
+	DECLARE_KOBJ_ATTR_STORE_HEX_PROC(attr_name, var_name, store) \
+	DECLARE_KOBJ_ATTR(attr_name)
+
+/*
+ * Declaring KOBJ attributes with string variable
+ */
+#define DECLARE_KOBJ_ATTR_SHOW_STR(attr_name, var_name) \
+	static ssize_t attr_name##_show( \
+		struct kobject *kobj, \
+		struct kobj_attribute *attr, \
+		char *buf) \
+	{ \
+		return snprintf(buf, PAGE_SIZE, "%s", var_name); \
+	}
+
+#define DECLARE_KOBJ_ATTR_RO_STR(attr_name, var_name) \
+	DECLARE_KOBJ_ATTR_SHOW_STR(attr_name, var_name) \
+	DECLARE_KOBJ_ATTR_RO(attr_name)
+
+/*
+ * Declaring KOBJ attributes with integer list variable
+ */
+#define DECLARE_KOBJ_ATTR_INT_LIST_ITEM(list_name, list) \
+	static struct list_name##_list_item_t { \
+		int	key; \
+		int	val; \
+	} const list_name##_list_item[] = { list };
+#define DECLARE_KOBJ_ATTR_SHOW_INT_LIST(attr_name, var_name, list_name) \
+	static ssize_t attr_name##_show( \
+		struct kobject *kobj, \
+		struct kobj_attribute *attr, \
+		char *buf) \
+	{ \
+		int	i; \
+		for (i = 0; i < ARRAY_SIZE(list_name##_list_item); i++) { \
+			if (var_name == list_name##_list_item[i].key) { \
+				return snprintf(buf, \
+						PAGE_SIZE, \
+						"%d\n", \
+						list_name##_list_item[i].val); \
+			} \
+		} \
+		return snprintf(buf, PAGE_SIZE, "%d\n", -1); \
+	}
+#define DECLARE_KOBJ_ATTR_STORE_INT_LIST(attr_name, var_name, list_name) \
+	static ssize_t attr_name##_store( \
+		struct kobject *kobj, \
+		struct kobj_attribute *attr, \
+		const char *buf, \
+		size_t n) \
+	{ \
+		int	value; \
+		int	i; \
+		if (kstrtoint(buf, 10, &value) != 0) \
+			return -EINVAL; \
+		for (i = 0; i < ARRAY_SIZE(list_name##_list_item); i++) { \
+			if (value == list_name##_list_item[i].val) { \
+				var_name = list_name##_list_item[i].key; \
+				return n; \
+			} \
+		} \
+		return -EINVAL; \
+	}
+#define DECLARE_KOBJ_ATTR_INT_LIST(attr_name, var_name, list_name) \
+	DECLARE_KOBJ_ATTR_SHOW_INT_LIST(attr_name, var_name, list_name) \
+	DECLARE_KOBJ_ATTR_STORE_INT_LIST(attr_name, var_name, list_name) \
+	DECLARE_KOBJ_ATTR(attr_name)
+
+/*
+ * Declaring KOBJ attributes with string list variable
+ */
+#define DECLARE_KOBJ_ATTR_STR_LIST_ITEM(list_name, list) \
+	static struct list_name##_list_item_t { \
+		int	key; \
+		char	*val; \
+	} const list_name##_list_item[] = { list };
+#define DECLARE_KOBJ_ATTR_SHOW_STR_LIST(attr_name, var_name, list_name) \
+	static ssize_t attr_name##_show( \
+		struct kobject *kobj, \
+		struct kobj_attribute *attr, \
+		char *buf) \
+	{ \
+		int	i; \
+		for (i = 0; i < ARRAY_SIZE(list_name##_list_item); i++) { \
+			if (var_name == list_name##_list_item[i].key) { \
+				return snprintf(buf, \
+						PAGE_SIZE, \
+						"%s\n", \
+						list_name##_list_item[i].val); \
+			} \
+		} \
+		return snprintf(buf, PAGE_SIZE, "%s\n", "ERR"); \
+	}
+#define DECLARE_KOBJ_ATTR_STORE_STR_LIST(attr_name, var_name, list_name) \
+	static ssize_t attr_name##_store( \
+		struct kobject *kobj, \
+		struct kobj_attribute *attr, \
+		const char *buf, \
+		size_t n) \
+	{ \
+		int	i; \
+		for (i = 0; i < ARRAY_SIZE(list_name##_list_item); i++) { \
+			if (strncasecmp(buf, \
+					list_name##_list_item[i].val, \
+					strlen(list_name##_list_item[i].val)) == 0) { \
+				var_name = list_name##_list_item[i].key; \
+				return n; \
+			} \
+		} \
+		return -EINVAL; \
+	}
+#define DECLARE_KOBJ_ATTR_STR_LIST(attr_name, var_name, list_name) \
+	DECLARE_KOBJ_ATTR_SHOW_STR_LIST(attr_name, var_name, list_name) \
+	DECLARE_KOBJ_ATTR_STORE_STR_LIST(attr_name, var_name, list_name) \
+	DECLARE_KOBJ_ATTR(attr_name)
+
+/*
+ *  MET Debug Message
+ */
+#define METINFO(format, ...)	pr_debug("[MET]%s: "format, __func__, ##__VA_ARGS__)
+#define METERROR(format, ...)	pr_debug("[MET][ERR]%s: "format, __func__, ##__VA_ARGS__)
+
+#endif	/* _MT_TYPEDEFS_H__ */
diff --git a/src/devtools/met-driver/4.19/mt2712/ondiemet.c b/src/devtools/met-driver/4.19/mt2712/ondiemet.c
new file mode 100644
index 0000000..5247fa7
--- /dev/null
+++ b/src/devtools/met-driver/4.19/mt2712/ondiemet.c
@@ -0,0 +1,92 @@
+/*
+ * 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 "ondiemet.h"
+
+/* record enabled modules */
+unsigned int ondiemet_module[ONDIEMET_NUM];
+EXPORT_SYMBOL(ondiemet_module);
+
+void (*scp_start[ONDIEMET_NUM]) (void) = {
+sspm_start, NULL, NULL};
+
+void (*scp_stop[ONDIEMET_NUM]) (void) = {
+sspm_stop, NULL, NULL};
+
+void (*scp_extract[ONDIEMET_NUM]) (void) = {
+sspm_extract, NULL, NULL};
+
+/* record which MCU is started to generate data */
+int ondiemet_module_started[ONDIEMET_NUM];
+
+int ondiemet_attr_init(struct device *dev)
+{
+	int ret;
+
+	ret = sspm_attr_init(dev);
+	if (ret != 0) {
+		pr_debug("can not create device file: sspm related\n");
+		return ret;
+	}
+
+	return 0;
+
+}
+
+int ondiemet_attr_uninit(struct device *dev)
+{
+	int ret;
+
+	ret = sspm_attr_uninit(dev);
+	if (ret != 0) {
+		pr_debug("can not delete device file: sspm related\n");
+		return ret;
+	}
+
+	return 0;
+
+}
+
+void ondiemet_start(void)
+{
+	int i;
+
+	for (i = 0; i < ONDIEMET_NUM; i++) {
+		if (ondiemet_module[i] != 0) {
+			ondiemet_module_started[i] = 1;
+			(*scp_start[i]) ();
+		}
+	}
+}
+
+void ondiemet_stop(void)
+{
+	int i;
+
+	for (i = 0; i < ONDIEMET_NUM; i++) {
+		if (ondiemet_module[i] != 0) {
+			(*scp_stop[i]) ();
+			ondiemet_module_started[i] = 0;
+		}
+	}
+}
+
+void ondiemet_extract(void)
+{
+	int i;
+
+	for (i = 0; i < ONDIEMET_NUM; i++) {
+		if (ondiemet_module[i] != 0)
+			(*scp_extract[i]) ();
+	}
+}
diff --git a/src/devtools/met-driver/4.19/mt2712/ondiemet.h b/src/devtools/met-driver/4.19/mt2712/ondiemet.h
new file mode 100644
index 0000000..3fff604
--- /dev/null
+++ b/src/devtools/met-driver/4.19/mt2712/ondiemet.h
@@ -0,0 +1,37 @@
+/*
+ * 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.
+ */
+
+#ifndef __ONDIEMET_H
+#define __ONDIEMET_H
+
+#include "ondiemet_log.h"
+
+extern void ondiemet_extract(void);
+extern void ondiemet_stop(void);
+extern void ondiemet_start(void);
+
+#define ONDIEMET_SSPM  0
+#define ONDIEMET_NUM  3		/* total number of supported */
+extern unsigned int ondiemet_module[];
+extern void sspm_start(void);
+extern void sspm_stop(void);
+extern void sspm_extract(void);
+extern int sspm_attr_init(struct device *dev);
+extern int sspm_attr_uninit(struct device *dev);
+
+extern int ondiemet_attr_init(struct device *dev);
+extern int ondiemet_attr_uninit(struct device *dev);
+
+extern int sspm_buffer_size;
+
+#endif				/* __ONDIEMET_H */
diff --git a/src/devtools/met-driver/4.19/mt2712/ondiemet_log.c b/src/devtools/met-driver/4.19/mt2712/ondiemet_log.c
new file mode 100644
index 0000000..4f3ad69
--- /dev/null
+++ b/src/devtools/met-driver/4.19/mt2712/ondiemet_log.c
@@ -0,0 +1,516 @@
+/*
+ * 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/kernel.h>
+#include <linux/errno.h>
+#include <linux/slab.h>
+#include <linux/list.h>
+#include <linux/debugfs.h>
+#include <linux/mutex.h>
+#include <linux/semaphore.h>
+#include <linux/freezer.h>
+#include <linux/uaccess.h>
+#include <linux/completion.h>
+
+#include "ondiemet_log.h"
+
+#define ONDIEMET_LOG_REQ 1
+/* TODO: abandon this constatnt */
+#define ONDIEMET_LOG_STOP 2
+
+#define PID_NONE (-1)
+
+#define ONDIEMET_LOG_STOP_MODE 0
+#define ONDIEMET_LOG_RUN_MODE 1
+#define ONDIEMET_LOG_DEBUG_MODE 2
+
+static int ondiemet_trace_run;
+static struct dentry *dbgfs_met_dir;
+
+struct mutex lock_tracef;
+struct ondiemet_log_req_q_t {
+	struct list_head listq;
+	struct mutex lockq;
+	/* struct semaphore new_evt_sema; */
+	struct completion new_evt_comp;
+	int closeq_flag;
+} ondiemet_log_req_q;
+
+struct ondiemet_log_req {
+	struct list_head list;
+	int cmd_type;
+	const char *src;
+	size_t num;
+
+	void (*on_fini_cb)(const void *p);
+	const void *param;
+};
+
+#define __ondiemet_log_req_init(req, cmd, s, n, pf, p)	\
+	do {						\
+		INIT_LIST_HEAD(&req->list);		\
+		req->cmd_type = cmd;			\
+		req->src = s;				\
+		req->num = n;				\
+		req->on_fini_cb = pf;			\
+		req->param = p;				\
+	} while (0)
+
+#define __ondiemet_log_req_fini(req)		        \
+	do {					        \
+		if (req->on_fini_cb)			\
+			req->on_fini_cb(req->param);	\
+		kfree(req);				\
+	} while (0)
+
+static void __ondiemet_log_req_q_init(struct ondiemet_log_req_q_t *q)
+{
+	INIT_LIST_HEAD(&q->listq);
+	mutex_init(&q->lockq);
+	/* sema_init(&q->new_evt_sema, 0); */
+	init_completion(&q->new_evt_comp);
+	q->closeq_flag = 1;
+}
+
+/* undequeue is seen as a roll-back operation, so it can be done even when the queue is closed */
+static void __ondiemet_log_req_undeq(struct ondiemet_log_req *req)
+{
+	mutex_lock(&ondiemet_log_req_q.lockq);
+	list_add(&req->list, &ondiemet_log_req_q.listq);
+	mutex_unlock(&ondiemet_log_req_q.lockq);
+
+	/* up(&ondiemet_log_req_q.new_evt_sema); */
+	complete(&ondiemet_log_req_q.new_evt_comp);
+}
+
+static int __ondiemet_log_req_enq(struct ondiemet_log_req *req)
+{
+	mutex_lock(&ondiemet_log_req_q.lockq);
+	if (ondiemet_log_req_q.closeq_flag) {
+		mutex_unlock(&ondiemet_log_req_q.lockq);
+		return -EBUSY;
+	}
+
+	list_add_tail(&req->list, &ondiemet_log_req_q.listq);
+	if (req->cmd_type == ONDIEMET_LOG_STOP)
+		ondiemet_log_req_q.closeq_flag = 1;
+	mutex_unlock(&ondiemet_log_req_q.lockq);
+
+	/* up(&ondiemet_log_req_q.new_evt_sema); */
+	complete(&ondiemet_log_req_q.new_evt_comp);
+
+	return 0;
+}
+
+int ondiemet_log_req_enq(const char *src, size_t num, void (*on_fini_cb)(const void *p), const void *param)
+{
+	struct ondiemet_log_req *req = kmalloc(sizeof(*req), GFP_KERNEL);
+
+	__ondiemet_log_req_init(req, ONDIEMET_LOG_REQ, src, num, on_fini_cb, param);
+	return __ondiemet_log_req_enq(req);
+}
+
+/*int down_freezable_interruptible(struct semaphore *sem) */
+int down_freezable_interruptible(struct completion *comp)
+{
+
+	int ret;
+
+	freezer_do_not_count();
+	/* ret = down_interruptible(sem); */
+	ret = wait_for_completion_interruptible(comp);
+	freezer_count();
+
+	return ret;
+}
+
+struct ondiemet_log_req *__ondiemet_log_req_deq(void)
+{
+	struct ondiemet_log_req *ret_req;
+
+	/*if (down_freezable_interruptible(&ondiemet_log_req_q.new_evt_sema))*/
+	if (down_freezable_interruptible(&ondiemet_log_req_q.new_evt_comp))
+		return NULL;
+
+	mutex_lock(&ondiemet_log_req_q.lockq);
+	ret_req = list_entry(ondiemet_log_req_q.listq.next, struct ondiemet_log_req, list);
+	list_del_init(&ret_req->list);
+	mutex_unlock(&ondiemet_log_req_q.lockq);
+
+	return ret_req;
+}
+
+void __ondiemet_log_req_open(void)
+{
+	mutex_lock(&ondiemet_log_req_q.lockq);
+	ondiemet_log_req_q.closeq_flag = 0;
+	mutex_unlock(&ondiemet_log_req_q.lockq);
+}
+
+int __ondiemet_log_req_closed(void)
+{
+	int ret;
+
+	mutex_lock(&ondiemet_log_req_q.lockq);
+	ret = ondiemet_log_req_q.closeq_flag && list_empty(&ondiemet_log_req_q.listq);
+	mutex_unlock(&ondiemet_log_req_q.lockq);
+
+	return ret;
+}
+
+int __ondiemet_log_req_working(void)
+{
+	int ret;
+
+	mutex_lock(&ondiemet_log_req_q.lockq);
+	ret = !ondiemet_log_req_q.closeq_flag;
+	mutex_unlock(&ondiemet_log_req_q.lockq);
+
+	return ret;
+}
+
+static void *__ondiemet_trace_seq_next(struct seq_file *seqf, loff_t *offset)
+{
+	struct ondiemet_log_req *next_req;
+
+	if (ondiemet_trace_run == ONDIEMET_LOG_DEBUG_MODE)
+		pr_debug("[met] __ondiemet_trace_seq_next: pid: %d\n", current->pid);
+
+	if (__ondiemet_log_req_closed())
+		return NULL;
+
+	next_req = __ondiemet_log_req_deq();
+
+	if (next_req == NULL)
+		return NULL;
+
+	if (next_req->cmd_type == ONDIEMET_LOG_STOP) {
+		__ondiemet_log_req_fini(next_req);
+		return NULL;
+	}
+
+	return (void *) next_req;
+}
+
+struct mutex lock_trace_owner_pid;
+pid_t trace_owner_pid = PID_NONE;
+static void *ondiemet_trace_seq_start(struct seq_file *seqf, loff_t *offset)
+{
+	void *ret;
+
+	if (ondiemet_trace_run == ONDIEMET_LOG_DEBUG_MODE) {
+		pr_debug("[met] ondiemet_trace_seq_start: locked_pid: %d, pid: %d, offset: %llu\n",
+			 trace_owner_pid, current->pid, *offset);
+	}
+
+	if (!mutex_trylock(&lock_tracef))
+		return NULL;
+
+	mutex_lock(&lock_trace_owner_pid);
+	trace_owner_pid = current->pid;
+	mutex_unlock(&lock_trace_owner_pid);
+
+	ret = __ondiemet_trace_seq_next(seqf, offset);
+
+	return ret;
+}
+
+static void *ondiemet_trace_seq_next(struct seq_file *seqf, void *p, loff_t *offset)
+{
+	if (ondiemet_trace_run == ONDIEMET_LOG_DEBUG_MODE)
+		pr_debug("[met] ondiemet_trace_seq_next: pid: %d\n", current->pid);
+
+	(*offset)++;
+	return __ondiemet_trace_seq_next(seqf, offset);
+}
+
+static int ondiemet_trace_seq_show(struct seq_file *seqf, void *p)
+{
+	struct ondiemet_log_req *req = (struct ondiemet_log_req *) p;
+	size_t l_sz;
+	size_t r_sz;
+	struct ondiemet_log_req *l_req;
+	struct ondiemet_log_req *r_req;
+	int ret;
+
+	if (ondiemet_trace_run == ONDIEMET_LOG_DEBUG_MODE)
+		pr_debug("[met] ondiemet_trace_seq_show: pid: %d\n", current->pid);
+
+	if (req->num >= seqf->size) {
+		l_req = kmalloc(sizeof(*req), GFP_KERNEL);
+		r_req = req;
+
+		l_sz = seqf->size >> 1;
+		r_sz = req->num - l_sz;
+		__ondiemet_log_req_init(l_req, ONDIEMET_LOG_REQ, req->src, l_sz, NULL, NULL);
+		__ondiemet_log_req_init(r_req, ONDIEMET_LOG_REQ, req->src + l_sz,
+					r_sz, req->on_fini_cb, req->param);
+
+		__ondiemet_log_req_undeq(r_req);
+		req = l_req;
+
+		if (ondiemet_trace_run == ONDIEMET_LOG_DEBUG_MODE)
+			pr_debug("[met] ondiemet_trace_seq_show: split request\n");
+	}
+
+	ret = seq_write(seqf, req->src, req->num);
+
+	if (ret) {
+		/* check if seq_file buffer overflows */
+		if (seqf->count == seqf->size) {
+			__ondiemet_log_req_undeq(req);
+		} else {
+			if (ondiemet_trace_run == ONDIEMET_LOG_DEBUG_MODE)
+				pr_debug("[met] ondiemet_trace_seq_show: reading trace record failed, some data may be lost or corrupted\n");
+			__ondiemet_log_req_fini(req);
+		}
+		return 0;
+	}
+
+	__ondiemet_log_req_fini(req);
+	return 0;
+}
+
+static void ondiemet_trace_seq_stop(struct seq_file *seqf, void *p)
+{
+	if (ondiemet_trace_run == ONDIEMET_LOG_DEBUG_MODE)
+		pr_debug("[met] ondiemet_trace_seq_stop: pid: %d\n", current->pid);
+
+	mutex_lock(&lock_trace_owner_pid);
+	if (current->pid == trace_owner_pid) {
+		trace_owner_pid = PID_NONE;
+		mutex_unlock(&lock_tracef);
+	}
+	mutex_unlock(&lock_trace_owner_pid);
+}
+
+static const struct seq_operations ondiemet_trace_seq_ops = {
+	.start = ondiemet_trace_seq_start,
+	.next = ondiemet_trace_seq_next,
+	.stop = ondiemet_trace_seq_stop,
+	.show = ondiemet_trace_seq_show
+};
+
+static int ondiemet_trace_open(struct inode *inode, struct file *fp)
+{
+	return seq_open(fp, &ondiemet_trace_seq_ops);
+}
+
+static const struct file_operations ondiemet_trace_fops = {
+	.owner = THIS_MODULE,
+	.open = ondiemet_trace_open,
+	.read = seq_read,
+	.llseek = seq_lseek,
+	.release = seq_release
+};
+
+/*struct semaphore log_start_sema;*/
+struct completion log_start_comp;
+int ondiemet_log_manager_start(void)
+{
+	int ret;
+
+	/* TODO: choose a better return value */
+	if (__ondiemet_log_req_working())
+		return -EINVAL;
+
+	if (!__ondiemet_log_req_closed()) {
+		/*ret = down_killable(&log_start_sema);*/
+		ret = wait_for_completion_killable(&log_start_comp);
+		if (ret)
+			return ret;
+	}
+
+	__ondiemet_log_req_open();
+
+	return 0;
+}
+
+/*struct semaphore log_stop_sema;*/
+struct completion log_stop_comp;
+static void __log_stop_cb(const void *p)
+{
+	/* up(&log_start_sema); */
+	/* up(&log_stop_sema); */
+	complete(&log_start_comp);
+	complete(&log_stop_comp);
+}
+
+int ondiemet_log_manager_stop(void)
+{
+	int ret;
+	struct ondiemet_log_req *req;
+
+	/* TODO: choose a better return value */
+	if (__ondiemet_log_req_closed())
+		return -EINVAL;
+
+	req = kmalloc(sizeof(*req), GFP_KERNEL);
+
+	__ondiemet_log_req_init(req, ONDIEMET_LOG_STOP, NULL, 0, __log_stop_cb, NULL);
+	/*sema_init(&log_start_sema, 0); */
+	/*sema_init(&log_stop_sema, 0); */
+	init_completion(&log_start_comp);
+	init_completion(&log_stop_comp);
+
+	ret = __ondiemet_log_req_enq(req);
+	if (ret)
+		return ret;
+
+	/* XXX: blocking may be break by SIGKILL */
+	/*return down_killable(&log_stop_sema);*/
+	return wait_for_completion_killable(&log_stop_comp);
+}
+
+int ondiemet_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 = kstrtouint(str, 16, value);
+	} else {
+		ret = kstrtouint(str, 10, value);
+	}
+
+	if (ret != 0)
+		return -1;
+
+	return 0;
+}
+
+/* XXX: seq_file will output only when a page is filled */
+static ssize_t ondiemet_log_write_store(struct device *dev,
+					struct device_attribute *attr,
+					const char *buf,
+					size_t count)
+{
+	char *plog = NULL;
+
+	plog = kmalloc_array(count, sizeof(*plog), GFP_KERNEL);
+	if (!plog) {
+		/* TODO: use a better error code */
+		return -EINVAL;
+	}
+
+	memcpy(plog, buf, count);
+
+	mutex_lock(&dev->mutex);
+	ondiemet_log_req_enq(plog, strnlen(plog, count), kfree, plog);
+	mutex_unlock(&dev->mutex);
+
+	return count;
+}
+
+static DEVICE_ATTR(ondiemet_log_write, 0664, NULL, ondiemet_log_write_store);
+
+static ssize_t ondiemet_log_run_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+	int sz;
+
+	mutex_lock(&dev->mutex);
+	sz = snprintf(buf, PAGE_SIZE, "%d\n", ondiemet_trace_run);
+	mutex_unlock(&dev->mutex);
+	return sz;
+}
+
+static ssize_t ondiemet_log_run_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
+{
+	int ret;
+	int prev_run_state;
+
+	mutex_lock(&dev->mutex);
+
+	prev_run_state = ondiemet_trace_run;
+
+	if (kstrtoint(buf, 10, &ondiemet_trace_run) != 0)
+		return -EINVAL;
+
+	if (ondiemet_trace_run <= ONDIEMET_LOG_STOP_MODE) {
+		ondiemet_trace_run = ONDIEMET_LOG_STOP_MODE;
+		ondiemet_log_manager_stop();
+
+		if (prev_run_state == ONDIEMET_LOG_DEBUG_MODE)
+			device_remove_file(dev, &dev_attr_ondiemet_log_write);
+	} else if (ondiemet_trace_run == ONDIEMET_LOG_RUN_MODE) {
+		ondiemet_trace_run = ONDIEMET_LOG_RUN_MODE;
+		ondiemet_log_manager_start();
+
+		if (prev_run_state == ONDIEMET_LOG_DEBUG_MODE)
+			device_remove_file(dev, &dev_attr_ondiemet_log_write);
+	} else {
+		ondiemet_trace_run = ONDIEMET_LOG_DEBUG_MODE;
+		ondiemet_log_manager_start();
+
+		if (prev_run_state != ONDIEMET_LOG_DEBUG_MODE) {
+			ret = device_create_file(dev, &dev_attr_ondiemet_log_write);
+			if (ret != 0)
+				pr_debug("[met] can not create device node: ondiemet_log_write\n");
+		}
+	}
+
+	mutex_unlock(&dev->mutex);
+
+	return count;
+}
+
+static DEVICE_ATTR(ondiemet_log_run, 0660, ondiemet_log_run_show, ondiemet_log_run_store);
+
+int ondiemet_log_manager_init(struct device *dev)
+{
+	int ret;
+	struct dentry *d;
+
+	mutex_init(&lock_tracef);
+
+	__ondiemet_log_req_q_init(&ondiemet_log_req_q);
+
+	/*sema_init(&log_start_sema, 0);*/
+	/*sema_init(&log_stop_sema, 0);*/
+	init_completion(&log_start_comp);
+	init_completion(&log_stop_comp);
+
+	dbgfs_met_dir = debugfs_create_dir("ondiemet", NULL);
+	if (!dbgfs_met_dir) {
+		pr_debug("[met] can not create debugfs directory: met\n");
+		return -ENOMEM;
+	}
+
+	mutex_init(&lock_trace_owner_pid);
+
+	d = debugfs_create_file("trace", 0644, dbgfs_met_dir, NULL, &ondiemet_trace_fops);
+	if (!d) {
+		pr_debug("[met] can not create devide node in debugfs: ondiemet_trace\n");
+		return -ENOMEM;
+	}
+
+	ondiemet_trace_run = __ondiemet_log_req_working();
+	ret = device_create_file(dev, &dev_attr_ondiemet_log_run);
+	if (ret != 0) {
+		pr_debug("[met] can not create device node: ondiemet_log_run\n");
+		return ret;
+	}
+
+	return 0;
+}
+
+int ondiemet_log_manager_uninit(struct device *dev)
+{
+	device_remove_file(dev, &dev_attr_ondiemet_log_run);
+	debugfs_remove_recursive(dbgfs_met_dir);
+	return 0;
+}
diff --git a/src/devtools/met-driver/4.19/mt2712/ondiemet_log.h b/src/devtools/met-driver/4.19/mt2712/ondiemet_log.h
new file mode 100644
index 0000000..cfe8be9
--- /dev/null
+++ b/src/devtools/met-driver/4.19/mt2712/ondiemet_log.h
@@ -0,0 +1,27 @@
+/*
+ * 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.
+ */
+
+#ifndef _ONDIEMET_LOG_H_
+#define _ONDIEMET_LOG_H_
+
+#include <linux/device.h>
+
+int ondiemet_log_manager_init(struct device *dev);
+int ondiemet_log_manager_uninit(struct device *dev);
+int ondiemet_log_manager_start(void);
+/* Log manager can be reactivated by inserting new requests, i.e., calling ondiemet_log_req_enq() */
+int ondiemet_log_manager_stop(void);
+int ondiemet_log_req_enq(const char *src, size_t num, void (*on_fini_cb) (const void *p),
+			 const void *param);
+
+#endif				/* _ONDIEMET_LOG_H_ */
diff --git a/src/devtools/met-driver/4.19/mt2712/power.c b/src/devtools/met-driver/4.19/mt2712/power.c
new file mode 100644
index 0000000..c57d907
--- /dev/null
+++ b/src/devtools/met-driver/4.19/mt2712/power.c
@@ -0,0 +1,68 @@
+/*
+ * 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.
+ */
+
+/*
+ * 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/cpufreq.h>
+#include <trace/events/power.h>
+
+#include "power.h"
+#include "met_drv.h"
+#include "met_kernel_symbol.h"
+
+noinline void cpu_frequency(unsigned int frequency, unsigned int cpu_id)
+{
+	/* suppose this symbol is available, otherwise, the met.ko will fail */
+	met_cpu_frequency_symbol(frequency, cpu_id);
+}
+
+void force_power_log(int cpu)
+{
+	struct cpufreq_policy *p;
+
+	if (cpu == POWER_LOG_ALL) {
+		for_each_possible_cpu(cpu) {
+			p = cpufreq_cpu_get(cpu);
+			if (p != NULL) {
+				cpu_frequency(p->cur, cpu);
+				cpufreq_cpu_put(p);
+			} else {
+				cpu_frequency(0, cpu);
+			}
+		}
+	} else {
+		p = cpufreq_cpu_get(cpu);
+		if (p != NULL) {
+			cpu_frequency(p->cur, cpu);
+			cpufreq_cpu_put(p);
+		} else {
+			cpu_frequency(0, cpu);
+		}
+	}
+}
+
+void force_power_log_val(unsigned int frequency, int cpu)
+{
+	cpu_frequency(frequency, cpu);
+}
diff --git a/src/devtools/met-driver/4.19/mt2712/power.h b/src/devtools/met-driver/4.19/mt2712/power.h
new file mode 100644
index 0000000..8a0e8f0
--- /dev/null
+++ b/src/devtools/met-driver/4.19/mt2712/power.h
@@ -0,0 +1,21 @@
+/*
+ * 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.
+ */
+
+#ifndef _POWER_H_
+#define _POWER_H_
+
+#define POWER_LOG_ALL	-1
+void force_power_log(int cpu);
+void force_power_log_val(unsigned int frequency, int cpu);
+
+#endif				/* _POWER_H_ */
diff --git a/src/devtools/met-driver/4.19/mt2712/sampler.c b/src/devtools/met-driver/4.19/mt2712/sampler.c
new file mode 100644
index 0000000..dca6883
--- /dev/null
+++ b/src/devtools/met-driver/4.19/mt2712/sampler.c
@@ -0,0 +1,714 @@
+/*
+ * 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/kernel.h>
+#include <linux/cpuhotplug.h>
+#include <linux/cpu.h>
+#include <linux/sched.h>
+#include <linux/notifier.h>
+#include <linux/module.h>
+#include <linux/irq.h>
+#if 0				/* fix me later, no such file on current tree */
+#include <mach/mt_cpuxgpt.h>
+#endif
+#include <asm/arch_timer.h>
+
+#define	MET_USER_EVENT_SUPPORT
+#include "interface.h"
+#include "sampler.h"
+#include "met_struct.h"
+#include "util.h"
+#include "switch.h"
+#include "trace.h"
+#include "met_drv.h"
+#include "met_tag.h" /* for tracing_mark_write */
+
+#include "cpu_pmu.h" /* for using kernel perf PMU driver */
+#include "cpu_pmu_v2.h" /* for using kernel perf PMU v2 driver */
+#include "met_kernel_symbol.h"
+
+#undef	DEBUG_CPU_NOTIFY
+/* #define DEBUG_CPU_NOTIFY */
+#if	defined(DEBUG_CPU_NOTIFY)
+#ifdef CONFIG_MET_MODULE
+#define	dbg_met_tag_oneshot	met_tag_oneshot_real
+#else
+#define	dbg_met_tag_oneshot	met_tag_oneshot
+#endif /* CONFIG_MET_MODULE */
+#else
+#define	dbg_met_tag_oneshot(class_id, name, value)	({ 0; })
+#endif
+
+static int start;
+static unsigned int online_cpu_map;
+static int curr_polling_cpu;
+static int cpu_related_cnt;
+
+static int pmu_profiling_version = 0;
+
+static DEFINE_PER_CPU(unsigned int, perf_cpuid);
+
+static int preferred_cpu_list[] = { 0, 4, 1, 2, 3, 5, 6, 7 };
+
+int get_pmu_profiling_version()
+{
+	return pmu_profiling_version;
+}
+
+static int calc_preferred_polling_cpu(unsigned int cpu_map)
+{
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(preferred_cpu_list); i++) {
+		if (cpu_map & (1 << preferred_cpu_list[i]))
+			return preferred_cpu_list[i];
+	}
+
+	return -1;
+}
+
+static void wq_sync_buffer(struct work_struct *work)
+{
+	int cpu;
+	struct delayed_work *dw = container_of(work, struct delayed_work, work);
+	struct met_cpu_struct *met_cpu_ptr = container_of(dw, struct met_cpu_struct, dwork);
+
+	cpu = smp_processor_id();
+	if (met_cpu_ptr->cpu != cpu) {
+		/* panic("ERROR"); */
+		return;
+	}
+
+	/* sync_samples(cpu); */
+	/* don't re-add the work if we're shutting down */
+	if (met_cpu_ptr->work_enabled)
+		schedule_delayed_work(dw, DEFAULT_TIMER_EXPIRE);
+}
+
+static enum hrtimer_restart met_hrtimer_notify(struct hrtimer *hrtimer)
+{
+	int cpu;
+	int *count;
+	unsigned long long stamp;
+	struct met_cpu_struct *met_cpu_ptr = container_of(hrtimer, struct met_cpu_struct, hrtimer);
+	struct metdevice *c;
+#if	defined(DEBUG_CPU_NOTIFY)
+	char msg[32];
+#endif
+
+	cpu = smp_processor_id();
+#if	defined(DEBUG_CPU_NOTIFY)
+	{
+		char msg[32];
+
+		snprintf(msg, sizeof(msg), "met_hrtimer notify_%d", cpu);
+		dbg_met_tag_oneshot(0, msg, 1);
+	}
+#endif
+
+	if (met_cpu_ptr->cpu != cpu) {
+		/* panic("ERROR2"); */
+		dbg_met_tag_oneshot(0, msg, -3);
+		return HRTIMER_NORESTART;
+	}
+
+	list_for_each_entry(c, &met_list, list) {
+		if (c->ondiemet_mode == 0) {
+			if ((c->mode == 0) || (c->timed_polling == NULL))
+				continue;
+		} else if (c->ondiemet_mode == 1) {
+			if ((c->mode == 0) || (c->ondiemet_timed_polling == NULL))
+				continue;
+		} else if (c->ondiemet_mode == 2) {
+			if ((c->mode == 0) || ((c->timed_polling == NULL)
+					       && (c->ondiemet_timed_polling == NULL)))
+				continue;
+		}
+
+		count = per_cpu_ptr(c->polling_count, cpu);
+		if ((*count) > 0) {
+			(*count)--;
+			continue;
+		}
+
+		*(count) = c->polling_count_reload;
+
+		stamp = cpu_clock(cpu);
+
+		if (c->cpu_related == 0) {
+			if (cpu == curr_polling_cpu) {
+				if (c->ondiemet_mode == 0) {
+					c->timed_polling(stamp, 0);
+				} else if (c->ondiemet_mode == 1) {
+					c->ondiemet_timed_polling(stamp, 0);
+				} else if (c->ondiemet_mode == 2) {
+					if (c->timed_polling)
+						c->timed_polling(stamp, 0);
+					if (c->ondiemet_timed_polling)
+						c->ondiemet_timed_polling(stamp, 0);
+				}
+			}
+		} else {
+			if (c->ondiemet_mode == 0) {
+				c->timed_polling(stamp, cpu);
+			} else if (c->ondiemet_mode == 1) {
+				c->ondiemet_timed_polling(stamp, cpu);
+			} else if (c->ondiemet_mode == 2) {
+				if (c->timed_polling)
+					c->timed_polling(stamp, 0);
+				if (c->ondiemet_timed_polling)
+					c->ondiemet_timed_polling(stamp, 0);
+			}
+		}
+	}
+
+	if (met_cpu_ptr->hrtimer_online_check) {
+		online_cpu_map |= (1 << cpu);
+		met_cpu_ptr->hrtimer_online_check = 0;
+		dbg_met_tag_oneshot(0, "met_online check done", cpu);
+		if (calc_preferred_polling_cpu(online_cpu_map) == cpu) {
+			curr_polling_cpu = cpu;
+			dbg_met_tag_oneshot(0, "met_curr polling cpu", cpu);
+		}
+	}
+
+	if (met_cpu_ptr->work_enabled) {
+		hrtimer_forward_now(hrtimer, ns_to_ktime(DEFAULT_HRTIMER_EXPIRE));
+		dbg_met_tag_oneshot(0, msg, 0);
+		return HRTIMER_RESTART;
+	}
+	dbg_met_tag_oneshot(0, msg, 0);
+	return HRTIMER_NORESTART;
+}
+
+static void __met_hrtimer_start(void *unused)
+{
+	struct met_cpu_struct *met_cpu_ptr = NULL;
+	struct hrtimer *hrtimer = NULL;
+	/* struct delayed_work *dw; */
+	struct metdevice *c;
+
+	met_cpu_ptr = this_cpu_ptr(&met_cpu);
+#if	defined(DEBUG_CPU_NOTIFY)
+	{
+		char msg[32];
+
+		snprintf(msg, sizeof(msg), "met_hrtimer status_%d", met_cpu_ptr->cpu);
+		dbg_met_tag_oneshot(0, msg, 1);
+	}
+#endif
+	/*
+	 * do not open HRtimer when EVENT timer enable
+	 */
+//	if (!(met_switch.mode & MT_SWITCH_EVENT_TIMER)) {
+		hrtimer = &met_cpu_ptr->hrtimer;
+		/* dw = &met_cpu_ptr->dwork; */
+
+		hrtimer_init(hrtimer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
+		hrtimer->function = met_hrtimer_notify;
+//	}
+
+	list_for_each_entry(c, &met_list, list) {
+		if (c->ondiemet_mode == 0) {
+			if ((c->cpu_related) && (c->mode) && (c->start))
+				c->start();
+		} else if (c->ondiemet_mode == 1) {
+			if (((c->cpu_related)) && (c->mode) && (c->ondiemet_start))
+				c->ondiemet_start();
+		} else if (c->ondiemet_mode == 2) {
+			if ((c->cpu_related) && (c->mode) && (c->start))
+				c->start();
+			if (((c->cpu_related)) && (c->mode) && (c->ondiemet_start))
+				c->ondiemet_start();
+		}
+	}
+	/*
+	 * do not open HRtimer when EVENT timer enable
+	 */
+//	if (!(met_switch.mode & MT_SWITCH_EVENT_TIMER)) {
+		if (DEFAULT_HRTIMER_EXPIRE) {
+			met_cpu_ptr->work_enabled = 1;
+			/* schedule_delayed_work_on(smp_processor_id(), dw, DEFAULT_TIMER_EXPIRE); */
+			hrtimer_start(hrtimer, ns_to_ktime(DEFAULT_HRTIMER_EXPIRE),
+				      HRTIMER_MODE_REL_PINNED);
+		}
+//	}
+}
+
+static void __met_hrtimer_stop(void *unused)
+{
+	struct met_cpu_struct *met_cpu_ptr;
+	struct hrtimer *hrtimer;
+	/* struct delayed_work *dw; */
+	struct metdevice *c;
+
+	met_cpu_ptr = this_cpu_ptr(&met_cpu);
+#if	defined(DEBUG_CPU_NOTIFY)
+	{
+		char msg[32];
+
+		snprintf(msg, sizeof(msg), "met_hrtimer status_%d", met_cpu_ptr->cpu);
+		dbg_met_tag_oneshot(0, msg, 0);
+	}
+#endif
+	/*
+	 * do not open HRtimer when EVENT timer enable
+	 */
+//	if (!(met_switch.mode & MT_SWITCH_EVENT_TIMER)) {
+		hrtimer = &met_cpu_ptr->hrtimer;
+		/* dw = &met_cpu_ptr->dwork; */
+
+		met_cpu_ptr->work_enabled = 0;
+		hrtimer_cancel(hrtimer);
+		/* cancel_delayed_work_sync(dw); */
+//	}
+	list_for_each_entry(c, &met_list, list) {
+		if (c->ondiemet_mode == 0) {
+			if ((c->cpu_related) && (c->mode) && (c->stop))
+				c->stop();
+		} else if (c->ondiemet_mode == 1) {
+			if ((c->cpu_related) && (c->mode) && (c->ondiemet_stop))
+				c->ondiemet_stop();
+		} else if (c->ondiemet_mode == 2) {
+			if ((c->cpu_related) && (c->mode) && (c->stop))
+				c->stop();
+			if ((c->cpu_related) && (c->mode) && (c->ondiemet_stop))
+				c->ondiemet_stop();
+		}
+	}
+}
+
+static int met_pmu_cpu_notify(enum met_action action, unsigned int cpu)
+{
+	struct met_cpu_struct *met_cpu_ptr;
+	struct delayed_work *dw;
+	int preferred_polling_cpu;
+
+	if (start == 0)
+		return NOTIFY_OK;
+
+#if	defined(DEBUG_CPU_NOTIFY)
+	{
+		char msg[32];
+
+		snprintf(msg, sizeof(msg), "met_cpu notify_%d", cpu);
+		dbg_met_tag_oneshot(0, msg, action);
+	}
+#elif	defined(PR_CPU_NOTIFY)
+	{
+		char msg[32];
+
+		if (met_cpu_notify) {
+			snprintf(msg, sizeof(msg), "met_cpu notify_%d", cpu);
+			dbg_met_tag_oneshot(0, msg, action);
+		}
+	}
+#endif
+
+	if (cpu < 0 || cpu >= NR_CPUS)
+		return NOTIFY_OK;
+
+	switch (action) {
+	case MET_CPU_ONLINE:
+		met_cpu_ptr = &per_cpu(met_cpu, cpu);
+		met_cpu_ptr->hrtimer_online_check = 1;
+		dbg_met_tag_oneshot(0, "met_online check", cpu);
+
+		if (cpu_related_cnt == 0) {
+			/*printk("%s, %d: curr_polling_cpu is alive = %d\n",
+			 *		__func__, __LINE__, online_cpu_map & (1 << curr_polling_cpu));
+			 */
+
+			online_cpu_map |= (1 << cpu);
+
+			/* check curr_polling_cpu is alive, if it is down,
+			 * start current cpu hrtimer, and change it to be currr_pollling_cpu
+			 */
+			if ((online_cpu_map & (1 << curr_polling_cpu)) == 0) {
+				met_smp_call_function_single_symbol(cpu, __met_hrtimer_start, NULL, 1);
+				curr_polling_cpu = cpu;
+			}
+		} else
+			met_smp_call_function_single_symbol(cpu, __met_hrtimer_start, NULL, 1);
+
+		if (met_cpu_pmu_method != 0) {
+			if (pmu_profiling_version == 1)
+				met_perf_cpupmu_online(cpu);
+#ifdef MET_SUPPORT_CPUPMU_V2
+			else if (pmu_profiling_version == 2)
+				met_perf_cpupmu_online_v2(cpu);
+#endif
+		}
+
+#ifdef CONFIG_CPU_FREQ
+		force_power_log(cpu);
+#endif
+		break;
+
+	case MET_CPU_OFFLINE:
+		online_cpu_map &= ~(1 << cpu);
+		dbg_met_tag_oneshot(0, "met_offline cpu", cpu);
+		if (cpu == curr_polling_cpu) {
+			/* printk("%s, %d: curr_polling_cpu %d is down\n",
+			 *		__func__, __LINE__, curr_polling_cpu);
+			 */
+			preferred_polling_cpu = calc_preferred_polling_cpu(online_cpu_map);
+			/* printk("%s, %d: preferred_polling_cpu = %d\n",
+			 *		__func__, __LINE__, preferred_polling_cpu);
+			 */
+			if (preferred_polling_cpu != -1) {
+				curr_polling_cpu = preferred_polling_cpu;
+				dbg_met_tag_oneshot(0, "met_curr polling cpu", curr_polling_cpu);
+
+				if (cpu_related_cnt == 0)
+					/* printk("%s, %d: start cpu %d hrtimer start\n",
+					 *		__func__, __LINE__, curr_polling_cpu);
+					 */
+					met_smp_call_function_single_symbol(curr_polling_cpu, __met_hrtimer_start, NULL, 1);
+			}
+		}
+
+		met_smp_call_function_single_symbol(cpu, __met_hrtimer_stop, NULL, 1);
+		if (met_cpu_pmu_method != 0) {
+			if (pmu_profiling_version == 1) {
+				per_cpu(perf_cpuid, cpu) = cpu;
+				met_smp_call_function_single_symbol(cpu, met_perf_cpupmu_down, (void *)&per_cpu(perf_cpuid, cpu), 1);
+			}
+#ifdef MET_SUPPORT_CPUPMU_V2
+			else if (pmu_profiling_version == 2) {
+				per_cpu(perf_cpuid, cpu) = cpu;
+				met_smp_call_function_single_symbol(cpu, met_perf_cpupmu_down_v2, (void *)&per_cpu(perf_cpuid, cpu), 1);
+			}
+#endif
+		}
+
+		met_cpu_ptr = &per_cpu(met_cpu, cpu);
+		dw = &met_cpu_ptr->dwork;
+		cancel_delayed_work_sync(dw);
+
+		/* sync_samples(cpu); */
+		break;
+	default:
+		break;
+	}
+
+	return NOTIFY_OK;
+}
+
+static int _met_pmu_cpu_notify_online(unsigned int cpu)
+{
+	met_pmu_cpu_notify(MET_CPU_ONLINE, cpu);
+
+	return 0;
+}
+
+static int _met_pmu_cpu_notify_offline(unsigned int cpu)
+{
+	met_pmu_cpu_notify(MET_CPU_OFFLINE, cpu);
+
+	return 0;
+}
+
+int sampler_start(void)
+{
+	int ret, cpu;
+	struct met_cpu_struct *met_cpu_ptr;
+	struct metdevice *c;
+	int preferred_polling_cpu;
+
+	met_set_suspend_notify(0);
+
+#ifdef	CONFIG_CPU_FREQ
+	force_power_log(POWER_LOG_ALL);
+#endif
+
+	for_each_possible_cpu(cpu) {
+		met_cpu_ptr = &per_cpu(met_cpu, cpu);
+		met_cpu_ptr->work_enabled = 0;
+		met_cpu_ptr->hrtimer_online_check = 0;
+		hrtimer_init(&met_cpu_ptr->hrtimer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
+		met_cpu_ptr->hrtimer.function = met_hrtimer_notify;
+		INIT_DELAYED_WORK(&met_cpu_ptr->dwork, wq_sync_buffer);
+	}
+
+	start = 0;
+	ret = cpuhp_setup_state_nocalls(CPUHP_AP_ONLINE_DYN,
+						   "met:online",
+						   _met_pmu_cpu_notify_online,
+						   _met_pmu_cpu_notify_offline);
+
+	list_for_each_entry(c, &met_list, list) {
+
+		if (try_module_get(c->owner) == 0)
+			continue;
+#ifdef CONFIG_MET_ARM_32BIT
+		if (strcmp(c->name, "cpu") == 0) {
+			if ((c->mode) && (c->start)) {
+				pmu_profiling_version = 1;
+				cpu_related_cnt = 1;
+				if (met_cpu_pmu_method != 0)
+					met_perf_cpupmu_start();
+				else
+					c->start();
+			}
+			continue;
+		}
+#endif
+
+#ifdef MET_SUPPORT_CPUPMU_V2
+		if (strcmp(c->name, "cpu-pmu") == 0) {
+			if ((c->mode) && (c->start)) {
+				pmu_profiling_version = 2;
+				cpu_related_cnt = 1;
+				if (met_cpu_pmu_method != 0)
+					met_perf_cpupmu_start_v2();
+				else
+					c->start();
+			}
+			continue;
+		} else if (strcmp(c->name, "cpu") == 0) {
+			if ((c->mode) && (c->start)) {
+				pmu_profiling_version = 1;
+				cpu_related_cnt = 1;
+				if (met_cpu_pmu_method != 0)
+					met_perf_cpupmu_start();
+				else
+					c->start();
+			}
+			continue;
+		}
+#endif
+		if ((c->mode) && (c->cpu_related == 1))
+			cpu_related_cnt = 1;
+
+		if (c->ondiemet_mode == 0) {
+			if ((!(c->cpu_related)) && (c->mode) && (c->start))
+				c->start();
+		} else if (c->ondiemet_mode == 1) {
+			if ((!(c->cpu_related)) && (c->mode) && (c->ondiemet_start))
+				c->ondiemet_start();
+		} else if (c->ondiemet_mode == 2) {
+			if ((!(c->cpu_related)) && (c->mode) && (c->start))
+				c->start();
+			if ((!(c->cpu_related)) && (c->mode) && (c->ondiemet_start))
+				c->ondiemet_start();
+		}
+	}
+
+	get_online_cpus();
+	online_cpu_map = 0;
+	for_each_online_cpu(cpu) {
+		online_cpu_map |= (1 << cpu);
+	}
+	dbg_met_tag_oneshot(0, "met_online cpu map", online_cpu_map);
+	preferred_polling_cpu = calc_preferred_polling_cpu(online_cpu_map);
+	if (preferred_polling_cpu != -1)
+		curr_polling_cpu = preferred_polling_cpu;
+	dbg_met_tag_oneshot(0, "met_curr polling cpu", curr_polling_cpu);
+	start = 1;
+
+	if (cpu_related_cnt == 0)
+		met_smp_call_function_single_symbol(curr_polling_cpu, __met_hrtimer_start, NULL, 1);
+	else
+		on_each_cpu(__met_hrtimer_start, NULL, 1);
+	put_online_cpus();
+
+	return ret;
+}
+
+void sampler_stop(void)
+{
+	int cpu;
+	struct met_cpu_struct *met_cpu_ptr;
+	struct metdevice *c;
+	struct delayed_work *dw;
+
+	get_online_cpus();
+
+	on_each_cpu(__met_hrtimer_stop, NULL, 1);
+/* for_each_online_cpu(cpu) { */
+	for_each_possible_cpu(cpu) {	/* Just for case */
+		met_cpu_ptr = &per_cpu(met_cpu, cpu);
+		dw = &met_cpu_ptr->dwork;
+		cancel_delayed_work_sync(dw);
+		/* sync_samples(cpu); */
+	}
+
+	start = 0;
+	put_online_cpus();
+
+	cpuhp_remove_state_nocalls(CPUHP_AP_ONLINE_DYN);
+
+	list_for_each_entry(c, &met_list, list) {
+#ifdef CONFIG_MET_ARM_32BIT
+		if (strcmp(c->name, "cpu") == 0) {
+			pmu_profiling_version = 0;
+			if ((c->mode) && (c->stop)) {
+				if (met_cpu_pmu_method != 0)
+					met_perf_cpupmu_stop();
+				else
+					c->stop();
+			}
+			module_put(c->owner);
+			continue;
+		}
+#endif
+
+#ifdef MET_SUPPORT_CPUPMU_V2
+		if (strcmp(c->name, "cpu-pmu") == 0) {
+			pmu_profiling_version = 0;
+			if ((c->mode) && (c->stop)) {
+				if (met_cpu_pmu_method != 0)
+					met_perf_cpupmu_stop_v2();
+				else
+					c->stop();
+			}
+			module_put(c->owner);
+			continue;
+		}
+		else if (strcmp(c->name, "cpu") == 0) {
+			pmu_profiling_version = 0;
+			if ((c->mode) && (c->stop)) {
+				if (met_cpu_pmu_method != 0)
+					met_perf_cpupmu_stop();
+				else
+					c->stop();
+			}
+			module_put(c->owner);
+			continue;
+		}
+#endif
+		if (c->ondiemet_mode == 0) {
+			if ((!(c->cpu_related)) && (c->mode) && (c->stop))
+				c->stop();
+		} else if (c->ondiemet_mode == 1) {
+			if ((!(c->cpu_related)) && (c->mode) && (c->ondiemet_stop))
+				c->ondiemet_stop();
+		} else if (c->ondiemet_mode == 2) {
+			if ((!(c->cpu_related)) && (c->mode) && (c->stop))
+				c->stop();
+			if ((!(c->cpu_related)) && (c->mode) && (c->ondiemet_stop))
+				c->ondiemet_stop();
+		}
+		module_put(c->owner);
+	}
+
+	cpu_related_cnt = 0;
+}
+
+#if 0 /* cann't use static now */
+enum {
+	MET_SUSPEND = 1,
+	MET_RESUME = 2,
+};
+
+static noinline void tracing_mark_write(int op)
+{
+	switch (op) {
+	case MET_SUSPEND:
+		MET_TRACE("C|0|MET_SUSPEND|1");
+		break;
+	case MET_RESUME:
+		MET_TRACE("C|0|MET_SUSPEND|0");
+		break;
+	}
+}
+#endif
+
+int met_hrtimer_suspend(void)
+{
+	struct metdevice *c;
+
+	met_set_suspend_notify(1);
+	/* tracing_mark_write(MET_SUSPEND); */
+//	tracing_mark_write(TYPE_MET_SUSPEND, 0, 0, 0, 0, 0);
+	if (start == 0)
+		return 0;
+
+	list_for_each_entry(c, &met_list, list) {
+		if (c->suspend)
+			c->suspend();
+	}
+
+	/* get current COUNT */
+	MET_TRACE("TS: %llu GPT: %llX", sched_clock(), arch_counter_get_cntvct());
+	return 0;
+}
+
+void met_hrtimer_resume(void)
+{
+	struct metdevice *c;
+
+	/* get current COUNT */
+	MET_TRACE("TS: %llu GPT: %llX", sched_clock(), arch_counter_get_cntvct());
+
+	/* tracing_mark_write(MET_RESUME); */
+//	tracing_mark_write(TYPE_MET_RESUME, 0, 0, 0, 0, 0);
+	if (start == 0)
+		return;
+
+	list_for_each_entry(c, &met_list, list) {
+		if (c->resume)
+			c->resume();
+	}
+}
+
+/*
+ * event timer:
+ * register IRQ, sched_switch event to monitor Polling count
+ * count can be printed at any live cpu.
+ */
+void met_event_timer_notify(void)
+{
+	unsigned long long stamp;
+	struct metdevice *c;
+	int cpu = -1;
+
+	if (start == 0)
+		return;
+
+	cpu = smp_processor_id();
+	list_for_each_entry(c, &met_list, list) {
+		stamp = local_clock();
+
+		if (c->prev_stamp == 0)
+			c->prev_stamp = stamp;
+
+		/* Critical Section Start */
+		/* try spinlock to prevent a event print twice between config time interval */
+		if (!spin_trylock(&(c->my_lock)))
+			continue;
+
+		/*
+		 * DEFAULT_HRTIMER_EXPIRE (met_hrtimer_expire):
+		 * sample_rate == 0 --> always print
+		 * sample_rate == 1000 --> print interval larger than 1 ms
+		 */
+		if (DEFAULT_HRTIMER_EXPIRE == 0 || (stamp - c->prev_stamp) < DEFAULT_HRTIMER_EXPIRE) {
+			spin_unlock(&(c->my_lock));
+			continue;
+		}
+
+		c->prev_stamp = stamp;
+		spin_unlock(&(c->my_lock));
+		/* Critical Section End */
+
+		if ((c->mode == 0) || (c->timed_polling == NULL))
+			continue;
+
+		stamp = local_clock();
+		c->timed_polling(stamp, cpu);
+	}
+}
+
diff --git a/src/devtools/met-driver/4.19/mt2712/sampler.h b/src/devtools/met-driver/4.19/mt2712/sampler.h
new file mode 100644
index 0000000..ae780c0
--- /dev/null
+++ b/src/devtools/met-driver/4.19/mt2712/sampler.h
@@ -0,0 +1,54 @@
+/*
+ * 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.
+ */
+
+#ifndef _SAMPLER_H_
+#define _SAMPLER_H_
+
+/*
+ * sampling rate: 1ms
+ * log generating rate: 10ms
+ */
+#if 0
+#define DEFAULT_TIMER_EXPIRE (HZ / 100)
+#define DEFAULT_HRTIMER_EXPIRE (TICK_NSEC / 10)
+#else
+extern int met_timer_expire;	/* in jiffies */
+extern int met_hrtimer_expire;	/* in us */
+#define DEFAULT_TIMER_EXPIRE (met_timer_expire)
+#define DEFAULT_HRTIMER_EXPIRE (met_hrtimer_expire)
+#endif
+/*
+ * sampling rate: 10ms
+ * log generating rate: 100ms
+ */
+/* #define DEFAULT_TIMER_EXPIRE (HZ / 10) */
+/* #define DEFAULT_HRTIMER_EXPIRE (TICK_NSEC / 1) */
+
+int met_hrtimer_start(void);
+void met_hrtimer_stop(void);
+int sampler_start(void);
+void sampler_stop(void);
+
+extern struct list_head met_list;
+extern void add_cookie(struct pt_regs *regs, int cpu);
+extern int met_hrtimer_suspend(void);
+extern void met_hrtimer_resume(void);
+extern void met_event_timer_notify(void);
+
+extern int get_pmu_profiling_version(void);
+
+#ifdef CONFIG_CPU_FREQ
+#include "power.h"
+#endif
+
+#endif				/* _SAMPLER_H_ */
diff --git a/src/devtools/met-driver/4.19/mt2712/sspm/ondiemet_sspm.c b/src/devtools/met-driver/4.19/mt2712/sspm/ondiemet_sspm.c
new file mode 100644
index 0000000..08db3fa
--- /dev/null
+++ b/src/devtools/met-driver/4.19/mt2712/sspm/ondiemet_sspm.c
@@ -0,0 +1,493 @@
+/*
+ * 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/delay.h>
+#include <linux/module.h> /* symbol_get */
+
+#include "ondiemet_sspm.h"
+#define MET_USER_EVENT_SUPPORT
+#include "met_drv.h"
+
+#ifdef CONFIG_MTK_TINYSYS_SSPM_SUPPORT
+#ifdef CONFIG_MTK_GMO_RAM_OPTIMIZE
+#ifdef CONFIG_MET_ARM_32BIT
+#include <asm/dma-mapping.h> /* arm_coherent_dma_ops */
+#else /* CONFIG_MET_ARM_32BIT */
+#include <linux/dma-mapping.h>
+#endif /* CONFIG_MET_ARM_32BIT */
+#else /* CONFIG_MTK_GMO_RAM_OPTIMIZE */
+#include "sspm_reservedmem.h"
+#include "sspm_reservedmem_define.h"
+#endif /* CONFIG_MTK_GMO_RAM_OPTIMIZE */
+
+dma_addr_t ondiemet_sspm_log_phy_addr;
+void *ondiemet_sspm_log_virt_addr;
+uint32_t ondiemet_sspm_log_size = 0x400000;
+
+/* SSPM_LOG_FILE 0 */
+/* SSPM_LOG_SRAM 1 */
+/* SSPM_LOG_DRAM 2 */
+int sspm_log_mode;
+/* SSPM_RUN_NORMAL mode 0 */
+/* SSPM_RUN_CONTINUOUS mode 1 */
+int sspm_run_mode;
+int met_sspm_log_discard = -1;
+int sspm_log_size = 100;
+
+int sspm_buffer_size;
+int sspm_buf_available;
+EXPORT_SYMBOL(sspm_buf_available);
+int sspm_buf_mapped = -1; /* get buffer by MET itself */
+
+static ssize_t sspm_buffer_size_show(struct device *dev, struct device_attribute *attr, char *buf);
+static DEVICE_ATTR(sspm_buffer_size, 0444, sspm_buffer_size_show, NULL);
+
+static ssize_t sspm_available_show(struct device *dev, struct device_attribute *attr, char *buf);
+static DEVICE_ATTR(sspm_available, 0444, sspm_available_show, NULL);
+
+static ssize_t sspm_log_discard_show(struct device *dev, struct device_attribute *attr, char *buf);
+static DEVICE_ATTR(sspm_log_discard, 0444, sspm_log_discard_show, NULL);
+
+static ssize_t sspm_log_mode_show(struct device *dev, struct device_attribute *attr, char *buf);
+static ssize_t sspm_log_mode_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count);
+static DEVICE_ATTR(sspm_log_mode, 0664, sspm_log_mode_show, sspm_log_mode_store);
+
+static ssize_t sspm_log_size_show(struct device *dev, struct device_attribute *attr, char *buf);
+static ssize_t sspm_log_size_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count);
+static DEVICE_ATTR(sspm_log_size, 0664, sspm_log_size_show, sspm_log_size_store);
+
+
+static ssize_t sspm_run_mode_show(struct device *dev, struct device_attribute *attr, char *buf);
+static ssize_t sspm_run_mode_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count);
+static DEVICE_ATTR(sspm_run_mode, 0664, sspm_run_mode_show, sspm_run_mode_store);
+
+static ssize_t sspm_modules_show(struct device *dev, struct device_attribute *attr, char *buf);
+static ssize_t sspm_modules_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count);
+static DEVICE_ATTR(sspm_modules, 0664, sspm_modules_show, sspm_modules_store);
+
+static ssize_t sspm_op_ctrl_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count);
+static DEVICE_ATTR(sspm_op_ctrl, 0220, NULL, sspm_op_ctrl_store);
+
+static ssize_t sspm_buffer_size_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+	int i;
+
+	mutex_lock(&dev->mutex);
+	i = snprintf(buf, PAGE_SIZE, "%d\n", sspm_buffer_size);
+	mutex_unlock(&dev->mutex);
+	return i;
+}
+
+static ssize_t sspm_available_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+	int i;
+
+	mutex_lock(&dev->mutex);
+	i = snprintf(buf, PAGE_SIZE, "%d\n", 1);
+	mutex_unlock(&dev->mutex);
+	return i;
+}
+
+static ssize_t sspm_log_discard_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+	int i;
+
+	mutex_lock(&dev->mutex);
+	i = snprintf(buf, PAGE_SIZE, "%d\n", met_sspm_log_discard);
+	mutex_unlock(&dev->mutex);
+	return i;
+}
+
+static ssize_t sspm_log_mode_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+	int i;
+
+	mutex_lock(&dev->mutex);
+	i = snprintf(buf, PAGE_SIZE, "%d\n", sspm_log_mode);
+	mutex_unlock(&dev->mutex);
+	return i;
+}
+
+static ssize_t sspm_log_mode_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
+{
+	int value;
+
+	if (kstrtoint(buf, 0, &value) != 0)
+		return -EINVAL;
+	mutex_lock(&dev->mutex);
+	sspm_log_mode = value;
+	mutex_unlock(&dev->mutex);
+	return count;
+}
+
+
+static ssize_t sspm_log_size_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+	int i;
+
+	mutex_lock(&dev->mutex);
+	i = snprintf(buf, PAGE_SIZE, "%d\n", sspm_log_size);
+	mutex_unlock(&dev->mutex);
+	return i;
+}
+
+static ssize_t sspm_log_size_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
+{
+	int value;
+
+	if (kstrtoint(buf, 0, &value) != 0)
+		return -EINVAL;
+	mutex_lock(&dev->mutex);
+	sspm_log_size = value;
+	mutex_unlock(&dev->mutex);
+	return count;
+}
+
+
+static ssize_t sspm_run_mode_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+	int i;
+
+	mutex_lock(&dev->mutex);
+	i = snprintf(buf, PAGE_SIZE, "%d\n", sspm_run_mode);
+	mutex_unlock(&dev->mutex);
+	return i;
+}
+
+static ssize_t sspm_run_mode_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
+{
+	int value;
+
+	if (kstrtoint(buf, 0, &value) != 0)
+		return -EINVAL;
+	mutex_lock(&dev->mutex);
+	sspm_run_mode = value;
+	mutex_unlock(&dev->mutex);
+	return count;
+}
+
+static ssize_t sspm_op_ctrl_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
+{
+	int value;
+
+	if (kstrtoint(buf, 0, &value) != 0)
+		return -EINVAL;
+	mutex_lock(&dev->mutex);
+	if (value == 1)
+		sspm_start();
+	else if (value == 2)
+		sspm_stop();
+	else if (value == 3)
+		sspm_extract();
+	else if (value == 4)
+		sspm_flush();
+	mutex_unlock(&dev->mutex);
+	return count;
+}
+
+static ssize_t sspm_modules_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+	int i;
+
+	mutex_lock(&dev->mutex);
+	i = snprintf(buf, PAGE_SIZE, "%x\n", ondiemet_module[ONDIEMET_SSPM]);
+	mutex_unlock(&dev->mutex);
+	return i;
+}
+
+static ssize_t sspm_modules_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
+{
+	uint32_t value;
+
+	if (kstrtouint(buf, 0, &value) != 0)
+		return -EINVAL;
+	mutex_lock(&dev->mutex);
+	ondiemet_module[ONDIEMET_SSPM] = value;
+	mutex_unlock(&dev->mutex);
+	return count;
+}
+
+int sspm_attr_init(struct device *dev)
+{
+	int ret;
+
+#ifdef CONFIG_MTK_GMO_RAM_OPTIMIZE
+#ifdef CONFIG_MET_ARM_32BIT
+	struct dma_map_ops *ops = (struct dma_map_ops *)symbol_get(arm_coherent_dma_ops);
+
+	if (ops && ops->alloc) {
+		dev->coherent_dma_mask = DMA_BIT_MASK(32);
+		ondiemet_sspm_log_virt_addr = ops->alloc(dev,
+						ondiemet_sspm_log_size,
+						&ondiemet_sspm_log_phy_addr,
+						GFP_KERNEL,
+						0);
+	}
+#else /* CONFIG_MET_ARM_32BIT */
+	/* dma_alloc */
+	ondiemet_sspm_log_virt_addr = dma_alloc_coherent(dev,
+			ondiemet_sspm_log_size,
+			&ondiemet_sspm_log_phy_addr,
+			GFP_KERNEL);
+#endif /* CONFIG_MET_ARM_32BIT */
+#else /* CONFIG_MTK_GMO_RAM_OPTIMIZE */
+
+	phys_addr_t (*sspm_reserve_mem_get_phys_sym)(unsigned int id) = NULL;
+	phys_addr_t (*sspm_reserve_mem_get_virt_sym)(unsigned int id) = NULL;
+	phys_addr_t (*sspm_reserve_mem_get_size_sym)(unsigned int id) = NULL;
+
+	sspm_reserve_mem_get_phys_sym = (phys_addr_t (*)(unsigned int id))symbol_get(sspm_reserve_mem_get_virt);
+	sspm_reserve_mem_get_virt_sym = (phys_addr_t (*)(unsigned int id))symbol_get(sspm_reserve_mem_get_phys);
+	sspm_reserve_mem_get_size_sym = (phys_addr_t (*)(unsigned int id))symbol_get(sspm_reserve_mem_get_size);
+	if (sspm_reserve_mem_get_phys_sym)
+		ondiemet_sspm_log_virt_addr = (void*)sspm_reserve_mem_get_virt(MET_MEM_ID);
+	if (sspm_reserve_mem_get_virt_sym)
+		ondiemet_sspm_log_phy_addr = sspm_reserve_mem_get_phys(MET_MEM_ID);
+	if (sspm_reserve_mem_get_size_sym)
+		ondiemet_sspm_log_size = sspm_reserve_mem_get_size(MET_MEM_ID);
+#endif /* CONFIG_MTK_GMO_RAM_OPTIMIZE */
+
+	ret = device_create_file(dev, &dev_attr_sspm_buffer_size);
+	if (ret != 0) {
+		pr_debug("can not create device file: sspm_buffer_size\n");
+		return ret;
+	}
+
+	ret = device_create_file(dev, &dev_attr_sspm_available);
+	if (ret != 0) {
+		pr_debug("can not create device file: sspm_available\n");
+		return ret;
+	}
+
+	ret = device_create_file(dev, &dev_attr_sspm_log_discard);
+	if (ret != 0) {
+		pr_debug("can not create device file: sspm_log_discard\n");
+		return ret;
+	}
+	ret = device_create_file(dev, &dev_attr_sspm_log_mode);
+	if (ret != 0) {
+		pr_debug("can not create device file: sspm_log_mode\n");
+		return ret;
+	}
+	ret = device_create_file(dev, &dev_attr_sspm_log_size);
+	if (ret != 0) {
+		pr_debug("can not create device file: sspm_log_size\n");
+		return ret;
+	}
+	ret = device_create_file(dev, &dev_attr_sspm_run_mode);
+	if (ret != 0) {
+		pr_debug("can not create device file: sspm_run_mode\n");
+		return ret;
+	}
+	ret = device_create_file(dev, &dev_attr_sspm_op_ctrl);
+	if (ret != 0) {
+		pr_debug("can not create device file: sspm_op_ctrl\n");
+		return ret;
+	}
+	ret = device_create_file(dev, &dev_attr_sspm_modules);
+	if (ret != 0) {
+		pr_debug("can not create device file: sspm_modules\n");
+		return ret;
+	}
+
+	if (ondiemet_sspm_log_virt_addr != NULL) {
+		start_sspm_ipi_recv_thread();
+		sspm_buf_available = 1;
+		sspm_buffer_size = ondiemet_sspm_log_size;
+	} else {
+		sspm_buf_available = 0;
+		sspm_buffer_size = -1;
+	}
+
+	return 0;
+}
+
+int sspm_attr_uninit(struct device *dev)
+{
+	/* dma_free */
+	if (ondiemet_sspm_log_virt_addr != NULL) {
+#ifdef CONFIG_MTK_GMO_RAM_OPTIMIZE
+#ifdef CONFIG_MET_ARM_32BIT
+		struct dma_map_ops *ops = (struct dma_map_ops *)symbol_get(arm_coherent_dma_ops);
+
+		if (ops && ops->free) {
+			ops->free(dev,
+				ondiemet_sspm_log_size,
+				ondiemet_sspm_log_virt_addr,
+				ondiemet_sspm_log_phy_addr,
+				0);
+		}
+#else /* CONFIG_MET_ARM_32BIT */
+		dma_free_coherent(dev,
+			ondiemet_sspm_log_size,
+			ondiemet_sspm_log_virt_addr,
+			ondiemet_sspm_log_phy_addr);
+#endif /* CONFIG_MET_ARM_32BIT */
+#endif /* CONFIG_MTK_GMO_RAM_OPTIMIZE */
+		ondiemet_sspm_log_virt_addr = NULL;
+		stop_sspm_ipi_recv_thread();
+	}
+
+	device_remove_file(dev, &dev_attr_sspm_buffer_size);
+	device_remove_file(dev, &dev_attr_sspm_available);
+	device_remove_file(dev, &dev_attr_sspm_log_discard);
+	device_remove_file(dev, &dev_attr_sspm_log_mode);
+	device_remove_file(dev, &dev_attr_sspm_log_size);
+	device_remove_file(dev, &dev_attr_sspm_run_mode);
+	device_remove_file(dev, &dev_attr_sspm_op_ctrl);
+	device_remove_file(dev, &dev_attr_sspm_modules);
+
+	return 0;
+}
+
+#if 0 /* move to sspm_attr_init() */
+void sspm_get_buffer_info(void)
+{
+	if (ondiemet_sspm_log_virt_addr != NULL) {
+		sspm_buf_available = 1;
+		sspm_buffer_size = ondiemet_sspm_log_size;
+	} else {
+		sspm_buf_available = 0;
+		sspm_buffer_size = -1;
+	}
+}
+#endif
+
+extern const char *met_get_platform_name(void);
+void sspm_start(void)
+{
+	int32_t ret = 0;
+	uint32_t rdata;
+	uint32_t ipi_buf[4];
+	const char* platform_name = NULL;
+	unsigned int platform_id = 0;
+	met_sspm_log_discard = -1;
+
+	/* clear DRAM buffer */
+	if (ondiemet_sspm_log_virt_addr != NULL)
+		memset_io((void *)ondiemet_sspm_log_virt_addr, 0, ondiemet_sspm_log_size);
+	else
+		return;
+
+	platform_name = met_get_platform_name();
+	if (platform_name) {
+		char buf[5];
+
+		memset(buf, 0x0, 5);
+		memcpy(buf, &platform_name[2], 4);
+		ret = kstrtouint(buf, 10, &platform_id);
+	}
+
+	/* send DRAM physical address */
+	ipi_buf[0] = MET_MAIN_ID | MET_BUFFER_INFO;
+	ipi_buf[1] = (unsigned int)ondiemet_sspm_log_phy_addr; /* address */
+	if (ret == 0)
+		ipi_buf[2] = platform_id;
+	else
+		ipi_buf[2] = 0;
+	ipi_buf[3] = 0;
+	ret = sspm_ipi_send_sync(IPI_ID_MET, IPI_OPT_WAIT, (void *)ipi_buf, 0, &rdata, 1);
+
+	/* start ondiemet now */
+	ipi_buf[0] = MET_MAIN_ID | MET_OP | MET_OP_START;
+	ipi_buf[1] = ondiemet_module[ONDIEMET_SSPM];
+	ipi_buf[2] = sspm_log_mode;
+	ipi_buf[3] = sspm_run_mode;
+	ret = sspm_ipi_send_sync(IPI_ID_MET, IPI_OPT_WAIT, (void *)ipi_buf, 0, &rdata, 1);
+}
+
+void sspm_stop(void)
+{
+	int32_t ret;
+	uint32_t rdata;
+	uint32_t ipi_buf[4];
+
+	if (sspm_buf_available == 1) {
+		ipi_buf[0] = MET_MAIN_ID|MET_OP|MET_OP_STOP;
+		ipi_buf[1] = 0;
+		ipi_buf[2] = 0;
+		ipi_buf[3] = 0;
+		ret = sspm_ipi_send_sync(IPI_ID_MET, IPI_OPT_WAIT, (void *)ipi_buf, 0, &rdata, 1);
+	}
+}
+
+void sspm_extract(void)
+{
+	int32_t ret;
+	uint32_t rdata;
+	uint32_t ipi_buf[4];
+	int32_t count;
+
+	count = 20;
+	if (sspm_buf_available == 1) {
+		while ((sspm_buffer_dumping == 1) && (count != 0)) {
+			msleep(50);
+			count--;
+		}
+		ipi_buf[0] = MET_MAIN_ID|MET_OP|MET_OP_EXTRACT;
+		ipi_buf[1] = 0;
+		ipi_buf[2] = 0;
+		ipi_buf[3] = 0;
+		ret = sspm_ipi_send_sync(IPI_ID_MET, IPI_OPT_WAIT, (void *)ipi_buf, 0, &rdata, 1);
+	}
+
+	if (sspm_run_mode == SSPM_RUN_NORMAL)
+		ondiemet_module[ONDIEMET_SSPM] = 0;
+}
+
+void sspm_flush(void)
+{
+	int32_t ret;
+	uint32_t rdata;
+	uint32_t ipi_buf[4];
+
+	if (sspm_buf_available == 1) {
+		ipi_buf[0] = MET_MAIN_ID|MET_OP|MET_OP_FLUSH;
+		ipi_buf[1] = 0;
+		ipi_buf[2] = 0;
+		ipi_buf[3] = 0;
+		ret = sspm_ipi_send_sync(IPI_ID_MET, IPI_OPT_WAIT, (void *)ipi_buf, 0, &rdata, 1);
+	}
+
+	if (sspm_run_mode == SSPM_RUN_NORMAL)
+		ondiemet_module[ONDIEMET_SSPM] = 0;
+}
+#else /* CONFIG_MTK_TINYSYS_SSPM_SUPPORT */
+int sspm_buffer_size = -1;
+
+int sspm_attr_init(struct device *dev)
+{
+	return 0;
+}
+
+int sspm_attr_uninit(struct device *dev)
+{
+	return 0;
+}
+
+void sspm_start(void)
+{
+}
+
+void sspm_stop(void)
+{
+}
+
+void sspm_extract(void)
+{
+}
+
+void sspm_flush(void)
+{
+}
+
+#endif /* CONFIG_MTK_TINYSYS_SSPM_SUPPORT */
diff --git a/src/devtools/met-driver/4.19/mt2712/sspm/ondiemet_sspm.h b/src/devtools/met-driver/4.19/mt2712/sspm/ondiemet_sspm.h
new file mode 100644
index 0000000..6fa37c9
--- /dev/null
+++ b/src/devtools/met-driver/4.19/mt2712/sspm/ondiemet_sspm.h
@@ -0,0 +1,137 @@
+/*
+ * 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.
+ */
+
+#ifndef __ONDIEMET_SSPM_H
+#define __ONDIEMET_SSPM_H
+
+#ifdef CONFIG_MTK_TINYSYS_SSPM_SUPPORT
+#include "ondiemet.h"
+#include "sspm_ipi.h"
+#include <linux/dma-mapping.h>
+
+/* we may use IPI_ID_PLATFORM for mt6759 to reduce SRAM */
+#ifndef IPI_ID_MET
+/* #define IPI_ID_MET IPI_ID_TST1 */
+#define IPI_ID_MET IPI_ID_PLATFORM
+#endif
+
+/* MET IPI command definition: mbox 0 */
+/* main func ID: bit[31-24]; sub func ID: bit[23-18]; argu 0: bit[17-0] */
+#define MET_MAIN_ID_MASK        0xff000000 /* bit 31 - 24 */
+#define MET_SUB_ID_MASK         0x00fc0000 /* bit 23 - 18 */
+#define MET_ARGU0_MASK          0x0003ffff /* bit 17 - 0 */
+#define FUNC_BIT_SHIFT          18
+#define MID_BIT_SHIFT           9
+#define MET_MAIN_ID             0x06000000
+/* handle argument and attribute */
+#define PROCESS_ARGU            0x01
+#define PROCESS_ATTR            0x02
+#define MODULE_ID_MASK          0x3fe00 /* bit 9 - 17 */
+#define ARGUMENT_MASK           0x01ff  /* bit 0 - 8 */
+
+/* the following command is used for AP to MD32 */
+#define MET_OP            (1 << FUNC_BIT_SHIFT)
+/* argu 0: start: 0x01; stop: 0x02; extract: 0x03 */
+#define MET_OP_START        0x00000001
+#define MET_OP_STOP         0x00000002
+#define MET_OP_EXTRACT      0x00000003
+#define MET_OP_FLUSH        0x00000004
+#define MET_SR            (2 << FUNC_BIT_SHIFT) /* sample rate */
+#define MET_MODULE        (3 << FUNC_BIT_SHIFT) /* module enable/disable */
+#define MET_ARGU          (4 << FUNC_BIT_SHIFT) /* argument passing */
+#define MET_ATTR          (5 << FUNC_BIT_SHIFT) /* attribute passing */
+/* system memory information for on-die-met log data */
+#define MET_BUFFER_INFO   (6 << FUNC_BIT_SHIFT)
+#define MET_TIMESTAMP     (7 << FUNC_BIT_SHIFT) /* timestamp info */
+#define MET_GPT           (8 << FUNC_BIT_SHIFT) /* GPT counter reading */
+#define MET_REQ_AP2MD     (9 << FUNC_BIT_SHIFT) /* user defined command */
+#define MET_RESP_AP2MD    (10 << FUNC_BIT_SHIFT) /* may no need */
+/* mode: bit 15 - 0: */
+/*  Bit 0: MD32 SRAM mode; Bit 1: System DRAM mode */
+/*  value: 0: output to next level of storage; 1: loop in its own storage */
+#define MET_DATA_MODE     (11 << FUNC_BIT_SHIFT) /* log output mode */
+/* start/stop read data into MD32 SRAM buffer; both DMA and met_printf() */
+#define MET_DATA_OP       (12 << FUNC_BIT_SHIFT) /* data read operation */
+/* the following command is used for MD32 to AP */
+#define MET_DUMP_BUFFER   (13 << FUNC_BIT_SHIFT)
+#define MET_REQ_MD2AP     (14 << FUNC_BIT_SHIFT) /* user defined command */
+#define MET_CLOSE_FILE    (15 << FUNC_BIT_SHIFT) /* Inform to close the SD file */
+#define MET_RESP_MD2AP    (16 << FUNC_BIT_SHIFT)
+#define MET_RUN_MODE      (17 << FUNC_BIT_SHIFT)
+
+/* Note: the module ID and its bit pattern should be fixed as below */
+/* DMA based module first */
+enum {
+	MID_PMQOS = 0,
+	MID_VCORE_DVFS,
+	MID_EMI,
+	MID_THERMAL_CPU,
+	MID_WALL_TIME,
+	MID_CPU_DVFS,
+	MID_GPU_DVFS,
+	MID_PTPOD,
+	MID_SPM,
+	MID_PROFILE,
+
+	MID_COMMON = 0x1F
+};
+
+#define ID_PMQOS       (1 << MID_PMQOS)
+#define ID_SMI         (1 << MID_SMI)
+#define ID_EMI         (1 << MID_EMI)
+#define ID_THERMAL_CPU (1 << MID_THERMAL_CPU)
+#define ID_WALL_TIME   (1 << MID_WALL_TIME)
+#define ID_CPU_DVFS    (1 << MID_CPU_DVFS)
+#define ID_GPU_DVFS    (1 << MID_GPU_DVFS)
+#define ID_VCORE_DVFS  (1 << MID_VCORE_DVFS)
+#define ID_PTPOD       (1 << MID_PTPOD)
+#define ID_SPM         (1 << MID_SPM)
+#define ID_PROFILE     (1 << MID_PROFILE)
+#define ID_COMMON      (1 << MID_COMMON)
+
+
+extern void ondiemet_extract(void);
+extern void ondiemet_stop(void);
+extern void ondiemet_start(void);
+
+extern void start_sspm_ipi_recv_thread(void);
+extern void stop_sspm_ipi_recv_thread(void);
+
+extern unsigned int ondiemet_ipi_buf[];
+
+/* extern phys_addr_t ondiemet_sspm_log_phy_addr, ondiemet_sspm_log_virt_addr; */
+extern dma_addr_t ondiemet_sspm_log_phy_addr;
+
+extern void *ondiemet_sspm_log_virt_addr;
+extern uint32_t ondiemet_sspm_log_size;
+
+extern int ondiemet_attr_init(struct device *dev);
+extern int ondiemet_attr_uninit(struct device *dev);
+extern int met_sspm_log_discard;
+
+#define SSPM_LOG_FILE 0
+#define SSPM_LOG_SRAM 1
+#define SSPM_LOG_DRAM 2
+extern int sspm_log_mode;
+#define SSPM_RUN_NORMAL 0
+#define SSPM_RUN_CONTINUOUS 1
+extern int sspm_run_mode;
+
+/* extern void sspm_get_buffer_info(void); */
+extern int sspm_buf_available;
+extern int sspm_buffer_dumping;
+
+void sspm_flush(void);
+
+#endif /* CONFIG_MTK_TINYSYS_SSPM_SUPPORT */
+#endif /* __ONDIEMET_SSPM_H */
diff --git a/src/devtools/met-driver/4.19/mt2712/switch.h b/src/devtools/met-driver/4.19/mt2712/switch.h
new file mode 100644
index 0000000..14397d7
--- /dev/null
+++ b/src/devtools/met-driver/4.19/mt2712/switch.h
@@ -0,0 +1,37 @@
+/*
+ * 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.
+ */
+
+#ifndef __MT_SWITCH__
+#define __MT_SWITCH__
+/*
+ * =========================
+ * !!!!!!!!!!!NOTICE!!!!!!!!
+ * =========================
+ * MT_SWITCH OPTION must delcare as Mask value
+ * And sort them from smallest to largest
+ * MT_SWITCH_MX_ITEM was used to determine argument range
+*/
+enum {
+	/* =================== */
+	/* user define mt switch event */
+	/* =================== */
+	MT_SWITCH_SCHEDSWITCH = 0x0001,
+	MT_SWITCH_64_32BIT = 0x0002,
+	MT_SWITCH_TAGPOLLING = 0x0004,
+	MT_SWITCH_EVENT_TIMER = 0x0008,
+	/* =================== */
+	MT_SWITCH_MX_ITEM
+};
+
+extern struct metdevice met_switch;
+#endif
diff --git a/src/devtools/met-driver/4.19/mt2712/trace.h b/src/devtools/met-driver/4.19/mt2712/trace.h
new file mode 100644
index 0000000..f259b7a
--- /dev/null
+++ b/src/devtools/met-driver/4.19/mt2712/trace.h
@@ -0,0 +1,113 @@
+/*
+ * 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.
+ */
+
+#ifndef _TRACE_H_
+#define _TRACE_H_
+
+
+extern void (*mp_cp_ptr)(unsigned long long timestamp,
+	       struct task_struct *task,
+	       unsigned long program_counter,
+	       unsigned long dcookie,
+	       unsigned long offset,
+	       unsigned char cnt, unsigned int *value);
+
+#define MP_FMT1	"%x\n"
+#define MP_FMT2	"%x,%x\n"
+#define MP_FMT3	"%x,%x,%x\n"
+#define MP_FMT4	"%x,%x,%x,%x\n"
+#define MP_FMT5	"%x,%x,%x,%x,%x\n"
+#define MP_FMT6	"%x,%x,%x,%x,%x,%x\n"
+#define MP_FMT7	"%x,%x,%x,%x,%x,%x,%x\n"
+#define MP_FMT8	"%x,%x,%x,%x,%x,%x,%x,%x\n"
+#define MP_FMT9	"%x,%x,%x,%x,%x,%x,%x,%x,%x\n"
+#define MP_FMT10 "%x,%x,%x,%x,%x,%x,%x,%x,%x,%x\n"
+#define MP_FMT11 "%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x\n"
+#define MP_FMT12 "%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x\n"
+#define MP_FMT13 "%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x\n"
+#define MP_FMT14 "%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x\n"
+#define MP_FMT15 "%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x\n"
+
+#define MET_GENERAL_PRINT(FUNC, count, value) \
+do { \
+	switch (count) { \
+	case 1: { \
+		FUNC(MP_FMT1, value[0]); \
+		} \
+		break; \
+	case 2: { \
+		FUNC(MP_FMT2, value[0], value[1]); \
+		} \
+		break; \
+	case 3: { \
+		FUNC(MP_FMT3, value[0], value[1], value[2]); \
+		} \
+		break; \
+	case 4: { \
+		FUNC(MP_FMT4, value[0], value[1], value[2], value[3]); \
+		} \
+		break; \
+	case 5: { \
+		FUNC(MP_FMT5, value[0], value[1], value[2], value[3], value[4]); \
+		} \
+		break; \
+	case 6: { \
+		FUNC(MP_FMT6, value[0], value[1], value[2], value[3], value[4], value[5]); \
+		} \
+		break; \
+	case 7: { \
+		FUNC(MP_FMT7, value[0], value[1], value[2], value[3], value[4], value[5], value[6]); \
+		} \
+		break; \
+	case 8: { \
+		FUNC(MP_FMT8, value[0], value[1], value[2], value[3], value[4], value[5], value[6], value[7]); \
+		} \
+		break; \
+	case 9: { \
+		FUNC(MP_FMT9, value[0], value[1], value[2], value[3], value[4], value[5], value[6], value[7], \
+				value[8]); \
+		} \
+		break; \
+	case 10: { \
+		FUNC(MP_FMT10, value[0], value[1], value[2], value[3], value[4], value[5], value[6], value[7], \
+				value[8], value[9]); \
+		} \
+		break; \
+	case 11: { \
+		FUNC(MP_FMT11, value[0], value[1], value[2], value[3], value[4], value[5], value[6], value[7], \
+				value[8], value[9], value[10]); \
+		} \
+		break; \
+	case 12: { \
+		FUNC(MP_FMT12, value[0], value[1], value[2], value[3], value[4], value[5], value[6], value[7], \
+				value[8], value[9], value[10], value[11]); \
+		} \
+		break; \
+	case 13: { \
+		FUNC(MP_FMT13, value[0], value[1], value[2], value[3], value[4], value[5], value[6], value[7], \
+				value[8], value[9], value[10], value[11], value[12]); \
+		} \
+		break; \
+	case 14: { \
+		FUNC(MP_FMT14, value[0], value[1], value[2], value[3], value[4], value[5], value[6], value[7], \
+				value[8], value[9], value[10], value[11], value[12], value[13]); \
+		} \
+		break; \
+	case 15: { \
+		FUNC(MP_FMT15, value[0], value[1], value[2], value[3], value[4], value[5], value[6], value[7], \
+				value[8], value[9], value[10], value[11], value[12], value[13], value[14]); \
+		} \
+		break; \
+	} \
+} while (0)
+#endif /* _TRACE_H_ */
diff --git a/src/devtools/met-driver/4.19/mt2712/util.c b/src/devtools/met-driver/4.19/mt2712/util.c
new file mode 100644
index 0000000..051a3bd
--- /dev/null
+++ b/src/devtools/met-driver/4.19/mt2712/util.c
@@ -0,0 +1,88 @@
+/*
+ * 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 "util.h"
+#include <linux/fs.h>
+#include <linux/kernel.h>
+/* #include <asm/uaccess.h> */
+#include <linux/uaccess.h>
+
+#ifdef FILELOG
+
+static char tmp[1000] = { 0 };
+
+ /*TODO*/
+/**
+ * open file
+ * @param name path to open
+ * @return file pointer
+ */
+struct file *open_file(const char *name)
+{
+	struct file *fp = NULL;
+
+	fp = filp_open(name, O_WRONLY | O_APPEND /*| O_TRUNC */  | O_CREAT, 0664);
+	if (unlikely(fp == NULL)) {
+		pr_debug(KERNEL_INFO "can not open result file");
+		return NULL;
+	}
+	return fp;
+}
+
+/**
+ * write to file
+ * @param fp file pointer
+ * @param format format string
+ * @param ... variable-length subsequent arguments
+ */
+void write_file(struct file *fp, const char *format, ...)
+{
+	va_list va;
+	mm_segment_t fs = get_fs();
+
+	va_start(va, format);
+	vsnprintf(tmp, sizeof(tmp), format, va);
+	set_fs(KERNEL_DS);
+	vfs_write(fp, tmp, strlen(tmp), &(fp->f_pos));
+	set_fs(fs);
+	va_end(va);
+}
+
+/**
+ * close file
+ * @param fp file pointer
+ * @return exit code
+ */
+int close_file(struct file *fp)
+{
+	if (likely(fp != NULL)) {
+		filp_close(fp, NULL);
+		fp = NULL;
+		return 0;
+	}
+	pr_debug("cannot close file pointer:%p\n", fp);
+	return -1;
+}
+
+void filelog(char *str)
+{
+	struct file *fp;
+
+	fp = open_file("/data/met.log");
+	if (fp != NULL) {
+		write_file(fp, "%s", str);
+		close_file(fp);
+	}
+}
+
+#endif				/* FILELOG */
diff --git a/src/devtools/met-driver/4.19/mt2712/util.h b/src/devtools/met-driver/4.19/mt2712/util.h
new file mode 100644
index 0000000..5730376
--- /dev/null
+++ b/src/devtools/met-driver/4.19/mt2712/util.h
@@ -0,0 +1,25 @@
+/*
+ * 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.
+ */
+
+#ifndef _SRC_UTIL_H_
+#define _SRC_UTIL_H_
+
+/* #define FILELOG 1 */
+
+#ifdef FILELOG
+void filelog(char *str);
+#else
+#define filelog(str)
+#endif
+
+#endif				/* _SRC_UTIL_H_ */
diff --git a/src/devtools/met-driver/4.19/mt2712/v6_pmu_hw.c b/src/devtools/met-driver/4.19/mt2712/v6_pmu_hw.c
new file mode 100644
index 0000000..a1c5635
--- /dev/null
+++ b/src/devtools/met-driver/4.19/mt2712/v6_pmu_hw.c
@@ -0,0 +1,290 @@
+/*
+ * 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 <asm/system.h> */
+#include <linux/smp.h>
+#include "cpu_pmu.h"
+#include "v6_pmu_name.h"
+
+enum ARM_TYPE {
+	ARM1136 = 0xB36,
+	ARM1156 = 0xB56,
+	ARM1176 = 0xB76,
+	CHIP_UNKNOWN = 0xFFF
+};
+
+struct chip_pmu {
+	enum ARM_TYPE type;
+	struct pmu_desc *desc;
+	unsigned int count;
+	const char *cpu_name;
+};
+
+static struct chip_pmu chips[] = {
+	{ARM1136, arm11_pmu_desc, ARM11_PMU_DESC_COUNT, "arm1136"},
+	{ARM1156, arm11_pmu_desc, ARM11_PMU_DESC_COUNT, "arm1156"},
+	{ARM1176, arm11_pmu_desc, ARM11_PMU_DESC_COUNT, "arm1176"},
+};
+static struct chip_pmu chip_unknown = { CHIP_UNKNOWN, NULL, 0, "Unknown CPU" };
+
+#define CHIP_PMU_COUNT (sizeof(chips) / sizeof(struct chip_pmu))
+
+static struct chip_pmu *chip;
+
+/* define V6_PMU_HW_DEBUG */
+#ifdef V6_PMU_HW_DEBUG
+#define v6pmu_hw_debug(fmt, arg...)     pr_debug(fmt, ##arg)
+#else
+#define v6pmu_hw_debug(fmt, arg...)     do {} while (0)
+#endif
+
+#define ARMV6_PMCR_ENABLE               (1 << 0)
+#define ARMV6_PMCR_CTR01_RESET          (1 << 1)
+#define ARMV6_PMCR_CCOUNT_RESET         (1 << 2)
+#define ARMV6_PMCR_CCOUNT_DIV           (1 << 3)
+#define ARMV6_PMCR_COUNT0_IEN           (1 << 4)
+#define ARMV6_PMCR_COUNT1_IEN           (1 << 5)
+#define ARMV6_PMCR_CCOUNT_IEN           (1 << 6)
+#define ARMV6_PMCR_COUNT0_OVERFLOW      (1 << 8)
+#define ARMV6_PMCR_COUNT1_OVERFLOW      (1 << 9)
+#define ARMV6_PMCR_CCOUNT_OVERFLOW      (1 << 10)
+#define ARMV6_PMCR_EVT_COUNT0_SHIFT     20
+#define ARMV6_PMCR_EVT_COUNT0_MASK      (0xFF << ARMV6_PMCR_EVT_COUNT0_SHIFT)
+#define ARMV6_PMCR_EVT_COUNT1_SHIFT     12
+#define ARMV6_PMCR_EVT_COUNT1_MASK      (0xFF << ARMV6_PMCR_EVT_COUNT1_SHIFT)
+
+#define ARMV6_PMCR_OVERFLOWED_MASK \
+	(ARMV6_PMCR_COUNT0_OVERFLOW | ARMV6_PMCR_COUNT1_OVERFLOW | \
+	ARMV6_PMCR_CCOUNT_OVERFLOW)
+
+enum armv6_counters {
+	ARMV6_COUNTER0 = 0,
+	ARMV6_COUNTER1,
+	ARMV6_CYCLE_COUNTER,
+};
+
+static inline unsigned long armv6_pmcr_read(void)
+{
+	u32 val;
+
+	asm volatile ("mrc   p15, 0, %0, c15, c12, 0":"=r" (val));
+	return val;
+}
+
+static inline void armv6_pmcr_write(unsigned long val)
+{
+	asm volatile ("mcr   p15, 0, %0, c15, c12, 0"::"r" (val));
+}
+
+static inline unsigned int armv6_pmu_read_count(unsigned int idx)
+{
+	unsigned long value = 0;
+
+	if (idx == ARMV6_CYCLE_COUNTER)
+		asm volatile ("mrc   p15, 0, %0, c15, c12, 1":"=r" (value));
+	else if (idx == ARMV6_COUNTER0)
+		asm volatile ("mrc   p15, 0, %0, c15, c12, 2":"=r" (value));
+	else if (idx == ARMV6_COUNTER1)
+		asm volatile ("mrc   p15, 0, %0, c15, c12, 3":"=r" (value));
+
+	return value;
+}
+
+static inline void armv6_pmu_overflow(void)
+{
+	unsigned int val;
+
+	val = armv6_pmcr_read();
+	val |= ARMV6_PMCR_OVERFLOWED_MASK;
+	armv6_pmcr_write(val);
+}
+
+static inline unsigned int armv6_pmu_control_read(void)
+{
+	u32 val;
+
+	asm volatile ("mrc   p15, 0, %0, c15, c12, 0":"=r" (val));
+	return val;
+}
+
+static inline void armv6_pmu_control_write(unsigned int setting)
+{
+	unsigned long val;
+
+	val = armv6_pmcr_read();
+	val |= setting;
+	armv6_pmcr_write(val);
+}
+
+static void armv6_pmu_hw_reset_all(void)
+{
+	unsigned long val;
+
+	val = armv6_pmcr_read();
+	val &= ~ARMV6_PMCR_ENABLE;	/* disable all counters */
+	val |= (ARMV6_PMCR_CTR01_RESET | ARMV6_PMCR_CCOUNT_RESET);	/* reset CCNT, PMNC1/2 counter to zero */
+	armv6_pmcr_write(val);
+
+	armv6_pmu_overflow();
+}
+
+static void armv6pmu_enable_event(int idx, unsigned short config)
+{
+	unsigned long val, mask, evt;
+
+	if (idx == ARMV6_CYCLE_COUNTER) {
+		mask = 0;
+		evt = ARMV6_PMCR_CCOUNT_IEN;
+	} else if (idx == ARMV6_COUNTER0) {
+		mask = ARMV6_PMCR_EVT_COUNT0_MASK;
+		evt = (config << ARMV6_PMCR_EVT_COUNT0_SHIFT) | ARMV6_PMCR_COUNT0_IEN;
+	} else if (idx == ARMV6_COUNTER1) {
+		mask = ARMV6_PMCR_EVT_COUNT1_MASK;
+		evt = (config << ARMV6_PMCR_EVT_COUNT1_SHIFT) | ARMV6_PMCR_COUNT1_IEN;
+	} else {
+		pr_debug("invalid counter number (%d)\n", idx);
+		return;
+	}
+
+	/*
+	 * Mask out the current event and set the counter to count the event
+	 * that we're interested in.
+	 */
+	val = armv6_pmcr_read();
+	val &= ~mask;
+	val |= evt;
+	armv6_pmcr_write(val);
+}
+
+static int armv6_pmu_hw_get_event_desc(int i, int event, char *event_desc)
+{
+	if (event_desc == NULL)
+		return -1;
+
+	for (i = 0; i < chip->count; i++) {
+		if (chip->desc[i].event == event) {
+			strncpy(event_desc, chip->desc[i].name, MXSIZE_PMU_DESC - 1);
+			break;
+		}
+	}
+
+	if (i == chip->count)
+		return -1;
+
+	return 0;
+}
+
+static int armv6_pmu_hw_check_event(struct met_pmu *pmu, int idx, int event)
+{
+	int i;
+
+	/* Check if event is duplicate */
+	for (i = 0; i < idx; i++) {
+		if (pmu[i].event == event)
+			break;
+	}
+	if (i < idx) {
+		/* pr_debug("++++++ found duplicate event 0x%02x i=%d\n", event, i); */
+		return -1;
+	}
+
+	for (i = 0; i < chip->count; i++) {
+		if (chip->desc[i].event == event)
+			break;
+	}
+
+	if (i == chip->count)
+		return -1;
+
+	return 0;
+}
+
+static void armv6_pmu_hw_start(struct met_pmu *pmu, int count)
+{
+	int i;
+	int generic = count - 1;
+
+	armv6_pmu_hw_reset_all();
+
+	for (i = 0; i < generic; i++) {
+		if (pmu[i].mode == MODE_POLLING)
+			armv6pmu_enable_event(i, pmu[i].event);
+	}
+
+	if (pmu[count - 1].mode == MODE_POLLING)
+		armv6pmu_enable_event(2, pmu[2].event);
+
+	armv6_pmu_control_write(ARMV6_PMCR_ENABLE);
+}
+
+static void armv6_pmu_hw_stop(int count)
+{
+	armv6_pmu_hw_reset_all();
+}
+
+static unsigned int armv6_pmu_hw_polling(struct met_pmu *pmu, int count, unsigned int *pmu_value)
+{
+	int i, cnt = 0;
+	int generic = count - 1;
+
+	for (i = 0; i < generic; i++) {
+		if (pmu[i].mode == MODE_POLLING) {
+			pmu_value[cnt] = armv6_pmu_read_count(i);
+			cnt++;
+		}
+	}
+
+	if (pmu[count - 1].mode == MODE_POLLING) {
+		pmu_value[cnt] = armv6_pmu_read_count(2);
+		cnt++;
+	}
+
+	armv6_pmu_control_write(ARMV6_PMCR_ENABLE | ARMV6_PMCR_CTR01_RESET |
+				ARMV6_PMCR_CCOUNT_RESET);
+
+	return cnt;
+}
+
+struct cpu_pmu_hw armv6_pmu = {
+	.name = "armv6_pmu",
+	.get_event_desc = armv6_pmu_hw_get_event_desc,
+	.check_event = armv6_pmu_hw_check_event,
+	.start = armv6_pmu_hw_start,
+	.stop = armv6_pmu_hw_stop,
+	.polling = armv6_pmu_hw_polling,
+};
+
+struct cpu_pmu_hw *v6_cpu_pmu_hw_init(int typeid)
+{
+	int i;
+
+	for (i = 0; i < CHIP_PMU_COUNT; i++) {
+		if (chips[i].type == typeid) {
+			chip = &(chips[i]);
+
+			break;
+		}
+	}
+
+	if (chip == NULL) {
+		chip = &chip_unknown;
+
+		return NULL;
+	}
+
+	armv6_pmu.nr_cnt = 3;
+	armv6_pmu.cpu_name = chip->cpu_name;
+
+	return &armv6_pmu;
+}
diff --git a/src/devtools/met-driver/4.19/mt2712/v6_pmu_hw.h b/src/devtools/met-driver/4.19/mt2712/v6_pmu_hw.h
new file mode 100644
index 0000000..a532f13
--- /dev/null
+++ b/src/devtools/met-driver/4.19/mt2712/v6_pmu_hw.h
@@ -0,0 +1,21 @@
+/*
+ * 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.
+ */
+
+
+#ifndef __V6_PMU_HW_H__
+#define __V6_PMU_HW_H__
+
+extern struct cpu_pmu_hw armv6_pmu;
+extern struct cpu_pmu_hw *v6_cpu_pmu_hw_init(int typeid);
+
+#endif
diff --git a/src/devtools/met-driver/4.19/mt2712/v6_pmu_name.h b/src/devtools/met-driver/4.19/mt2712/v6_pmu_name.h
new file mode 100644
index 0000000..c1cdd91
--- /dev/null
+++ b/src/devtools/met-driver/4.19/mt2712/v6_pmu_name.h
@@ -0,0 +1,43 @@
+/*
+ * 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.
+ */
+
+
+#ifndef _V6_PMU_NAME_H_
+#define _V6_PMU_NAME_H_
+
+/* ARM11 */
+struct pmu_desc arm11_pmu_desc[] = {
+	{0x00, "ICACHE_MISS"},
+	{0x01, "IBUF_STALL"},
+	{0x02, "DDEP_STALL"},
+	{0x03, "ITLB_MISS"},
+	{0x04, "DTLB_MISS"},
+	{0x05, "BR_EXEC"},
+	{0x06, "BR_MISPREDICT"},
+	{0x07, "CPU_INST"},
+	{0x09, "DCACHE_HIT"},
+	{0x0A, "L1D_CACHE"},
+	{0x0B, "L1D_CACHE_REFILL"},
+	{0x0C, "DCACHE_WBACK"},
+	{0x0D, "SW_PC_CHANGE"},
+	{0x0F, "MAIN_TLB_MISS"},
+	{0x10, "EXPL_D_ACCESS"},
+	{0x11, "LSU_FULL_STALL"},
+	{0x12, "WBUF_DRAINED"},
+	{0xFF, "CPU_CYCLES"},
+	{0x20, "NOP"},
+};
+
+#define ARM11_PMU_DESC_COUNT (sizeof(arm11_pmu_desc) / sizeof(struct pmu_desc))
+
+#endif				/* _V6_PMU_NAME_H_ */
diff --git a/src/devtools/met-driver/4.19/mt2712/v7_pmu_hw.c b/src/devtools/met-driver/4.19/mt2712/v7_pmu_hw.c
new file mode 100644
index 0000000..161bf22
--- /dev/null
+++ b/src/devtools/met-driver/4.19/mt2712/v7_pmu_hw.c
@@ -0,0 +1,303 @@
+/*
+ * 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 <asm/system.h> */
+#include <linux/smp.h>
+#include "cpu_pmu.h"
+#include "v6_pmu_hw.h"
+#include "v7_pmu_name.h"
+#include "v8_pmu_name.h"	/* for 32-bit build of arm64 cpu */
+
+#define ARMV7_PMCR_E		(1 << 0)	/* enable all counters */
+#define ARMV7_PMCR_P		(1 << 1)
+#define ARMV7_PMCR_C		(1 << 2)
+#define ARMV7_PMCR_D		(1 << 3)
+#define ARMV7_PMCR_X		(1 << 4)
+#define ARMV7_PMCR_DP		(1 << 5)
+#define ARMV7_PMCR_N_SHIFT		11	/* Number of counters supported */
+#define ARMV7_PMCR_N_MASK		0x1f
+#define ARMV7_PMCR_MASK			0x3f	/* mask for writable bits */
+
+
+enum ARM_TYPE {
+	CORTEX_A7 = 0xC07,
+	CORTEX_A9 = 0xC09,
+	CORTEX_A12 = 0xC0D,
+	CORTEX_A15 = 0xC0F,
+	CORTEX_A17 = 0xC0E,
+	CORTEX_A53 = 0xD03,
+	CORTEX_A57 = 0xD07,
+	CHIP_UNKNOWN = 0xFFF
+};
+
+struct chip_pmu {
+	enum ARM_TYPE type;
+	struct pmu_desc *desc;
+	unsigned int count;
+	const char *cpu_name;
+};
+
+static struct chip_pmu chips[] = {
+	{CORTEX_A7, a7_pmu_desc, A7_PMU_DESC_COUNT, "Cortex-A7"},
+	{CORTEX_A9, a9_pmu_desc, A9_PMU_DESC_COUNT, "Cortex-A9"},
+	{CORTEX_A12, a7_pmu_desc, A7_PMU_DESC_COUNT, "Cortex-A12"},
+	{CORTEX_A15, a7_pmu_desc, A7_PMU_DESC_COUNT, "Cortex-A15"},
+	{CORTEX_A17, a7_pmu_desc, A7_PMU_DESC_COUNT, "Cortex-A17"},
+	{CORTEX_A53, a53_pmu_desc, A53_PMU_DESC_COUNT, "Cortex-A53"},
+	{CORTEX_A57, a7_pmu_desc, A7_PMU_DESC_COUNT, "Cortex-A57"},
+};
+static struct chip_pmu chip_unknown = { CHIP_UNKNOWN, NULL, 0, "Unknown CPU" };
+
+#define CHIP_PMU_COUNT (sizeof(chips) / sizeof(struct chip_pmu))
+
+static struct chip_pmu *chip;
+
+static enum ARM_TYPE armv7_get_ic(void)
+{
+	unsigned int value;
+	/* Read Main ID Register */
+	asm volatile ("mrc p15, 0, %0, c0, c0, 0":"=r" (value));
+
+	value = (value & 0xffff) >> 4;	/* primary part number */
+	return value;
+}
+
+static inline void armv7_pmu_counter_select(unsigned int idx)
+{
+	asm volatile ("mcr p15, 0, %0, c9, c12, 5"::"r" (idx));
+	isb();
+}
+
+static inline void armv7_pmu_type_select(unsigned int idx, unsigned int type)
+{
+	armv7_pmu_counter_select(idx);
+	asm volatile ("mcr p15, 0, %0, c9, c13, 1"::"r" (type));
+}
+
+static inline unsigned int armv7_pmu_read_count(unsigned int idx)
+{
+	unsigned int value;
+
+	if (idx == 31) {
+		asm volatile ("mrc p15, 0, %0, c9, c13, 0":"=r" (value));
+	} else {
+		armv7_pmu_counter_select(idx);
+		asm volatile ("mrc p15, 0, %0, c9, c13, 2":"=r" (value));
+	}
+	return value;
+}
+
+static inline void armv7_pmu_write_count(int idx, u32 value)
+{
+	if (idx == 31) {
+		asm volatile ("mcr p15, 0, %0, c9, c13, 0"::"r" (value));
+	} else {
+		armv7_pmu_counter_select(idx);
+		asm volatile ("mcr p15, 0, %0, c9, c13, 2"::"r" (value));
+	}
+}
+
+static inline void armv7_pmu_enable_count(unsigned int idx)
+{
+	asm volatile ("mcr p15, 0, %0, c9, c12, 1"::"r" (1 << idx));
+}
+
+static inline void armv7_pmu_disable_count(unsigned int idx)
+{
+	asm volatile ("mcr p15, 0, %0, c9, c12, 2"::"r" (1 << idx));
+}
+
+static inline void armv7_pmu_enable_intr(unsigned int idx)
+{
+	asm volatile ("mcr p15, 0, %0, c9, c14, 1"::"r" (1 << idx));
+}
+
+static inline void armv7_pmu_disable_intr(unsigned int idx)
+{
+	asm volatile ("mcr p15, 0, %0, c9, c14, 2"::"r" (1 << idx));
+}
+
+static inline unsigned int armv7_pmu_overflow(void)
+{
+	unsigned int val;
+
+	asm volatile ("mrc p15, 0, %0, c9, c12, 3":"=r" (val));	/* read */
+	asm volatile ("mcr p15, 0, %0, c9, c12, 3"::"r" (val));
+	return val;
+}
+
+static inline unsigned int armv7_pmu_control_read(void)
+{
+	u32 val;
+
+	asm volatile ("mrc p15, 0, %0, c9, c12, 0":"=r" (val));
+	return val;
+}
+
+static inline void armv7_pmu_control_write(unsigned int val)
+{
+	val &= ARMV7_PMCR_MASK;
+	isb();
+	asm volatile ("mcr p15, 0, %0, c9, c12, 0"::"r" (val));
+}
+
+static int armv7_pmu_hw_get_counters(void)
+{
+	int count = armv7_pmu_control_read();
+	/* N, bits[15:11] */
+	count = ((count >> ARMV7_PMCR_N_SHIFT) & ARMV7_PMCR_N_MASK);
+	return count;
+}
+
+static void armv7_pmu_hw_reset_all(int generic_counters)
+{
+	int i;
+
+	armv7_pmu_control_write(ARMV7_PMCR_C | ARMV7_PMCR_P);
+	/* generic counter */
+	for (i = 0; i < generic_counters; i++) {
+		armv7_pmu_disable_intr(i);
+		armv7_pmu_disable_count(i);
+	}
+	/* cycle counter */
+	armv7_pmu_disable_intr(31);
+	armv7_pmu_disable_count(31);
+	armv7_pmu_overflow();	/* clear overflow */
+}
+
+static int armv7_pmu_hw_get_event_desc(int i, int event, char *event_desc)
+{
+	if (event_desc == NULL)
+		return -1;
+
+	for (i = 0; i < chip->count; i++) {
+		if (chip->desc[i].event == event) {
+			strncpy(event_desc, chip->desc[i].name, MXSIZE_PMU_DESC - 1);
+			break;
+		}
+	}
+	if (i == chip->count)
+		return -1;
+
+	return 0;
+}
+
+static int armv7_pmu_hw_check_event(struct met_pmu *pmu, int idx, int event)
+{
+	int i;
+
+	/* Check if event is duplicate */
+	for (i = 0; i < idx; i++) {
+		if (pmu[i].event == event)
+			break;
+	}
+	if (i < idx) {
+		/* pr_debug("++++++ found duplicate event 0x%02x i=%d\n", event, i); */
+		return -1;
+	}
+
+	for (i = 0; i < chip->count; i++) {
+		if (chip->desc[i].event == event)
+			break;
+	}
+
+	if (i == chip->count)
+		return -1;
+
+	return 0;
+}
+
+static void armv7_pmu_hw_start(struct met_pmu *pmu, int count)
+{
+	int i;
+	int generic = count - 1;
+
+	armv7_pmu_hw_reset_all(generic);
+	for (i = 0; i < generic; i++) {
+		if (pmu[i].mode == MODE_POLLING) {
+			armv7_pmu_type_select(i, pmu[i].event);
+			armv7_pmu_enable_count(i);
+		}
+	}
+	if (pmu[count - 1].mode == MODE_POLLING) {	/* cycle counter */
+		armv7_pmu_enable_count(31);
+	}
+	armv7_pmu_control_write(ARMV7_PMCR_E);
+}
+
+static void armv7_pmu_hw_stop(int count)
+{
+	int generic = count - 1;
+
+	armv7_pmu_hw_reset_all(generic);
+}
+
+static unsigned int armv7_pmu_hw_polling(struct met_pmu *pmu, int count, unsigned int *pmu_value)
+{
+	int i, cnt = 0;
+	int generic = count - 1;
+
+	for (i = 0; i < generic; i++) {
+		if (pmu[i].mode == MODE_POLLING) {
+			pmu_value[cnt] = armv7_pmu_read_count(i);
+			cnt++;
+		}
+	}
+	if (pmu[count - 1].mode == MODE_POLLING) {
+		pmu_value[cnt] = armv7_pmu_read_count(31);
+		cnt++;
+	}
+	armv7_pmu_control_write(ARMV7_PMCR_C | ARMV7_PMCR_P | ARMV7_PMCR_E);
+
+	return cnt;
+}
+
+
+struct cpu_pmu_hw armv7_pmu = {
+	.name = "armv7_pmu",
+	.get_event_desc = armv7_pmu_hw_get_event_desc,
+	.check_event = armv7_pmu_hw_check_event,
+	.start = armv7_pmu_hw_start,
+	.stop = armv7_pmu_hw_stop,
+	.polling = armv7_pmu_hw_polling,
+};
+
+struct cpu_pmu_hw *cpu_pmu_hw_init(void)
+{
+	int i;
+	enum ARM_TYPE type;
+	struct cpu_pmu_hw *pmu = NULL;
+
+	type = armv7_get_ic();
+	for (i = 0; i < CHIP_PMU_COUNT; i++) {
+		if (chips[i].type == type) {
+			chip = &(chips[i]);
+			break;
+		}
+	}
+
+	if (chip != NULL) {
+		armv7_pmu.nr_cnt = armv7_pmu_hw_get_counters() + 1;
+		armv7_pmu.cpu_name = chip->cpu_name;
+		pmu = &armv7_pmu;
+	} else {
+		pmu = v6_cpu_pmu_hw_init(type);
+	}
+
+	if ((chip == NULL) && (pmu == NULL)) {
+		chip = &chip_unknown;
+		return NULL;
+	}
+
+	return pmu;
+}
diff --git a/src/devtools/met-driver/4.19/mt2712/v7_pmu_name.h b/src/devtools/met-driver/4.19/mt2712/v7_pmu_name.h
new file mode 100644
index 0000000..3219cd9
--- /dev/null
+++ b/src/devtools/met-driver/4.19/mt2712/v7_pmu_name.h
@@ -0,0 +1,127 @@
+/*
+ * 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.
+ */
+
+#ifndef _V7_PMU_NAME_H_
+#define _V7_PMU_NAME_H_
+
+/* Cortex-A7 */
+struct pmu_desc a7_pmu_desc[] = {
+	{0x00, "SW_INCR"},
+	{0x01, "L1I_CACHE_REFILL"},
+	{0x02, "L1I_TLB_REFILL"},
+	{0x03, "L1D_CACHE_REFILL"},
+	{0x04, "L1D_CACHE"},
+	{0x05, "L1D_TLB_REFILL"},
+	{0x06, "LD_RETIRED"},
+	{0x07, "ST_RETIRED"},
+	/* {0x08, "INST_RETIRED"}, */
+	{0x08, "CPU_INST"},
+	{0x09, "EXC_TAKEN"},
+	{0x0A, "EXC_RETURN"},
+	{0x0B, "CID_WRITE_RETIRED"},
+	{0x0C, "PC_WRITE_RETIRED"},
+	{0x0D, "BR_IMMED_RETIRED"},
+	{0x0E, "BR_RETURN_RETIRED"},
+	{0x0F, "UNALIGNED_LDST_RETIRED"},
+	{0x10, "BR_MIS_PRED"},
+	{0x12, "BR_PRED"},
+	{0x13, "MEM_ACCESS"},
+	{0x14, "L1I_CACHE"},
+	{0x15, "L1D_CACHE_WB"},
+	{0x16, "L2D_CACHE"},
+	{0x17, "L2D_CACHE_REFILL"},
+	{0x18, "L2D_CACHE_WB"},
+	{0x19, "BUS_ACCESS"},
+	{0x1D, "BUS_CYCLES"},
+	{0x60, "BUS_READ_ACCESS"},
+	{0x61, "BUS_WRITE_ACCESS"},
+	{0x86, "IRQ_EXC_TAKEN"},
+	{0x87, "FIQ_EXC_TAKEN"},
+	{0xC0, "EXT_MEM_REQ"},
+	{0xC1, "NO_CACHE_EXT_MEM_REQ"},
+	{0xC2, "PREFETCH_LINEFILL"},
+	{0xC3, "PREFETCH_LINEFILL_DROPPED"},
+	{0xC4, "ENT_READ_ALLOC_MODE"},
+	{0xC5, "READ_ALLOC_MODE"},
+	{0xC7, "ETM_EXT_OUT0"},
+	{0xC8, "ETM_EXT_OUT1"},
+	{0xC9, "DATA_WRITE_STALL"},
+	{0xCA, "DATA_READ_SNOOP_CLUSTER"},
+	{0xFF, "CPU_CYCLES"}
+};
+
+/* Cortex-A9 */
+struct pmu_desc a9_pmu_desc[] = {
+	{0x00, "SW_INCR"},
+	{0x01, "L1I_CACHE_REFILL"},
+	{0x02, "L1I_TLB_REFILL"},
+	{0x03, "L1D_CACHE_REFILL"},
+	{0x04, "L1D_CACHE"},
+	{0x05, "L1D_TLB_REFILL"},
+	{0x06, "LD_RETIRED"},
+	{0x07, "ST_RETIRED"},
+	{0x09, "EXC_TAKEN"},
+	{0x0A, "EXC_RETURN"},
+	{0x0B, "CID_WRITE_RETIRED"},
+	{0x0C, "PC_WRITE_RETIRED"},
+	{0x0D, "BR_IMMED_RETIRED"},
+	{0x0F, "UNALIGNED_LDST_RETIRED"},
+	{0x10, "BR_MIS_PRED"},
+	{0x12, "BR_PRED"},
+	{0x40, "JAVA_BC_EXEC"},
+	{0x41, "SW_JAVA_BC_EXEC"},
+	{0x42, "JAZELLE_BB_EXEC"},
+	{0x50, "CO_LF_MISS"},
+	{0x51, "CO_LF_HIT"},
+	{0x60, "ICACHE_DEP_STALL"},
+	{0x61, "DCACHE_DEP_STALL"},
+	{0x62, "M_TLB_STALL"},
+	{0x63, "STREX_PASSED"},
+	{0x64, "STREX_FAILED"},
+	{0x65, "DATA_EVICT"},
+	{0x66, "ISSUE_NO_DISP"},
+	{0x67, "ISSUE_EMPTY"},
+	/* {0x68, "INS_RENAME"}, */
+	{0x68, "CPU_INST"},
+	{0x6E, "PRED_FN_RET"},
+	{0x70, "MAIN_EXEC_INST"},
+	{0x71, "SEC_EXEC_INST"},
+	{0x72, "LOAD_STORE_INST"},
+	{0x73, "FLOAT_INST_RR"},
+	{0x74, "NEON_INST_RR"},
+	{0x80, "STALL_PLD"},
+	{0x81, "STALL_WRITE"},
+	{0x82, "STALL_INST_M_TLB_MISS"},
+	{0x83, "STALL_DATA_M_TLB_MISS"},
+	{0x84, "STALL_INST_U_TLB"},
+	{0x85, "STALL_DATA_U_TLB"},
+	{0x86, "STALL_DMB"},
+	{0x8A, "INT_CLK_EN"},
+	{0x8B, "DATA_E_CLK_EN"},
+	{0x90, "ISB_INST"},
+	{0x91, "DSB_INST"},
+	{0x92, "INS_DMB"},
+	{0x93, "EXT_IRQ"},
+	{0xA0, "PLE_CACHE_REQ_COMP"},
+	{0xA1, "PLE_CACHE_REQ_SKP"},
+	{0xA2, "PLE_FIFO_FLUSH"},
+	{0xA3, "PLE_REQ_COMP"},
+	{0xA4, "PLE_FIFO_OF"},
+	{0xA5, "PLE_REQ_PRG"},
+	{0xFF, "CPU_CYCLES"}
+};
+
+#define A7_PMU_DESC_COUNT (sizeof(a7_pmu_desc) / sizeof(struct pmu_desc))
+#define A9_PMU_DESC_COUNT (sizeof(a9_pmu_desc) / sizeof(struct pmu_desc))
+
+#endif				/* _V7_PMU_NAME_H_ */
diff --git a/src/devtools/met-driver/4.19/mt2712/v8_pmu_hw.c b/src/devtools/met-driver/4.19/mt2712/v8_pmu_hw.c
new file mode 100644
index 0000000..6a286af
--- /dev/null
+++ b/src/devtools/met-driver/4.19/mt2712/v8_pmu_hw.c
@@ -0,0 +1,304 @@
+/*
+ * 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/smp.h>
+#include "interface.h"
+#include "cpu_pmu.h"
+#include "v8_pmu_name.h"
+
+/*
+ * Per-CPU PMCR: config reg
+ */
+#define ARMV8_PMCR_E		(1 << 0)	/* Enable all counters */
+#define ARMV8_PMCR_P		(1 << 1)	/* Reset all counters */
+#define ARMV8_PMCR_C		(1 << 2)	/* Cycle counter reset */
+#define ARMV8_PMCR_D		(1 << 3)	/* CCNT counts every 64th cpu cycle */
+#define ARMV8_PMCR_X		(1 << 4)	/* Export to ETM */
+#define ARMV8_PMCR_DP		(1 << 5)	/* Disable CCNT if non-invasive debug */
+#define	ARMV8_PMCR_N_SHIFT	11	/* Number of counters supported */
+#define	ARMV8_PMCR_N_MASK	0x1f
+#define	ARMV8_PMCR_MASK		0x3f	/* Mask for writable bits */
+
+/*
+ * PMOVSR: counters overflow flag status reg
+ */
+#define	ARMV8_OVSR_MASK		0xffffffff	/* Mask for writable bits */
+#define	ARMV8_OVERFLOWED_MASK	ARMV8_OVSR_MASK
+
+
+enum ARM_TYPE {
+	CORTEX_A53 = 0xD03,
+	CORTEX_A35 = 0xD04,
+	CORTEX_A57 = 0xD07,
+	CORTEX_A72 = 0xD08,
+	CORTEX_A73 = 0xD09,
+	CHIP_UNKNOWN = 0xFFF
+};
+
+struct chip_pmu {
+	enum ARM_TYPE type;
+	struct pmu_desc *desc;
+	unsigned int count;
+	const char *cpu_name;
+};
+
+static struct chip_pmu chips[] = {
+	{CORTEX_A53, a53_pmu_desc, A53_PMU_DESC_COUNT, "Cortex-A53"},
+	{CORTEX_A35, a53_pmu_desc, A53_PMU_DESC_COUNT, "Cortex-A35"},
+	{CORTEX_A57, a53_pmu_desc, A53_PMU_DESC_COUNT, "Cortex-A57"},
+	{CORTEX_A72, a53_pmu_desc, A53_PMU_DESC_COUNT, "Cortex-A72"},
+	{CORTEX_A73, a53_pmu_desc, A53_PMU_DESC_COUNT, "Cortex-A73"},
+};
+static struct chip_pmu chip_unknown = { CHIP_UNKNOWN, NULL, 0, "Unknown CPU" };
+
+#define CHIP_PMU_COUNT (sizeof(chips) / sizeof(struct chip_pmu))
+
+static struct chip_pmu *chip;
+
+static enum ARM_TYPE armv8_get_ic(void)
+{
+	unsigned int value;
+	/* Read Main ID Register */
+	asm volatile ("mrs %x0, midr_el1":"=r"(value));
+
+	value = (value & 0xffff) >> 4;	/* primary part number */
+	return value;
+}
+
+static inline void armv8_pmu_counter_select(unsigned int idx)
+{
+	asm volatile ("msr pmselr_el0, %x0"::"r" (idx));
+	isb();
+}
+
+static inline void armv8_pmu_type_select(unsigned int idx, unsigned int type)
+{
+	armv8_pmu_counter_select(idx);
+	asm volatile ("msr pmxevtyper_el0, %x0"::"r" (type));
+}
+
+static inline unsigned int armv8_pmu_read_count(unsigned int idx)
+{
+	unsigned int value;
+
+	if (idx == 31) {
+		asm volatile ("mrs %x0, pmccntr_el0":"=r" (value));
+	} else {
+		armv8_pmu_counter_select(idx);
+		asm volatile ("mrs %x0, pmxevcntr_el0":"=r" (value));
+	}
+	return value;
+}
+
+static inline void armv8_pmu_write_count(int idx, u32 value)
+{
+	if (idx == 31) {
+		asm volatile ("msr pmccntr_el0, %x0"::"r" (value));
+	} else {
+		armv8_pmu_counter_select(idx);
+		asm volatile ("msr pmxevcntr_el0, %x0"::"r" (value));
+	}
+}
+
+static inline void armv8_pmu_enable_count(unsigned int idx)
+{
+	asm volatile ("msr pmcntenset_el0, %x0"::"r" (1 << idx));
+}
+
+static inline void armv8_pmu_disable_count(unsigned int idx)
+{
+	asm volatile ("msr pmcntenclr_el0, %x0"::"r" (1 << idx));
+}
+
+static inline void armv8_pmu_enable_intr(unsigned int idx)
+{
+	asm volatile ("msr pmintenset_el1, %x0"::"r" (1 << idx));
+}
+
+static inline void armv8_pmu_disable_intr(unsigned int idx)
+{
+	asm volatile ("msr pmintenclr_el1, %x0"::"r" (1 << idx));
+	isb();
+	asm volatile ("msr pmovsclr_el0, %x0"::"r" (1 << idx));
+	isb();
+}
+
+static inline unsigned int armv8_pmu_overflow(void)
+{
+	unsigned int val;
+
+	asm volatile ("mrs %x0, pmovsclr_el0":"=r" (val));	/* read */
+	val &= ARMV8_OVSR_MASK;
+	asm volatile ("mrs %x0, pmovsclr_el0"::"r" (val));
+	return val;
+}
+
+static inline unsigned int armv8_pmu_control_read(void)
+{
+	unsigned int val;
+
+	asm volatile ("mrs %x0, pmcr_el0":"=r" (val));
+	return val;
+}
+
+static inline void armv8_pmu_control_write(u32 val)
+{
+	val &= ARMV8_PMCR_MASK;
+	isb();
+	asm volatile ("msr pmcr_el0, %x0"::"r" (val));
+}
+
+static int armv8_pmu_hw_get_counters(void)
+{
+	int count = armv8_pmu_control_read();
+	/* N, bits[15:11] */
+	count = ((count >> ARMV8_PMCR_N_SHIFT) & ARMV8_PMCR_N_MASK);
+	return count;
+}
+
+static void armv8_pmu_hw_reset_all(int generic_counters)
+{
+	int i;
+
+	armv8_pmu_control_write(ARMV8_PMCR_C | ARMV8_PMCR_P);
+	/* generic counter */
+	for (i = 0; i < generic_counters; i++) {
+		armv8_pmu_disable_intr(i);
+		armv8_pmu_disable_count(i);
+	}
+	/* cycle counter */
+	armv8_pmu_disable_intr(31);
+	armv8_pmu_disable_count(31);
+	armv8_pmu_overflow();	/* clear overflow */
+}
+
+static int armv8_pmu_hw_get_event_desc(int i, int event, char *event_desc)
+{
+	if (event_desc == NULL)
+		return -1;
+
+	for (i = 0; i < chip->count; i++) {
+		if (chip->desc[i].event == event) {
+			strncpy(event_desc, chip->desc[i].name, MXSIZE_PMU_DESC - 1);
+			break;
+		}
+	}
+	if (i == chip->count)
+		return -1;
+
+	return 0;
+}
+
+static int armv8_pmu_hw_check_event(struct met_pmu *pmu, int idx, int event)
+{
+	int i;
+
+	/* Check if event is duplicate */
+	for (i = 0; i < idx; i++) {
+		if (pmu[i].event == event)
+			break;
+	}
+	if (i < idx) {
+		/* pr_debug("++++++ found duplicate event 0x%02x i=%d\n", event, i); */
+		return -1;
+	}
+
+	for (i = 0; i < chip->count; i++) {
+		if (chip->desc[i].event == event)
+			break;
+	}
+
+	if (i == chip->count)
+		return -1;
+
+	return 0;
+}
+
+static void armv8_pmu_hw_start(struct met_pmu *pmu, int count)
+{
+	int i;
+	int generic = count - 1;
+
+	armv8_pmu_hw_reset_all(generic);
+	for (i = 0; i < generic; i++) {
+		if (pmu[i].mode == MODE_POLLING) {
+			armv8_pmu_type_select(i, pmu[i].event);
+			armv8_pmu_enable_count(i);
+		}
+	}
+	if (pmu[count - 1].mode == MODE_POLLING) {	/* cycle counter */
+		armv8_pmu_enable_count(31);
+	}
+	armv8_pmu_control_write(ARMV8_PMCR_E);
+}
+
+static void armv8_pmu_hw_stop(int count)
+{
+	int generic = count - 1;
+
+	armv8_pmu_hw_reset_all(generic);
+}
+
+static unsigned int armv8_pmu_hw_polling(struct met_pmu *pmu, int count, unsigned int *pmu_value)
+{
+	int i, cnt = 0;
+	int generic = count - 1;
+
+	for (i = 0; i < generic; i++) {
+		if (pmu[i].mode == MODE_POLLING) {
+			pmu_value[cnt] = armv8_pmu_read_count(i);
+			cnt++;
+		}
+	}
+	if (pmu[count - 1].mode == MODE_POLLING) {
+		pmu_value[cnt] = armv8_pmu_read_count(31);
+		cnt++;
+	}
+	armv8_pmu_control_write(ARMV8_PMCR_C | ARMV8_PMCR_P | ARMV8_PMCR_E);
+
+	return cnt;
+}
+
+
+struct cpu_pmu_hw armv8_pmu = {
+	.name = "armv8_pmu",
+	.get_event_desc = armv8_pmu_hw_get_event_desc,
+	.check_event = armv8_pmu_hw_check_event,
+	.start = armv8_pmu_hw_start,
+	.stop = armv8_pmu_hw_stop,
+	.polling = armv8_pmu_hw_polling,
+};
+
+struct cpu_pmu_hw *cpu_pmu_hw_init(void)
+{
+	int i;
+	enum ARM_TYPE type;
+
+	type = armv8_get_ic();
+	PR_BOOTMSG("CPU TYPE - v8: %x\n", (unsigned int)type);
+	for (i = 0; i < CHIP_PMU_COUNT; i++) {
+		if (chips[i].type == type) {
+			chip = &(chips[i]);
+			break;
+		}
+	}
+	if (i == CHIP_PMU_COUNT) {
+		chip = &chip_unknown;
+		return NULL;
+	}
+
+	armv8_pmu.nr_cnt = armv8_pmu_hw_get_counters() + 1;
+	armv8_pmu.cpu_name = chip->cpu_name;
+
+	return &armv8_pmu;
+}
diff --git a/src/devtools/met-driver/4.19/mt2712/v8_pmu_hw_v2.c b/src/devtools/met-driver/4.19/mt2712/v8_pmu_hw_v2.c
new file mode 100644
index 0000000..c6d4b21
--- /dev/null
+++ b/src/devtools/met-driver/4.19/mt2712/v8_pmu_hw_v2.c
@@ -0,0 +1,497 @@
+/*

+ * 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/smp.h> /* on_each_cpu */

+#include <linux/cpumask.h> /* for_each_possible_cpu(cpu) */

+

+#include "interface.h"

+#include "cpu_pmu_v2.h"

+#include "v8_pmu_hw_v2.h"

+#include "met_kernel_symbol.h"

+

+

+/*******************************************************************************

+*				Type Define

+*******************************************************************************/

+/*

+ * Per-CPU PMCR: config reg

+ */

+#define ARMV8_PMCR_E		(1 << 0)	/* Enable all counters */

+#define ARMV8_PMCR_P		(1 << 1)	/* Reset all counters */

+#define ARMV8_PMCR_C		(1 << 2)	/* Cycle counter reset */

+#define ARMV8_PMCR_D		(1 << 3)	/* CCNT counts every 64th cpu cycle */

+#define ARMV8_PMCR_X		(1 << 4)	/* Export to ETM */

+#define ARMV8_PMCR_DP		(1 << 5)	/* Disable CCNT if non-invasive debug */

+#define	ARMV8_PMCR_N_SHIFT	11		/* Number of counters supported */

+#define	ARMV8_PMCR_N_MASK	0x1f

+#define	ARMV8_PMCR_MASK		0x3f		/* Mask for writable bits */

+

+/*

+ * PMOVSR: counters overflow flag status reg

+ */

+#define	ARMV8_OVSR_MASK		0xffffffff	/* Mask for writable bits */

+#define	ARMV8_OVERFLOWED_MASK	ARMV8_OVSR_MASK

+

+

+/*******************************************************************************

+*				Fuction Pototypes

+*******************************************************************************/

+static int armv8_pmu_hw_get_event_desc(int event, char *event_desc);

+static int armv8_pmu_hw_check_event(struct met_pmu_v2 *pmu, int idx, int event);

+static void armv8_pmu_hw_start(struct met_pmu_v2 *pmu, int count);

+static void armv8_pmu_hw_stop(int count);

+static unsigned int armv8_pmu_hw_polling(struct met_pmu_v2 *pmu, int count, unsigned int *pmu_value);

+

+

+/*******************************************************************************

+*				Globe Variables

+*******************************************************************************/

+struct pmu_desc_v2 a53_pmu_desc_v2[] = {

+	{0x00, "SW_INCR"},

+	{0x01, "L1I_CACHE_REFILL"},

+	{0x02, "L1I_TLB_REFILL"},

+	{0x03, "L1D_CACHE_REFILL"},

+	{0x04, "L1D_CACHE"},

+	{0x05, "L1D_TLB_REFILL"},

+	{0x06, "LD_RETIRED"},

+	{0x07, "ST_RETIRED"},

+	{0x08, "INST_RETIRED"},

+	{0x09, "EXC_TAKEN"},

+	{0x0A, "EXC_RETURN"},

+	{0x0B, "CID_WRITE_RETIRED"},

+	{0x0C, "PC_WRITE_RETIRED"},

+	{0x0D, "BR_IMMED_RETIRED"},

+	{0x0E, "BR_RETURN_RETIRED"},

+	{0x0F, "UNALIGNED_LDST_RETIRED"},

+

+	{0x10, "BR_MIS_PRED"},

+	{0x11, "CPU_CYCLES"},

+	{0x12, "BR_PRED"},

+	{0x13, "MEM_ACCESS"},

+	{0x14, "L1I_CACHE"},

+	{0x15, "L1D_CACHE_WB"},

+	{0x16, "L2D_CACHE"},

+	{0x17, "L2D_CACHE_REFILL"},

+	{0x18, "L2D_CACHE_WB"},

+	{0x19, "BUS_ACCESS"},

+	{0x1A, "MEMORY_ERROR"},

+	{0x1D, "BUS_CYCLES"},

+	{0x1E, "CHAIN"},

+

+	{0x60, "BUS_READ_ACCESS"},

+	{0x61, "BUS_WRITE_ACCESS"},

+

+	{0x7A, "BR_INDIRECT_SPEC"},

+

+	{0x86, "IRQ_EXC_TAKEN"},

+	{0x87, "FIQ_EXC_TAKEN"},

+

+	{0xC0, "EXT_MEM_REQ"},

+	{0xC1, "NO_CACHE_EXT_MEM_REQ"},

+	{0xC2, "PREFETCH_LINEFILL"},

+	{0xC4, "ENT_READ_ALLOC_MODE"},

+	{0xC5, "READ_ALLOC_MODE"},

+	{0xC6, "PRE_DECODE_ERROR"},

+	{0xC7, "WRITE_STALL"},

+	{0xC8, "SCU_SNOOP_DATA_FROM_ANOTHER_CPU"},

+	{0xC9, "CONDITIONAL_BRANCH_EXE"},

+	{0xCA, "INDIRECT_BRANCH_MISPREDICT"},

+	{0xCB, "INDIRECT_BRANCH_MISPREDICT_ADDR"},/*"INDIRECT_BRANCH_MISPREDICT_ADDR_MISSCOMPARE" */

+	{0xCC, "COND_BRANCH_MISPREDICT"},

+

+	{0xD0, "L1_INST_CACHE_MEM_ERR"},

+

+	{0xE1, "ICACHE_MISS_STALL"},

+	{0xE2, "DPU_IQ_EMPTY"},

+	{0xE4, "NOT_FPU_NEON_INTERLOCK"},

+	{0xE5, "LOAD_STORE_INTERLOCK"},

+	{0xE6, "FPU_NEON_INTERLOCK"},

+	{0xE7, "LOAD_MISS_STALL"},

+	{0xE8, "STORE_STALL"},

+	{0xFF, "CPU_CYCLES"}

+};

+#define A53_PMU_DESC_COUNT (sizeof(a53_pmu_desc_v2) / sizeof(struct pmu_desc_v2))

+

+/* Cortex-A73 */

+struct pmu_desc_v2 a73_pmu_desc_v2[] = {

+	{0x00, "SW_INCR"},

+	{0x01, "L1I_CACHE_REFILL"},

+	{0x02, "L1I_TLB_REFILL"},

+	{0x03, "L1D_CACHE_REFILL"},

+	{0x04, "L1D_CACHE"},

+	{0x05, "L1D_TLB_REFILL"},

+	{0x08, "INST_RETIRED"},

+	{0x09, "EXC_TAKEN"},

+	{0x0A, "EXC_RETURN"},

+	{0x0B, "CID_WRITE_RETIRED"},

+	{0x0C, "PC_WRITE_RETIRED"},

+	{0x0D, "BR_IMMED_RETIRED"},

+	{0x0E, "BR_RETURN_RETIRED"},

+

+	{0x10, "BR_MIS_PRED"},

+	{0x11, "CPU_CYCLES"},

+	{0x12, "BR_PRED"},

+	{0x13, "MEM_ACCESS"},

+	{0x14, "L1I_CACHE"},

+	{0x15, "L1D_CACHE_WB"},

+	{0x16, "L2D_CACHE"},

+	{0x17, "L2D_CACHE_REFILL"},

+	{0x18, "L2D_CACHE_WB"},

+	{0x19, "BUS_ACCESS"},

+	{0x1B, "INT_SPEC"},

+	{0x1C, "TTBR_WRITE_RETIRED"},

+	{0x1D, "BUS_CYCLES"},

+	{0x1E, "CHAIN"},

+

+	{0x40, "L1D_CACHE_RD"},

+	{0x41, "L1D_CACHE_WR"},

+

+	{0x50, "L2D_CACHE_RD"},

+	{0x51, "L2D_CACHE_WR"},

+	{0x56, "L2D_CACHE_WB_VICTIM"},

+	{0x57, "L2D_CACHE_WB_CLEAN"},

+	{0x58, "L2D_CACHE_INVAL"},

+

+	{0x62, "BUS_ACCESS_SHARED"},

+	{0x63, "BUS_ACCESS_NOT_SHARED"},

+	{0x64, "BUS_ACCESS_NORMAL"},

+	{0x65, "BUS_ACCESS_SO_DIV"},

+	{0x66, "MEM_ACCESS_RD"},

+	{0x67, "MEM_ACCESS_WR"},

+	{0x6A, "UNALIGNED_LDST_SPEC"},

+	{0x6C, "LDREX_SPEC"},

+	{0x6E, "STREC_FAIL_SPEC"},

+

+	{0x70, "LD_SPEC"},

+	{0x71, "ST_SPEC"},

+	{0x72, "LDST_SPEC"},

+	{0x73, "DP_SPEC"},

+	{0x74, "ASE_SPEC"},

+	{0x75, "VFP_SPEC"},

+	{0x77, "CRYPTO_SPEC"},

+	{0x7A, "BR_INDIRECT_SPEC"},

+	{0x7C, "ISB_SPEC"},

+	{0x7D, "DSB_SPEC"},

+	{0x7E, "DMB_SPEC"},

+

+	{0x8A, "EXC_HVC"},

+

+	{0xC0, "LF_STALL"},

+	{0xC1, "PTW_STALL"},

+	{0xC2, "I_TAG_RAM_RD"},

+	{0xC3, "I_DATA_RAM_RD"},

+	{0xC4, "I_BTAC_RAM_RD"},

+

+	{0xD3, "D_LSU_SLOT_FULL"},

+	{0xD8, "LS_IQ_FULL"},

+	{0xD9, "DP_IQ_FULL"},

+	{0xDA, "DE_IQ_FULL"},

+	{0xDC, "EXC_TRAP_HYP"},

+	{0xDE, "ETM_EXT_OUT0"},

+	{0xDF, "ETM_EXT_OUT1"},

+

+	{0xE0, "MMU_PTW"},

+	{0xE1, "MMU_PTW_ST1"},

+	{0xE2, "MMU_PTW_ST2"},

+	{0xE3, "MMU_PTW_LSU"},

+	{0xE4, "MMU_PTW_ISIDE"},

+	{0xE5, "MMU_PTW_PLD"},

+	{0xE6, "MMU_PTW_CP15"},

+	{0xE7, "PLD_UTLB_REFILL"},

+	{0xE8, "CP15_UTLB_REFILL"},

+	{0xE9, "UTLB_FLUSH"},

+	{0xEA, "TLB_ACESS"},

+	{0xEB, "TLB_MISS"},

+	{0xEC, "DCACHE_SELF_HIT_VIPT"},

+        {0xEE, "CYCLES_L2_IDLE"},

+        {0xEF, "CPU_DECODE_UNIT_STALLED"},

+	{0xFF, "CPU_CYCLES"}

+};

+#define A73_PMU_DESC_COUNT (sizeof(a73_pmu_desc_v2) / sizeof(struct pmu_desc_v2))

+

+

+static struct chip_pmu_v2 *gChip[MXNR_CPU_V2];

+static struct chip_pmu_v2 chips[] = {

+	{CORTEX_A53, a53_pmu_desc_v2, A53_PMU_DESC_COUNT, 0, "Cortex-A53"},

+	{CORTEX_A35, a53_pmu_desc_v2, A53_PMU_DESC_COUNT, 0, "Cortex-A35"},

+	{CORTEX_A57, a53_pmu_desc_v2, A53_PMU_DESC_COUNT, 0, "Cortex-A57"},

+	{CORTEX_A72, a53_pmu_desc_v2, A53_PMU_DESC_COUNT, 0, "Cortex-A72"},

+	{CORTEX_A73, a73_pmu_desc_v2, A73_PMU_DESC_COUNT, 0, "Cortex-A73"},

+};

+static struct chip_pmu_v2 chip_unknown = { CHIP_UNKNOWN, NULL, 0, 0, "Unknown CPU" };

+#define CHIP_PMU_COUNT (sizeof(chips) / sizeof(struct chip_pmu_v2))

+

+struct cpu_pmu_hw_v2 armv8_pmu_v2 = {

+	.name = "armv8_pmu",

+	.get_event_desc = armv8_pmu_hw_get_event_desc,

+	.check_event = armv8_pmu_hw_check_event,

+	.start = armv8_pmu_hw_start,

+	.stop = armv8_pmu_hw_stop,

+	.polling = armv8_pmu_hw_polling,

+};

+

+

+/*******************************************************************************

+*				Iplement Start

+*******************************************************************************/

+static struct chip_pmu_v2 *get_chip_pmu_by_cpu_id(int cpu)

+{

+	return gChip[cpu];

+}

+

+static void set_chip_pmu_by_cpu_id(int cpu, struct chip_pmu_v2 *chip)

+{

+	gChip[cpu] = chip;

+}

+

+static inline void armv8_pmu_counter_select(unsigned int idx)

+{

+	asm volatile ("msr pmselr_el0, %x0"::"r" (idx));

+	isb();

+}

+

+static inline void armv8_pmu_type_select(unsigned int idx, unsigned int type)

+{

+	armv8_pmu_counter_select(idx);

+	asm volatile ("msr pmxevtyper_el0, %x0"::"r" (type));

+}

+

+static inline unsigned int armv8_pmu_read_count(unsigned int idx)

+{

+	unsigned int value;

+

+	if (idx == 31) {

+		asm volatile ("mrs %x0, pmccntr_el0":"=r" (value));

+	} else {

+		armv8_pmu_counter_select(idx);

+		asm volatile ("mrs %x0, pmxevcntr_el0":"=r" (value));

+	}

+	return value;

+}

+

+static inline void armv8_pmu_write_count(int idx, u32 value)

+{

+	if (idx == 31) {

+		asm volatile ("msr pmccntr_el0, %x0"::"r" (value));

+	} else {

+		armv8_pmu_counter_select(idx);

+		asm volatile ("msr pmxevcntr_el0, %x0"::"r" (value));

+	}

+}

+

+static inline void armv8_pmu_enable_count(unsigned int idx)

+{

+	asm volatile ("msr pmcntenset_el0, %x0"::"r" (1 << idx));

+}

+

+static inline void armv8_pmu_disable_count(unsigned int idx)

+{

+	asm volatile ("msr pmcntenclr_el0, %x0"::"r" (1 << idx));

+}

+

+static inline void armv8_pmu_enable_intr(unsigned int idx)

+{

+	asm volatile ("msr pmintenset_el1, %x0"::"r" (1 << idx));

+}

+

+static inline void armv8_pmu_disable_intr(unsigned int idx)

+{

+	asm volatile ("msr pmintenclr_el1, %x0"::"r" (1 << idx));

+	isb();

+	asm volatile ("msr pmovsclr_el0, %x0"::"r" (1 << idx));

+	isb();

+}

+

+static inline unsigned int armv8_pmu_overflow(void)

+{

+	unsigned int val;

+

+	asm volatile ("mrs %x0, pmovsclr_el0":"=r" (val));	/* read */

+	val &= ARMV8_OVSR_MASK;

+	asm volatile ("mrs %x0, pmovsclr_el0"::"r" (val));

+	return val;

+}

+

+static inline unsigned int armv8_pmu_control_read(void)

+{

+	unsigned int val;

+

+	asm volatile ("mrs %x0, pmcr_el0":"=r" (val));

+	return val;

+}

+

+static inline void armv8_pmu_control_write(u32 val)

+{

+	val &= ARMV8_PMCR_MASK;

+	isb();

+	asm volatile ("msr pmcr_el0, %x0"::"r" (val));

+}

+

+static int armv8_pmu_hw_get_counters(void)

+{

+	int count = armv8_pmu_control_read();

+	/* N, bits[15:11] */

+	count = ((count >> ARMV8_PMCR_N_SHIFT) & ARMV8_PMCR_N_MASK);

+	return count;

+}

+

+static void armv8_pmu_hw_reset_all(int generic_counters)

+{

+	int i;

+

+	armv8_pmu_control_write(ARMV8_PMCR_C | ARMV8_PMCR_P);

+	/* generic counter */

+	for (i = 0; i < generic_counters; i++) {

+		armv8_pmu_disable_intr(i);

+		armv8_pmu_disable_count(i);

+	}

+	/* cycle counter */

+	armv8_pmu_disable_intr(31);

+	armv8_pmu_disable_count(31);

+	armv8_pmu_overflow();	/* clear overflow */

+}

+

+static int armv8_pmu_hw_get_event_desc(int event, char *event_desc)

+{

+	int i;

+	int this_cpu;

+	struct chip_pmu_v2 *chip_pmu;

+

+	this_cpu = smp_processor_id();

+	chip_pmu = get_chip_pmu_by_cpu_id(this_cpu);

+	if (event_desc == NULL)

+		return -1;

+

+	for (i = 0; i < chip_pmu->pmu_count; i++) {

+		if (chip_pmu->desc[i].event == event) {

+			strncpy(event_desc, chip_pmu->desc[i].name, MXSIZE_PMU_DESC - 1);

+			break;

+		}

+	}

+	if (i == chip_pmu->pmu_count)

+		return -1;

+

+	return 0;

+}

+

+static int armv8_pmu_hw_check_event(struct met_pmu_v2 *pmu, int idx, int event)

+{

+	int this_cpu;

+	struct chip_pmu_v2 *chip_pmu;

+	int i;

+

+	this_cpu = smp_processor_id();

+	chip_pmu = get_chip_pmu_by_cpu_id(this_cpu);

+	for (i = 0; i < chip_pmu->pmu_count; i++) {

+		if (chip_pmu->desc[i].event == event)

+			break;

+	}

+

+	if (i == chip_pmu->pmu_count) {

+                PR_BOOTMSG("%s:%d => i=%d, pmu_count=%d\n", __FUNCTION__, __LINE__, i, chip_pmu->pmu_count);

+                return -1;

+        }

+

+	return 0;

+}

+

+static void armv8_pmu_hw_start(struct met_pmu_v2 *pmu, int count)

+{

+	int i;

+	int generic = count - 1;

+

+	armv8_pmu_hw_reset_all(generic);

+	for (i = 0; i < generic; i++) {

+		if (pmu[i].mode == MODE_POLLING) {

+			armv8_pmu_type_select(i, pmu[i].event);

+			armv8_pmu_enable_count(i);

+		}

+	}

+	if (pmu[count - 1].mode == MODE_POLLING)

+		armv8_pmu_enable_count(31);

+

+	armv8_pmu_control_write(ARMV8_PMCR_E);

+}

+

+static void armv8_pmu_hw_stop(int count)

+{

+	int generic = count - 1;

+

+	armv8_pmu_hw_reset_all(generic);

+}

+

+static unsigned int armv8_pmu_hw_polling(struct met_pmu_v2 *pmu, int count, unsigned int *pmu_value)

+{

+	int i, cnt = 0;

+	int generic = count - 1;

+

+	for (i = 0; i < generic; i++) {

+		if (pmu[i].mode == MODE_POLLING) {

+			pmu_value[cnt] = armv8_pmu_read_count(i);

+			cnt++;

+		}

+	}

+	if (pmu[count - 1].mode == MODE_POLLING) {

+		pmu_value[cnt] = armv8_pmu_read_count(31);

+		cnt++;

+	}

+	armv8_pmu_control_write(ARMV8_PMCR_C | ARMV8_PMCR_P | ARMV8_PMCR_E);

+

+	return cnt;

+}

+

+static void armv8_get_ic(void *info)

+{

+	unsigned int value;

+	unsigned int *type = (unsigned int *)info;

+

+	/* Read Main ID Register */

+	asm("mrs %x0, midr_el1":"=r"(value));

+	*type = (value & 0xffff) >> 4;	/* primary part number */

+}

+

+struct cpu_pmu_hw_v2 *cpu_pmu_hw_init_v2(void)

+{

+	enum ARM_TYPE_v2 type;

+	struct chip_pmu_v2 *chip;

+	int this_cpu = smp_processor_id();

+	int cpu;

+	int i;

+

+	for_each_possible_cpu(cpu) {

+		if (cpu == this_cpu)

+			armv8_get_ic(&type);

+		else

+			met_smp_call_function_single_symbol(cpu, armv8_get_ic, &type, 1);

+

+		PR_BOOTMSG("CPU TYPE - v8: %x\n", (unsigned int)type);

+		for (i = 0; i < CHIP_PMU_COUNT; i++) {

+			if (chips[i].type == type) {

+				chip = &(chips[i]);

+				chip->hw_count = armv8_pmu_hw_get_counters() + 1;

+				set_chip_pmu_by_cpu_id(cpu, chip);

+				armv8_pmu_v2.chip_pmu[cpu] = chip;

+				if (chip->hw_count >= armv8_pmu_v2.max_hw_count)

+					armv8_pmu_v2.max_hw_count = chip->hw_count;

+				break;

+			}

+		}

+		if (i == CHIP_PMU_COUNT) {

+			set_chip_pmu_by_cpu_id(cpu, &chip_unknown);

+			return NULL;

+		}

+	}

+

+	return &armv8_pmu_v2;

+}

+

diff --git a/src/devtools/met-driver/4.19/mt2712/v8_pmu_hw_v2.h b/src/devtools/met-driver/4.19/mt2712/v8_pmu_hw_v2.h
new file mode 100644
index 0000000..4666d0c
--- /dev/null
+++ b/src/devtools/met-driver/4.19/mt2712/v8_pmu_hw_v2.h
@@ -0,0 +1,24 @@
+/*

+ * 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.

+ */

+

+#ifndef _V8_PMU_V2_NAME_H_

+#define _V8_PMU_V2_NAME_H_

+

+#include "cpu_pmu_v2.h"

+

+

+/*******************************************************************************

+*                                Fuction Pototypes

+*******************************************************************************/

+struct cpu_pmu_hw_v2 *cpu_pmu_hw_init_v2(void);

+#endif				/* _V8_PMU_V2_NAME_H_ */

diff --git a/src/devtools/met-driver/4.19/mt2712/v8_pmu_name.h b/src/devtools/met-driver/4.19/mt2712/v8_pmu_name.h
new file mode 100644
index 0000000..d3bf3b1
--- /dev/null
+++ b/src/devtools/met-driver/4.19/mt2712/v8_pmu_name.h
@@ -0,0 +1,78 @@
+/*
+ * 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.
+ */
+
+
+#ifndef _V8_PMU_NAME_H_
+#define _V8_PMU_NAME_H_
+
+/* Cortex-A53 */
+/* CA53 & CA73 intersection event list */
+struct pmu_desc a53_pmu_desc[] = {
+	{0x00, "SW_INCR"},
+	{0x01, "L1I_CACHE_REFILL"},
+	{0x02, "L1I_TLB_REFILL"},
+	{0x03, "L1D_CACHE_REFILL"},
+	{0x04, "L1D_CACHE"},
+	{0x05, "L1D_TLB_REFILL"},
+/*	{0x06, "LD_RETIRED"}, */
+/*	{0x07, "ST_RETIRED"}, */
+	{0x08, "INST_RETIRED"},
+	{0x09, "EXC_TAKEN"},
+	{0x0A, "EXC_RETURN"},
+	{0x0B, "CID_WRITE_RETIRED"},
+/*	{0x0C, "PC_WRITE_RETIRED"}, */
+/*	{0x0D, "BR_IMMED_RETIRED"}, */
+/*	{0x0E, "BR_RETURN_RETIRED"}, */
+/*	{0x0F, "UNALIGNED_LDST_RETIRED"}, */
+	{0x10, "BR_MIS_PRED"},
+	{0x11, "CPU_CYCLES"},
+	{0x12, "BR_PRED"},
+	{0x13, "MEM_ACCESS"},
+	{0x14, "L1I_CACHE"},
+	{0x15, "L1D_CACHE_WB"},
+	{0x16, "L2D_CACHE"},
+	{0x17, "L2D_CACHE_REFILL"},
+	{0x18, "L2D_CACHE_WB"},
+	{0x19, "BUS_ACCESS"},
+	{0x1A, "MEMORY_ERROR"},
+	{0x1D, "BUS_CYCLES"},
+	{0x60, "BUS_READ_ACCESS"},
+	{0x61, "BUS_WRITE_ACCESS"},
+	{0x86, "IRQ_EXC_TAKEN"},
+	{0x87, "FIQ_EXC_TAKEN"},
+/*	{0xC0, "EXT_MEM_REQ"}, */
+/*	{0xC1, "NO_CACHE_EXT_MEM_REQ"}, */
+/*	{0xC2, "PREFETCH_LINEFILL"}, */
+/*	{0xC4, "ENT_READ_ALLOC_MODE"}, */
+/*	{0xC5, "READ_ALLOC_MODE"}, */
+/*	{0xC6, "PRE_DECODE_ERROR"}, */
+/*	{0xC7, "WRITE_STALL"}, */
+/*	{0xC8, "SCU_SNOOP_DATA_FROM_ANOTHER_CPU"}, */
+/*	{0xC9, "CONDITIONAL_BRANCH_EXE"}, */
+/*	{0xCA, "INDIRECT_BRANCH_MISPREDICT"}, */
+/*	{0xCB, "INDIRECT_BRANCH_MISPREDICT_ADDR"},"INDIRECT_BRANCH_MISPREDICT_ADDR_MISSCOMPARE" */
+/*	{0xCC, "COND_BRANCH_MISPREDICT"}, */
+/*	{0xD0, "L1_INST_CACHE_MEM_ERR"}, */
+/*	{0xE1, "ICACHE_MISS_STALL"}, */
+/*	{0xE2, "DPU_IQ_EMPTY"}, */
+/*	{0xE4, "NOT_FPU_NEON_INTERLOCK"}, */
+/*	{0xE5, "LOAD_STORE_INTERLOCK"}, */
+/*	{0xE6, "FPU_NEON_INTERLOCK"}, */
+/*	{0xE7, "LOAD_MISS_STALL"}, */
+/*	{0xE8, "STORE_STALL"}, */
+	{0xFF, "CPU_CYCLES"}
+};
+
+#define A53_PMU_DESC_COUNT (sizeof(a53_pmu_desc) / sizeof(struct pmu_desc))
+
+#endif				/* _V8_PMU_NAME_H_ */
diff --git a/src/devtools/met-driver/4.19/mt2712/version.h b/src/devtools/met-driver/4.19/mt2712/version.h
new file mode 100644
index 0000000..7bb6c77
--- /dev/null
+++ b/src/devtools/met-driver/4.19/mt2712/version.h
@@ -0,0 +1,14 @@
+/*
+ * 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.
+ */
+
+#define MET_BACKEND_VERSION "6.1.0"