[Feature]add MT2731_MP2_MR2_SVN388 baseline version
Change-Id: Ief04314834b31e27effab435d3ca8ba33b499059
diff --git a/src/devtools/met-driver/4.14/common/Kbuild.yocto b/src/devtools/met-driver/4.14/common/Kbuild.yocto
new file mode 100644
index 0000000..a04e158
--- /dev/null
+++ b/src/devtools/met-driver/4.14/common/Kbuild.yocto
@@ -0,0 +1,359 @@
+################################################################################
+# MET Kernel module mode
+################################################################################
+$(info ======== Build met.ko ... ========)
+MET_CORE := common
+obj-m := met.o
+
+ifneq ($(wildcard $(MET_PLF_DIR)/Kbuild.platform.include),)
+ include $(MET_PLF_DIR)/Kbuild.platform.include
+else
+ $(info ======= Missing $(MET_PLF_DIR)/Kbuild.platform.include ========)
+endif
+
+ccflags-y += -DCONFIG_MET_MODULE
+ccflags-y += -DMET_PLF_USE
+ccflags-y += -I$(MET_COMMON_DIR)
+ccflags-y += -I$(MET_PLF_DIR)
+ccflags-y += -I$(KERNEL_SRC)/include/
+ccflags-y += -I$(KERNEL_SRC)/drivers/misc/mediatek/include/
+ccflags-y += -I$(KERNEL_SRC)/drivers/misc/mediatek/include/mt-plat/
+ccflags-y += -I$(KERNEL_SRC)/drivers/misc/mediatek/include/mt-plat/$(MTK_PLATFORM)/include/
+
+ccflags-y += $(EXTRA_ARGS) $(EXTRA_CFLAGS)
+ccflags-y += -DMTK_PLATFORM=$(MTK_PLATFORM)
+
+met-y := $(MET_CORE)/met_main.o \
+ $(MET_CORE)/met_backlight.o \
+ $(MET_CORE)/met_tag_ex.o \
+ $(MET_CORE)/interface.o \
+ $(MET_CORE)/sampler.o \
+ $(MET_CORE)/dummy_header.o \
+ $(MET_CORE)/util.o \
+ $(MET_CORE)/stat.o \
+ $(MET_CORE)/cookie.o \
+ $(MET_CORE)/mem_stat.o \
+ $(MET_CORE)/switch.o \
+ $(MET_CORE)/trace_event.o \
+ $(MET_CORE)/core_plf_init.o \
+ $(MET_CORE)/core_plf_trace.o \
+ $(MET_CORE)/ondiemet.o \
+ $(MET_CORE)/ondiemet_log.o \
+ $(MET_CORE)/sspm/ondiemet_sspm.o
+
+CFLAGS_interface.o += -DMET_USER_EVENT_SUPPORT
+CFLAGS_met_tag_ex.o += -DMET_USER_EVENT_SUPPORT
+
+$(info CPUPMU_VERSION = $(CPUPMU_VERSION))
+ifeq ("$(CPUPMU_VERSION)", "V8_2")
+ ccflags-y += -DCPUPMU_V8_2
+endif
+
+$(info ARCH = $(ARCH))
+ifeq ($(ARCH), mips)
+ met-y += $(MET_CORE)/mips_pmu_hw.o
+endif #ifeq ($(ARCH), mips)
+
+ifeq ($(ARCH), arm)
+ ccflags-y += -DCONFIG_MET_ARM_32BIT
+ met-y += $(MET_CORE)/cpu_pmu.o
+ met-y += $(MET_CORE)/v7_pmu_hw.o
+ met-y += $(MET_CORE)/v6_pmu_hw.o
+endif #ifeq ($(ARCH), arm)
+
+ifeq ($(ARCH), arm64)
+ met-y += $(MET_CORE)/cpu_pmu.o
+ met-y += $(MET_CORE)/v8_pmu_hw.o
+endif
+
+$(info CONFIG_CPU_FREQ = $(CONFIG_CPU_FREQ))
+ifeq ($(CONFIG_CPU_FREQ),y)
+ met-y += $(MET_CORE)/power.o
+endif
+
+################################################################################
+# MET_SPM_TWAM
+################################################################################
+FEATURE_SPMTWAM := $(if $(FEATURE_SPMTWAM),$(FEATURE_SPMTWAM),y)
+$(info FEATURE_SPMTWAM = $(FEATURE_SPMTWAM))
+
+ifneq ($(FEATURE_SPMTWAM), n)
+ MET_SPM_TWAM := y
+
+ # for mtk_spm.h
+ ifneq ("$(wildcard $(KERNEL_SRC)/drivers/misc/mediatek/base/power/include/mtk_spm.h)","")
+ ccflags-y += -I$(KERNEL_SRC)/drivers/misc/mediatek/base/power/include/
+ ccflags-y += -I$(MET_COMMON_DIR)/spmtwam/include/
+ else
+ MET_SPM_TWAM = n
+ $(info ========= Missing $(KERNEL_SRC)/drivers/misc/mediatek/base/power/include/mtk_spm.h ========)
+ $(info ======== disable MET_SPM_TWAM ========)
+ endif
+else
+ MET_SPM_TWAM := n
+endif
+
+$(info SPMTWAM_VERSION = $(SPMTWAM_VERSION))
+$(info SPMTWAM_IDLE_SIGNAL_SUPPORT = $(SPMTWAM_IDLE_SIGNAL_SUPPORT))
+
+ifeq ("$(SPMTWAM_IDLE_SIGNAL_SUPPORT)", "single")
+ ccflags-y += -DSPMTWAM_SINGLE_IDLE_SIGNAL
+endif
+
+ifeq ("$(SPMTWAM_IDLE_SIGNAL_SUPPORT)", "multiple")
+ ccflags-y += -DSPMTWAM_MULTIPLE_IDLE_SIGNAL
+endif
+
+ifeq ("$(SPMTWAM_VERSION)", "ap")
+ ccflags-y += -DSPMTWAM_AP
+ met-$(MET_SPM_TWAM) += $(MET_CORE)/spmtwam/ap/met_spmtwam.o
+endif
+
+ifeq ("$(SPMTWAM_VERSION)", "sspm")
+ ccflags-y += -DSPMTWAM_SSPM
+ met-$(MET_SPM_TWAM) += $(MET_CORE)/spmtwam/sspm/met_spmtwam.o
+endif
+
+################################################################################
+# MET_SSPM_EMI
+################################################################################
+FEATURE_SSPM_EMI := $(if $(FEATURE_SSPM_EMI),$(FEATURE_SSPM_EMI),y)
+$(info FEATURE_SSPM_EMI = $(FEATURE_SSPM_EMI))
+
+MET_SSPM_EMI := $(if $(filter n,$(FEATURE_SSPM_EMI)),n,y)
+
+met-$(MET_SSPM_EMI) += $(MET_CORE)/met_emi.o \
+ $(MET_CORE)/mtk_emi_bm.o
+
+################################################################################
+# MET_GPU
+################################################################################
+FEATURE_GPU := $(if $(FEATURE_GPU),$(FEATURE_GPU),y)
+$(info FEATURE_GPU = $(FEATURE_GPU))
+
+ifneq ($(FEATURE_GPU), n)
+ MET_GPU := y
+
+ # for mtk_gpufreq.h
+ ifneq ("$(wildcard $(KERNEL_SRC)/drivers/misc/mediatek/base/power/$(MTK_PLATFORM)/mtk_gpufreq.h)","")
+ ccflags-y += -I$(KERNEL_SRC)/drivers/misc/mediatek/base/power/$(MTK_PLATFORM)/
+ else
+ MET_GPU = n
+ $(info ======= Missing $(KERNEL_SRC)/drivers/misc/mediatek/base/power/$(MTK_PLATFORM)/mtk_gpufreq.h ========)
+ $(info ======== disable MET_GPU ========)
+ endif
+
+ # for mtk_gpu_utility.h
+ ifneq ("$(wildcard $(KERNEL_SRC)/drivers/misc/mediatek/include/mt-plat/mtk_gpu_utility.h)","")
+ ccflags-y += -I$(KERNEL_SRC)/drivers/misc/mediatek/include/mt-plat/
+ else
+ MET_GPU = n
+ $(info ======== Missing $(KERNEL_SRC)/drivers/misc/mediatek/include/mt-plat/mtk_gpu_utility.h ========)
+ $(info ======== disable MET_GPU ========)
+ endif
+
+ ifneq ($(CONFIG_MTK_GPU_SUPPORT), y)
+ MET_GPU = n
+ $(info ======== CONFIG_MTK_GPU_SUPPORT = n ========)
+ $(info ======== disable MET_GPU ========)
+ endif
+else
+ MET_GPU := n
+endif
+
+met-$(MET_GPU) += $(MET_CORE)/mtk_gpu_metmonitor.o
+
+
+################################################################################
+# MET_VCOREDVFS
+################################################################################
+FEATURE_VCOREDVFS := $(if $(FEATURE_VCOREDVFS),$(FEATURE_VCOREDVFS),y)
+$(info FEATURE_VCOREDVFS = $(FEATURE_VCOREDVFS))
+
+ifneq ($(FEATURE_VCOREDVFS), n)
+ MET_VCOREDVFS := y
+
+ # for mtk_vcorefs_manager.h
+ ifneq ("$(wildcard $(MET_VCOREDVFS_INC)/mtk_vcorefs_manager.h)","")
+ ccflags-y += -I$(MET_VCOREDVFS_INC)/
+ else
+ MET_VCOREDVFS = n
+ $(info ======== Missing $(MET_VCOREDVFS_INC)/mtk_vcorefs_manager.h ========)
+ $(info ======== disable MET_VCOREDVFS ========)
+ endif
+
+ # for mtk_vcorefs_governor.h
+ ifneq ("$(wildcard $(MET_VCOREDVFS_INC)/mtk_vcorefs_governor.h)","")
+ ccflags-y += -I$(MET_VCOREDVFS_INC)
+ else
+ MET_VCOREDVFS = n
+ $(info ======== Missing $(MET_VCOREDVFS_INC)/mtk_vcorefs_governor.h ========)
+ $(info ======== disable MET_VCOREDVFS ========)
+ endif
+
+ # for helio-dvfsrc.h
+ ifneq ("$(wildcard $(KERNEL_SRC)/drivers/devfreq/helio-dvfsrc.h)","")
+ ccflags-y += -I$(KERNEL_SRC)/drivers/devfreq/
+ else
+ MET_VCOREDVFS = n
+ $(info ======== Missing $(KERNEL_SRC)/drivers/devfreq/helio-dvfsrc.h ========)
+ $(info ======== disable MET_VCOREDVFS ========)
+ endif
+else
+ MET_VCOREDVFS := n
+endif
+
+met-$(MET_VCOREDVFS) += $(MET_CORE)/met_vcoredvfs.o
+
+
+################################################################################
+# MET_PTPOD
+################################################################################
+FEATURE_PTPOD := $(if $(FEATURE_PTPOD),$(FEATURE_PTPOD),y)
+$(info FEATURE_PTPOD = $(FEATURE_PTPOD))
+
+ifneq ($(FEATURE_PTPOD), n)
+ MET_PTPOD := y
+
+ # for mtk_gpufreq.h
+ ifneq ("$(wildcard $(KERNEL_SRC)/drivers/misc/mediatek/base/power/$(MTK_PLATFORM)/mtk_gpufreq.h)","")
+ ccflags-y += -I$(KERNEL_SRC)/drivers/misc/mediatek/base/power/$(MTK_PLATFORM)/
+ else
+ MET_PTPOD = n
+ $(info ======== Missing $(KERNEL_SRC)/drivers/misc/mediatek/base/power/$(MTK_PLATFORM)/mtk_gpufreq.h ========)
+ $(info ======== disable MET_PTPOD ========)
+ endif
+
+ # for mtk_cpufreq_api.h
+ ifneq ("$(wildcard $(KERNEL_SRC)/drivers/misc/mediatek/include/mt-plat/$(MTK_PLATFORM)/include/mach/mtk_cpufreq_api.h)","")
+ ccflags-y += -I$(KERNEL_SRC)/drivers/misc/mediatek/include/mt-plat/$(MTK_PLATFORM)/include/
+ else
+ MET_PTPOD = n
+ $(info ======== Missing $(KERNEL_SRC)/drivers/misc/mediatek/include/mt-plat/$(MTK_PLATFORM)/include/mach/mtk_cpufreq_api.h ========)
+ $(info ======== disable MET_PTPOD ========)
+ endif
+
+ # for mtk_cpufreq_config.h
+ ifneq ("$(wildcard $(MET_PTPOD_INC)/mtk_cpufreq_config.h)","")
+ ccflags-y += -I$(MET_PTPOD_INC)
+ else
+ MET_PTPOD = n
+ $(info ======== Missing $(MET_PTPOD_INC)/mtk_cpufreq_config.h ========)
+ $(info ======== disable MET_PTPOD ========)
+ endif
+else
+ MET_PTPOD := n
+endif
+
+met-$(MET_PTPOD) += $(MET_CORE)/met_ptpod.o
+
+
+################################################################################
+# MET_CPUDSU
+################################################################################
+FEATURE_CPUDSU := $(if $(FEATURE_CPUDSU),$(FEATURE_CPUDSU),y)
+$(info FEATURE_CPUDSU = $(FEATURE_CPUDSU))
+
+MET_CPUDSU := $(if $(filter n,$(FEATURE_CPUDSU)),n,y)
+
+met-$(MET_CPUDSU) += $(MET_CORE)/cpu_dsu.o \
+ $(MET_CORE)/v8_dsu_hw.o
+
+################################################################################
+# MET_WALLTIME
+################################################################################
+FEATURE_WALLTIME := $(if $(FEATURE_WALLTIME),$(FEATURE_WALLTIME),y)
+$(info FEATURE_WALLTIME = $(FEATURE_WALLTIME))
+
+MET_WALLTIME := $(if $(filter n,$(FEATURE_WALLTIME)),n,y)
+
+met-$(MET_WALLTIME) += $(MET_CORE)/met_wall_time.o
+
+################################################################################
+# MET_SMI
+################################################################################
+FEATURE_SMI := $(if $(FEATURE_SMI),$(FEATURE_SMI),y)
+$(info FEATURE_SMI = $(FEATURE_SMI))
+
+MET_SMI := $(if $(filter n,$(FEATURE_SMI)),n,y)
+
+met-$(MET_SMI) += $(MET_CORE)/sspm/sspm_met_smi.o
+
+################################################################################
+# EVENT_POWER
+################################################################################
+FEATURE_EVENT_POWER := $(if $(FEATURE_EVENT_POWER),$(FEATURE_EVENT_POWER),y)
+$(info FEATURE_EVENT_POWER = $(FEATURE_EVENT_POWER))
+
+ifeq ($(FEATURE_EVENT_POWER), y)
+ ccflags-y += -DMET_EVENT_POWER_SUPPORT
+endif
+
+################################################################################
+# On-die-met SSPM only module
+################################################################################
+FEATURE_ONDIEMET := $(if $(FEATURE_ONDIEMET),$(FEATURE_ONDIEMET),y)
+ifeq ($(FEATURE_ONDIEMET), y)
+ FEATURE_ONDIEMET_WALLTIME := $(if $(FEATURE_ONDIEMET_WALLTIME),$(FEATURE_ONDIEMET_WALLTIME),y)
+else
+ FEATURE_ONDIEMET_WALLTIME := n
+endif
+
+$(info FEATURE_ONDIEMET = $(FEATURE_ONDIEMET))
+$(info FEATURE_ONDIEMET_WALLTIME = $(FEATURE_ONDIEMET_WALLTIME))
+
+ifneq ($(FEATURE_ONDIEMET), n)
+ subdir-ccflags-y += -DONDIEMET_SUPPORT
+
+ ifeq ($(CONFIG_MTK_TINYSYS_SSPM_SUPPORT),)
+ $(info CONFIG_MTK_TINYSYS_SSPM_SUPPORT = n)
+ else
+ $(info CONFIG_MTK_TINYSYS_SSPM_SUPPORT = $(CONFIG_MTK_TINYSYS_SSPM_SUPPORT))
+ endif
+
+ ifeq ($(CONFIG_MTK_TINYSYS_SSPM_SUPPORT),y)
+ # for sspm_ipi.h
+ subdir-ccflags-y += -I$(KERNEL_SRC)/drivers/misc/mediatek/sspm
+ subdir-ccflags-y += -I$(KERNEL_SRC)/drivers/misc/mediatek/sspm/$(MTK_PLATFORM)
+ met-y += $(MET_CORE)/sspm/sspm_ipi_handle.o
+ met-y += $(MET_CORE)/sspm/sspm_common.o
+ ccflags-y += -DMTK_TINYSYS_SSPM_SUPPORT
+
+ ifneq ("$(wildcard $(KERNEL_SRC)/drivers/misc/mediatek/sspm/$(MTK_PLATFORM)/sspm_ipi_define.h)","")
+ subdir-ccflags-y += -I$(KERNEL_SRC)/drivers/misc/mediatek/sspm \
+ -I$(KERNEL_SRC)/drivers/misc/mediatek/sspm/$(MTK_PLATFORM)
+
+ SYS_SSPM_READY := y
+ else
+ $(info ======== Missing $(KERNEL_SRC)/drivers/misc/mediatek/sspm/$(MTK_PLATFORM)/sspm_ipi_define.h========)
+ $(info ======== disable ALL ondiemet feature ========)
+
+ SYS_SSPM_READY := n
+ endif
+ else
+ $(info ======== CONFIG_MTK_TINYSYS_SSPM_SUPPORT = n ========)
+ $(info ======== disable ALL ondiemet feature ========)
+
+ SYS_SSPM_READY := n
+ endif
+
+ ifeq ($(SYS_SSPM_READY), y)
+ MET_SSPM_WALLTIME := $(if $(filter n,$(FEATURE_ONDIEMET_WALLTIME)),n,y)
+
+ met-$(MET_SSPM_WALLTIME) += $(MET_CORE)/sspm/sspm_walltime.o
+ endif
+endif
+
+##############################################################################################
+# include $(MET_PLF_DIR)/Kbuild
+##############################################################################################
+ifneq ($(wildcard $(MET_PLF_DIR)/Kbuild.yocto),)
+ include $(MET_PLF_DIR)/Kbuild.yocto
+else
+ $(info ======= Missing $(MET_PLF_DIR)/Kbuild.yocto ========)
+endif
+
+#################################################################################
+# add met_device flags
+#################################################################################
+ccflags-y += $(foreach v, $(filter MET_%,$(.VARIABLES)), $(if $(filter $($(v)),y),-D$(v)))
diff --git a/src/devtools/met-driver/4.14/common/cookie.c b/src/devtools/met-driver/4.14/common/cookie.c
new file mode 100644
index 0000000..0e309fb
--- /dev/null
+++ b/src/devtools/met-driver/4.14/common/cookie.c
@@ -0,0 +1,281 @@
+/*
+ * 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/cpu.h>
+#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 DEFINE_PER_CPU(int, cpu_status);
+
+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) {
+ 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
+#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;
+
+ if (per_cpu(cpu_status, cpu) != MET_CPU_ONLINE)
+ return;
+
+ 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)
+{
+ int cpu = raw_smp_processor_id();
+ per_cpu(cpu_status, cpu) = MET_CPU_ONLINE;
+ /* 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;
+}
+
+static void met_cookie_cpu_state_notify(long cpu, unsigned long action)
+{
+ per_cpu(cpu_status, cpu) = action;
+}
+
+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,
+ .cpu_state_notify = met_cookie_cpu_state_notify,
+};
diff --git a/src/devtools/met-driver/4.14/common/core_plf_init.c b/src/devtools/met-driver/4.14/common/core_plf_init.c
new file mode 100644
index 0000000..bd778a1
--- /dev/null
+++ b/src/devtools/met-driver/4.14/common/core_plf_init.c
@@ -0,0 +1,421 @@
+/*
+ * 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"
+#include "plf_init.h"
+
+#undef DEBUG
+
+#ifdef MET_GPU
+/*
+ * GPU
+ */
+bool (*mtk_get_gpu_loading_symbol)(unsigned int *pLoading);
+bool (*mtk_get_gpu_block_symbol)(unsigned int *pBlock);
+bool (*mtk_get_gpu_idle_symbol)(unsigned int *pIdle);
+bool (*mtk_get_gpu_dvfs_from_symbol)(MTK_GPU_DVFS_TYPE *peType, unsigned long *pulFreq);
+bool (*mtk_get_gpu_sub_loading_symbol)(unsigned int *pLoading);
+bool (*mtk_get_3D_fences_count_symbol)(int *pi32Count);
+bool (*mtk_get_gpu_memory_usage_symbol)(unsigned int *pMemUsage);
+bool (*mtk_get_gpu_power_loading_symbol)(unsigned int *pLoading);
+bool (*mtk_get_custom_boost_gpu_freq_symbol)(unsigned long *pulFreq);
+bool (*mtk_get_custom_upbound_gpu_freq_symbol)(unsigned long *pulFreq);
+bool (*mtk_get_vsync_based_target_freq_symbol)(unsigned long *pulFreq);
+bool (*mtk_get_vsync_offset_event_status_symbol)(unsigned int *pui32EventStatus);
+bool (*mtk_get_vsync_offset_debug_status_symbol)(unsigned int *pui32EventStatus);
+bool (*mtk_enable_gpu_perf_monitor_symbol)(bool enable);
+bool (*mtk_get_gpu_pmu_init_symbol)(GPU_PMU *pmus, int pmu_size, int *ret_size);
+bool (*mtk_get_gpu_pmu_swapnreset_symbol)(GPU_PMU *pmus, int pmu_size);
+#if 1
+bool (*mtk_get_gpu_pmu_deinit_symbol)(void);
+bool (*mtk_get_gpu_pmu_swapnreset_stop_symbol)(void);
+#endif
+unsigned int (*mt_gpufreq_get_cur_freq_symbol)(void);
+unsigned int (*mt_gpufreq_get_thermal_limit_freq_symbol)(void);
+bool (*mtk_register_gpu_power_change_symbol)(const char *name, gpu_power_change_notify_fp callback);
+bool (*mtk_unregister_gpu_power_change_symbol)(const char *name);
+#endif /* MET_GPU */
+
+#ifdef MET_VCOREDVFS
+/*
+ * VCORE DVFS
+ */
+#include <helio-dvfsrc.h>
+
+int (*vcorefs_get_num_opp_symbol)(void);
+int (*vcorefs_get_opp_info_num_symbol)(void);
+char ** (*vcorefs_get_opp_info_name_symbol)(void);
+unsigned int * (*vcorefs_get_opp_info_symbol)(void);
+int (*vcorefs_get_src_req_num_symbol)(void);
+char ** (*vcorefs_get_src_req_name_symbol)(void);
+unsigned int * (*vcorefs_get_src_req_symbol)(void);
+#endif /* MET_VCOREDVFS */
+
+#ifdef MET_SSPM_EMI
+void *(*mt_cen_emi_base_get_symbol)(void);
+unsigned int (*get_dram_data_rate_symbol)(void);
+unsigned int (*get_ddr_type_symbol)(void);
+unsigned int (*get_cur_ddr_ratio_symbol)(void);
+#endif /* MET_SSPM_EMI */
+
+#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);
+unsigned int (*get_ddr_type_symbol)(void);
+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);
+unsigned int (*get_cur_ddr_ratio_symbol)(void);
+#endif
+
+#ifdef MET_PTPOD
+unsigned int (*mt_gpufreq_get_cur_volt_symbol)(void);
+unsigned int (*mt_cpufreq_get_cur_volt_symbol)(unsigned int cluster_id);
+#endif /* MET_PTPOD */
+
+#ifdef MET_SPM_TWAM
+/* ap side used */
+#ifdef SPMTWAM_AP
+void (*spm_twam_enable_monitor_symbol)(const struct twam_sig *twamsig, bool speed_mode);
+void (*spm_twam_register_handler_symbol)(twam_handler_t handler);
+void (*spm_twam_disable_monitor_symbol)(void);
+void (*spm_twam_set_idle_select_symbol)(unsigned int sel);
+void (*spm_twam_set_window_length_symbol)(unsigned int len);
+void (*spm_twam_set_mon_type_symbol)(struct twam_sig *mon);
+#endif
+
+/* sspm side used */
+#ifdef SPMTWAM_SSPM
+void (*spm_twam_enable_monitor_symbol)(bool en_monitor, bool debug_signal, twam_handler_t cb_handler);
+bool (*spm_twam_met_enable_symbol)(void);
+void (*spm_twam_config_channel_symbol)(struct twam_cfg *cfg, bool speed_mode, unsigned int window_len_hz);
+#endif
+#endif
+
+
+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_GPU
+ _MET_SYMBOL_GET(mtk_get_gpu_loading);
+ _MET_SYMBOL_GET(mtk_get_gpu_block);
+ _MET_SYMBOL_GET(mtk_get_gpu_idle);
+ _MET_SYMBOL_GET(mtk_get_gpu_dvfs_from);
+ _MET_SYMBOL_GET(mtk_get_gpu_sub_loading);
+ _MET_SYMBOL_GET(mtk_get_3D_fences_count);
+ _MET_SYMBOL_GET(mtk_get_gpu_memory_usage);
+ _MET_SYMBOL_GET(mtk_get_gpu_power_loading);
+ _MET_SYMBOL_GET(mtk_get_custom_boost_gpu_freq);
+ _MET_SYMBOL_GET(mtk_get_custom_upbound_gpu_freq);
+ _MET_SYMBOL_GET(mtk_get_vsync_based_target_freq);
+ _MET_SYMBOL_GET(mtk_get_vsync_offset_event_status);
+ _MET_SYMBOL_GET(mtk_get_vsync_offset_debug_status);
+ _MET_SYMBOL_GET(mtk_enable_gpu_perf_monitor);
+ _MET_SYMBOL_GET(mtk_get_gpu_pmu_init);
+ _MET_SYMBOL_GET(mtk_get_gpu_pmu_swapnreset);
+ _MET_SYMBOL_GET(mt_gpufreq_get_cur_freq);
+ _MET_SYMBOL_GET(mt_gpufreq_get_thermal_limit_freq);
+ _MET_SYMBOL_GET(mtk_register_gpu_power_change);
+ _MET_SYMBOL_GET(mtk_unregister_gpu_power_change);
+#if 1
+ _MET_SYMBOL_GET(mtk_get_gpu_pmu_swapnreset_stop);
+ _MET_SYMBOL_GET(mtk_get_gpu_pmu_deinit);
+#endif
+#endif /* MET_GPU */
+
+#ifdef MET_VCOREDVFS
+ _MET_SYMBOL_GET(vcorefs_get_num_opp);
+ _MET_SYMBOL_GET(vcorefs_get_opp_info_num);
+ _MET_SYMBOL_GET(vcorefs_get_opp_info_name);
+ _MET_SYMBOL_GET(vcorefs_get_opp_info);
+ _MET_SYMBOL_GET(vcorefs_get_src_req_num);
+ _MET_SYMBOL_GET(vcorefs_get_src_req_name);
+ _MET_SYMBOL_GET(vcorefs_get_src_req);
+#endif
+
+#ifdef MET_SSPM_EMI
+ _MET_SYMBOL_GET(mt_cen_emi_base_get);
+ _MET_SYMBOL_GET(get_dram_data_rate);
+ _MET_SYMBOL_GET(get_ddr_type);
+ _MET_SYMBOL_GET(get_cur_ddr_ratio);
+#endif
+
+#ifdef MET_AP_EMI
+ _MET_SYMBOL_GET(get_dram_data_rate);
+ _MET_SYMBOL_GET(get_ddr_type);
+ _MET_SYMBOL_GET(mt_cen_emi_base_get);
+ _MET_SYMBOL_GET(mt_chn_emi_base_get);
+ _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);
+/* _MET_SYMBOL_GET(get_cur_ddr_ratio); */
+#endif
+
+#ifdef MET_PTPOD
+ _MET_SYMBOL_GET(mt_gpufreq_get_cur_volt);
+ _MET_SYMBOL_GET(mt_cpufreq_get_cur_volt);
+#endif
+
+#ifdef MET_SPM_TWAM
+ /* ap side used */
+#ifdef SPMTWAM_AP
+ _MET_SYMBOL_GET(spm_twam_enable_monitor);
+ _MET_SYMBOL_GET(spm_twam_register_handler);
+ _MET_SYMBOL_GET(spm_twam_disable_monitor);
+ _MET_SYMBOL_GET(spm_twam_set_idle_select);
+ _MET_SYMBOL_GET(spm_twam_set_window_length);
+ _MET_SYMBOL_GET(spm_twam_set_mon_type);
+#endif
+
+ /* sspm side used */
+#ifdef SPMTWAM_SSPM
+ _MET_SYMBOL_GET(spm_twam_enable_monitor);
+ _MET_SYMBOL_GET(spm_twam_met_enable);
+ _MET_SYMBOL_GET(spm_twam_config_channel);
+#endif
+#endif
+
+ 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_GPU
+ _MET_SYMBOL_PUT(mtk_get_gpu_loading);
+ _MET_SYMBOL_PUT(mtk_get_gpu_block);
+ _MET_SYMBOL_PUT(mtk_get_gpu_idle);
+ _MET_SYMBOL_PUT(mtk_get_gpu_dvfs_from);
+ _MET_SYMBOL_PUT(mtk_get_gpu_sub_loading);
+ _MET_SYMBOL_PUT(mtk_get_3D_fences_count);
+ _MET_SYMBOL_PUT(mtk_get_gpu_memory_usage);
+ _MET_SYMBOL_PUT(mtk_get_gpu_power_loading);
+ _MET_SYMBOL_PUT(mtk_get_custom_boost_gpu_freq);
+ _MET_SYMBOL_PUT(mtk_get_custom_upbound_gpu_freq);
+ _MET_SYMBOL_PUT(mtk_get_vsync_based_target_freq);
+ _MET_SYMBOL_PUT(mtk_get_vsync_offset_event_status);
+ _MET_SYMBOL_PUT(mtk_get_vsync_offset_debug_status);
+ _MET_SYMBOL_PUT(mtk_enable_gpu_perf_monitor);
+ _MET_SYMBOL_PUT(mtk_get_gpu_pmu_init);
+ _MET_SYMBOL_PUT(mtk_get_gpu_pmu_swapnreset);
+ _MET_SYMBOL_PUT(mt_gpufreq_get_cur_freq);
+ _MET_SYMBOL_PUT(mt_gpufreq_get_thermal_limit_freq);
+ _MET_SYMBOL_PUT(mtk_register_gpu_power_change);
+ _MET_SYMBOL_PUT(mtk_unregister_gpu_power_change);
+#if 1
+ _MET_SYMBOL_PUT(mtk_get_gpu_pmu_swapnreset_stop);
+ _MET_SYMBOL_PUT(mtk_get_gpu_pmu_deinit);
+#endif
+#endif /* MET_GPU */
+
+#ifdef MET_VCOREDVFS
+ _MET_SYMBOL_PUT(vcorefs_get_num_opp);
+ _MET_SYMBOL_PUT(vcorefs_get_opp_info_num);
+ _MET_SYMBOL_PUT(vcorefs_get_opp_info_name);
+ _MET_SYMBOL_PUT(vcorefs_get_opp_info);
+ _MET_SYMBOL_PUT(vcorefs_get_src_req_num);
+ _MET_SYMBOL_PUT(vcorefs_get_src_req_name);
+ _MET_SYMBOL_PUT(vcorefs_get_src_req);
+#endif
+
+#ifdef MET_SSPM_EMI
+ _MET_SYMBOL_PUT(mt_cen_emi_base_get);
+ _MET_SYMBOL_PUT(get_dram_data_rate);
+ _MET_SYMBOL_PUT(get_ddr_type);
+ _MET_SYMBOL_PUT(get_cur_ddr_ratio);
+#endif
+
+#ifdef MET_AP_EMI
+ _MET_SYMBOL_PUT(get_dram_data_rate);
+ _MET_SYMBOL_PUT(get_ddr_type);
+ _MET_SYMBOL_PUT(mt_cen_emi_base_get);
+ _MET_SYMBOL_PUT(mt_chn_emi_base_get);
+ _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);
+/* _MET_SYMBOL_PUT(get_cur_ddr_ratio); */
+#endif
+
+#ifdef MET_PTPOD
+ _MET_SYMBOL_PUT(mt_gpufreq_get_cur_volt);
+ _MET_SYMBOL_PUT(mt_cpufreq_get_cur_volt);
+#endif
+
+#ifdef MET_SPM_TWAM
+ /* ap side used */
+#ifdef SPMTWAM_AP
+ _MET_SYMBOL_PUT(spm_twam_enable_monitor);
+ _MET_SYMBOL_PUT(spm_twam_register_handler);
+ _MET_SYMBOL_PUT(spm_twam_disable_monitor);
+ _MET_SYMBOL_PUT(spm_twam_set_idle_select);
+ _MET_SYMBOL_PUT(spm_twam_set_window_length);
+ _MET_SYMBOL_PUT(spm_twam_set_mon_type);
+#endif
+
+ /* sspm side used */
+#ifdef SPMTWAM_SSPM
+ _MET_SYMBOL_PUT(spm_twam_enable_monitor);
+ _MET_SYMBOL_PUT(spm_twam_met_enable);
+ _MET_SYMBOL_PUT(spm_twam_config_channel);
+#endif
+#endif
+
+ return 0;
+}
+
+int core_plf_init(void)
+{
+ /*initial met external symbol*/
+ met_symbol_get();
+
+#ifdef MET_GPU
+ met_register(&met_gpu);
+ met_register(&met_gpudvfs);
+ met_register(&met_gpumem);
+ met_register(&met_gpupwr);
+ met_register(&met_gpu_pmu);
+#ifdef MET_GPU_STALL_MONITOR
+ met_register(&met_gpu_stall);
+#endif
+#endif
+
+#ifdef MET_VCOREDVFS
+ met_register(&met_vcoredvfs);
+#endif
+
+#ifdef MET_SSPM_EMI
+ met_register(&met_sspm_emi);
+#endif
+
+#ifdef MET_AP_EMI
+ met_register(&met_emi);
+#endif
+
+#ifdef MET_SMI
+ met_register(&met_sspm_smi);
+#endif
+
+#ifdef MET_PTPOD
+ met_register(&met_ptpod);
+#endif
+
+#ifdef MET_WALLTIME
+ met_register(&met_wall_time);
+#endif
+
+#ifdef MTK_TINYSYS_SSPM_SUPPORT
+ met_register(&met_sspm_common);
+#endif
+
+#if defined(CONFIG_MTK_TINYSYS_SSPM_SUPPORT) && defined(ONDIEMET_SUPPORT)
+#ifdef MET_SSPM_WALLTIME
+ met_register(&met_sspm_walltime);
+#endif
+#endif /* CONFIG_MTK_TINYSYS_SSPM_SUPPORT && ONDIEMET_SUPPORT */
+
+#ifdef MET_CPUDSU
+ met_register(&met_cpudsu);
+#endif
+
+#ifdef MET_SPM_TWAM
+ met_register(&met_spmtwam);
+#endif
+
+ return 0;
+}
+
+void core_plf_exit(void)
+{
+ /*release met external symbol*/
+ met_symbol_put();
+
+#ifdef MET_GPU
+ met_deregister(&met_gpu);
+ met_deregister(&met_gpudvfs);
+ met_deregister(&met_gpumem);
+ met_deregister(&met_gpupwr);
+ met_deregister(&met_gpu_pmu);
+#ifdef MET_GPU_STALL_MONITOR
+ met_deregister(&met_gpu_stall);
+#endif
+#endif
+
+#ifdef MET_VCOREDVFS
+ met_deregister(&met_vcoredvfs);
+#endif
+
+#ifdef MET_SSPM_EMI
+ met_deregister(&met_sspm_emi);
+#endif
+
+#ifdef MET_AP_EMI
+ met_deregister(&met_emi);
+#endif
+
+#ifdef MET_SMI
+ met_deregister(&met_sspm_smi);
+#endif
+
+#ifdef MET_PTPOD
+ met_deregister(&met_ptpod);
+#endif
+
+#ifdef MET_WALLTIME
+ met_deregister(&met_wall_time);
+#endif
+
+#ifdef MTK_TINYSYS_SSPM_SUPPORT
+ met_deregister(&met_sspm_common);
+#endif
+
+#if defined(CONFIG_MTK_TINYSYS_SSPM_SUPPORT) && defined(ONDIEMET_SUPPORT)
+#ifdef MET_SSPM_WALLTIME
+ met_deregister(&met_sspm_walltime);
+#endif
+#endif /* CONFIG_MTK_TINYSYS_SSPM_SUPPORT && ONDIEMET_SUPPORT */
+
+#ifdef MET_CPUDSU
+ met_deregister(&met_cpudsu);
+#endif
+
+#ifdef MET_SPM_TWAM
+ met_deregister(&met_spmtwam);
+#endif
+
+}
diff --git a/src/devtools/met-driver/4.14/common/core_plf_init.h b/src/devtools/met-driver/4.14/common/core_plf_init.h
new file mode 100644
index 0000000..ae53627
--- /dev/null
+++ b/src/devtools/met-driver/4.14/common/core_plf_init.h
@@ -0,0 +1,197 @@
+/*
+ * 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_GPU
+/*
+ * GPU
+ */
+#include <mtk_gpu_utility.h>
+#include <mtk_gpufreq.h>
+#include "met_gpu_monitor.h"
+
+extern bool mtk_get_gpu_loading(unsigned int *pLoading);
+extern bool mtk_get_gpu_block(unsigned int *pBlock);
+extern bool mtk_get_gpu_idle(unsigned int *pIdle);
+extern bool mtk_get_gpu_dvfs_from(MTK_GPU_DVFS_TYPE *peType, unsigned long *pulFreq);
+extern bool mtk_get_gpu_sub_loading(unsigned int *pLoading);
+extern bool mtk_get_3D_fences_count(int *pi32Count);
+extern bool mtk_get_gpu_memory_usage(unsigned int *pMemUsage);
+extern bool mtk_get_gpu_power_loading(unsigned int *pLoading);
+extern bool mtk_get_custom_boost_gpu_freq(unsigned int *pui32FreqLevel);
+extern bool mtk_get_custom_upbound_gpu_freq(unsigned int *pui32FreqLevel);
+extern bool mtk_get_vsync_based_target_freq(unsigned long *pulFreq);
+extern bool mtk_get_vsync_offset_event_status(unsigned int *pui32EventStatus);
+extern bool mtk_get_vsync_offset_debug_status(unsigned int *pui32EventStatus);
+extern bool mtk_enable_gpu_perf_monitor(bool enable);
+extern bool mtk_get_gpu_pmu_init(GPU_PMU *pmus, int pmu_size, int *ret_size);
+extern bool mtk_get_gpu_pmu_swapnreset(GPU_PMU *pmus, int pmu_size);
+
+
+extern bool (*mtk_get_gpu_loading_symbol)(unsigned int *pLoading);
+extern bool (*mtk_get_gpu_block_symbol)(unsigned int *pBlock);
+extern bool (*mtk_get_gpu_idle_symbol)(unsigned int *pIdle);
+extern bool (*mtk_get_gpu_dvfs_from_symbol)(MTK_GPU_DVFS_TYPE *peType, unsigned long *pulFreq);
+extern bool (*mtk_get_gpu_sub_loading_symbol)(unsigned int *pLoading);
+extern bool (*mtk_get_3D_fences_count_symbol)(int *pi32Count);
+extern bool (*mtk_get_gpu_memory_usage_symbol)(unsigned int *pMemUsage);
+extern bool (*mtk_get_gpu_power_loading_symbol)(unsigned int *pLoading);
+extern bool (*mtk_get_custom_boost_gpu_freq_symbol)(unsigned long *pulFreq);
+extern bool (*mtk_get_custom_upbound_gpu_freq_symbol)(unsigned long *pulFreq);
+extern bool (*mtk_get_vsync_based_target_freq_symbol)(unsigned long *pulFreq);
+extern bool (*mtk_get_vsync_offset_event_status_symbol)(unsigned int *pui32EventStatus);
+extern bool (*mtk_get_vsync_offset_debug_status_symbol)(unsigned int *pui32EventStatus);
+extern bool (*mtk_enable_gpu_perf_monitor_symbol)(bool enable);
+extern bool (*mtk_get_gpu_pmu_init_symbol)(GPU_PMU *pmus, int pmu_size, int *ret_size);
+extern bool (*mtk_get_gpu_pmu_swapnreset_symbol)(GPU_PMU *pmus, int pmu_size);
+extern bool (*mtk_get_gpu_pmu_deinit_symbol)(void);
+extern bool (*mtk_get_gpu_pmu_swapnreset_stop_symbol)(void);
+
+extern bool mtk_register_gpu_power_change(const char *name, gpu_power_change_notify_fp callback);
+extern bool mtk_unregister_gpu_power_change(const char *name);
+extern bool (*mtk_register_gpu_power_change_symbol)(const char *name,
+ gpu_power_change_notify_fp callback);
+extern bool (*mtk_unregister_gpu_power_change_symbol)(const char *name);
+
+
+extern unsigned int (*mt_gpufreq_get_cur_freq_symbol)(void);
+extern unsigned int (*mt_gpufreq_get_thermal_limit_freq_symbol)(void);
+
+extern struct metdevice met_gpu;
+extern struct metdevice met_gpudvfs;
+extern struct metdevice met_gpumem;
+extern struct metdevice met_gpupwr;
+extern struct metdevice met_gpu_pmu;
+#ifdef MET_GPU_STALL_MONITOR
+extern struct metdevice met_gpu_stall;
+#endif
+#endif /* MET_GPU */
+
+
+#ifdef MET_VCOREDVFS
+/*
+ * VCORE DVFS
+ */
+extern int vcorefs_get_num_opp(void);
+extern int vcorefs_get_opp_info_num(void);
+extern char ** vcorefs_get_opp_info_name(void);
+extern unsigned int * vcorefs_get_opp_info(void);
+extern int vcorefs_get_src_req_num(void);
+extern char ** vcorefs_get_src_req_name(void);
+extern unsigned int * vcorefs_get_src_req(void);
+
+extern int (*vcorefs_get_num_opp_symbol)(void);
+extern int (*vcorefs_get_opp_info_num_symbol)(void);
+extern char ** (*vcorefs_get_opp_info_name_symbol)(void);
+extern unsigned int * (*vcorefs_get_opp_info_symbol)(void);
+extern int (*vcorefs_get_src_req_num_symbol)(void);
+extern char ** (*vcorefs_get_src_req_name_symbol)(void);
+extern unsigned int * (*vcorefs_get_src_req_symbol)(void);
+
+extern struct metdevice met_vcoredvfs;
+#endif /* MET_VCOREDVFS */
+
+
+#ifdef MET_SSPM_EMI
+extern void *mt_cen_emi_base_get(void);
+extern unsigned int get_dram_data_rate(void); /* in Mhz */
+extern int get_ddr_type(void);
+extern void *get_cur_ddr_ratio(void);
+
+extern void *(*mt_cen_emi_base_get_symbol)(void);
+extern unsigned int (*get_dram_data_rate_symbol)(void); /* in Mhz */
+extern unsigned int (*get_ddr_type_symbol)(void);
+extern unsigned int (*get_cur_ddr_ratio_symbol)(void);
+
+
+
+extern struct metdevice met_sspm_emi;
+#endif /* MET_SSPM_EMI */
+
+#ifdef MET_SMI
+extern struct metdevice met_sspm_smi;
+#endif
+
+#ifdef MET_PTPOD
+#include <mtk_gpufreq.h>
+#include <mach/mtk_cpufreq_api.h>
+#include <mtk_cpufreq_config.h>
+
+extern unsigned int (*mt_gpufreq_get_cur_volt_symbol)(void);
+extern unsigned int (*mt_cpufreq_get_cur_volt_symbol)(unsigned int cluster_id);
+
+extern struct metdevice met_ptpod;
+#endif /* MET_PTPOD */
+
+#ifdef MET_WALLTIME
+extern struct metdevice met_wall_time;
+#endif
+
+#ifdef MTK_TINYSYS_SSPM_SUPPORT
+extern struct metdevice met_sspm_common;
+#endif /* MTK_TINYSYS_SSPM_SUPPORT */
+
+#if defined(CONFIG_MTK_TINYSYS_SSPM_SUPPORT) && defined(ONDIEMET_SUPPORT)
+#ifdef MET_SSPM_WALLTIME
+extern struct metdevice met_sspm_walltime;
+#endif
+#endif /* CONFIG_MTK_TINYSYS_SSPM_SUPPORT */
+
+#ifdef MET_CPUDSU
+extern struct metdevice met_cpudsu;
+#endif
+
+#ifdef MET_SPM_TWAM
+#include "mtk_spm.h"
+
+/* ap side used */
+#ifdef SPMTWAM_AP
+extern void spm_twam_enable_monitor(const struct twam_sig *twamsig, bool speed_mode);
+extern void spm_twam_register_handler(twam_handler_t handler);
+extern void spm_twam_disable_monitor(void);
+extern void spm_twam_set_idle_select(unsigned int sel);
+extern void spm_twam_set_window_length(unsigned int len);
+extern void spm_twam_set_mon_type(struct twam_sig *mon);
+
+extern void (*spm_twam_enable_monitor_symbol)(const struct twam_sig *twamsig, bool speed_mode);
+extern void (*spm_twam_register_handler_symbol)(twam_handler_t handler);
+extern void (*spm_twam_disable_monitor_symbol)(void);
+extern void (*spm_twam_set_idle_select_symbol)(unsigned int sel);
+extern void (*spm_twam_set_window_length_symbol)(unsigned int len);
+extern void (*spm_twam_set_mon_type_symbol)(struct twam_sig *mon);
+#endif
+
+/* sspm side used */
+#ifdef SPMTWAM_SSPM
+extern void spm_twam_enable_monitor(bool en_monitor, bool debug_signal, twam_handler_t cb_handler);
+extern bool spm_twam_met_enable(void);
+extern void spm_twam_config_channel(struct twam_cfg *cfg, bool speed_mode, unsigned int window_len_hz);
+
+extern void (*spm_twam_enable_monitor_symbol)(bool en_monitor, bool debug_signal, twam_handler_t cb_handler);
+extern bool (*spm_twam_met_enable_symbol)(void);
+extern void (*spm_twam_config_channel_symbol)(struct twam_cfg *cfg, bool speed_mode, unsigned int window_len_hz);
+#endif
+
+extern struct metdevice met_spmtwam;
+#endif
+
+
+#endif /*__CORE_PLF_INIT_H__*/
diff --git a/src/devtools/met-driver/4.14/common/core_plf_trace.c b/src/devtools/met-driver/4.14/common/core_plf_trace.c
new file mode 100644
index 0000000..0dc7090
--- /dev/null
+++ b/src/devtools/met-driver/4.14/common/core_plf_trace.c
@@ -0,0 +1,322 @@
+/*
+ * 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);
+
diff --git a/src/devtools/met-driver/4.14/common/core_plf_trace.h b/src/devtools/met-driver/4.14/common/core_plf_trace.h
new file mode 100644
index 0000000..5c89efd
--- /dev/null
+++ b/src/devtools/met-driver/4.14/common/core_plf_trace.h
@@ -0,0 +1,29 @@
+/*
+ * 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);
+
+#endif /* _CORE_PLF_TRACE_H_ */
diff --git a/src/devtools/met-driver/4.14/common/cpu_dsu.c b/src/devtools/met-driver/4.14/common/cpu_dsu.c
new file mode 100644
index 0000000..092d49b
--- /dev/null
+++ b/src/devtools/met-driver/4.14/common/cpu_dsu.c
@@ -0,0 +1,362 @@
+/*
+ * 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/perf_event.h>
+#include "met_drv.h"
+#include "met_kernel_symbol.h"
+#include "interface.h"
+#include "trace.h"
+#include "cpu_dsu.h"
+#include "core_plf_init.h"
+
+
+struct cpu_dsu_hw *cpu_dsu;
+static int counter_cnt;
+static struct kobject *kobj_dsu;
+static int nr_arg;
+static unsigned long long perfCurr[MXNR_DSU_EVENTS];
+static unsigned long long perfPrev[MXNR_DSU_EVENTS];
+static int perfCntFirst[MXNR_DSU_EVENTS];
+static struct perf_event * pevent[MXNR_DSU_EVENTS];
+static struct perf_event_attr pevent_attr[MXNR_DSU_EVENTS];
+static unsigned int perf_device_type = 7;
+static ssize_t perf_type_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
+{
+ return snprintf(buf, PAGE_SIZE, "%d\n", perf_device_type);
+}
+
+static ssize_t perf_type_store(struct kobject *kobj, struct kobj_attribute *attr, const char *buf, size_t n)
+{
+ if (kstrtouint(buf, 0, &perf_device_type) != 0)
+ return -EINVAL;
+
+ return n;
+}
+static struct kobj_attribute perf_type_attr = __ATTR(perf_type, 0664, perf_type_show, perf_type_store);
+
+noinline void mp_dsu(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.
+ */
+}
+
+static void perf_cpudsu_polling(unsigned long long stamp, int cpu)
+{
+ int event_count = cpu_dsu->event_count;
+ struct met_dsu *pmu = cpu_dsu->pmu;
+ int i, count;
+ unsigned long long delta;
+ struct perf_event *ev;
+ unsigned int pmu_value[MXNR_DSU_EVENTS];
+ u64 value;
+
+ count = 0;
+ for (i = 0; i < event_count; i++) {
+ if (pmu[i].mode == 0)
+ continue;
+
+ ev = pevent[i];
+ if ((ev != NULL) && (ev->state == PERF_EVENT_STATE_ACTIVE)) {
+ met_perf_event_read_local_symbol(ev, &value);
+ perfCurr[i] = value;
+ delta = (perfCurr[i] - perfPrev[i]);
+ perfPrev[i] = perfCurr[i];
+ if (perfCntFirst[i] == 1) {
+ /* we shall omit delta counter when we get first counter */
+ perfCntFirst[i] = 0;
+ continue;
+ }
+ pmu_value[count] = (unsigned int)delta;
+ count++;
+ }
+ }
+
+ if (count == counter_cnt)
+ mp_dsu(count, pmu_value);
+}
+
+static int perf_thread_set_perf_events(unsigned int cpu)
+{
+ int i, size;
+ struct perf_event *ev;
+ struct perf_event_attr *ev_attr;
+ int event_count = cpu_dsu->event_count;
+ struct met_dsu *pmu = cpu_dsu->pmu;
+
+ size = sizeof(struct perf_event_attr);
+
+ for (i = 0; i < event_count; i++) {
+ pevent[i] = NULL;
+ if (!pmu[i].mode)
+ continue; /* Skip disabled counters */
+ perfPrev[i] = 0;
+ perfCurr[i] = 0;
+ ev_attr = pevent_attr+i;
+ memset(ev_attr, 0, size);
+ ev_attr->config = pmu[i].event;
+ ev_attr->type = perf_device_type;
+ ev_attr->size = size;
+ ev_attr->sample_period = 0;
+ ev_attr->pinned = 1;
+
+ ev = perf_event_create_kernel_counter(ev_attr, cpu, NULL, dummy_handler, NULL);
+ if (IS_ERR(ev))
+ continue;
+ if (ev->state != PERF_EVENT_STATE_ACTIVE) {
+ perf_event_release_kernel(ev);
+ continue;
+ }
+ pevent[i] = ev;
+ if (ev != NULL)
+ perf_event_enable(ev);
+ } /* for all PMU counter */
+ return 0;
+}
+
+void met_perf_cpudsu_down(void)
+{
+ int i;
+ struct perf_event *ev;
+ int event_count;
+ struct met_dsu *pmu;
+
+ if (met_cpudsu.mode == 0)
+ return;
+ event_count = cpu_dsu->event_count;
+ pmu = cpu_dsu->pmu;
+ for (i = 0; i < event_count; i++) {
+ if (!pmu[i].mode)
+ continue;
+ ev = pevent[i];
+ if ((ev != NULL) && (ev->state == PERF_EVENT_STATE_ACTIVE)) {
+ perf_event_disable(ev);
+ perf_event_release_kernel(ev);
+ }
+ pevent[i] = NULL;
+ }
+ //perf_delayed_work_setup = NULL;
+}
+
+inline static void met_perf_cpudsu_start(int cpu)
+{
+ if (met_cpudsu.mode == 0)
+ return;
+ if (cpu != 0)
+ return;
+ perf_thread_set_perf_events(cpu);
+}
+
+static int cpudsu_create_subfs(struct kobject *parent)
+{
+ int ret = 0;
+ cpu_dsu = cpu_dsu_hw_init();
+ if (cpu_dsu == NULL) {
+ PR_BOOTMSG("Failed to init CPU PMU HW!!\n");
+ return -ENODEV;
+ }
+ kobj_dsu = parent;
+ ret = sysfs_create_file(kobj_dsu, &perf_type_attr.attr);
+ if (ret != 0) {
+ PR_BOOTMSG("Failed to create perf_type in sysfs\n");
+ goto out;
+ }
+ out:
+ return ret;
+}
+
+static void cpudsu_delete_subfs(void)
+{
+}
+
+void met_perf_cpudsu_polling(unsigned long long stamp, int cpu)
+{
+ perf_cpudsu_polling(stamp, cpu);
+}
+
+static void cpudsu_start(void)
+{
+ int cpu = raw_smp_processor_id();
+ for_each_online_cpu(cpu)
+ met_perf_cpudsu_start(cpu);
+}
+
+static void cpudsu_stop(void)
+{
+ met_perf_cpudsu_down();
+}
+
+static const char header[] =
+ "met-info [000] 0.0: met_dsu_pmu_header: DSU";
+
+static const char help[] =
+ " --dsu=EVENT select DSU-PMU events.\n"
+ " you can enable at most \"%d general purpose events\"\n";
+
+static int cpudsu_print_help(char *buf, int len)
+{
+ return snprintf(buf, PAGE_SIZE, help, cpu_dsu->event_count);
+}
+
+static int reset_driver_stat(void)
+{
+ int i;
+ int event_count;
+ struct met_dsu *pmu;
+
+ met_cpudsu.mode = 0;
+ event_count = cpu_dsu->event_count;
+ pmu = cpu_dsu->pmu;
+ counter_cnt = 0;
+ nr_arg = 0;
+ for (i = 0; i < event_count; i++) {
+ pmu[i].mode = MODE_DISABLED;
+ pmu[i].event = 0;
+ pmu[i].freq = 0;
+ }
+ return 0;
+}
+
+static int cpudsu_print_header(char *buf, int len)
+{
+ int first;
+ int i, ret;
+ int event_count;
+ struct met_dsu *pmu;
+ ret = 0;
+
+ ret += snprintf(buf + ret, PAGE_SIZE - ret, "# mp_dsu: pmu_value1, ...\n");
+ event_count = cpu_dsu->event_count;
+ pmu = cpu_dsu->pmu;
+ first = 1;
+ for (i = 0; i < event_count; i++) {
+ if (pmu[i].mode == 0)
+ continue;
+ if (first) {
+ ret += snprintf(buf + ret, PAGE_SIZE - ret, header);
+ first = 0;
+ }
+ ret += snprintf(buf + ret, PAGE_SIZE - ret, ",0x%x", pmu[i].event);
+ pmu[i].mode = 0;
+ }
+ ret += snprintf(buf + ret, PAGE_SIZE - ret, "\n");
+ reset_driver_stat();
+ return ret;
+}
+
+static int met_parse_num_list(char *arg, int len, int *list, int list_cnt)
+{
+ int nr_num = 0;
+ char *num;
+ int num_len;
+
+ /* search ',' as the splitter */
+ while (len) {
+ num = arg;
+ num_len = 0;
+ if (list_cnt <= 0)
+ return -1;
+ while (len) {
+ len--;
+ if (*arg == ',') {
+ *(arg++) = '\0';
+ break;
+ }
+ arg++;
+ num_len++;
+ }
+ if (met_parse_num(num, list, num_len) < 0)
+ return -1;
+ list++;
+ list_cnt--;
+ nr_num++;
+ }
+ return nr_num;
+}
+
+static int cpudsu_process_argument(const char *arg, int len)
+{
+ int nr_events, event_list[MXNR_DSU_EVENTS];
+ int i;
+ int nr_counters;
+ struct met_dsu *pmu;
+ int arg_nr;
+ int counters;
+ int event_no;
+
+ /* get event_list */
+ if ((nr_events = met_parse_num_list((char*)arg, len, event_list, ARRAY_SIZE(event_list))) <= 0)
+ goto arg_out;
+
+ /* for each cpu in cpu_list, add all the events in event_list */
+ nr_counters = cpu_dsu->event_count;
+ pmu = cpu_dsu->pmu;
+ arg_nr = nr_arg;
+
+ /*
+ * setup nr_counters for linux native perf mode.
+ * because the selected events are stored in pmu,
+ * so nr_counters can't large then event count in pmu.
+ */
+ counters = perf_num_counters();
+ if (counters < nr_counters)
+ nr_counters = counters;
+
+ if (nr_counters == 0)
+ goto arg_out;
+
+ for (i = 0; i < nr_events; i++) {
+ event_no = event_list[i];
+ /*
+ * check if event is duplicate,
+ * but may not include 0xff when met_cpu_dsu_method == 0.
+ */
+ if (cpu_dsu->check_event(pmu, arg_nr, event_no) < 0)
+ goto arg_out;
+ if (arg_nr >= nr_counters)
+ goto arg_out;
+ pmu[arg_nr].mode = MODE_POLLING;
+ pmu[arg_nr].event = event_no;
+ pmu[arg_nr].freq = 0;
+ arg_nr++;
+ counter_cnt++;
+ }
+ nr_arg = arg_nr;
+ met_cpudsu.mode = 1;
+ return 0;
+
+arg_out:
+ reset_driver_stat();
+ return -EINVAL;
+}
+
+struct metdevice met_cpudsu = {
+ .name = "dsu",
+ .type = MET_TYPE_PMU,
+ .cpu_related = 0,
+ .create_subfs = cpudsu_create_subfs,
+ .delete_subfs = cpudsu_delete_subfs,
+ .start = cpudsu_start,
+ .stop = cpudsu_stop,
+ .polling_interval = 1,
+ .timed_polling = met_perf_cpudsu_polling,
+ .print_help = cpudsu_print_help,
+ .print_header = cpudsu_print_header,
+ .process_argument = cpudsu_process_argument
+};
diff --git a/src/devtools/met-driver/4.14/common/cpu_dsu.h b/src/devtools/met-driver/4.14/common/cpu_dsu.h
new file mode 100644
index 0000000..084c81a
--- /dev/null
+++ b/src/devtools/met-driver/4.14/common/cpu_dsu.h
@@ -0,0 +1,49 @@
+/*
+ * 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 _CPU_DSU_H_
+#define _CPU_DSU_H_
+
+#include <linux/device.h>
+
+#define MODE_DISABLED 0
+#define MODE_INTERRUPT 1
+#define MODE_POLLING 2
+
+#define MXNR_CPU NR_CPUS
+
+#define MXNR_DSU_EVENTS 8 /* max number of pmu counter for armv8 is 6+1 */
+struct met_dsu {
+ unsigned char mode;
+ unsigned short event;
+ unsigned long freq;
+ struct kobject *kobj_cpu_dsu;
+};
+
+struct cpu_dsu_hw {
+ const char *name;
+ int nr_cnt;
+ int (*check_event)(struct met_dsu *pmu, int idx, int event);
+ void (*start)(struct met_dsu *pmu, int count);
+ void (*stop)(int count);
+ unsigned int (*polling)(struct met_dsu *pmu, int count, unsigned int *pmu_value);
+ struct met_dsu *pmu;
+ int event_count;
+};
+
+
+struct cpu_dsu_hw *cpu_dsu_hw_init(void);
+
+
+
+#endif /* _CPU_DSU_H_ */
diff --git a/src/devtools/met-driver/4.14/common/cpu_pmu.c b/src/devtools/met-driver/4.14/common/cpu_pmu.c
new file mode 100644
index 0000000..36a6402
--- /dev/null
+++ b/src/devtools/met-driver/4.14/common/cpu_pmu.c
@@ -0,0 +1,831 @@
+/*
+ * 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/cpu.h>
+#include <linux/cpu_pm.h>
+#include <linux/perf_event.h>
+
+#if (defined(CONFIG_ARM64) || defined(CONFIG_ARM))
+#include <linux/platform_device.h>
+#include <linux/perf/arm_pmu.h>
+#endif
+
+#include "met_drv.h"
+#include "met_kernel_symbol.h"
+#include "interface.h"
+#include "trace.h"
+#include "cpu_pmu.h"
+#include "mtk_typedefs.h"
+
+struct cpu_pmu_hw *cpu_pmu;
+static int counter_cnt[MXNR_CPU];
+static int nr_arg[MXNR_CPU];
+
+int met_perf_cpupmu_status;
+
+static int mtk_pmu_event_enable = 0;
+static struct kobject *kobj_cpu;
+DECLARE_KOBJ_ATTR_INT(mtk_pmu_event_enable, mtk_pmu_event_enable);
+#define KOBJ_ATTR_LIST \
+ do { \
+ KOBJ_ATTR_ITEM(mtk_pmu_event_enable); \
+ } while (0)
+
+#ifdef CONFIG_CPU_PM
+static int use_cpu_pm_pmu_notifier = 0;
+
+/* helper notifier for maintaining pmu states before cpu state transition */
+static int cpu_pm_pmu_notify(struct notifier_block *b,
+ unsigned long cmd,
+ void *p)
+{
+ int ii;
+ int cpu, count;
+ unsigned int pmu_value[MXNR_PMU_EVENTS];
+
+ if (!met_perf_cpupmu_status)
+ return NOTIFY_OK;
+
+ cpu = raw_smp_processor_id();
+
+ switch (cmd) {
+ case CPU_PM_ENTER:
+ count = cpu_pmu->polling(cpu_pmu->pmu[cpu], cpu_pmu->event_count[cpu], pmu_value);
+ for (ii = 0; ii < count; ii ++)
+ cpu_pmu->cpu_pm_unpolled_loss[cpu][ii] += pmu_value[ii];
+
+ cpu_pmu->stop(cpu_pmu->event_count[cpu]);
+ break;
+ case CPU_PM_ENTER_FAILED:
+ case CPU_PM_EXIT:
+ cpu_pmu->start(cpu_pmu->pmu[cpu], cpu_pmu->event_count[cpu]);
+ break;
+ default:
+ return NOTIFY_DONE;
+ }
+ return NOTIFY_OK;
+}
+
+struct notifier_block cpu_pm_pmu_notifier = {
+ .notifier_call = cpu_pm_pmu_notify,
+};
+#endif
+
+static DEFINE_PER_CPU(unsigned long long[MXNR_PMU_EVENTS], perfCurr);
+static DEFINE_PER_CPU(unsigned long long[MXNR_PMU_EVENTS], perfPrev);
+static DEFINE_PER_CPU(int[MXNR_PMU_EVENTS], perfCntFirst);
+static DEFINE_PER_CPU(struct perf_event * [MXNR_PMU_EVENTS], pevent);
+static DEFINE_PER_CPU(struct perf_event_attr [MXNR_PMU_EVENTS], pevent_attr);
+static DEFINE_PER_CPU(int, perfSet);
+static DEFINE_PER_CPU(unsigned int, perf_task_init_done);
+static DEFINE_PER_CPU(int, perf_cpuid);
+static DEFINE_PER_CPU(struct delayed_work, cpu_pmu_dwork_setup);
+static DEFINE_PER_CPU(struct delayed_work*, perf_delayed_work_setup);
+static DEFINE_PER_CPU(struct delayed_work, cpu_pmu_dwork_down);
+static DEFINE_PER_CPU(int, cpu_status);
+
+#ifdef CPUPMU_V8_2
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <mt-plat/sync_write.h>
+#include <mt-plat/mtk_io.h>
+
+static char mcucfg_desc[] = "mediatek,mcucfg";
+static void __iomem *mcucfg_base = NULL;
+#define DBG_CONTROL_CPU6 ((unsigned long)mcucfg_base + 0x3000 + 0x308) /* DBG_CONTROL */
+#define DBG_CONTROL_CPU7 ((unsigned long)mcucfg_base + 0x3800 + 0x308) /* DBG_CONTROL */
+#define ENABLE_MTK_PMU_EVENTS_OFFSET 1
+static int restore_dbg_ctrl_cpu6;
+static int restore_dbg_ctrl_cpu7;
+
+int cpu_pmu_debug_init(void)
+{
+ struct device_node *node = NULL;
+ unsigned int value6,value7;
+
+ /*for A75 MTK internal event*/
+ if (mcucfg_base == NULL) {
+ node = of_find_compatible_node(NULL, NULL, mcucfg_desc);
+ if (node == NULL) {
+ MET_TRACE("[MET_PMU_DB] of_find node == NULL\n");
+ pr_debug("[MET_PMU_DB] of_find node == NULL\n");
+ goto out;
+ }
+ mcucfg_base = of_iomap(node, 0);
+ of_node_put(node);
+ if (mcucfg_base == NULL) {
+ MET_TRACE("[MET_PMU_DB] mcucfg_base == NULL\n");
+ pr_debug("[MET_PMU_DB] mcucfg_base == NULL\n");
+ goto out;
+ }
+ MET_TRACE("[MET_PMU_DB] regbase %08lx\n", DBG_CONTROL_CPU7);
+ pr_debug("[MET_PMU_DB] regbase %08lx\n", DBG_CONTROL_CPU7);
+ }
+
+ value6 = readl(IOMEM(DBG_CONTROL_CPU6));
+ if (value6 & (1 << ENABLE_MTK_PMU_EVENTS_OFFSET)) {
+ restore_dbg_ctrl_cpu6 = 1;
+ } else {
+ restore_dbg_ctrl_cpu6 = 0;
+ mt_reg_sync_writel(value6 | (1 << ENABLE_MTK_PMU_EVENTS_OFFSET), DBG_CONTROL_CPU6);
+ }
+
+ value7 = readl(IOMEM(DBG_CONTROL_CPU7));
+ if (value7 & (1 << ENABLE_MTK_PMU_EVENTS_OFFSET)) {
+ restore_dbg_ctrl_cpu7 = 1;
+ } else {
+ restore_dbg_ctrl_cpu7 = 0;
+ mt_reg_sync_writel(value7 | (1 << ENABLE_MTK_PMU_EVENTS_OFFSET), DBG_CONTROL_CPU7);
+ }
+
+ value6 = readl(IOMEM(DBG_CONTROL_CPU6));
+ value7 = readl(IOMEM(DBG_CONTROL_CPU7));
+ MET_TRACE("[MET_PMU_DB]DBG_CONTROL_CPU6 = %08x, DBG_CONTROL_CPU7 = %08x\n", value6, value7);
+ pr_debug("[MET_PMU_DB]DBG_CONTROL_CPU6 = %08x, DBG_CONTROL_CPU7 = %08x\n", value6, value7);
+ return 1;
+
+out:
+ if (mcucfg_base != NULL) {
+ iounmap(mcucfg_base);
+ mcucfg_base = NULL;
+ }
+ MET_TRACE("[MET_PMU_DB]DBG_CONTROL init error");
+ pr_debug("[MET_PMU_DB]DBG_CONTROL init error");
+ return 0;
+}
+
+int cpu_pmu_debug_uninit(void)
+{
+ unsigned int value6,value7;
+
+ if (restore_dbg_ctrl_cpu6 == 0) {
+ value6 = readl(IOMEM(DBG_CONTROL_CPU6));
+ mt_reg_sync_writel(value6 & (~(1 << ENABLE_MTK_PMU_EVENTS_OFFSET)), DBG_CONTROL_CPU6);
+ }
+ if (restore_dbg_ctrl_cpu7 == 0) {
+ value7 = readl(IOMEM(DBG_CONTROL_CPU7));
+ mt_reg_sync_writel(value7 & (~(1 << ENABLE_MTK_PMU_EVENTS_OFFSET)), DBG_CONTROL_CPU7);
+ }
+
+ value6 = readl(IOMEM(DBG_CONTROL_CPU6));
+ value7 = readl(IOMEM(DBG_CONTROL_CPU7));
+ MET_TRACE("[MET_PMU_DB]DBG_CONTROL_CPU6 = %08x, DBG_CONTROL_CPU7 = %08x\n", value6, value7);
+ pr_debug("[MET_PMU_DB]DBG_CONTROL_CPU6 = %08x, DBG_CONTROL_CPU7 = %08x\n", value6, value7);
+
+ if (mcucfg_base != NULL) {
+ iounmap(mcucfg_base);
+ mcucfg_base = NULL;
+ }
+ restore_dbg_ctrl_cpu6 = 0;
+ restore_dbg_ctrl_cpu7 = 0;
+ return 1;
+}
+#endif
+
+
+
+
+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.
+ */
+}
+
+static void perf_cpupmu_polling(unsigned long long stamp, int cpu)
+{
+ int event_count = cpu_pmu->event_count[cpu];
+ struct met_pmu *pmu = cpu_pmu->pmu[cpu];
+ int i, count;
+ unsigned long long delta;
+ struct perf_event *ev;
+ unsigned int pmu_value[MXNR_PMU_EVENTS];
+ u64 value;
+
+ if (per_cpu(perfSet, cpu) == 0)
+ return;
+
+ count = 0;
+ for (i = 0; i < event_count; 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);
+ 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[count] = (unsigned int)delta;
+ count++;
+ }
+ }
+
+ if (count == counter_cnt[cpu])
+ mp_cpu(count, pmu_value);
+}
+
+static struct perf_event* perf_event_create(int cpu, unsigned short event, int count)
+{
+ struct perf_event_attr *ev_attr;
+ struct perf_event *ev;
+
+ ev_attr = per_cpu(pevent_attr, cpu)+count;
+ memset(ev_attr, 0, sizeof(*ev_attr));
+ if (event == 0xff) {
+ ev_attr->config = PERF_COUNT_HW_CPU_CYCLES;
+ ev_attr->type = PERF_TYPE_HARDWARE;
+ } else {
+ ev_attr->config = event;
+ ev_attr->type = PERF_TYPE_RAW;
+ }
+ ev_attr->size = sizeof(*ev_attr);
+ ev_attr->sample_period = 0;
+ ev_attr->pinned = 1;
+
+ ev = perf_event_create_kernel_counter(ev_attr, cpu, NULL, dummy_handler, NULL);
+ if (IS_ERR(ev))
+ return NULL;
+ do {
+ if (ev->state == PERF_EVENT_STATE_ACTIVE)
+ break;
+ if (ev->state == PERF_EVENT_STATE_ERROR) {
+ perf_event_enable(ev);
+ if (ev->state == PERF_EVENT_STATE_ACTIVE)
+ break;
+ }
+ perf_event_release_kernel(ev);
+ return NULL;
+ } while (0);
+
+ return ev;
+}
+
+static void perf_event_release(int cpu, struct perf_event *ev)
+{
+ if (ev->state == PERF_EVENT_STATE_ACTIVE)
+ perf_event_disable(ev);
+ perf_event_release_kernel(ev);
+}
+
+static int perf_thread_set_perf_events(int cpu)
+{
+ int i, size;
+ struct perf_event *ev;
+
+ size = sizeof(struct perf_event_attr);
+ if (per_cpu(perfSet, cpu) == 0) {
+ int event_count = cpu_pmu->event_count[cpu];
+ struct met_pmu *pmu = cpu_pmu->pmu[cpu];
+ for (i = 0; i < event_count; i++) {
+ if (!pmu[i].mode)
+ continue; /* Skip disabled counters */
+ ev = perf_event_create(cpu, pmu[i].event, i);
+ if (ev == NULL) {
+ met_cpupmu.mode = 0;
+ met_perf_cpupmu_status = 0;
+
+ MET_TRACE("[MET_PMU] failed to register pmu event %4x\n", pmu[i].event);
+ pr_notice("[MET_PMU] failed to register pmu event %4x\n", pmu[i].event);
+ continue;
+ }
+
+ MET_TRACE("[MET_PMU] registered pmu slot: [%d] evt=%#04x\n", ev->hw.idx, pmu[i].event);
+ pr_debug("[MET_PMU] registered pmu slot: [%d] evt=%#04x\n", ev->hw.idx, pmu[i].event);
+
+ per_cpu(pevent, cpu)[i] = ev;
+ per_cpu(perfPrev, cpu)[i] = 0;
+ per_cpu(perfCurr, cpu)[i] = 0;
+ 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)
+{
+ 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);
+ }
+}
+
+static void met_perf_cpupmu_start(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 = &per_cpu(cpu_pmu_dwork_setup, cpu);
+ INIT_DELAYED_WORK(dwork, perf_thread_setup);
+ dwork->cpu = cpu;
+ schedule_delayed_work_on(cpu, dwork, 0);
+ per_cpu(perf_delayed_work_setup, cpu) = dwork;
+ }
+}
+
+static void perf_thread_down(struct work_struct *work)
+{
+ struct delayed_work *dwork = to_delayed_work(work);
+ int cpu, i;
+ struct perf_event *ev;
+ int event_count;
+ struct met_pmu *pmu;
+
+ cpu = dwork->cpu;
+ if (per_cpu(perfSet, cpu) == 0)
+ return;
+
+ per_cpu(perfSet, cpu) = 0;
+ event_count = cpu_pmu->event_count[cpu];
+ pmu = cpu_pmu->pmu[cpu];
+ for (i = 0; i < event_count; i++) {
+ ev = per_cpu(pevent, cpu)[i];
+ if (ev != NULL) {
+ perf_event_release(cpu, ev);
+ per_cpu(pevent, cpu)[i] = NULL;
+ }
+ }
+ per_cpu(perf_task_init_done, cpu) = 0;
+ per_cpu(perf_delayed_work_setup, cpu) = NULL;
+}
+
+static void met_perf_cpupmu_stop(int cpu)
+{
+ struct delayed_work *dwork;
+
+ per_cpu(perf_cpuid, cpu) = cpu;
+ dwork = &per_cpu(cpu_pmu_dwork_down, cpu);
+ INIT_DELAYED_WORK(dwork, perf_thread_down);
+ dwork->cpu = cpu;
+ schedule_delayed_work_on(cpu, dwork, 0);
+}
+
+static int cpupmu_create_subfs(struct kobject *parent)
+{
+ int ret = 0;
+
+ cpu_pmu = cpu_pmu_hw_init();
+ if (cpu_pmu == NULL) {
+ PR_BOOTMSG("Failed to init CPU PMU HW!!\n");
+ return -ENODEV;
+ }
+
+ kobj_cpu = parent;
+
+#define KOBJ_ATTR_ITEM(attr_name) \
+ do { \
+ ret = sysfs_create_file(kobj_cpu, &attr_name ## _attr.attr); \
+ if (ret != 0) { \
+ pr_notice("Failed to create " #attr_name " in sysfs\n"); \
+ return ret; \
+ } \
+ } while (0)
+ KOBJ_ATTR_LIST;
+#undef KOBJ_ATTR_ITEM
+
+ return 0;
+}
+
+static void cpupmu_delete_subfs(void)
+{
+#define KOBJ_ATTR_ITEM(attr_name) \
+ sysfs_remove_file(kobj_cpu, &attr_name ## _attr.attr)
+
+ if (kobj_cpu != NULL) {
+ KOBJ_ATTR_LIST;
+ kobj_cpu = NULL;
+ }
+#undef KOBJ_ATTR_ITEM
+}
+
+void met_perf_cpupmu_polling(unsigned long long stamp, int cpu)
+{
+ int count;
+ unsigned int pmu_value[MXNR_PMU_EVENTS];
+
+ if (per_cpu(cpu_status, cpu) != MET_CPU_ONLINE)
+ return;
+
+ if (met_cpu_pmu_method) {
+ perf_cpupmu_polling(stamp, cpu);
+ } else {
+ count = cpu_pmu->polling(cpu_pmu->pmu[cpu], cpu_pmu->event_count[cpu], pmu_value);
+
+#ifdef CONFIG_CPU_PM
+ if (met_cpu_pm_pmu_reconfig) {
+ int ii;
+ for (ii = 0; ii < count; ii ++)
+ pmu_value[ii] += cpu_pmu->cpu_pm_unpolled_loss[cpu][ii];
+ }
+#endif
+
+ mp_cpu(count, pmu_value);
+
+#ifdef CONFIG_CPU_PM
+ if (met_cpu_pm_pmu_reconfig) {
+ memset(cpu_pmu->cpu_pm_unpolled_loss[cpu], 0, sizeof (cpu_pmu->cpu_pm_unpolled_loss[0]));
+ }
+#endif
+ }
+}
+
+static void cpupmu_start(void)
+{
+ int cpu = raw_smp_processor_id();
+
+ if (met_cpu_pmu_method)
+ met_perf_cpupmu_start(cpu);
+ else {
+ nr_arg[cpu] = 0;
+ cpu_pmu->start(cpu_pmu->pmu[cpu], cpu_pmu->event_count[cpu]);
+ }
+ met_perf_cpupmu_status = 1;
+ per_cpu(cpu_status, cpu) = MET_CPU_ONLINE;
+}
+
+
+static void cpupmu_unique_start(void)
+{
+#ifdef CPUPMU_V8_2
+ int ret = 0;
+ if (mtk_pmu_event_enable == 1){
+ ret = cpu_pmu_debug_init();
+ if (ret == 0)
+ PR_BOOTMSG("Failed to init CPU PMU debug!!\n");
+ }
+#endif
+
+#ifdef CONFIG_CPU_PM
+ use_cpu_pm_pmu_notifier = 0;
+ if (met_cpu_pm_pmu_reconfig) {
+ if (met_cpu_pmu_method) {
+ met_cpu_pm_pmu_reconfig = 0;
+ MET_TRACE("[MET_PMU] met_cpu_pmu_method=%d, met_cpu_pm_pmu_reconfig forced disabled\n", met_cpu_pmu_method);
+ pr_debug("[MET_PMU] met_cpu_pmu_method=%d, met_cpu_pm_pmu_reconfig forced disabled\n", met_cpu_pmu_method);
+ } else {
+ memset(cpu_pmu->cpu_pm_unpolled_loss, 0, sizeof (cpu_pmu->cpu_pm_unpolled_loss));
+ cpu_pm_register_notifier(&cpu_pm_pmu_notifier);
+ use_cpu_pm_pmu_notifier = 1;
+ }
+ }
+#else
+ if (met_cpu_pm_pmu_reconfig) {
+ met_cpu_pm_pmu_reconfig = 0;
+ MET_TRACE("[MET_PMU] CONFIG_CPU_PM=%d, met_cpu_pm_pmu_reconfig forced disabled\n", CONFIG_CPU_PM);
+ pr_debug("[MET_PMU] CONFIG_CPU_PM=%d, met_cpu_pm_pmu_reconfig forced disabled\n", CONFIG_CPU_PM);
+ }
+#endif
+ MET_TRACE("[MET_PMU] met_cpu_pm_pmu_reconfig=%u\n", met_cpu_pm_pmu_reconfig);
+ pr_debug("[MET_PMU] met_cpu_pm_pmu_reconfig=%u\n", met_cpu_pm_pmu_reconfig);
+
+ return;
+}
+
+static void cpupmu_stop(void)
+{
+ int cpu = raw_smp_processor_id();
+
+ met_perf_cpupmu_status = 0;
+ if (met_cpu_pmu_method)
+ met_perf_cpupmu_stop(cpu);
+ else
+ cpu_pmu->stop(cpu_pmu->event_count[cpu]);
+}
+
+static void cpupmu_unique_stop(void)
+{
+#ifdef CPUPMU_V8_2
+ if (mtk_pmu_event_enable == 1)
+ cpu_pmu_debug_uninit();
+#endif
+
+#ifdef CONFIG_CPU_PM
+ if (use_cpu_pm_pmu_notifier) {
+ cpu_pm_unregister_notifier(&cpu_pm_pmu_notifier);
+ }
+#endif
+ return;
+}
+
+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: %d";
+
+static const char help[] =
+ " --pmu-cpu-evt=[cpu_list:]event_list select CPU-PMU events in %s\n"
+ " cpu_list: specify the cpu_id list or apply to all the cores\n"
+ " example: 0,1,2\n"
+ " event_list: specify the event number\n"
+ " example: 0x8,0xff\n";
+
+static int cpupmu_print_help(char *buf, int len)
+{
+ return snprintf(buf, PAGE_SIZE, help, cpu_pmu->cpu_name);
+}
+
+static int reset_driver_stat(void)
+{
+ int cpu, i;
+ int event_count;
+ struct met_pmu *pmu;
+
+ met_cpupmu.mode = 0;
+ for_each_possible_cpu(cpu) {
+ event_count = cpu_pmu->event_count[cpu];
+ pmu = cpu_pmu->pmu[cpu];
+ counter_cnt[cpu] = 0;
+ nr_arg[cpu] = 0;
+ for (i = 0; i < event_count; i++) {
+ pmu[i].mode = MODE_DISABLED;
+ pmu[i].event = 0;
+ pmu[i].freq = 0;
+ }
+ }
+
+ return 0;
+}
+
+static int cpupmu_print_header(char *buf, int len)
+{
+ int cpu, i, ret, first;
+ int event_count;
+ struct met_pmu *pmu;
+
+ ret = 0;
+
+ /*append CPU PMU access method*/
+ if (met_cpu_pmu_method)
+ ret += snprintf(buf + ret, PAGE_SIZE,
+ "met-info [000] 0.0: CPU_PMU_method: perf APIs\n");
+ else
+ ret += snprintf(buf + ret, PAGE_SIZE,
+ "met-info [000] 0.0: CPU_PMU_method: MET pmu driver\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, "# mp_cpu: pmu_value1, ...\n");
+
+ for_each_possible_cpu(cpu) {
+ event_count = cpu_pmu->event_count[cpu];
+ pmu = cpu_pmu->pmu[cpu];
+ first = 1;
+ for (i = 0; i < event_count; i++) {
+ if (pmu[i].mode == 0)
+ continue;
+ if (first) {
+ ret += snprintf(buf + ret, PAGE_SIZE - ret, header, cpu);
+ first = 0;
+ }
+ ret += snprintf(buf + ret, PAGE_SIZE - ret, ",0x%x", pmu[i].event);
+ pmu[i].mode = 0;
+ }
+ if (!first)
+ ret += snprintf(buf + ret, PAGE_SIZE - ret, "\n");
+ }
+
+ reset_driver_stat();
+
+ return ret;
+}
+
+static int met_parse_num_list(char *arg, int len, int *list, int list_cnt)
+{
+ int nr_num = 0;
+ char *num;
+ int num_len;
+
+ /* search ',' as the splitter */
+ while (len) {
+ num = arg;
+ num_len = 0;
+ if (list_cnt <= 0)
+ return -1;
+ while (len) {
+ len--;
+ if (*arg == ',') {
+ *(arg++) = '\0';
+ break;
+ }
+ arg++;
+ num_len++;
+ }
+ if (met_parse_num(num, list, num_len) < 0)
+ return -1;
+ list++;
+ list_cnt--;
+ nr_num++;
+ }
+
+ return nr_num;
+}
+
+static int cpupmu_process_argument(const char *arg, int len)
+{
+ char *arg1 = (char*)arg;
+ int len1 = len;
+ int cpu, cpu_list[MXNR_CPU];
+ int nr_events, event_list[MXNR_PMU_EVENTS];
+ int i;
+ int nr_counters;
+ struct met_pmu *pmu;
+ int arg_nr;
+ int event_no;
+
+ /*
+ * split cpu_list and event_list by ':'
+ * arg, len: cpu_list when found (i < len)
+ * arg1, len1: event_list
+ */
+ for (i = 0; i < len; i++) {
+ if (arg[i] == ':') {
+ arg1[i] = '\0';
+ arg1 += i+1;
+ len1 = len - i - 1;
+ len = i;
+ break;
+ }
+ }
+
+ /*
+ * setup cpu_list array
+ * 1: selected
+ * 0: unselected
+ */
+ if (arg1 != arg) { /* is cpu_id list specified? */
+ int list[MXNR_CPU], cnt;
+ int cpu_id;
+ if ((cnt = met_parse_num_list((char*)arg, len, list, ARRAY_SIZE(list))) <= 0)
+ goto arg_out;
+ memset(cpu_list, 0, sizeof(cpu_list));
+ for (i = 0; i < cnt; i++) {
+ cpu_id = list[i];
+ if (cpu_id < 0 || cpu_id >= ARRAY_SIZE(cpu_list))
+ goto arg_out;
+ cpu_list[cpu_id] = 1;
+ }
+ }
+ else
+ memset(cpu_list, 1, sizeof(cpu_list));
+
+ /* get event_list */
+ if ((nr_events = met_parse_num_list(arg1, len1, event_list, ARRAY_SIZE(event_list))) <= 0)
+ goto arg_out;
+
+ /* for each cpu in cpu_list, add all the events in event_list */
+ for_each_possible_cpu(cpu) {
+ pmu = cpu_pmu->pmu[cpu];
+ arg_nr = nr_arg[cpu];
+
+ if (cpu_list[cpu] == 0)
+ continue;
+
+ if (met_cpu_pmu_method) {
+ nr_counters = perf_num_counters();
+ } else {
+ nr_counters = cpu_pmu->event_count[cpu];
+ }
+
+ pr_debug("[MET_PMU] pmu slot count=%d\n", nr_counters);
+
+ if (nr_counters == 0)
+ goto arg_out;
+
+ for (i = 0; i < nr_events; i++) {
+ event_no = event_list[i];
+ /*
+ * check if event is duplicate,
+ * but may not include 0xff when met_cpu_pmu_method == 0.
+ */
+ if (cpu_pmu->check_event(pmu, arg_nr, event_no) < 0)
+ goto arg_out;
+
+ /*
+ * test if this event is available when in perf_APIs mode
+ */
+ if (met_cpu_pmu_method) {
+ struct perf_event *ev;
+ ev = perf_event_create(cpu, event_no, arg_nr);
+ if (ev == NULL) {
+ pr_debug("!!!!!!!! [MET_PMU] failed pmu alloction test (event_no=%#04x)\n", event_no);
+ } else {
+ perf_event_release(cpu, ev);
+ }
+ }
+
+ if (met_cpu_pmu_method) {
+ if (arg_nr >= nr_counters)
+ goto arg_out;
+ pmu[arg_nr].mode = MODE_POLLING;
+ pmu[arg_nr].event = event_no;
+ pmu[arg_nr].freq = 0;
+ arg_nr++;
+ } else {
+ if (event_no == 0xff) {
+ if (pmu[nr_counters-1].mode == MODE_POLLING)
+ goto arg_out;
+ pmu[nr_counters-1].mode = MODE_POLLING;
+ pmu[nr_counters-1].event = 0xff;
+ pmu[nr_counters-1].freq = 0;
+ } else {
+ if (arg_nr >= (nr_counters - 1))
+ goto arg_out;
+ pmu[arg_nr].mode = MODE_POLLING;
+ pmu[arg_nr].event = event_no;
+ pmu[arg_nr].freq = 0;
+ arg_nr++;
+ }
+ }
+ counter_cnt[cpu]++;
+ }
+ nr_arg[cpu] = arg_nr;
+ }
+
+ met_cpupmu.mode = 1;
+ return 0;
+
+arg_out:
+ reset_driver_stat();
+ return -EINVAL;
+}
+
+
+static void cpupmu_cpu_state_notify(long cpu, unsigned long action)
+{
+ per_cpu(cpu_status, cpu) = action;
+
+#if (defined(CONFIG_ARM64) || defined(CONFIG_ARM))
+ if (met_cpu_pmu_method && action == MET_CPU_OFFLINE) {
+ struct perf_event *event = NULL;
+ struct arm_pmu *armpmu = NULL;
+ struct platform_device *pmu_device = NULL;
+ int irq = 0;
+
+ event = per_cpu(pevent, cpu)[0];
+ if (event)
+ armpmu = to_arm_pmu(event->pmu);
+ pr_debug("!!!!!!!! %s_%ld, event=%p\n", __FUNCTION__, cpu, event);
+
+ if (armpmu)
+ pmu_device = armpmu->plat_device;
+ pr_debug("!!!!!!!! %s_%ld, armpmu=%p\n", __FUNCTION__, cpu, armpmu);
+
+ if (pmu_device)
+ irq = platform_get_irq(pmu_device, 0);
+ pr_debug("!!!!!!!! %s_%ld, pmu_device=%p\n", __FUNCTION__, cpu, pmu_device);
+
+ if (irq > 0)
+ disable_percpu_irq(irq);
+ pr_debug("!!!!!!!! %s_%ld, irq=%d\n", __FUNCTION__, cpu, irq);
+ }
+#endif
+}
+
+
+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,
+ .uniq_start = cpupmu_unique_start,
+ .stop = cpupmu_stop,
+ .uniq_stop = cpupmu_unique_stop,
+ .polling_interval = 1,
+ .timed_polling = met_perf_cpupmu_polling,
+ .print_help = cpupmu_print_help,
+ .print_header = cpupmu_print_header,
+ .process_argument = cpupmu_process_argument,
+ .cpu_state_notify = cpupmu_cpu_state_notify
+};
diff --git a/src/devtools/met-driver/4.14/common/cpu_pmu.h b/src/devtools/met-driver/4.14/common/cpu_pmu.h
new file mode 100644
index 0000000..3d046d5
--- /dev/null
+++ b/src/devtools/met-driver/4.14/common/cpu_pmu.h
@@ -0,0 +1,65 @@
+/*
+ * 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 _CPU_PMU_H_
+#define _CPU_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 NR_CPUS
+
+#define MXNR_PMU_EVENTS 8 /* max number of pmu counter for armv8 is 6+1 */
+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[MXNR_CPU];
+ int event_count[MXNR_CPU];
+ /*
+ * used for compensation of pmu counter loss
+ * between end of polling and start of cpu pm
+ */
+ unsigned int cpu_pm_unpolled_loss[MXNR_CPU][MXNR_PMU_EVENTS];
+};
+
+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 int met_perf_cpupmu_status;
+extern void met_perf_cpupmu_polling(unsigned long long stamp, int cpu);
+
+#endif /* _CPU_PMU_H_ */
diff --git a/src/devtools/met-driver/4.14/common/dummy_header.c b/src/devtools/met-driver/4.14/common/dummy_header.c
new file mode 100644
index 0000000..f8d1187
--- /dev/null
+++ b/src/devtools/met-driver/4.14/common/dummy_header.c
@@ -0,0 +1,108 @@
+/*
+ * 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/string.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+
+#include "interface.h"
+#include "met_drv.h"
+
+static struct kobject *kobj_met_dummy;
+static char header_str[PAGE_SIZE];
+static int header_str_len;
+struct metdevice met_dummy_header;
+
+static ssize_t dummy_str_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf);
+static ssize_t dummy_str_store(struct kobject *kobj,
+ struct kobj_attribute *attr, const char *buf, size_t n);
+static struct kobj_attribute dummy_attr = __ATTR(dummy_str, 0664, dummy_str_show, dummy_str_store);
+
+
+static ssize_t dummy_str_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
+{
+ int ret;
+
+ ret = snprintf(buf, PAGE_SIZE, "%s", header_str);
+
+ return ret;
+}
+
+static ssize_t dummy_str_store(struct kobject *kobj,
+ struct kobj_attribute *attr, const char *buf, size_t n)
+{
+ int ret = 0;
+ char *ptr = header_str;
+
+ if ((header_str_len + strlen(buf)) < PAGE_SIZE) {
+ ret = snprintf(ptr + header_str_len, PAGE_SIZE - header_str_len, "%s\n", buf);
+ header_str_len += ret;
+ }
+ met_dummy_header.mode = 1;
+
+ return n;
+}
+
+static int dummy_reset(void)
+{
+ met_dummy_header.mode = 0;
+ memset(header_str, 0x00, PAGE_SIZE);
+ header_str_len = 0;
+
+ return 0;
+}
+
+static int dummy_print_header(char *buf, int len)
+{
+ if (header_str_len > 0)
+ len = snprintf(buf, PAGE_SIZE, "%s", header_str);
+ else
+ len = snprintf(buf, PAGE_SIZE, "# dummy header is empty\n");
+
+ return len;
+}
+
+static int dummy_create(struct kobject *parent)
+{
+ int ret = 0;
+
+ kobj_met_dummy = parent;
+ ret = sysfs_create_file(kobj_met_dummy, &dummy_attr.attr);
+ if (ret != 0) {
+ pr_debug("Failed to create montype0 in sysfs\n");
+ return ret;
+ }
+
+ return ret;
+}
+
+static void dummy_delete(void)
+{
+ sysfs_remove_file(kobj_met_dummy, &dummy_attr.attr);
+ kobj_met_dummy = NULL;
+}
+
+struct metdevice met_dummy_header = {
+ .name = "dummy_header",
+ .type = MET_TYPE_MISC,
+ .cpu_related = 0,
+ .start = NULL,
+ .stop = NULL,
+ .reset = dummy_reset,
+ .polling_interval = 0,
+ .timed_polling = NULL,
+ .print_help = NULL,
+ .print_header = dummy_print_header,
+ .create_subfs = dummy_create,
+ .delete_subfs = dummy_delete,
+};
diff --git a/src/devtools/met-driver/4.14/common/interface.c b/src/devtools/met-driver/4.14/common/interface.c
new file mode 100644
index 0000000..f8bb644
--- /dev/null
+++ b/src/devtools/met-driver/4.14/common/interface.c
@@ -0,0 +1,1441 @@
+/*
+ * 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_power.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;
+/*
+ * controls whether re-configuring pmu events after leaving cpu off state
+ */
+unsigned int met_cpu_pm_pmu_reconfig = 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);
+
+static ssize_t cpu_pm_pmu_reconfig_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf);
+static ssize_t cpu_pm_pmu_reconfig_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf,
+ size_t count);
+static DEVICE_ATTR(cpu_pm_pmu_reconfig,
+ 0664,
+ cpu_pm_pmu_reconfig_show,
+ cpu_pm_pmu_reconfig_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 ssize_t cpu_pm_pmu_reconfig_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ return snprintf(buf, PAGE_SIZE, "%d\n", met_cpu_pm_pmu_reconfig);
+}
+
+static ssize_t cpu_pm_pmu_reconfig_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_pm_pmu_reconfig = 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;
+ }
+
+ ret = device_create_file(met_device.this_device, &dev_attr_cpu_pm_pmu_reconfig);
+ if (ret != 0) {
+ pr_debug("can not create device file: cpu_pm_pmu_reconfig\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);
+ met_register(&met_memstat);
+ met_register(&met_switch);
+#ifdef MET_EVENT_POWER_SUPPORT
+ met_register(&met_trace_event);
+ met_ext_api.met_pm_qos_update_request = pm_qos_update_request;
+ met_ext_api.met_pm_qos_update_target = pm_qos_update_target;
+#endif
+
+ met_register(&met_dummy_header);
+
+ met_register(&met_backlight);
+ met_ext_api.enable_met_backlight_tag = enable_met_backlight_tag_real;
+ met_ext_api.output_met_backlight_tag = output_met_backlight_tag_real;
+
+#ifdef MET_USER_EVENT_SUPPORT
+ tag_reg((struct file_operations * const) met_device.fops, &met_device.this_device->kobj);
+#endif
+
+ met_register(&met_stat);
+
+ 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;
+
+ met_deregister(&met_stat);
+
+#ifdef MET_USER_EVENT_SUPPORT
+ tag_unreg();
+#endif
+
+ met_deregister(&met_dummy_header);
+#ifdef MET_EVENT_POWER_SUPPORT
+ met_deregister(&met_trace_event);
+ met_ext_api.met_pm_qos_update_request = NULL;
+ met_ext_api.met_pm_qos_update_target = NULL;
+#endif
+ met_deregister(&met_switch);
+ met_deregister(&met_memstat);
+ met_deregister(&met_cpupmu);
+ met_deregister(&met_cookie);
+ met_deregister(&met_backlight);
+ met_ext_api.enable_met_backlight_tag = NULL;
+ met_ext_api.output_met_backlight_tag = NULL;
+
+ 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_cpu_pm_pmu_reconfig);
+
+ 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.14/common/interface.h b/src/devtools/met-driver/4.14/common/interface.h
new file mode 100644
index 0000000..6cbbe6d
--- /dev/null
+++ b/src/devtools/met-driver/4.14/common/interface.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 __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_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;
+
+/*
+ * controls whether re-configuring pmu events after leaving cpu off state
+ */
+extern unsigned int met_cpu_pm_pmu_reconfig;
+
+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.14/common/mem_stat.c b/src/devtools/met-driver/4.14/common/mem_stat.c
new file mode 100644
index 0000000..2409e70
--- /dev/null
+++ b/src/devtools/met-driver/4.14/common/mem_stat.c
@@ -0,0 +1,313 @@
+/*
+ * 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/mm.h>
+#include <linux/slab.h>
+#include <linux/sched.h>
+#include <linux/cpu.h>
+#include <linux/module.h>
+#include <linux/fs.h>
+
+#include "met_drv.h"
+#include "mem_stat.h"
+#include "trace.h"
+
+
+/* define MEMSTAT_DEBUG */
+#ifdef MEMSTAT_DEBUG
+#define debug_memstat(fmt, arg...) pr_debug(fmt, ##arg)
+#else
+#define debug_memstat(fmt, arg...) do {} while (0)
+#endif
+
+struct metdevice met_memstat;
+
+unsigned int phy_memstat_mask;
+unsigned int vir_memstat_mask;
+
+#define MAX_PHY_MEMSTAT_EVENT_AMOUNT 6
+#define MAX_VIR_MEMSTAT_EVENT_AMOUNT 6
+
+struct mem_event phy_memstat_table[] = {
+ {FREE_MEM, "free_mem", "Free Memory"}
+};
+
+#define PHY_MEMSTAT_TABLE_SIZE (sizeof(phy_memstat_table) / sizeof(struct mem_event))
+
+struct mem_event vir_memstat_table[] = {
+ {FILE_PAGES, "file_pages", "File Pages"},
+ {FILE_DIRTY, "file_dirty", "FD APP->FS(KB)"},
+ {NUM_DIRTIED, "num_dirtied", "Num Dirtied"},
+ {WRITE_BACK, "write_back", "WB. FS->Block IO(KB)"},
+ {NUM_WRITTEN, "num_written", "Num Written"},
+ {PG_FAULT_CNT, "pg_fault_cnt", "Page Fault Count"}
+};
+
+#define VIR_MEMSTAT_TABLE_SIZE (sizeof(vir_memstat_table) / sizeof(struct mem_event))
+
+int vm_event_counters_enable;
+unsigned long *vm_status;
+static struct delayed_work dwork;
+
+noinline void memstat(unsigned int cnt, unsigned int *value)
+{
+ MET_GENERAL_PRINT(MET_TRACE, cnt, value);
+}
+
+static int get_phy_memstat(unsigned int *value)
+{
+ int i, cnt = 0;
+ struct sysinfo info;
+
+#define K(x) ((x) << (PAGE_SHIFT - 10))
+
+ si_meminfo(&info);
+
+ for (i = 0; i < MAX_PHY_MEMSTAT_EVENT_AMOUNT; i++) {
+ if (phy_memstat_mask & (1 << i)) {
+ switch (i) {
+ case FREE_MEM:
+ value[cnt] = K(info.freeram);
+ break;
+ }
+
+ cnt++;
+ }
+ }
+
+ return cnt;
+}
+
+static int get_vir_memstat(unsigned int *value)
+{
+ int i, cnt = 0;
+
+ for (i = 0; i < NR_VM_ZONE_STAT_ITEMS; i++)
+ vm_status[i] = global_zone_page_state(i);
+
+ all_vm_events(vm_status + NR_VM_ZONE_STAT_ITEMS);
+
+ for (i = 0; i < MAX_VIR_MEMSTAT_EVENT_AMOUNT; i++) {
+ if (vir_memstat_mask & (1 << i)) {
+ switch (i) {
+ case FILE_PAGES:
+ value[cnt] = vm_status[NR_FILE_PAGES] << (PAGE_SHIFT - 10);
+ break;
+ case FILE_DIRTY:
+ value[cnt] = vm_status[NR_FILE_DIRTY] << (PAGE_SHIFT - 10);
+ break;
+ case NUM_DIRTIED:
+ value[cnt] = vm_status[NR_DIRTIED] << (PAGE_SHIFT - 10);
+ break;
+ case WRITE_BACK:
+ value[cnt] = vm_status[NR_WRITEBACK] << (PAGE_SHIFT - 10);
+ break;
+ case NUM_WRITTEN:
+ value[cnt] = vm_status[NR_WRITTEN] << (PAGE_SHIFT - 10);
+ break;
+ case PG_FAULT_CNT:
+ value[cnt] = vm_status[NR_VM_ZONE_STAT_ITEMS + PGFAULT];
+ break;
+ }
+
+ cnt++;
+ }
+ }
+
+ return cnt;
+}
+
+static void wq_get_memstat(struct work_struct *work)
+{
+ int total_event_amount = 0, phy_event_amount = 0;
+ unsigned int stat_val[MAX_PHY_MEMSTAT_EVENT_AMOUNT + MAX_VIR_MEMSTAT_EVENT_AMOUNT];
+
+ memset(stat_val, 0, sizeof(unsigned int) * (MAX_PHY_MEMSTAT_EVENT_AMOUNT + MAX_VIR_MEMSTAT_EVENT_AMOUNT));
+ total_event_amount = phy_event_amount = get_phy_memstat(stat_val);
+
+ if (vm_event_counters_enable)
+ total_event_amount += get_vir_memstat(&(stat_val[phy_event_amount]));
+
+ if (total_event_amount <= (MAX_PHY_MEMSTAT_EVENT_AMOUNT + MAX_VIR_MEMSTAT_EVENT_AMOUNT))
+ memstat(total_event_amount-1, stat_val);
+}
+
+void met_memstat_polling(unsigned long long stamp, int cpu)
+{
+ schedule_delayed_work(&dwork, 0);
+}
+
+static void met_memstat_start(void)
+{
+ int stat_items_size = 0;
+
+ stat_items_size = NR_VM_ZONE_STAT_ITEMS * sizeof(unsigned long);
+
+#ifdef CONFIG_VM_EVENT_COUNTERS
+ stat_items_size += sizeof(struct vm_event_state);
+#endif
+
+ vm_status = kmalloc(stat_items_size, GFP_KERNEL);
+ if (vm_status == NULL)
+ return;
+ INIT_DELAYED_WORK(&dwork, wq_get_memstat);
+}
+
+static void met_memstat_stop(void)
+{
+ kfree(vm_status);
+ cancel_delayed_work_sync(&dwork);
+}
+
+static const char help[] =
+" --memstat=[phy_mem_stat|vir_mem_stat]:event_name enable sampling physical & virtual memory status\n";
+
+static int met_memstat_print_help(char *buf, int len)
+{
+ int i, l;
+
+ l = snprintf(buf, PAGE_SIZE, help);
+
+ for (i = 0; i < PHY_MEMSTAT_TABLE_SIZE; i++)
+ l += snprintf(buf + l, PAGE_SIZE - l, " --memstat=phy_mem_stat:%s\n",
+ phy_memstat_table[i].name);
+
+#ifdef CONFIG_VM_EVENT_COUNTERS
+ for (i = 0; i < VIR_MEMSTAT_TABLE_SIZE; i++)
+ l += snprintf(buf + l, PAGE_SIZE - l, " --memstat=vir_mem_stat:%s\n",
+ vir_memstat_table[i].name);
+#endif
+
+ return l;
+}
+
+static const char header[] = "met-info [000] 0.0: ms_ud_sys_header: memstat,";
+
+
+static int met_memstat_print_header(char *buf, int len)
+{
+ int i, l;
+ int event_amount = 0;
+
+ l = snprintf(buf, PAGE_SIZE, header);
+
+ for (i = 0; i < MAX_PHY_MEMSTAT_EVENT_AMOUNT; i++) {
+ if ((phy_memstat_mask & (1 << i)) && (i < PHY_MEMSTAT_TABLE_SIZE)) {
+ l += snprintf(buf + l, PAGE_SIZE - l, phy_memstat_table[i].header_name);
+ l += snprintf(buf + l, PAGE_SIZE - l, ",");
+ event_amount++;
+ }
+ }
+
+#ifdef CONFIG_VM_EVENT_COUNTERS
+ for (i = 0; i < MAX_VIR_MEMSTAT_EVENT_AMOUNT; i++) {
+ if ((vir_memstat_mask & (1 << i)) && (i < VIR_MEMSTAT_TABLE_SIZE)) {
+ l += snprintf(buf + l, PAGE_SIZE - l, vir_memstat_table[i].header_name);
+ l += snprintf(buf + l, PAGE_SIZE - l, ",");
+ event_amount++;
+ }
+ }
+#endif
+
+ for (i = 0; i < event_amount; i++) {
+ l += snprintf(buf + l, PAGE_SIZE - l, "x");
+ l += snprintf(buf + l, PAGE_SIZE - l, ",");
+ }
+
+ phy_memstat_mask = 0;
+ vir_memstat_mask = 0;
+
+ l += snprintf(buf + l, PAGE_SIZE - l, "\n");
+
+ return l;
+}
+
+static int met_memstat_process_argument(const char *arg, int len)
+{
+ int i, found_event = 0;
+ char choice[16], event[32];
+ char *pch;
+ int str_len;
+
+
+#ifdef CONFIG_VM_EVENT_COUNTERS
+ vm_event_counters_enable = 1;
+#endif
+
+ pch = strchr(arg, ':');
+ if (pch == NULL)
+ goto error;
+
+ memset(choice, 0, sizeof(choice));
+ memset(event, 0, sizeof(event));
+
+ str_len = (int)(pch - arg);
+ memcpy(choice, arg, str_len);
+ memcpy(event, arg + str_len + 1, len - (str_len + 1));
+
+ if (strncmp(choice, "phy_mem_stat", 12) == 0) {
+ for (i = 0; i < PHY_MEMSTAT_TABLE_SIZE; i++) {
+ if (strncmp(event, phy_memstat_table[i].name, MAX_EVENT_NAME_LEN) == 0) {
+ phy_memstat_mask |= (1 << phy_memstat_table[i].id);
+ found_event = 1;
+
+ break;
+ }
+ }
+ } else if (strncmp(choice, "vir_mem_stat", 12) == 0) {
+ if (!vm_event_counters_enable) {
+ pr_debug("[%s] %d: CONFIG_VM_EVENT_COUNTERS is not configured\n", __func__,
+ __LINE__);
+ goto error;
+ }
+
+ for (i = 0; i < VIR_MEMSTAT_TABLE_SIZE; i++) {
+ if (strncmp(event, vir_memstat_table[i].name, MAX_EVENT_NAME_LEN) == 0) {
+ vir_memstat_mask |= (1 << vir_memstat_table[i].id);
+ found_event = 1;
+
+ break;
+ }
+ }
+ } else {
+ pr_debug("[%s] %d: only support phy_mem_stat & vir_mem_stat keyword\n", __func__,
+ __LINE__);
+ goto error;
+ }
+
+ if (!found_event) {
+ pr_debug("[%s] %d: input event name error\n", __func__, __LINE__);
+ goto error;
+ }
+
+ met_memstat.mode = 1;
+ return 0;
+
+error:
+ met_memstat.mode = 0;
+ return -EINVAL;
+}
+
+struct metdevice met_memstat = {
+ .name = "memstat",
+ .type = MET_TYPE_PMU,
+ .cpu_related = 0,
+ .start = met_memstat_start,
+ .stop = met_memstat_stop,
+ .polling_interval = 1,
+ .timed_polling = met_memstat_polling,
+ .tagged_polling = met_memstat_polling,
+ .print_help = met_memstat_print_help,
+ .print_header = met_memstat_print_header,
+ .process_argument = met_memstat_process_argument
+};
diff --git a/src/devtools/met-driver/4.14/common/mem_stat.h b/src/devtools/met-driver/4.14/common/mem_stat.h
new file mode 100644
index 0000000..d716075
--- /dev/null
+++ b/src/devtools/met-driver/4.14/common/mem_stat.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 __MEM_STAT_H__
+#define __MEM_STAT_H__
+
+#define MAX_EVENT_NAME_LEN 32
+
+enum phy_mem_event_id {
+ FREE_MEM = 0
+};
+
+enum vir_mem_event_id {
+ FILE_PAGES = 0,
+ FILE_DIRTY,
+ NUM_DIRTIED,
+ WRITE_BACK,
+ NUM_WRITTEN,
+ PG_FAULT_CNT
+};
+
+struct mem_event {
+ int id;
+ char name[32];
+ char header_name[32];
+};
+
+#endif
diff --git a/src/devtools/met-driver/4.14/common/met_api_tbl.h b/src/devtools/met-driver/4.14/common/met_api_tbl.h
new file mode 100644
index 0000000..1119ac4
--- /dev/null
+++ b/src/devtools/met-driver/4.14/common/met_api_tbl.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.
+ */
+
+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);
+ void (*met_pm_qos_update_request)(int pm_qos_class, s32 value, char *owner);
+ void (*met_pm_qos_update_target)(unsigned int action, int prev_value, int curr_value);
+ 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.14/common/met_backlight.c b/src/devtools/met-driver/4.14/common/met_backlight.c
new file mode 100644
index 0000000..ffaa08d
--- /dev/null
+++ b/src/devtools/met-driver/4.14/common/met_backlight.c
@@ -0,0 +1,109 @@
+/*
+ * 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>
+
+#define MET_USER_EVENT_SUPPORT
+#include "met_drv.h"
+#include "trace.h"
+
+static int met_backlight_enable;
+static DEFINE_SPINLOCK(met_backlight_lock);
+static struct kobject *kobj_met_backlight;
+
+static ssize_t bl_tag_enable_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf);
+static ssize_t bl_tag_enable_store(struct kobject *kobj,
+ struct kobj_attribute *attr, const char *buf, size_t n);
+static struct kobj_attribute bl_tag_enable_attr =
+__ATTR(backlight_tag_enable, 0664, bl_tag_enable_show, bl_tag_enable_store);
+
+int enable_met_backlight_tag_real(void)
+{
+ return met_backlight_enable;
+}
+
+int output_met_backlight_tag_real(int level)
+{
+ int ret;
+ unsigned long flags;
+
+ spin_lock_irqsave(&met_backlight_lock, flags);
+#ifdef CONFIG_MET_MODULE
+ ret = met_tag_oneshot_real(33880, "_MM_BL_", level);
+#else
+ ret = met_tag_oneshot(33880, "_MM_BL_", level);
+#endif
+ spin_unlock_irqrestore(&met_backlight_lock, flags);
+
+ return ret;
+}
+
+static ssize_t bl_tag_enable_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
+{
+ int ret;
+
+ ret = snprintf(buf, PAGE_SIZE, "%d\n", met_backlight_enable);
+
+ return ret;
+}
+
+static ssize_t bl_tag_enable_store(struct kobject *kobj,
+ struct kobj_attribute *attr, const char *buf, size_t n)
+{
+ int value;
+
+ if ((n == 0) || (buf == NULL))
+ return -EINVAL;
+
+ if (kstrtoint(buf, 0, &value) != 0)
+ return -EINVAL;
+
+ if (value < 0)
+ return -EINVAL;
+
+ met_backlight_enable = value;
+
+ return n;
+}
+
+static int met_backlight_create(struct kobject *parent)
+{
+ int ret = 0;
+
+ kobj_met_backlight = parent;
+
+ ret = sysfs_create_file(kobj_met_backlight, &bl_tag_enable_attr.attr);
+ if (ret != 0) {
+ pr_debug("Failed to create montype0 in sysfs\n");
+ return ret;
+ }
+
+ return ret;
+}
+
+static void met_backlight_delete(void)
+{
+ sysfs_remove_file(kobj_met_backlight, &bl_tag_enable_attr.attr);
+ kobj_met_backlight = NULL;
+}
+
+struct metdevice met_backlight = {
+ .name = "backlight",
+ .owner = THIS_MODULE,
+ .type = MET_TYPE_BUS,
+ .create_subfs = met_backlight_create,
+ .delete_subfs = met_backlight_delete,
+ .cpu_related = 0,
+};
+EXPORT_SYMBOL(met_backlight);
diff --git a/src/devtools/met-driver/4.14/common/met_drv.h b/src/devtools/met-driver/4.14/common/met_drv.h
new file mode 100644
index 0000000..bfe6490
--- /dev/null
+++ b/src/devtools/met-driver/4.14/common/met_drv.h
@@ -0,0 +1,276 @@
+/*
+ * 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 (*uniq_start)(void);
+ void (*stop)(void);
+ void (*uniq_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 (*cpu_state_notify)(long cpu, unsigned long action);
+
+ 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_real(void);
+extern int output_met_backlight_tag_real(int level);
+
+#endif /* MET_DRV */
diff --git a/src/devtools/met-driver/4.14/common/met_emi.c b/src/devtools/met-driver/4.14/common/met_emi.c
new file mode 100644
index 0000000..9770cc9
--- /dev/null
+++ b/src/devtools/met-driver/4.14/common/met_emi.c
@@ -0,0 +1,950 @@
+/*
+ * 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/sched.h>
+#include <linux/device.h>
+#include <linux/miscdevice.h>
+#include <linux/module.h>
+#include <linux/string.h>
+
+#ifdef CONFIG_ARM
+//#include <asm/dma-mapping.h> /* arm_coherent_dma_ops */
+#endif /* CONFIG_ARM */
+
+#include <linux/dma-mapping.h>
+
+#define MET_USER_EVENT_SUPPORT
+#include "met_drv.h"
+#include "trace.h"
+
+#include "mtk_typedefs.h"
+#include "core_plf_init.h"
+/* #include "plf_trace.h" */
+#include "mtk_emi_bm.h"
+#include "interface.h"
+
+#if defined(CONFIG_MTK_TINYSYS_SSPM_SUPPORT) && defined(ONDIEMET_SUPPORT)
+#include "sspm/ondiemet_sspm.h"
+#endif
+
+/*======================================================================*/
+/* Global variable definitions */
+/*======================================================================*/
+int emi_TP_busfiltr_enable;
+static int msel_enable;
+static unsigned int msel_group1 = _GP_1_Default;
+static unsigned int msel_group2 = _GP_2_Default;
+static unsigned int msel_group3 = _GP_3_Default;
+
+/* Global variables */
+static struct kobject *kobj_emi;
+
+static int ttype1_16_en = BM_TTYPE1_16_DISABLE;
+static int ttype17_21_en = BM_TTYPE17_21_DISABLE;
+
+
+
+static int times;
+static ssize_t test_store(struct kobject *kobj,
+ struct kobj_attribute *attr,
+ const char *buf,
+ size_t n)
+{
+ int i;
+ unsigned int *src_addr_v = NULL;
+ dma_addr_t src_addr_p;
+#ifdef CONFIG_ARM
+ struct dma_map_ops *ops = (struct dma_map_ops *)symbol_get(arm_dma_ops);
+#endif /* CONFIG_ARM */
+
+ if ((n == 0) || (buf == NULL))
+ return -EINVAL;
+ if (kstrtoint(buf, 10, ×) != 0)
+ return -EINVAL;
+ if (times < 0)
+ return -EINVAL;
+
+ if (times > 5000)
+ return -EINVAL;
+
+#ifdef CONFIG_ARM
+ if (ops && ops->alloc) {
+ (met_device.this_device)->coherent_dma_mask = DMA_BIT_MASK(32);
+ src_addr_v = ops->alloc(met_device.this_device,
+ PAGE_SIZE,
+ &src_addr_p,
+ GFP_KERNEL,
+ 0);
+ }
+#endif /* CONFIG_ARM */
+
+#ifdef CONFIG_ARM64
+ /* dma_alloc */
+ src_addr_v = dma_alloc_coherent(met_device.this_device,
+ PAGE_SIZE,
+ &src_addr_p,
+ GFP_KERNEL);
+#endif /* CONFIG_ARM64 */
+
+ if (src_addr_v == NULL) {
+#ifdef CONFIG_MET_MODULE
+ met_tag_oneshot_real(0, "test dma alloc fail", PAGE_SIZE);
+#else
+ met_tag_oneshot(0, "test dma alloc fail", PAGE_SIZE);
+#endif
+ return -ENOMEM;
+ }
+
+ preempt_disable();
+#ifdef CONFIG_MET_MODULE
+ met_tag_start_real(0, "TEST_EMI");
+#else
+ met_tag_start(0, "TEST_EMI");
+#endif
+ for (i = 0; i < times; i++) {
+ memset(src_addr_v, 2*i, PAGE_SIZE);
+#ifdef CONFIG_MET_MODULE
+ met_tag_oneshot_real(0, "TEST_EMI", PAGE_SIZE);
+#else
+ met_tag_oneshot(0, "TEST_EMI", PAGE_SIZE);
+#endif
+ }
+#ifdef CONFIG_MET_MODULE
+ met_tag_end_real(0, "TEST_EMI");
+#else
+ met_tag_end(0, "TEST_EMI");
+#endif
+
+ my_preempt_enable();
+
+#ifdef CONFIG_ARM
+ /* dma_free */
+ if (ops && ops->free) {
+ ops->free(met_device.this_device,
+ PAGE_SIZE,
+ src_addr_v,
+ src_addr_p,
+ 0);
+ }
+#endif /* CONFIG_ARM */
+
+#ifdef CONFIG_ARM64
+ /* dma_free */
+ if (src_addr_v != NULL)
+ dma_free_coherent(met_device.this_device,
+ PAGE_SIZE,
+ src_addr_v,
+ src_addr_p);
+#endif /* CONFIG_ARM64 */
+
+ return n;
+}
+
+/*======================================================================*/
+/* KOBJ Declarations */
+/*======================================================================*/
+DECLARE_KOBJ_ATTR_INT(emi_TP_busfiltr_enable, emi_TP_busfiltr_enable);
+DECLARE_KOBJ_ATTR_INT(msel_enable, msel_enable);
+DECLARE_KOBJ_ATTR_HEX_CHECK(msel_group1, msel_group1, msel_group1 > 0 && msel_group1 <= _ALL);
+DECLARE_KOBJ_ATTR_HEX_CHECK(msel_group2, msel_group2, msel_group2 > 0 && msel_group2 <= _ALL);
+DECLARE_KOBJ_ATTR_HEX_CHECK(msel_group3, msel_group3, msel_group3 > 0 && msel_group3 <= _ALL);
+
+
+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);
+
+
+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);
+
+DECLARE_KOBJ_ATTR_STR_LIST_ITEM(
+ ttype_master,
+ KOBJ_ITEM_LIST(
+ { _M0, "M0" },
+ { _M1, "M1" },
+ { _M2, "M2" },
+ { _M3, "M3" },
+ { _M4, "M4" },
+ { _M5, "M5" },
+ { _M6, "M6" },
+ { _M7, "M7" }
+ )
+ );
+
+
+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" }
+ )
+ );
+
+
+DECLARE_KOBJ_ATTR_SHOW_INT(test, times);
+
+DECLARE_KOBJ_ATTR(test);
+
+
+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_TTYPE_MASTER(nr) \
+ DECLARE_KOBJ_ATTR_STR_LIST(ttype ## nr ## _master, ttype_master_val[nr - 1], ttype_master)
+
+#define DECLARE_KOBJ_TTYPE_NBEAT(nr) \
+ DECLARE_KOBJ_ATTR_INT_LIST(ttype ## nr ## _nbeat, ttype_nbeat_val[nr - 1], ttype_nbeat)
+
+#define DECLARE_KOBJ_TTYPE_NBYTE(nr) \
+ DECLARE_KOBJ_ATTR_INT_LIST(ttype ## nr ## _nbyte, ttype_nbyte_val[nr - 1], ttype_nbyte)
+
+#define DECLARE_KOBJ_TTYPE_BURST(nr) \
+ DECLARE_KOBJ_ATTR_STR_LIST(ttype ## nr ## _burst, ttype_burst_val[nr - 1], ttype_burst)
+
+#define DECLARE_KOBJ_TTYPE_RW(nr) \
+ DECLARE_KOBJ_ATTR_STR_LIST(ttype ## nr ## _rw, ttype_rw_val[nr - 1], ttype_rw)
+
+#define DECLARE_KOBJ_TTYPE_BUSID_VAL(nr) \
+ DECLARE_KOBJ_ATTR_HEX(ttype ## nr ## _busid, ttype_busid_val[nr - 1])
+
+DECLARE_KOBJ_TTYPE_MASTER(1);
+DECLARE_KOBJ_TTYPE_NBEAT(1);
+DECLARE_KOBJ_TTYPE_NBYTE(1);
+DECLARE_KOBJ_TTYPE_BURST(1);
+DECLARE_KOBJ_TTYPE_RW(1);
+DECLARE_KOBJ_TTYPE_BUSID_VAL(1);
+
+DECLARE_KOBJ_TTYPE_MASTER(2);
+DECLARE_KOBJ_TTYPE_NBEAT(2);
+DECLARE_KOBJ_TTYPE_NBYTE(2);
+DECLARE_KOBJ_TTYPE_BURST(2);
+DECLARE_KOBJ_TTYPE_RW(2);
+DECLARE_KOBJ_TTYPE_BUSID_VAL(2);
+
+DECLARE_KOBJ_TTYPE_MASTER(3);
+DECLARE_KOBJ_TTYPE_NBEAT(3);
+DECLARE_KOBJ_TTYPE_NBYTE(3);
+DECLARE_KOBJ_TTYPE_BURST(3);
+DECLARE_KOBJ_TTYPE_RW(3);
+DECLARE_KOBJ_TTYPE_BUSID_VAL(3);
+
+DECLARE_KOBJ_TTYPE_MASTER(4);
+DECLARE_KOBJ_TTYPE_NBEAT(4);
+DECLARE_KOBJ_TTYPE_NBYTE(4);
+DECLARE_KOBJ_TTYPE_BURST(4);
+DECLARE_KOBJ_TTYPE_RW(4);
+DECLARE_KOBJ_TTYPE_BUSID_VAL(4);
+
+DECLARE_KOBJ_TTYPE_MASTER(5);
+DECLARE_KOBJ_TTYPE_NBEAT(5);
+DECLARE_KOBJ_TTYPE_NBYTE(5);
+DECLARE_KOBJ_TTYPE_BURST(5);
+DECLARE_KOBJ_TTYPE_RW(5);
+DECLARE_KOBJ_TTYPE_BUSID_VAL(5);
+
+DECLARE_KOBJ_TTYPE_MASTER(6);
+DECLARE_KOBJ_TTYPE_NBEAT(6);
+DECLARE_KOBJ_TTYPE_NBYTE(6);
+DECLARE_KOBJ_TTYPE_BURST(6);
+DECLARE_KOBJ_TTYPE_RW(6);
+DECLARE_KOBJ_TTYPE_BUSID_VAL(6);
+
+DECLARE_KOBJ_TTYPE_MASTER(7);
+DECLARE_KOBJ_TTYPE_NBEAT(7);
+DECLARE_KOBJ_TTYPE_NBYTE(7);
+DECLARE_KOBJ_TTYPE_BURST(7);
+DECLARE_KOBJ_TTYPE_RW(7);
+DECLARE_KOBJ_TTYPE_BUSID_VAL(7);
+
+DECLARE_KOBJ_TTYPE_MASTER(8);
+DECLARE_KOBJ_TTYPE_NBEAT(8);
+DECLARE_KOBJ_TTYPE_NBYTE(8);
+DECLARE_KOBJ_TTYPE_BURST(8);
+DECLARE_KOBJ_TTYPE_RW(8);
+DECLARE_KOBJ_TTYPE_BUSID_VAL(8);
+
+DECLARE_KOBJ_TTYPE_MASTER(9);
+DECLARE_KOBJ_TTYPE_NBEAT(9);
+DECLARE_KOBJ_TTYPE_NBYTE(9);
+DECLARE_KOBJ_TTYPE_BURST(9);
+DECLARE_KOBJ_TTYPE_RW(9);
+DECLARE_KOBJ_TTYPE_BUSID_VAL(9);
+
+DECLARE_KOBJ_TTYPE_MASTER(10);
+DECLARE_KOBJ_TTYPE_NBEAT(10);
+DECLARE_KOBJ_TTYPE_NBYTE(10);
+DECLARE_KOBJ_TTYPE_BURST(10);
+DECLARE_KOBJ_TTYPE_RW(10);
+DECLARE_KOBJ_TTYPE_BUSID_VAL(10);
+
+DECLARE_KOBJ_TTYPE_MASTER(11);
+DECLARE_KOBJ_TTYPE_NBEAT(11);
+DECLARE_KOBJ_TTYPE_NBYTE(11);
+DECLARE_KOBJ_TTYPE_BURST(11);
+DECLARE_KOBJ_TTYPE_RW(11);
+DECLARE_KOBJ_TTYPE_BUSID_VAL(11);
+
+DECLARE_KOBJ_TTYPE_MASTER(12);
+DECLARE_KOBJ_TTYPE_NBEAT(12);
+DECLARE_KOBJ_TTYPE_NBYTE(12);
+DECLARE_KOBJ_TTYPE_BURST(12);
+DECLARE_KOBJ_TTYPE_RW(12);
+DECLARE_KOBJ_TTYPE_BUSID_VAL(12);
+
+DECLARE_KOBJ_TTYPE_MASTER(13);
+DECLARE_KOBJ_TTYPE_NBEAT(13);
+DECLARE_KOBJ_TTYPE_NBYTE(13);
+DECLARE_KOBJ_TTYPE_BURST(13);
+DECLARE_KOBJ_TTYPE_RW(13);
+DECLARE_KOBJ_TTYPE_BUSID_VAL(13);
+
+DECLARE_KOBJ_TTYPE_MASTER(14);
+DECLARE_KOBJ_TTYPE_NBEAT(14);
+DECLARE_KOBJ_TTYPE_NBYTE(14);
+DECLARE_KOBJ_TTYPE_BURST(14);
+DECLARE_KOBJ_TTYPE_RW(14);
+DECLARE_KOBJ_TTYPE_BUSID_VAL(14);
+
+DECLARE_KOBJ_TTYPE_MASTER(15);
+DECLARE_KOBJ_TTYPE_NBEAT(15);
+DECLARE_KOBJ_TTYPE_NBYTE(15);
+DECLARE_KOBJ_TTYPE_BURST(15);
+DECLARE_KOBJ_TTYPE_RW(15);
+DECLARE_KOBJ_TTYPE_BUSID_VAL(15);
+
+DECLARE_KOBJ_TTYPE_MASTER(16);
+DECLARE_KOBJ_TTYPE_NBEAT(16);
+DECLARE_KOBJ_TTYPE_NBYTE(16);
+DECLARE_KOBJ_TTYPE_BURST(16);
+DECLARE_KOBJ_TTYPE_RW(16);
+DECLARE_KOBJ_TTYPE_BUSID_VAL(16);
+
+DECLARE_KOBJ_TTYPE_MASTER(17);
+DECLARE_KOBJ_TTYPE_NBEAT(17);
+DECLARE_KOBJ_TTYPE_NBYTE(17);
+DECLARE_KOBJ_TTYPE_BURST(17);
+DECLARE_KOBJ_TTYPE_RW(17);
+DECLARE_KOBJ_TTYPE_BUSID_VAL(17);
+
+DECLARE_KOBJ_TTYPE_MASTER(18);
+DECLARE_KOBJ_TTYPE_NBEAT(18);
+DECLARE_KOBJ_TTYPE_NBYTE(18);
+DECLARE_KOBJ_TTYPE_BURST(18);
+DECLARE_KOBJ_TTYPE_RW(18);
+DECLARE_KOBJ_TTYPE_BUSID_VAL(18);
+
+DECLARE_KOBJ_TTYPE_MASTER(19);
+DECLARE_KOBJ_TTYPE_NBEAT(19);
+DECLARE_KOBJ_TTYPE_NBYTE(19);
+DECLARE_KOBJ_TTYPE_BURST(19);
+DECLARE_KOBJ_TTYPE_RW(19);
+DECLARE_KOBJ_TTYPE_BUSID_VAL(19);
+
+DECLARE_KOBJ_TTYPE_MASTER(20);
+DECLARE_KOBJ_TTYPE_NBEAT(20);
+DECLARE_KOBJ_TTYPE_NBYTE(20);
+DECLARE_KOBJ_TTYPE_BURST(20);
+DECLARE_KOBJ_TTYPE_RW(20);
+DECLARE_KOBJ_TTYPE_BUSID_VAL(20);
+
+DECLARE_KOBJ_TTYPE_MASTER(21);
+DECLARE_KOBJ_TTYPE_NBEAT(21);
+DECLARE_KOBJ_TTYPE_NBYTE(21);
+DECLARE_KOBJ_TTYPE_BURST(21);
+DECLARE_KOBJ_TTYPE_RW(21);
+DECLARE_KOBJ_TTYPE_BUSID_VAL(21);
+
+#define KOBJ_ATTR_ITEM_SERIAL_FNODE(nr) \
+ do { \
+ 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); \
+ } while (0)
+
+#define KOBJ_ATTR_LIST \
+ do { \
+ KOBJ_ATTR_ITEM(high_priority_filter); \
+ KOBJ_ATTR_ITEM(emi_TP_busfiltr_enable); \
+ KOBJ_ATTR_ITEM(msel_enable); \
+ KOBJ_ATTR_ITEM(msel_group1); \
+ KOBJ_ATTR_ITEM(msel_group2); \
+ KOBJ_ATTR_ITEM(msel_group3); \
+ 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); \
+ } while (0)
+
+/*======================================================================*/
+/* EMI Operations */
+/*======================================================================*/
+static void emi_init(void)
+{
+ unsigned int bmrw0_val = 0, bmrw1_val = 0, i, enable;
+ unsigned int msel_group_val[4];
+
+ MET_BM_SaveCfg();
+
+ if ((ttype1_16_en != BM_TTYPE1_16_ENABLE) && (emi_TP_busfiltr_enable != 1)) {
+ if (msel_enable) {
+ msel_group_val[0] = _ALL;
+ msel_group_val[1] = msel_group1;
+ msel_group_val[2] = msel_group2;
+ msel_group_val[3] = msel_group3;
+ } else {
+ msel_group_val[0] = _ALL;
+ msel_group_val[1] = _ALL;
+ msel_group_val[2] = _ALL;
+ msel_group_val[3] = _ALL;
+ }
+
+ MET_BM_SetLatencyCounter(1);
+
+ for (i = 1; i <= 4; i++) {
+ MET_BM_SetMonitorCounter(i,
+ msel_group_val[i - 1] & _ALL,
+ BM_TRANS_TYPE_4BEAT |
+ BM_TRANS_TYPE_8Byte |
+ BM_TRANS_TYPE_BURST_WRAP);
+ MET_BM_SetbusID(i, 0);
+ MET_BM_SetbusID_En(i, 0);
+ }
+ for (i = 0; i < 4; i++)
+ MET_BM_Set_WsctTsct_id_sel(i, 0);
+
+ } else if ((ttype1_16_en != BM_TTYPE1_16_ENABLE) && (emi_TP_busfiltr_enable == 1)) {
+ MET_BM_SetLatencyCounter(1);
+
+ for (i = 1; i <= 4; 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_SetbusID(i, ttype_busid_val[i - 1]);
+ MET_BM_SetbusID_En(i, 0);
+ }
+ for (i = 0; i < 4; i++)
+ MET_BM_Set_WsctTsct_id_sel(i, 1);
+
+ } else if ((ttype1_16_en == BM_TTYPE1_16_ENABLE) && (emi_TP_busfiltr_enable != 1)) {
+ 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_SetbusID(i, ttype_busid_val[i - 1]);
+
+ MET_BM_SetbusID_En(i, (ttype_busid_val[i - 1] > 0xffff) ? 0 : 1);
+ }
+ for (i = 0; i < 4; i++)
+ MET_BM_Set_WsctTsct_id_sel(i, 0);
+ } else { /* (ttype1_16_en == BM_TTYPE1_16_ENABLE) && (emi_TP_busfiltr_enable == 1) */
+
+ 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_SetbusID(i, ttype_busid_val[i - 1]);
+
+ MET_BM_SetbusID_En(i, (ttype_busid_val[i - 1] > 0xffff) ? 0 : 1);
+ }
+ for (i = 0; i < 4; i++)
+ MET_BM_Set_WsctTsct_id_sel(i, 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_SetbusID(i, ttype_busid_val[i - 1]);
+
+ MET_BM_SetbusID_En(i, (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 < BM_COUNTER_MAX; i++) {
+ if ((high_priority_filter & (1 << i)) == 0)
+ enable = 0;
+ else
+ enable = 1;
+
+ MET_BM_SetUltraHighFilter(i + 1, enable);
+ }
+
+}
+
+static inline int do_emi(void)
+{
+ return met_sspm_emi.mode;
+}
+
+/*======================================================================*/
+/* 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] = _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] = 0xfffff;
+ ttype_rw_val[i] = BM_TRANS_RW_DEFAULT;
+ }
+ ret = MET_BM_Init();
+ if (ret != 0) {
+ pr_debug("MET_BM_Init failed!!!\n");
+ ret = 0;
+ } else
+ emi_inited = 1;
+
+ kobj_emi = parent;
+
+#define KOBJ_ATTR_ITEM(attr_name) \
+do { \
+ ret = sysfs_create_file(kobj_emi, &attr_name##_attr.attr); \
+ if (ret != 0) { \
+ pr_debug("Failed to create " #attr_name " in sysfs\n"); \
+ return ret; \
+ } \
+} while (0)
+ 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_resume(void)
+{
+ if (!do_emi())
+ return;
+
+ emi_init();
+}
+
+
+#if defined(CONFIG_MTK_TINYSYS_SSPM_SUPPORT) && defined(ONDIEMET_SUPPORT)
+static const char help[] = " --emi monitor EMI banwidth\n";
+
+#define TTYPE_NAME_STR_LEN 64
+/* static char ttype_name[21][TTYPE_NAME_STR_LEN]; */
+
+static int emi_print_help(char *buf, int len)
+{
+ return snprintf(buf, PAGE_SIZE, help);
+}
+
+static int emi_print_header(char *buf, int len)
+{
+ int ret = 0;
+/* int ret_m[21]; */
+ int i = 0;
+
+#if 0
+ for (i = 0; i < 21; i++) {
+ int k;
+
+ 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, "ttype%d_%s",
+ i + 1, ttype_master_list_item[j].val);
+ break;
+ }
+ }
+ if (j == ARRAY_SIZE(ttype_master_list_item))
+ ret_m[i] = snprintf(ttype_name[i], TTYPE_NAME_STR_LEN, "ttype%d_%s",
+ i + 1, "unknown");
+ } else {
+ ret_m[i] = snprintf(ttype_name[i], TTYPE_NAME_STR_LEN, "ttype%d_%x",
+ i + 1, ttype_busid_val[i]);
+ }
+
+ 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);
+ }
+
+ 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);
+ }
+
+ 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);
+ }
+
+ 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);
+ }
+ }
+#endif
+
+ if ((ttype1_16_en != BM_TTYPE1_16_ENABLE) && (emi_TP_busfiltr_enable != 1)) {
+ if (msel_enable) {
+ ret += snprintf(buf + ret, PAGE_SIZE - ret,
+ "met-info [000] 0.0: met_emi_msel: %x,%x,%x\n",
+ msel_group1 & _ALL,
+ msel_group2 & _ALL,
+ msel_group3 & _ALL);
+ } else {
+ ret += snprintf(buf + ret, PAGE_SIZE - ret,
+ "met-info [000] 0.0: met_emi_msel: %x,%x,%x\n",
+ _ALL & _ALL,
+ _ALL & _ALL,
+ _ALL & _ALL);
+ }
+ } else {
+ ret += snprintf(buf + ret, PAGE_SIZE - ret,
+ "met-info [000] 0.0: met_emi_ttype_master: %x,%x,%x,%x\n",
+ ttype_master_val[0], ttype_master_val[1], ttype_master_val[2], ttype_master_val[3]);
+
+ if (emi_TP_busfiltr_enable == 1) {
+
+ ret += snprintf(buf + ret, PAGE_SIZE - ret,
+ "met-info [000] 0.0: met_emi_ttype_busid: %x,%x,%x,%x\n",
+ ttype_busid_val[0], ttype_busid_val[1], ttype_busid_val[2], ttype_busid_val[3]);
+ }
+ }
+
+ ret += snprintf(buf + ret, PAGE_SIZE - ret, "met-info [000] 0.0: met_emi_rw_cfg: ");
+ ret += snprintf(buf + ret, PAGE_SIZE - ret, "BOTH");
+
+ for (i = 0; i < 21; i++) {
+ if (ttype_rw_val[i] == BM_TRANS_RW_DEFAULT)
+ ret += snprintf(buf + ret, PAGE_SIZE - ret, ",DEFAULT");
+ else if (ttype_rw_val[i] == BM_TRANS_RW_READONLY)
+ ret += snprintf(buf + ret, PAGE_SIZE - ret, ",R");
+ else if (ttype_rw_val[i] == BM_TRANS_RW_WRITEONLY)
+ ret += snprintf(buf + ret, PAGE_SIZE - ret, ",W");
+ else
+ ret += snprintf(buf + ret, PAGE_SIZE - ret, ",BOTH");
+ }
+ ret += snprintf(buf + ret, PAGE_SIZE - ret, "\n");
+
+ ret += snprintf(buf + ret, PAGE_SIZE - ret,
+ "met-info [000] 0.0: met_emi_ultra_filter: %x\n", high_priority_filter);
+#if 0
+ if ((ttype1_16_en == BM_TTYPE1_16_ENABLE) && (ttype17_21_en == BM_TTYPE17_21_ENABLE)) {
+ 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) {
+ 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");
+ }
+#else
+ /* ttype header */
+ if (ttype17_21_en == BM_TTYPE17_21_ENABLE) {
+ int i = 0;
+ int j = 0;
+
+ /* ttype master list */
+ ret += snprintf(buf + ret, PAGE_SIZE - ret, "met-info [000] 0.0: met_emi_ttype_master_list: ");
+ for (i = 0; i < 21; i++) {
+ for (j = 0; j < ARRAY_SIZE(ttype_master_list_item); j++) {
+ if (ttype_master_val[i] == ttype_master_list_item[j].key) {
+ ret += snprintf(buf + ret, PAGE_SIZE - ret, "%s,", ttype_master_list_item[j].val);
+ }
+ }
+ }
+ /* remove the last comma */
+ snprintf(buf + ret -1, PAGE_SIZE - ret + 1, "\n");
+
+ /* ttype busid list */
+ ret += snprintf(buf + ret, PAGE_SIZE - ret, "met-info [000] 0.0: met_emi_ttype_busid_list: ");
+ for (i = 0; i < 21; i++)
+ ret += snprintf(buf + ret, PAGE_SIZE - ret, "%x,", ttype_busid_val[i]);
+
+ snprintf(buf + ret -1, PAGE_SIZE - ret + 1, "\n");
+
+ /* ttype nbeat list */
+ ret += snprintf(buf + ret, PAGE_SIZE - ret, "met-info [000] 0.0: met_emi_ttype_nbeat_list: ");
+ for (i = 0; i < 21; i++) {
+ for (j = 0; j < ARRAY_SIZE(ttype_nbeat_list_item); j++) {
+ if (ttype_nbeat_val[i] == ttype_nbeat_list_item[j].key) {
+ ret += snprintf(buf + ret, PAGE_SIZE - ret, "%d,", ttype_nbeat_list_item[j].val);
+ }
+ }
+ }
+ snprintf(buf + ret -1, PAGE_SIZE - ret + 1, "\n");
+
+ /* ttype nbyte list */
+ ret += snprintf(buf + ret, PAGE_SIZE - ret, "met-info [000] 0.0: met_emi_ttype_nbyte_list: ");
+ for (i = 0; i < 21; i++) {
+ for (j = 0; j < ARRAY_SIZE(ttype_nbyte_list_item); j++) {
+ if (ttype_nbyte_val[i] == ttype_nbyte_list_item[j].key) {
+ ret += snprintf(buf + ret, PAGE_SIZE - ret, "%d,", ttype_nbyte_list_item[j].val);
+ }
+ }
+ }
+ snprintf(buf + ret -1, PAGE_SIZE - ret + 1, "\n");
+
+ /* ttype burst list */
+ ret += snprintf(buf + ret, PAGE_SIZE - ret, "met-info [000] 0.0: met_emi_ttype_burst_list: ");
+ for (i = 0; i < 21; i++) {
+ for (j = 0; j < ARRAY_SIZE(ttype_burst_list_item); j++) {
+ if (ttype_burst_val[i] == ttype_burst_list_item[j].key) {
+ ret += snprintf(buf + ret, PAGE_SIZE - ret, "%s,", ttype_burst_list_item[j].val);
+ }
+ }
+ }
+ snprintf(buf + ret -1, PAGE_SIZE - ret + 1, "\n");
+
+ /* ttype enable */
+ ret += snprintf(buf + ret, PAGE_SIZE - ret, "met-info [000] 0.0: met_emi_ttype_enable: %d,%d\n",ttype1_16_en, ttype17_21_en);
+ }
+#endif
+
+ return ret;
+}
+
+static int ondiemet_emi_print_header(char *buf, int len)
+{
+ return emi_print_header(buf, len);
+}
+
+static void MET_BM_IPI_REGISTER_CB(void)
+{
+ int ret, i;
+ unsigned int rdata;
+ unsigned int ipi_buf[4];
+
+ for (i = 0; i < 4; i++)
+ ipi_buf[i] = 0;
+
+ if (sspm_buf_available == 1) {
+ ipi_buf[0] = MET_MAIN_ID | (MID_EMI << MID_BIT_SHIFT) | MET_ARGU | SET_REGISTER_CB;
+ ret = sspm_ipi_send_sync(IPI_ID_MET, IPI_OPT_WAIT, (void *)ipi_buf, 0, &rdata, 1);
+ }
+}
+
+static void MET_BM_IPI_configs(void)
+{
+ int ret, i;
+ unsigned int rdata;
+ unsigned int ipi_buf[4];
+
+ for (i = 0; i < 4; i++)
+ ipi_buf[i] = 0;
+
+ if (sspm_buf_available == 1) {
+ ipi_buf[0] = MET_MAIN_ID | (MID_EMI << MID_BIT_SHIFT) | MET_ARGU | SET_EBM_CONFIGS1;
+ ret = sspm_ipi_send_sync(IPI_ID_MET, IPI_OPT_WAIT, (void *)ipi_buf, 0, &rdata, 1);
+ }
+}
+
+static void ondiemet_emi_start(void)
+{
+ MET_BM_IPI_REGISTER_CB();
+ if (!emi_inited) {
+ if (MET_BM_Init() != 0) {
+ met_sspm_emi.mode = 0;
+ pr_notice("MET_BM_Init failed!!!\n");
+ return;
+ }
+ emi_inited = 1;
+ }
+ MET_BM_IPI_configs();
+
+ if (do_emi())
+ emi_init();
+
+ ondiemet_module[ONDIEMET_SSPM] |= ID_EMI;
+}
+
+static void emi_uninit(void)
+{
+ MET_BM_RestoreCfg();
+}
+
+static void ondiemet_emi_stop(void)
+{
+ if (!emi_inited)
+ return;
+
+ if (do_emi())
+ emi_uninit();
+}
+#endif
+
+struct metdevice met_sspm_emi = {
+ .name = "emi",
+ .owner = THIS_MODULE,
+ .type = MET_TYPE_BUS,
+ .create_subfs = met_emi_create,
+ .delete_subfs = met_emi_delete,
+ .resume = met_emi_resume,
+#if defined(CONFIG_MTK_TINYSYS_SSPM_SUPPORT) && defined(ONDIEMET_SUPPORT)
+ .ondiemet_start = ondiemet_emi_start,
+ .ondiemet_stop = ondiemet_emi_stop,
+ .ondiemet_print_help = emi_print_help,
+ .ondiemet_print_header = ondiemet_emi_print_header,
+#endif
+ .ondiemet_mode = 1,
+};
+EXPORT_SYMBOL(met_sspm_emi);
diff --git a/src/devtools/met-driver/4.14/common/met_emi_35.c b/src/devtools/met-driver/4.14/common/met_emi_35.c
new file mode 100644
index 0000000..411fc1a
--- /dev/null
+++ b/src/devtools/met-driver/4.14/common/met_emi_35.c
@@ -0,0 +1,2244 @@
+/*
+ * 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/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 "mtk_emi_bm_35.h"
+#include "interface.h"
+#include "met_dramc.h"
+
+#if defined(CONFIG_MTK_TINYSYS_SSPM_SUPPORT) && defined(ONDIEMET_SUPPORT)
+#include "sspm/ondiemet_sspm.h"
+#endif
+
+#define FILE_NODE_DBG
+
+/*======================================================================*/
+/* Global variable definitions */
+/*======================================================================*/
+/*ondiemet emi sampling interval in us */
+int emi_tsct_enable = 1;
+int emi_mdct_enable = 1;
+int emi_TP_busfiltr_enable;
+
+
+/* Dynamic MonitorCounter selection !!!EXPERIMENT!!! */
+static int msel_enable;
+static unsigned int msel_group1 = BM_MASTER_ALL;
+static unsigned int msel_group2 = BM_MASTER_ALL;
+static unsigned int msel_group3 = BM_MASTER_ALL;
+
+
+/* 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;
+
+static int dramc_pdir_enable;
+static int dram_chann_num = 1;
+
+enum SSPM_Mode {
+ CUSTOMER_MODE = 0x0,
+ UNDEFINE_MODE = 0x1,
+ INTERNAL_MODE = 0X2780
+};
+
+
+
+/*======================================================================*/
+/* KOBJ Declarations */
+/*======================================================================*/
+
+
+DECLARE_KOBJ_ATTR_INT(emi_TP_busfiltr_enable, emi_TP_busfiltr_enable);
+
+
+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);
+
+
+
+/* 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)
+{
+ unsigned int DRAM_TYPE;
+
+ if (get_ddr_type_symbol) {
+ DRAM_TYPE = get_ddr_type_symbol();
+
+ if ((DRAM_TYPE == 2) || (DRAM_TYPE == 3))
+ return dram_data_rate_MHz / DRAM_EMI_BASECLOCK_RATE_LP4 / DRAM_DATARATE;
+ else
+ return dram_data_rate_MHz / DRAM_EMI_BASECLOCK_RATE_LP3 / DRAM_DATARATE;
+ } else {
+ METERROR("[%s][%d]get_ddr_type_symbol = NULL , use the TYPE_LPDDR3 setting\n", __func__, __LINE__);
+ return dram_data_rate_MHz / DRAM_EMI_BASECLOCK_RATE_LP3 / DRAM_DATARATE;
+ }
+}
+
+
+/* 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" }
+ )
+ );
+
+
+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_TTYPE_MASTER(nr) \
+ DECLARE_KOBJ_ATTR_STR_LIST(ttype ## nr ## _master, ttype_master_val[nr - 1], ttype_master)
+
+#define DECLARE_KOBJ_TTYPE_NBEAT(nr) \
+ DECLARE_KOBJ_ATTR_INT_LIST(ttype ## nr ## _nbeat, ttype_nbeat_val[nr - 1], ttype_nbeat)
+
+#define DECLARE_KOBJ_TTYPE_NBYTE(nr) \
+ DECLARE_KOBJ_ATTR_INT_LIST(ttype ## nr ## _nbyte, ttype_nbyte_val[nr - 1], ttype_nbyte)
+
+#define DECLARE_KOBJ_TTYPE_BURST(nr) \
+ DECLARE_KOBJ_ATTR_STR_LIST(ttype ## nr ## _burst, ttype_burst_val[nr - 1], ttype_burst)
+
+#define DECLARE_KOBJ_TTYPE_RW(nr) \
+ DECLARE_KOBJ_ATTR_STR_LIST(ttype ## nr ## _rw, ttype_rw_val[nr - 1], ttype_rw)
+
+#define DECLARE_KOBJ_TTYPE_BUSID_VAL(nr) \
+ DECLARE_KOBJ_ATTR_HEX(ttype ## nr ## _busid, ttype_busid_val[nr - 1])
+
+DECLARE_KOBJ_TTYPE_MASTER(1);
+DECLARE_KOBJ_TTYPE_NBEAT(1);
+DECLARE_KOBJ_TTYPE_NBYTE(1);
+DECLARE_KOBJ_TTYPE_BURST(1);
+DECLARE_KOBJ_TTYPE_RW(1);
+DECLARE_KOBJ_TTYPE_BUSID_VAL(1);
+
+DECLARE_KOBJ_TTYPE_MASTER(2);
+DECLARE_KOBJ_TTYPE_NBEAT(2);
+DECLARE_KOBJ_TTYPE_NBYTE(2);
+DECLARE_KOBJ_TTYPE_BURST(2);
+DECLARE_KOBJ_TTYPE_RW(2);
+DECLARE_KOBJ_TTYPE_BUSID_VAL(2);
+
+DECLARE_KOBJ_TTYPE_MASTER(3);
+DECLARE_KOBJ_TTYPE_NBEAT(3);
+DECLARE_KOBJ_TTYPE_NBYTE(3);
+DECLARE_KOBJ_TTYPE_BURST(3);
+DECLARE_KOBJ_TTYPE_RW(3);
+DECLARE_KOBJ_TTYPE_BUSID_VAL(3);
+
+DECLARE_KOBJ_TTYPE_MASTER(4);
+DECLARE_KOBJ_TTYPE_NBEAT(4);
+DECLARE_KOBJ_TTYPE_NBYTE(4);
+DECLARE_KOBJ_TTYPE_BURST(4);
+DECLARE_KOBJ_TTYPE_RW(4);
+DECLARE_KOBJ_TTYPE_BUSID_VAL(4);
+
+DECLARE_KOBJ_TTYPE_MASTER(5);
+DECLARE_KOBJ_TTYPE_NBEAT(5);
+DECLARE_KOBJ_TTYPE_NBYTE(5);
+DECLARE_KOBJ_TTYPE_BURST(5);
+DECLARE_KOBJ_TTYPE_RW(5);
+DECLARE_KOBJ_TTYPE_BUSID_VAL(5);
+
+DECLARE_KOBJ_TTYPE_MASTER(6);
+DECLARE_KOBJ_TTYPE_NBEAT(6);
+DECLARE_KOBJ_TTYPE_NBYTE(6);
+DECLARE_KOBJ_TTYPE_BURST(6);
+DECLARE_KOBJ_TTYPE_RW(6);
+DECLARE_KOBJ_TTYPE_BUSID_VAL(6);
+
+DECLARE_KOBJ_TTYPE_MASTER(7);
+DECLARE_KOBJ_TTYPE_NBEAT(7);
+DECLARE_KOBJ_TTYPE_NBYTE(7);
+DECLARE_KOBJ_TTYPE_BURST(7);
+DECLARE_KOBJ_TTYPE_RW(7);
+DECLARE_KOBJ_TTYPE_BUSID_VAL(7);
+
+DECLARE_KOBJ_TTYPE_MASTER(8);
+DECLARE_KOBJ_TTYPE_NBEAT(8);
+DECLARE_KOBJ_TTYPE_NBYTE(8);
+DECLARE_KOBJ_TTYPE_BURST(8);
+DECLARE_KOBJ_TTYPE_RW(8);
+DECLARE_KOBJ_TTYPE_BUSID_VAL(8);
+
+DECLARE_KOBJ_TTYPE_MASTER(9);
+DECLARE_KOBJ_TTYPE_NBEAT(9);
+DECLARE_KOBJ_TTYPE_NBYTE(9);
+DECLARE_KOBJ_TTYPE_BURST(9);
+DECLARE_KOBJ_TTYPE_RW(9);
+DECLARE_KOBJ_TTYPE_BUSID_VAL(9);
+
+DECLARE_KOBJ_TTYPE_MASTER(10);
+DECLARE_KOBJ_TTYPE_NBEAT(10);
+DECLARE_KOBJ_TTYPE_NBYTE(10);
+DECLARE_KOBJ_TTYPE_BURST(10);
+DECLARE_KOBJ_TTYPE_RW(10);
+DECLARE_KOBJ_TTYPE_BUSID_VAL(10);
+
+DECLARE_KOBJ_TTYPE_MASTER(11);
+DECLARE_KOBJ_TTYPE_NBEAT(11);
+DECLARE_KOBJ_TTYPE_NBYTE(11);
+DECLARE_KOBJ_TTYPE_BURST(11);
+DECLARE_KOBJ_TTYPE_RW(11);
+DECLARE_KOBJ_TTYPE_BUSID_VAL(11);
+
+DECLARE_KOBJ_TTYPE_MASTER(12);
+DECLARE_KOBJ_TTYPE_NBEAT(12);
+DECLARE_KOBJ_TTYPE_NBYTE(12);
+DECLARE_KOBJ_TTYPE_BURST(12);
+DECLARE_KOBJ_TTYPE_RW(12);
+DECLARE_KOBJ_TTYPE_BUSID_VAL(12);
+
+DECLARE_KOBJ_TTYPE_MASTER(13);
+DECLARE_KOBJ_TTYPE_NBEAT(13);
+DECLARE_KOBJ_TTYPE_NBYTE(13);
+DECLARE_KOBJ_TTYPE_BURST(13);
+DECLARE_KOBJ_TTYPE_RW(13);
+DECLARE_KOBJ_TTYPE_BUSID_VAL(13);
+
+DECLARE_KOBJ_TTYPE_MASTER(14);
+DECLARE_KOBJ_TTYPE_NBEAT(14);
+DECLARE_KOBJ_TTYPE_NBYTE(14);
+DECLARE_KOBJ_TTYPE_BURST(14);
+DECLARE_KOBJ_TTYPE_RW(14);
+DECLARE_KOBJ_TTYPE_BUSID_VAL(14);
+
+DECLARE_KOBJ_TTYPE_MASTER(15);
+DECLARE_KOBJ_TTYPE_NBEAT(15);
+DECLARE_KOBJ_TTYPE_NBYTE(15);
+DECLARE_KOBJ_TTYPE_BURST(15);
+DECLARE_KOBJ_TTYPE_RW(15);
+DECLARE_KOBJ_TTYPE_BUSID_VAL(15);
+
+DECLARE_KOBJ_TTYPE_MASTER(16);
+DECLARE_KOBJ_TTYPE_NBEAT(16);
+DECLARE_KOBJ_TTYPE_NBYTE(16);
+DECLARE_KOBJ_TTYPE_BURST(16);
+DECLARE_KOBJ_TTYPE_RW(16);
+DECLARE_KOBJ_TTYPE_BUSID_VAL(16);
+
+DECLARE_KOBJ_TTYPE_MASTER(17);
+DECLARE_KOBJ_TTYPE_NBEAT(17);
+DECLARE_KOBJ_TTYPE_NBYTE(17);
+DECLARE_KOBJ_TTYPE_BURST(17);
+DECLARE_KOBJ_TTYPE_RW(17);
+DECLARE_KOBJ_TTYPE_BUSID_VAL(17);
+
+DECLARE_KOBJ_TTYPE_MASTER(18);
+DECLARE_KOBJ_TTYPE_NBEAT(18);
+DECLARE_KOBJ_TTYPE_NBYTE(18);
+DECLARE_KOBJ_TTYPE_BURST(18);
+DECLARE_KOBJ_TTYPE_RW(18);
+DECLARE_KOBJ_TTYPE_BUSID_VAL(18);
+
+DECLARE_KOBJ_TTYPE_MASTER(19);
+DECLARE_KOBJ_TTYPE_NBEAT(19);
+DECLARE_KOBJ_TTYPE_NBYTE(19);
+DECLARE_KOBJ_TTYPE_BURST(19);
+DECLARE_KOBJ_TTYPE_RW(19);
+DECLARE_KOBJ_TTYPE_BUSID_VAL(19);
+
+DECLARE_KOBJ_TTYPE_MASTER(20);
+DECLARE_KOBJ_TTYPE_NBEAT(20);
+DECLARE_KOBJ_TTYPE_NBYTE(20);
+DECLARE_KOBJ_TTYPE_BURST(20);
+DECLARE_KOBJ_TTYPE_RW(20);
+DECLARE_KOBJ_TTYPE_BUSID_VAL(20);
+
+DECLARE_KOBJ_TTYPE_MASTER(21);
+DECLARE_KOBJ_TTYPE_NBEAT(21);
+DECLARE_KOBJ_TTYPE_NBYTE(21);
+DECLARE_KOBJ_TTYPE_BURST(21);
+DECLARE_KOBJ_TTYPE_RW(21);
+DECLARE_KOBJ_TTYPE_BUSID_VAL(21);
+
+
+/* SEDA 3.5 ext */
+static unsigned int msel_group_ext_val[WSCT_AMOUNT];
+static unsigned int wsct_rw_val[WSCT_AMOUNT];
+
+char* const delim_comma = ",";
+char* const delim_coclon = ":";
+
+
+char msel_group_ext[FILE_NODE_DATA_LEN] = {'\0'};
+
+static void _clear_msel_group_ext(void) {
+ int i;
+
+ for (i=0;i<WSCT_AMOUNT;i++) {
+ msel_group_ext_val[i] = BM_MASTER_ALL;
+ }
+
+ /*WSCT 4~5 default is ultra, pre-ultra total*/
+ msel_group_ext[0] = '\0';
+}
+
+static ssize_t msel_group_ext_store(struct kobject *kobj,
+ struct kobj_attribute *attr,
+ const char *buf,
+ size_t n)
+{
+ /*parse wsct_id:group,
+ 1. split data by ","
+ 2. split subdata by ":"
+ 3. check the value is OK
+
+ don't clear the setting, do this by echo 1 > clear_setting
+ */
+
+ char *token, *cur= msel_group_ext;
+ char *_id = NULL, *_master_group = NULL;
+ int id_int = 0;
+
+ _clear_msel_group_ext();
+
+ snprintf(msel_group_ext, FILE_NODE_DATA_LEN, "%s", buf);
+ msel_group_ext[n-1]='\0';
+
+
+ while (cur != NULL) {
+ token = strsep(&cur, delim_comma);
+ PR_BOOTMSG("token: %s\n",token);
+ /*token EX: 4:0xff , (ID,master_group)*/
+
+ _id = strsep(&token, delim_coclon); // ID
+ _master_group = strsep(&token, delim_coclon);
+
+ PR_BOOTMSG("_id[%s] _master_group[%s]\n",_id,_master_group);
+
+ if (_id == NULL || _master_group == NULL) {
+ PR_BOOTMSG("err: _id[%s] _master_group[%s], para can't be NULL\n",_id,_master_group);
+ _clear_msel_group_ext();
+ return -EINVAL;
+ }
+
+ if (kstrtouint(_id, 0, &id_int) != 0) {
+ PR_BOOTMSG("_id[%s] trans to hex err\n",_id);
+ _clear_msel_group_ext();
+ return -EINVAL;
+ }
+
+
+ if ( id_int >= 0 && id_int < WSCT_AMOUNT) {
+ if (kstrtouint(_master_group, 0, &msel_group_ext_val[id_int]) != 0) {
+ PR_BOOTMSG("master_group[%s] trans to hex err\n",_master_group);
+ _clear_msel_group_ext();
+ return -EINVAL;
+ }
+ } else {
+ PR_BOOTMSG("id[%d] exceed the range, it must be 0~%d\n",id_int, WSCT_AMOUNT-1);
+ _clear_msel_group_ext();
+ return -EINVAL;
+ }
+ }
+#ifdef FILE_NODE_DBG
+ PR_BOOTMSG("input data [%s]\n",msel_group_ext);
+ /*PR_BOOTMSG("msel_group_ext_store size para n[%d]\n",n);*/
+ int i;
+ PR_BOOTMSG("save data\n");
+ for (i=0;i<WSCT_AMOUNT;i++) {
+ PR_BOOTMSG("id[%d]=%X\n",i,msel_group_ext_val[i]);
+ }
+#endif
+ return n;
+}
+
+static ssize_t msel_group_ext_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
+{
+ return snprintf(buf, PAGE_SIZE, "%s\n", msel_group_ext);
+}
+
+
+char wsct_rw[FILE_NODE_DATA_LEN] = {'\0'};
+
+static void _clear_wsct_rw(void) {
+ int i;
+
+ for (i=0;i<WSCT_AMOUNT;i++) {
+ wsct_rw_val[i] = BM_WSCT_RW_RWBOTH;
+ }
+ wsct_rw[0] = '\0';
+}
+
+static ssize_t wsct_rw_store(struct kobject *kobj,
+ struct kobj_attribute *attr,
+ const char *buf,
+ size_t n)
+{
+ char *token, *cur= wsct_rw;
+ char *_id = NULL, *_rw_type = NULL;
+ int id_int = 0;
+
+ _clear_wsct_rw();
+
+ snprintf(wsct_rw, FILE_NODE_DATA_LEN, "%s", buf);
+ wsct_rw[n-1]='\0';
+
+ while (cur != NULL) {
+ token = strsep(&cur, delim_comma);
+ PR_BOOTMSG("token: %s\n",token);
+ /*token EX: 4:R , 5:W (ID,RW)*/
+
+ _id = strsep(&token, delim_coclon); // ID
+ _rw_type = strsep(&token, delim_coclon);
+
+ if (_id == NULL || _rw_type == NULL) {
+ PR_BOOTMSG("err: _id[%s] _rw_type[%s], para can't be NULL\n",_id, _rw_type);
+ _clear_wsct_rw();
+ return -EINVAL;
+ }
+
+ PR_BOOTMSG("_id[%s] _rw_type[%s]\n",_id, _rw_type);
+ if (kstrtouint(_id, 0, &id_int) != 0) {
+ PR_BOOTMSG("_id[%s] trans to hex err\n",_id);
+ _clear_wsct_rw();
+ return -EINVAL;
+ }
+
+
+ if ( id_int >= 0 && id_int < WSCT_AMOUNT) {
+ if ( 0 == strncmp("NONE",_rw_type,4))
+ wsct_rw_val[id_int] = BM_WSCT_RW_DISABLE;
+ else if (0 == strncmp("R",_rw_type,4))
+ wsct_rw_val[id_int] = BM_WSCT_RW_READONLY;
+ else if (0 == strncmp("W",_rw_type,4))
+ wsct_rw_val[id_int] = BM_WSCT_RW_WRITEONLY;
+ else if (0 == strncmp("RW",_rw_type,4))
+ wsct_rw_val[id_int] = BM_WSCT_RW_RWBOTH;
+ else {
+ PR_BOOTMSG("_id[%s] has err rwtype[%s]\n", _id, _rw_type);
+ _clear_wsct_rw();
+ return -EINVAL;
+ }
+
+ } else {
+ PR_BOOTMSG("id[%d] exceed the range, it must be 0~%d\n",id_int, WSCT_AMOUNT-1);
+ _clear_wsct_rw();
+ return -EINVAL;
+ }
+ }
+
+#ifdef FILE_NODE_DBG
+ PR_BOOTMSG("wsct_rw_store input data [%s]\n",wsct_rw);
+ int i;
+ PR_BOOTMSG("rwtype save data\n");
+ for (i=0;i<WSCT_AMOUNT;i++) {
+ PR_BOOTMSG("id[%d]=%d\n",i,wsct_rw_val[i]);
+ }
+#endif
+ return n;
+}
+
+static ssize_t wsct_rw_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
+{
+ return snprintf(buf, PAGE_SIZE, "%s\n", wsct_rw);
+}
+
+
+static unsigned int WSCT_HPRI_DIS[WSCT_AMOUNT];
+static unsigned int WSCT_HPRI_SEL[WSCT_AMOUNT];
+char wsct_high_priority_enable[FILE_NODE_DATA_LEN] = {'\0'};
+
+static void _clear_wsct_high_priority_enable(void) {
+ int i;
+
+ for (i=0;i<WSCT_AMOUNT;i++) {
+ WSCT_HPRI_DIS[i] = 1;
+ WSCT_HPRI_SEL[i] = 0xF;
+ }
+
+ WSCT_HPRI_DIS[4] = 0;
+ WSCT_HPRI_SEL[4] = 0x8; /* ultra */
+
+ WSCT_HPRI_DIS[5] = 0;
+ WSCT_HPRI_SEL[5] = 0x4; /* pre_ultra */
+
+
+ wsct_high_priority_enable[0] = '\0';
+}
+
+static ssize_t wsct_high_priority_enable_store(struct kobject *kobj,
+ struct kobj_attribute *attr,
+ const char *buf,
+ size_t n)
+{
+ char *token, *cur= wsct_high_priority_enable;
+ char *_id = NULL, *_enable = NULL, *_level = NULL;
+ int id_int = 0, level_int = 0;
+
+ _clear_wsct_high_priority_enable();
+
+ snprintf(wsct_high_priority_enable, FILE_NODE_DATA_LEN, "%s", buf);
+ wsct_high_priority_enable[n-1]='\0';
+
+ while (cur != NULL) {
+ token = strsep(&cur, delim_comma);
+ PR_BOOTMSG("token: %s\n",token);
+ /*token EX: 4:R , 5:W (ID,RW)*/
+
+ _id = strsep(&token, delim_coclon); // ID
+ _enable = strsep(&token, delim_coclon);
+ _level = strsep(&token, delim_coclon);
+
+ PR_BOOTMSG("_id[%s] _enable[%s] _level[%s]\n",_id, _enable, _level);
+
+ if (_id == NULL || _enable == NULL || _level == NULL ) {
+ PR_BOOTMSG("err : _id[%s] _enable[%s] _level[%s], para can't be NULL\n",_id, _enable, _level);
+ _clear_wsct_high_priority_enable();
+ return -EINVAL;
+ }
+
+ if (kstrtouint(_id, 0, &id_int) != 0) {
+ PR_BOOTMSG("_id[%s] trans to hex err\n",_id);
+ _clear_wsct_high_priority_enable();
+ return -EINVAL;
+ }
+
+
+ if ( id_int >= 0 && id_int < WSCT_AMOUNT) {
+ if ( 0 == strncmp("disable", _enable, 7)) {
+
+ WSCT_HPRI_DIS[id_int] = 1;
+ WSCT_HPRI_SEL[id_int] = 0xf;
+ } else if ( 0 == strncmp("enable", _enable, 6)) {
+
+ WSCT_HPRI_DIS[id_int] = 0;
+ if (kstrtouint(_level, 0, &level_int) != 0) {
+ PR_BOOTMSG("_id[%s] trans ultraLevel[%s] to hex err\n",_id, _level);
+ _clear_wsct_high_priority_enable();
+ return -EINVAL;
+ }
+ WSCT_HPRI_SEL[id_int] = level_int & 0xF;
+ } else {
+ PR_BOOTMSG("_id[%s] has err enable[%s] (enable/disable)\n", _id, _enable);
+ _clear_wsct_high_priority_enable();
+ return -EINVAL;
+ }
+
+ } else {
+ PR_BOOTMSG("id[%d] exceed the range, it must be 0~%d\n",id_int, WSCT_AMOUNT-1);
+ _clear_wsct_high_priority_enable();
+ return -EINVAL;
+ }
+ }
+#ifdef FILE_NODE_DBG
+ PR_BOOTMSG("input data [%s]\n",wsct_high_priority_enable);
+ int i;
+ PR_BOOTMSG("wsct_high_priority_enable save data\n");
+ for (i=0;i<WSCT_AMOUNT;i++) {
+ PR_BOOTMSG("id[%d]=(%X,%X)\n", i, WSCT_HPRI_DIS[i], WSCT_HPRI_SEL[i]);
+ }
+#endif
+ return n;
+}
+
+static ssize_t wsct_high_priority_enable_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
+{
+ return snprintf(buf, PAGE_SIZE, "%s\n", wsct_high_priority_enable);
+}
+
+
+static unsigned int wsct_busid_val[WSCT_AMOUNT];
+static unsigned int wsct_idMask_val[WSCT_AMOUNT];
+
+char wsct_busid[FILE_NODE_DATA_LEN] = {'\0'};
+
+static void _clear_wsct_busid(void) {
+ int i;
+
+ for (i=0;i<WSCT_AMOUNT;i++) {
+ wsct_busid_val[i] = 0xfffff;
+ wsct_idMask_val[i] = 0x1FFF;
+ }
+ wsct_busid[0] = '\0';
+}
+
+static ssize_t wsct_busid_store(struct kobject *kobj,
+ struct kobj_attribute *attr,
+ const char *buf,
+ size_t n)
+{
+ char *token, *cur= wsct_busid;
+
+ char *_id = NULL, *_busid = NULL, *_idMask = NULL;
+ int id_int = 0, busid_int = 0, idMask_int = 0;
+
+ _clear_wsct_busid();
+
+ snprintf(wsct_busid, FILE_NODE_DATA_LEN, "%s", buf);
+ wsct_busid[n-1]='\0';
+
+ while (cur != NULL) {
+ token = strsep(&cur, delim_comma);
+ PR_BOOTMSG("token: %s\n",token);
+ /*token EX: 4:R , 5:W (ID,RW)*/
+
+ _id = strsep(&token, delim_coclon); // ID
+ _busid = strsep(&token, delim_coclon);
+ _idMask = strsep(&token, delim_coclon);
+
+ PR_BOOTMSG("_id[%s] _busid[%s] _idMask[%s]\n",_id, _busid, _idMask);
+
+ if (_id == NULL || _busid == NULL || _idMask == NULL) {
+ PR_BOOTMSG("err: _id[%s] _busid[%s] _idMask[%s] ,parameter can't be NULL\n",_id, _busid, _idMask);
+ _clear_wsct_busid();
+ return -EINVAL;
+ }
+
+
+ if (kstrtouint(_id, 0, &id_int) != 0) {
+ PR_BOOTMSG("_id[%s] trans to hex err\n",_id);
+ _clear_wsct_busid();
+ return -EINVAL;
+ }
+ if (kstrtouint(_busid, 0, &busid_int) != 0) {
+ PR_BOOTMSG("_busid[%s] trans to hex err\n",_busid);
+ _clear_wsct_busid();
+ return -EINVAL;
+ }
+ if (kstrtouint(_idMask, 0, &idMask_int) != 0) {
+ PR_BOOTMSG("_idMask[%s] trans to hex err\n",_idMask);
+ _clear_wsct_busid();
+ return -EINVAL;
+ }
+
+
+ if ( id_int >= 0 && id_int < WSCT_AMOUNT) {
+ wsct_busid_val[id_int] = busid_int;
+ wsct_idMask_val[id_int] = idMask_int;
+
+ } else {
+ PR_BOOTMSG("id[%d] exceed the range, it must be 0~%d\n",id_int, WSCT_AMOUNT-1);
+ _clear_wsct_busid();
+ return -EINVAL;
+ }
+ }
+#ifdef FILE_NODE_DBG
+ PR_BOOTMSG("input data [%s]\n",wsct_busid);
+ int i;
+ PR_BOOTMSG("wsct_busid save data\n");
+ for (i=0;i<WSCT_AMOUNT;i++) {
+ PR_BOOTMSG("id[%d](busid,idMask)=(%X,%X)\n", i, wsct_busid_val[i], wsct_idMask_val[i]);
+ }
+#endif
+ return n;
+}
+
+static ssize_t wsct_busid_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
+{
+ return snprintf(buf, PAGE_SIZE, "%s\n", wsct_busid);
+}
+
+
+static unsigned int wsct_chn_rank_sel_val[WSCT_AMOUNT];
+char wsct_chn_rank_sel[FILE_NODE_DATA_LEN] = {'\0'};
+
+static void _clear_wsct_chn_rank_sel(void) {
+ int i;
+
+ for (i=0;i<WSCT_AMOUNT;i++) {
+ wsct_chn_rank_sel_val[i] = 0xF;
+ }
+ wsct_chn_rank_sel[0] = '\0';
+}
+
+static ssize_t wsct_chn_rank_sel_store(struct kobject *kobj,
+ struct kobj_attribute *attr,
+ const char *buf,
+ size_t n)
+{
+ char *token, *cur= wsct_chn_rank_sel;
+ char *_id = NULL, *_chn_rank = NULL;
+ int id_int = 0, chn_rank_int = 0;
+
+ _clear_wsct_chn_rank_sel();
+
+ snprintf(wsct_chn_rank_sel, FILE_NODE_DATA_LEN, "%s", buf);
+ wsct_chn_rank_sel[n-1]='\0';
+
+
+ while (cur != NULL) {
+ token = strsep(&cur, delim_comma);
+ PR_BOOTMSG("token: %s\n",token);
+ /*token EX: 4:f , 5:C (ID,chn_rnk_sel)*/
+
+ _id = strsep(&token, delim_coclon); // ID
+ _chn_rank = strsep(&token, delim_coclon);
+
+ PR_BOOTMSG("_id[%s] _chn_rank[%s]\n",_id, _chn_rank);
+
+ if (_id == NULL || _chn_rank == NULL) {
+ PR_BOOTMSG("err : _id[%s] _chn_rank[%s], para can't be NULL\n",_id, _chn_rank);
+ _clear_wsct_chn_rank_sel();
+ return -EINVAL;
+ }
+
+
+ if (kstrtouint(_id, 0, &id_int) != 0) {
+ PR_BOOTMSG("_id[%s] trans to hex err\n",_id);
+ _clear_wsct_chn_rank_sel();
+ return -EINVAL;
+ }
+ if (kstrtouint(_chn_rank, 0, &chn_rank_int) != 0) {
+ PR_BOOTMSG("_chn_rank[%s] trans to hex err\n",_id);
+ _clear_wsct_chn_rank_sel();
+ return -EINVAL;
+ }
+
+ if ( id_int >= 0 && id_int < WSCT_AMOUNT) {
+ wsct_chn_rank_sel_val[id_int] = chn_rank_int;
+
+ } else {
+ PR_BOOTMSG("id[%d] exceed the range, it must be 0~%d\n",id_int, WSCT_AMOUNT-1);
+ _clear_wsct_chn_rank_sel();
+ return -EINVAL;
+ }
+ }
+
+#ifdef FILE_NODE_DBG
+ PR_BOOTMSG("wsct_chn_rank_sel input data [%s]\n",wsct_chn_rank_sel);
+ int i;
+ PR_BOOTMSG("wsct_chn_rank_sel_val save data\n");
+ for (i=0;i<WSCT_AMOUNT;i++) {
+ PR_BOOTMSG("id[%d]=%X\n",i,wsct_chn_rank_sel_val[i]);
+ }
+#endif
+ return n;
+}
+
+static ssize_t wsct_chn_rank_sel_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
+{
+ return snprintf(buf, PAGE_SIZE, "%s\n", wsct_chn_rank_sel);
+}
+
+static unsigned int wsct_byte_low_bnd_val[WSCT_AMOUNT];
+static unsigned int wsct_byte_up_bnd_val[WSCT_AMOUNT];
+static unsigned int wsct_byte_bnd_dis[WSCT_AMOUNT];
+char wsct_burst_range[FILE_NODE_DATA_LEN] = {'\0'};
+
+static void _clear_wsct_burst_range(void) {
+ int i;
+
+ for (i=0;i<WSCT_AMOUNT;i++) {
+ wsct_byte_low_bnd_val[i] = 0x0;
+ wsct_byte_up_bnd_val[i] = 0x1FF;
+ wsct_byte_bnd_dis[i] = 1;
+ }
+ wsct_burst_range[0] = '\0';
+}
+
+static ssize_t wsct_burst_range_store(struct kobject *kobj,
+ struct kobj_attribute *attr,
+ const char *buf,
+ size_t n)
+{
+ char *token, *cur= wsct_burst_range;
+ char *_id = NULL, *_low_bnd = NULL, *_up_bnd = NULL;
+ int id_int = 0, low_bnd_int = 0, up_bnd_int = 0;
+
+ _clear_wsct_burst_range();
+
+ snprintf(wsct_burst_range, FILE_NODE_DATA_LEN, "%s", buf);
+ wsct_burst_range[n-1]='\0';
+
+
+ while (cur != NULL) {
+ token = strsep(&cur, delim_comma);
+ PR_BOOTMSG("token: %s\n",token);
+ /*token EX: 4:f , 5:C (ID,chn_rnk_sel)*/
+
+ _id = strsep(&token, delim_coclon); // ID
+ _low_bnd = strsep(&token, delim_coclon);
+ _up_bnd = strsep(&token, delim_coclon);
+
+ PR_BOOTMSG("_id[%s] _low_bnd[%s] _up_bnd[%s]\n",_id, _low_bnd, _up_bnd);
+
+ if (_id == NULL || _low_bnd == NULL || _up_bnd == NULL) {
+ PR_BOOTMSG("err : _id[%s] _low_bnd[%s] _up_bnd[%s], para can't be NULL\n",_id, _low_bnd, _up_bnd);
+ _clear_wsct_burst_range();
+ return -EINVAL;
+ }
+
+ if (kstrtouint(_id, 0, &id_int) != 0) {
+ PR_BOOTMSG("_id[%s] trans to hex err\n",_id);
+ _clear_wsct_burst_range();
+ return -EINVAL;
+ }
+ if (kstrtouint(_low_bnd, 0, &low_bnd_int) != 0) {
+ PR_BOOTMSG("_low_bnd[%s] trans to hex err\n",_id);
+ _clear_wsct_burst_range();
+ return -EINVAL;
+ }
+ if (kstrtouint(_up_bnd, 0, &up_bnd_int) != 0) {
+ PR_BOOTMSG("_up_bnd[%s] trans to hex err\n",_id);
+ _clear_wsct_burst_range();
+ return -EINVAL;
+ }
+
+ if ( id_int >= 0 && id_int < WSCT_AMOUNT) {
+ wsct_byte_low_bnd_val[id_int] = low_bnd_int;
+ wsct_byte_up_bnd_val[id_int] = up_bnd_int;
+ wsct_byte_bnd_dis[id_int] = 0;
+ } else {
+ PR_BOOTMSG("id[%d] exceed the range, it must be 0~%d\n",id_int, WSCT_AMOUNT-1);
+ _clear_wsct_burst_range();
+ return -EINVAL;
+ }
+ }
+
+#ifdef FILE_NODE_DBG
+ PR_BOOTMSG("wsct_burst_range_store input data [%s]\n",wsct_burst_range);
+ int i;
+ PR_BOOTMSG("wsct_burst_range save data\n");
+ for (i=0;i<WSCT_AMOUNT;i++) {
+ PR_BOOTMSG("id[%d](low_bnd,up_bnd)=(%X,%X)\n",i,wsct_byte_low_bnd_val[i],wsct_byte_up_bnd_val[i]);
+ }
+#endif
+ return n;
+}
+
+static ssize_t wsct_burst_range_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
+{
+ return snprintf(buf, PAGE_SIZE, "%s\n", wsct_burst_range);
+}
+
+
+
+static unsigned int tsct_busid_enable_val[TSCT_AMOUNT];
+char tsct_busid_enable[FILE_NODE_DATA_LEN] = {'\0'};
+
+static void _clear_tsct_busid_enable(void) {
+ int i;
+
+ for (i=0;i<TSCT_AMOUNT;i++) {
+ tsct_busid_enable_val[i] = 0;
+ }
+ tsct_busid_enable[0] = '\0';
+}
+
+static ssize_t tsct_busid_enable_store(struct kobject *kobj,
+ struct kobj_attribute *attr,
+ const char *buf,
+ size_t n)
+{
+ char *token, *cur= tsct_busid_enable;
+ char *_id = NULL, *_enable = NULL;
+ int id_int = 0;
+
+ _clear_tsct_busid_enable();
+
+ snprintf(tsct_busid_enable, FILE_NODE_DATA_LEN, "%s", buf);
+ tsct_busid_enable[n-1]='\0';
+
+
+ while (cur != NULL) {
+ token = strsep(&cur, delim_comma);
+ PR_BOOTMSG("token: %s\n",token);
+ /*token EX: 4:R , 5:W (ID,RW)*/
+
+ _id = strsep(&token, delim_coclon); // ID
+ _enable = strsep(&token, delim_coclon);
+
+
+ PR_BOOTMSG("_id[%s] _enable[%s]\n",_id, _enable);
+
+ if (_id == NULL || _enable == NULL) {
+ PR_BOOTMSG("err : _id[%s] _enable[%s], para can't be NULL\n",_id, _enable);
+ _clear_tsct_busid_enable();
+ return -EINVAL;
+ }
+
+ if (kstrtouint(_id, 0, &id_int) != 0) {
+ PR_BOOTMSG("_id[%s] trans to hex err\n",_id);
+ _clear_tsct_busid_enable();
+ return -EINVAL;
+ }
+
+
+ if ( id_int >= 0 && id_int < TSCT_AMOUNT) {
+ if ( 0 == strncmp("disable", _enable, 7)) {
+ tsct_busid_enable_val[id_int] = 0;
+ } else if ( 0 == strncmp("enable", _enable, 6)) {
+ tsct_busid_enable_val[id_int] = 1;
+ } else {
+ PR_BOOTMSG("_id[%s] has err enable[%s] (enable/disable)\n", _id, _enable);
+ _clear_tsct_busid_enable();
+ return -EINVAL;
+ }
+
+ } else {
+ PR_BOOTMSG("id[%d] exceed the range, it must be 0~%d\n",id_int, TSCT_AMOUNT-1);
+ _clear_tsct_busid_enable();
+ return -EINVAL;
+ }
+ }
+#ifdef FILE_NODE_DBG
+ PR_BOOTMSG("tsct_busid_enable input data [%s]\n",tsct_busid_enable);
+ int i;
+ PR_BOOTMSG("wsct_high_priority_enable save data\n");
+ for (i=0;i<TSCT_AMOUNT;i++) {
+ PR_BOOTMSG("id[%d]=(%d)\n", i, tsct_busid_enable_val[i]);
+ }
+#endif
+ return n;
+}
+
+static ssize_t tsct_busid_enable_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
+{
+ return snprintf(buf, PAGE_SIZE, "%s\n", tsct_busid_enable);
+}
+
+
+/* use the origin para high_priority_filter to save the en/dis setting */
+static unsigned int TTYPE_HPRI_SEL[BM_COUNTER_MAX];
+char ttype_high_priority_ext[FILE_NODE_DATA_LEN] = {'\0'};
+
+static void _clear_ttype_high_priority_ext(void) {
+ int i;
+
+ for (i=0;i<BM_COUNTER_MAX;i++) {
+ TTYPE_HPRI_SEL[i] = 0xf;
+ }
+
+ high_priority_filter = 0x0;
+ ttype_high_priority_ext[0] = '\0';
+}
+
+static ssize_t ttype_high_priority_ext_store(struct kobject *kobj,
+ struct kobj_attribute *attr,
+ const char *buf,
+ size_t n)
+{
+ char *token, *cur= ttype_high_priority_ext;
+ char *_id = NULL, *_enable = NULL, *_level = NULL;
+ int id_int = 0, level_int = 0;
+
+ _clear_ttype_high_priority_ext();
+
+ snprintf(ttype_high_priority_ext, FILE_NODE_DATA_LEN, "%s", buf);
+ ttype_high_priority_ext[n-1]='\0';
+
+ while (cur != NULL) {
+ token = strsep(&cur, delim_comma);
+ PR_BOOTMSG("token: %s\n",token);
+ /*token EX: 4:R , 5:W (ID,RW)*/
+
+ _id = strsep(&token, delim_coclon); // ID
+ _enable = strsep(&token, delim_coclon);
+ _level = strsep(&token, delim_coclon);
+
+ PR_BOOTMSG("_id[%s] _enable[%s] _level[%s]\n",_id, _enable, _level);
+
+ if (_id == NULL || _enable == NULL || _level == NULL ) {
+ PR_BOOTMSG("err : _id[%s] _enable[%s] _level[%s], para can't be NULL\n",_id, _enable, _level);
+ _clear_ttype_high_priority_ext();
+ return -EINVAL;
+ }
+
+ if (kstrtouint(_id, 0, &id_int) != 0) {
+ PR_BOOTMSG("_id[%s] trans to hex err\n",_id);
+ _clear_ttype_high_priority_ext();
+ return -EINVAL;
+ }
+
+ id_int = id_int - 1;
+ if ( id_int >= 0 && id_int < BM_COUNTER_MAX) {
+ if ( 0 == strncmp("disable", _enable, 7)) {
+
+ high_priority_filter = ( high_priority_filter & ~(1<<id_int) ) | ( 0<<id_int );
+ TTYPE_HPRI_SEL[id_int] = 0xf;
+ } else if ( 0 == strncmp("enable", _enable, 6)) {
+
+ high_priority_filter = ( high_priority_filter & ~(1<<id_int) ) | ( 1<<id_int );
+ if (kstrtouint(_level, 0, &level_int) != 0) {
+ PR_BOOTMSG("_id[%s] trans ultraLevel[%s] to hex err\n",_id, _level);
+ _clear_ttype_high_priority_ext();
+ return -EINVAL;
+ }
+ TTYPE_HPRI_SEL[id_int] = level_int & 0xF;
+ } else {
+ PR_BOOTMSG("ttype_high_priority_ext: _id[%s] has err enable[%s] (enable/disable)\n", _id, _enable);
+ _clear_ttype_high_priority_ext();
+ return -EINVAL;
+ }
+
+ } else {
+ PR_BOOTMSG("id[%d] exceed the range, it must be 1~%d\n",id_int+1, BM_COUNTER_MAX);
+ _clear_ttype_high_priority_ext();
+ return -EINVAL;
+ }
+ }
+#ifdef FILE_NODE_DBG
+ PR_BOOTMSG("input data [%s]\n",ttype_high_priority_ext);
+
+ int i;
+ PR_BOOTMSG("wsct_high_priority_enable save data\n");
+ for (i=0;i<BM_COUNTER_MAX;i++) {
+ PR_BOOTMSG("id[%d]=(%X,%X)\n", i+1, high_priority_filter>>i & 0x1, TTYPE_HPRI_SEL[i]);
+ }
+#endif
+ return n;
+}
+
+static ssize_t ttype_high_priority_ext_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
+{
+ return snprintf(buf, PAGE_SIZE, "%s\n", ttype_high_priority_ext);
+}
+
+
+static unsigned int ttype_idMask_val[BM_COUNTER_MAX];
+char ttype_busid_ext[FILE_NODE_DATA_LEN] = {'\0'};
+
+static void _clear_ttype_busid_ext(void) {
+ int i;
+
+ for (i=0;i<BM_COUNTER_MAX;i++) {
+ ttype_busid_val[i] = 0xfffff;
+ ttype_idMask_val[i] = 0x1FFF;
+ }
+ ttype_busid_ext[0] = '\0';
+}
+
+/*id: 1~21*/
+static ssize_t ttype_busid_ext_store(struct kobject *kobj,
+ struct kobj_attribute *attr,
+ const char *buf,
+ size_t n)
+{
+ char *token, *cur= ttype_busid_ext;
+
+ char *_id = NULL, *_busid = NULL, *_idMask = NULL;
+ int id_int = 0, busid_int = 0, idMask_int = 0;
+
+ _clear_ttype_busid_ext();
+
+ snprintf(ttype_busid_ext, FILE_NODE_DATA_LEN, "%s", buf);
+ ttype_busid_ext[n-1]='\0';
+
+ while (cur != NULL) {
+ token = strsep(&cur, delim_comma);
+ PR_BOOTMSG("token: %s\n",token);
+ /*token EX: 4:R , 5:W (ID,RW)*/
+
+ _id = strsep(&token, delim_coclon); // ID
+ _busid = strsep(&token, delim_coclon);
+ _idMask = strsep(&token, delim_coclon);
+
+ PR_BOOTMSG("_id[%s] _busid[%s] _idMask[%s]\n",_id, _busid, _idMask);
+
+ if (_id == NULL || _busid == NULL || _idMask == NULL) {
+ PR_BOOTMSG("err: ttype_busid_ext _id[%s] _busid[%s] _idMask[%s] ,parameter can't be NULL\n",_id, _busid, _idMask);
+ _clear_ttype_busid_ext();
+ return -EINVAL;
+ }
+
+ if (kstrtouint(_id, 0, &id_int) != 0) {
+ PR_BOOTMSG("_id[%s] trans to hex err\n",_id);
+ _clear_ttype_busid_ext();
+ return -EINVAL;
+ }
+ if (kstrtouint(_busid, 0, &busid_int) != 0) {
+ PR_BOOTMSG("_busid[%s] trans to hex err\n",_busid);
+ _clear_ttype_busid_ext();
+ return -EINVAL;
+ }
+ if (kstrtouint(_idMask, 0, &idMask_int) != 0) {
+ PR_BOOTMSG("_idMask[%s] trans to hex err\n",_idMask);
+ _clear_ttype_busid_ext();
+ return -EINVAL;
+ }
+
+ id_int = id_int - 1;
+ if ( id_int >= 0 && id_int < BM_COUNTER_MAX) {
+ ttype_busid_val[id_int] = busid_int;
+ ttype_idMask_val[id_int] = idMask_int;
+
+ } else {
+ PR_BOOTMSG("ttype_busid_ext id[%d] exceed the range, it must be 1~%d\n",id_int+1, BM_COUNTER_MAX);
+ _clear_ttype_busid_ext();
+ return -EINVAL;
+ }
+ }
+#ifdef FILE_NODE_DBG
+ PR_BOOTMSG("ttype_busid_ext input data [%s]\n",ttype_busid_ext);
+
+ int i;
+ PR_BOOTMSG("ttype_busid_ext save data\n");
+ for (i=0;i<BM_COUNTER_MAX;i++) {
+ PR_BOOTMSG("id[%d](busid,idMask)=(%X,%X)\n", i+1, ttype_busid_val[i], ttype_idMask_val[i]);
+ }
+#endif
+ return n;
+}
+
+static ssize_t ttype_busid_ext_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
+{
+ return snprintf(buf, PAGE_SIZE, "%s\n", ttype_busid_ext);
+}
+
+
+static unsigned int ttype_chn_rank_sel_val[BM_COUNTER_MAX];
+char ttype_chn_rank_sel[FILE_NODE_DATA_LEN] = {'\0'};
+
+static void _clear_ttype_chn_rank_sel(void) {
+ int i;
+
+ for (i=0;i<BM_COUNTER_MAX;i++) {
+ ttype_chn_rank_sel_val[i] = 0xF;
+ }
+ ttype_chn_rank_sel[0] = '\0';
+}
+
+static ssize_t ttype_chn_rank_sel_store(struct kobject *kobj,
+ struct kobj_attribute *attr,
+ const char *buf,
+ size_t n)
+{
+ char *token, *cur= ttype_chn_rank_sel;
+ char *_id = NULL, *_chn_rank = NULL;
+ int id_int = 0, chn_rank_int = 0;
+
+ _clear_ttype_chn_rank_sel();
+
+ snprintf(ttype_chn_rank_sel, FILE_NODE_DATA_LEN, "%s", buf);
+ ttype_chn_rank_sel[n-1]='\0';
+
+ while (cur != NULL) {
+ token = strsep(&cur, delim_comma);
+ PR_BOOTMSG("token: %s\n",token);
+ /*token EX: 4:f , 5:C (ID,chn_rnk_sel)*/
+
+ _id = strsep(&token, delim_coclon); // ID
+ _chn_rank = strsep(&token, delim_coclon);
+
+ PR_BOOTMSG("_id[%s] _chn_rank[%s]\n",_id, _chn_rank);
+
+ if (_id == NULL || _chn_rank == NULL) {
+ PR_BOOTMSG("err (ttype_chn_rank_sel): _id[%s] _chn_rank[%s], para can't be NULL\n",_id, _chn_rank);
+ _clear_ttype_chn_rank_sel();
+ return -EINVAL;
+ }
+
+
+ if (kstrtouint(_id, 0, &id_int) != 0) {
+ PR_BOOTMSG("_id[%s] trans to hex err\n",_id);
+ _clear_ttype_chn_rank_sel();
+ return -EINVAL;
+ }
+ if (kstrtouint(_chn_rank, 0, &chn_rank_int) != 0) {
+ PR_BOOTMSG("_chn_rank[%s] trans to hex err\n",_id);
+ _clear_ttype_chn_rank_sel();
+ return -EINVAL;
+ }
+
+ id_int = id_int -1;
+
+ if ( id_int >= 0 && id_int < BM_COUNTER_MAX) {
+ ttype_chn_rank_sel[id_int] = chn_rank_int;
+
+ } else {
+ PR_BOOTMSG("id[%d] exceed the range, it must be 1~%d\n",id_int+1, BM_COUNTER_MAX);
+ _clear_ttype_chn_rank_sel();
+ return -EINVAL;
+ }
+ }
+
+#ifdef FILE_NODE_DBG
+ PR_BOOTMSG("ttype_chn_rank_sel input data [%s]\n",ttype_chn_rank_sel);
+
+ int i;
+ PR_BOOTMSG("wsct_chn_rank_sel_val save data\n");
+ for (i=0;i<BM_COUNTER_MAX;i++) {
+ PR_BOOTMSG("id[%d]=%X\n",i+1,ttype_chn_rank_sel[i]);
+ }
+#endif
+ return n;
+}
+
+static ssize_t ttype_chn_rank_sel_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
+{
+ return snprintf(buf, PAGE_SIZE, "%s\n", ttype_chn_rank_sel);
+}
+
+
+static unsigned int ttype_byte_low_bnd_val[BM_COUNTER_MAX];
+static unsigned int ttype_byte_up_bnd_val[BM_COUNTER_MAX];
+static unsigned int ttype_byte_bnd_dis[BM_COUNTER_MAX];
+char ttype_burst_range[FILE_NODE_DATA_LEN] = {'\0'};
+
+static void _clear_ttype_burst_range(void) {
+ int i;
+
+ for (i=0;i<BM_COUNTER_MAX;i++) {
+ ttype_byte_low_bnd_val[i] = 0x0;
+ ttype_byte_up_bnd_val[i] = 0x1FF;
+ ttype_byte_bnd_dis[i] = 1;
+ }
+ ttype_burst_range[0] = '\0';
+}
+
+static ssize_t ttype_burst_range_store(struct kobject *kobj,
+ struct kobj_attribute *attr,
+ const char *buf,
+ size_t n)
+{
+ char *token, *cur= ttype_burst_range;
+ char *_id = NULL, *_low_bnd = NULL, *_up_bnd = NULL;
+ int id_int = 0, low_bnd_int = 0, up_bnd_int = 0;
+
+ _clear_ttype_burst_range();
+
+ snprintf(ttype_burst_range, FILE_NODE_DATA_LEN, "%s", buf);
+ ttype_burst_range[n-1]='\0';
+
+
+ while (cur != NULL) {
+ token = strsep(&cur, delim_comma);
+ PR_BOOTMSG("token: %s\n",token);
+ /*token EX: 4:f , 5:C (ID,chn_rnk_sel)*/
+
+ _id = strsep(&token, delim_coclon); // ID
+ _low_bnd = strsep(&token, delim_coclon);
+ _up_bnd = strsep(&token, delim_coclon);
+
+ PR_BOOTMSG("_id[%s] _low_bnd[%s] _up_bnd[%s]\n",_id, _low_bnd, _up_bnd);
+
+ if (_id == NULL || _low_bnd == NULL || _up_bnd == NULL) {
+ PR_BOOTMSG("err (ttype_burst_range): _id[%s] _low_bnd[%s] _up_bnd[%s], para can't be NULL\n",
+ _id, _low_bnd, _up_bnd);
+ _clear_ttype_burst_range();
+ return -EINVAL;
+ }
+
+ if (kstrtouint(_id, 0, &id_int) != 0) {
+ PR_BOOTMSG("_id[%s] trans to hex err\n",_id);
+ _clear_ttype_burst_range();
+ return -EINVAL;
+ }
+ if (kstrtouint(_low_bnd, 0, &low_bnd_int) != 0) {
+ PR_BOOTMSG("_low_bnd[%s] trans to hex err\n",_id);
+ _clear_ttype_burst_range();
+ return -EINVAL;
+ }
+ if (kstrtouint(_up_bnd, 0, &up_bnd_int) != 0) {
+ PR_BOOTMSG("_up_bnd[%s] trans to hex err\n",_id);
+ _clear_ttype_burst_range();
+ return -EINVAL;
+ }
+
+ id_int = id_int - 1;
+ if ( id_int >= 0 && id_int < BM_COUNTER_MAX) {
+ ttype_byte_low_bnd_val[id_int] = low_bnd_int;
+ ttype_byte_up_bnd_val[id_int] = up_bnd_int;
+ ttype_byte_bnd_dis[id_int] = 0;
+ } else {
+ PR_BOOTMSG("id[%d] exceed the range, it must be 1~%d\n",id_int, BM_COUNTER_MAX);
+ _clear_ttype_burst_range();
+ return -EINVAL;
+ }
+ }
+
+#ifdef FILE_NODE_DBG
+ PR_BOOTMSG("ttype_burst_range_store input data [%s]\n",ttype_burst_range);
+
+ int i;
+ PR_BOOTMSG("ttype_burst_range save data\n");
+ for (i=0;i<BM_COUNTER_MAX;i++) {
+ PR_BOOTMSG("id[%d](low_bnd,up_bnd)=(%X,%X)\n",i+1,ttype_byte_low_bnd_val[i],ttype_byte_up_bnd_val[i]);
+ }
+#endif
+ return n;
+}
+
+static ssize_t ttype_burst_range_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
+{
+ return snprintf(buf, PAGE_SIZE, "%s\n", ttype_burst_range);
+}
+
+static int reserve_wsct_setting;
+DECLARE_KOBJ_ATTR_INT(reserve_wsct_setting, reserve_wsct_setting);
+
+static void _clear_setting(void) {
+ /*clear all file node para here*/
+
+ PR_BOOTMSG("clear EMI file node setting\n");
+
+ _clear_msel_group_ext();
+ _clear_wsct_rw();
+ _clear_wsct_high_priority_enable();
+ _clear_wsct_busid();
+ _clear_wsct_chn_rank_sel();
+ _clear_wsct_burst_range();
+
+ _clear_tsct_busid_enable();
+ _clear_ttype_high_priority_ext();
+ _clear_ttype_busid_ext();
+ _clear_ttype_chn_rank_sel();
+ _clear_ttype_burst_range();
+ reserve_wsct_setting = 0;
+
+
+
+ emi_TP_busfiltr_enable = 0;
+
+ high_priority_filter = 0x0;
+ rwtype = BM_BOTH_READ_WRITE;
+ dramc_pdir_enable = 1;
+
+
+ msel_enable = 0;
+ msel_group1 = BM_MASTER_ALL;
+ msel_group2 = BM_MASTER_ALL;
+ msel_group3 = BM_MASTER_ALL;
+
+
+ bw_limiter_enable = BM_BW_LIMITER_ENABLE;
+ ttype1_16_en = BM_TTYPE1_16_DISABLE;
+ ttype17_21_en = BM_TTYPE17_21_DISABLE;
+
+}
+
+static ssize_t clear_setting_store(struct kobject *kobj,
+ struct kobj_attribute *attr,
+ const char *buf,
+ size_t n)
+{
+ int value;
+
+ if ((n == 0) || (buf == NULL))
+ return -EINVAL;
+
+ if (kstrtoint(buf, 0, &value) != 0)
+ return -EINVAL;
+
+ if (value == 1)
+ _clear_setting();
+
+ return n;
+}
+
+static struct kobj_attribute clear_setting_attr = __ATTR_WO(clear_setting); // OK
+static struct kobj_attribute msel_group_ext_attr = __ATTR(msel_group_ext, 0664, msel_group_ext_show, msel_group_ext_store); //OK
+static struct kobj_attribute wsct_rw_attr = __ATTR(wsct_rw, 0664, wsct_rw_show, wsct_rw_store);
+static struct kobj_attribute wsct_high_priority_enable_attr = __ATTR(wsct_high_priority_enable, 0664, wsct_high_priority_enable_show, wsct_high_priority_enable_store);
+static struct kobj_attribute wsct_busid_attr = __ATTR(wsct_busid, 0664, wsct_busid_show, wsct_busid_store);
+static struct kobj_attribute wsct_chn_rank_sel_attr = __ATTR(wsct_chn_rank_sel, 0664, wsct_chn_rank_sel_show, wsct_chn_rank_sel_store);
+static struct kobj_attribute wsct_burst_range_attr = __ATTR(wsct_burst_range, 0664, wsct_burst_range_show, wsct_burst_range_store);
+static struct kobj_attribute tsct_busid_enable_attr = __ATTR(tsct_busid_enable, 0664, tsct_busid_enable_show, tsct_busid_enable_store);
+static struct kobj_attribute ttype_high_priority_ext_attr = __ATTR(ttype_high_priority_ext, 0664, ttype_high_priority_ext_show, ttype_high_priority_ext_store);
+static struct kobj_attribute ttype_busid_ext_attr = __ATTR(ttype_busid_ext, 0664, ttype_busid_ext_show, ttype_busid_ext_store);
+static struct kobj_attribute ttype_chn_rank_sel_attr = __ATTR(ttype_chn_rank_sel, 0664, ttype_chn_rank_sel_show, ttype_chn_rank_sel_store);
+static struct kobj_attribute ttype_burst_range_attr = __ATTR(ttype_burst_range, 0664, ttype_burst_range_show, ttype_burst_range_store);
+
+
+
+
+
+
+/**/
+#define KOBJ_ATTR_ITEM_SERIAL_FNODE(nr) \
+ do { \
+ 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); \
+ } while (0)
+
+#define KOBJ_ATTR_LIST \
+ do { \
+ KOBJ_ATTR_ITEM(high_priority_filter); \
+ KOBJ_ATTR_ITEM(emi_TP_busfiltr_enable); \
+ KOBJ_ATTR_ITEM(msel_enable); \
+ KOBJ_ATTR_ITEM(msel_group1); \
+ KOBJ_ATTR_ITEM(msel_group2); \
+ KOBJ_ATTR_ITEM(msel_group3); \
+ 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(bw_limiter_enable); \
+ KOBJ_ATTR_ITEM(dramc_pdir_enable); \
+ KOBJ_ATTR_ITEM(clear_setting);\
+ KOBJ_ATTR_ITEM(msel_group_ext);\
+ KOBJ_ATTR_ITEM(wsct_rw);\
+ KOBJ_ATTR_ITEM(wsct_high_priority_enable);\
+ KOBJ_ATTR_ITEM(wsct_busid);\
+ KOBJ_ATTR_ITEM(wsct_chn_rank_sel);\
+ KOBJ_ATTR_ITEM(wsct_burst_range);\
+ KOBJ_ATTR_ITEM(tsct_busid_enable);\
+ KOBJ_ATTR_ITEM(ttype_high_priority_ext);\
+ KOBJ_ATTR_ITEM(ttype_busid_ext);\
+ KOBJ_ATTR_ITEM(ttype_chn_rank_sel);\
+ KOBJ_ATTR_ITEM(ttype_burst_range);\
+ KOBJ_ATTR_ITEM(reserve_wsct_setting);\
+ } while (0)
+
+
+
+/*======================================================================*/
+/* EMI Operations */
+/*======================================================================*/
+static void emi_init(void)
+{
+ unsigned int bmrw0_val, bmrw1_val, i, enable;
+ /*unsigned int msel_group_val[4];*/
+
+ /*save origianl EMI config*/
+ MET_BM_SaveCfg();
+
+ /* get dram channel number */
+ dram_chann_num = MET_EMI_GetDramChannNum();
+
+ /* Init. EMI bus monitor */
+ MET_BM_SetReadWriteType(rwtype);
+
+ /*handle the ori */
+
+ if (ttype1_16_en != BM_TTYPE1_16_ENABLE) {
+ MET_BM_SetLatencyCounter(1); /*enable latency count*/
+ }
+ else {
+ MET_BM_SetLatencyCounter(0); /*disable latency count*/
+
+ 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]);
+ }
+ }
+
+ 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]);
+ }
+ }
+
+ PR_BOOTMSG("[%s]reserve_wsct_setting=%d\n",__func__,reserve_wsct_setting);
+
+ if (reserve_wsct_setting == 0) {
+ /* wsct 0 : total-all*/
+ msel_group_ext_val[0] = BM_MASTER_ALL;
+ wsct_rw_val[0] = BM_WSCT_RW_RWBOTH;
+ WSCT_HPRI_DIS[0] = 1;
+ WSCT_HPRI_SEL[0] = 0xF;
+ wsct_busid_val[0] = 0xFFFFF;
+ wsct_idMask_val[0] = 0x1FFF;
+ wsct_chn_rank_sel_val[0] = 0xF;
+ wsct_byte_bnd_dis[0] = 1;
+
+ /* wsct 4 : total-ultra*/
+ msel_group_ext_val[4] = BM_MASTER_ALL;
+ wsct_rw_val[4] = BM_WSCT_RW_RWBOTH;
+ WSCT_HPRI_DIS[4] = 0;
+ WSCT_HPRI_SEL[4] = 0x8; /* ultra */
+ wsct_busid_val[4] = 0xFFFFF;
+ wsct_idMask_val[4] = 0x1FFF;
+ wsct_chn_rank_sel_val[4] = 0xF;
+ wsct_byte_bnd_dis[4] = 1;
+
+ /* wsct 5 : total-pre_ultra*/
+ msel_group_ext_val[5] = BM_MASTER_ALL;
+ wsct_rw_val[5] = BM_WSCT_RW_RWBOTH;
+ WSCT_HPRI_DIS[5] = 0;
+ WSCT_HPRI_SEL[5] = 0x4; /* pre_ultra */
+ wsct_busid_val[5] = 0xFFFFF;
+ wsct_idMask_val[5] = 0x1FFF;
+ wsct_chn_rank_sel_val[5] = 0xF;
+ wsct_byte_bnd_dis[5] = 1;
+ }
+
+ if (msel_enable) {
+ /* if ole file node set, use the value */
+ if ( msel_group1 != BM_MASTER_ALL )
+ msel_group_ext_val[1] = msel_group1;
+
+ if ( msel_group2 != BM_MASTER_ALL )
+ msel_group_ext_val[2] = msel_group2;
+
+ if ( msel_group3 != BM_MASTER_ALL )
+ msel_group_ext_val[3] = msel_group3;
+
+ } else {
+ for ( i=1; i<=3; i++) {
+ msel_group_ext_val[i] = BM_MASTER_ALL;
+ }
+ }
+
+ MET_BM_SetWSCT_master_rw(msel_group_ext_val, wsct_rw_val);
+ MET_BM_SetWSCT_high_priority(WSCT_HPRI_DIS, WSCT_HPRI_SEL);
+ MET_BM_SetWSCT_busid_idmask(wsct_busid_val, wsct_idMask_val);
+ MET_BM_SetWSCT_chn_rank_sel(wsct_chn_rank_sel_val);
+ MET_BM_SetWSCT_burst_range(wsct_byte_bnd_dis, wsct_byte_low_bnd_val, wsct_byte_up_bnd_val);
+ MET_BM_SetTSCT_busid_enable(tsct_busid_enable_val);
+
+ MET_BM_SetTtype_high_priority_sel(high_priority_filter, TTYPE_HPRI_SEL);
+ MET_BM_SetTtype_busid_idmask(ttype_busid_val, ttype_idMask_val, ttype1_16_en, ttype17_21_en);
+ MET_BM_SetTtype_chn_rank_sel(ttype_chn_rank_sel_val);
+ MET_BM_SetTtype_burst_range(ttype_byte_bnd_dis, ttype_byte_low_bnd_val, ttype_byte_up_bnd_val);
+
+
+ bmrw0_val = 0;
+ for (i = 0; i < 16; i++)
+ bmrw0_val |= (ttype_rw_val[i] << (i * 2));
+
+ bmrw1_val = 0;
+ for (i = 16; i < 21; i++)
+ bmrw1_val |= (ttype_rw_val[i] << ((i-16) * 2));
+
+ MET_BM_SetTtypeCounterRW(bmrw0_val, bmrw1_val);
+
+}
+
+
+static void emi_uninit(void)
+{
+ MET_BM_RestoreCfg();
+}
+
+
+static inline int do_emi(void)
+{
+ return met_sspm_emi.mode;
+}
+
+
+
+
+
+
+
+/*======================================================================*/
+/* 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] = 0xfffff; /*default disable ttype bus sel if busid > 0xff_ff */
+ ttype_rw_val[i] = BM_TRANS_RW_DEFAULT;
+ }
+
+ _clear_msel_group_ext();
+ _clear_wsct_rw();
+ _clear_wsct_high_priority_enable();
+ _clear_wsct_busid();
+ _clear_wsct_chn_rank_sel();
+ _clear_wsct_burst_range();
+
+ _clear_tsct_busid_enable();
+ _clear_ttype_high_priority_ext();
+ _clear_ttype_high_priority_ext();
+ _clear_ttype_busid_ext();
+ _clear_ttype_chn_rank_sel();
+ _clear_ttype_burst_range();
+
+ reserve_wsct_setting = 0;
+
+
+ ret = MET_BM_Init();
+ if (ret != 0) {
+ pr_notice("MET_BM_Init failed!!!\n");
+ ret = 0; /* will retry later */
+ } else {
+ emi_inited = 1;
+ }
+
+ kobj_emi = parent;
+
+#define KOBJ_ATTR_ITEM(attr_name) \
+ do { \
+ ret = sysfs_create_file(kobj_emi, &attr_name ## _attr.attr); \
+ if (ret != 0) { \
+ pr_notice("Failed to create " #attr_name " in sysfs\n"); \
+ return ret; \
+ } \
+ } while (0)
+ 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_resume(void)
+{
+ if (!do_emi())
+ return;
+
+ emi_init();
+}
+
+
+static const 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;
+
+#if 1 /* move to AP side print header */
+/*#ifndef CONFIG_MTK_TINYSYS_SSPM_SUPPORT*/
+ unsigned int dram_data_rate_MHz;
+ unsigned int DRAM_TYPE;
+ unsigned int base_clock_rate;
+#endif
+
+
+ ret += snprintf(buf + ret, PAGE_SIZE - ret,
+ "met-info [000] 0.0: met_emi_wsct_amount: %d\n",WSCT_AMOUNT);
+
+ /* master selection header */
+ ret += snprintf(buf + ret, PAGE_SIZE - ret,
+ "met-info [000] 0.0: met_emi_msel: %x,%x,%x\n",
+ msel_group_ext_val[1] & BM_MASTER_ALL,
+ msel_group_ext_val[2] & BM_MASTER_ALL,
+ msel_group_ext_val[3] & BM_MASTER_ALL);
+
+ /*Ttype RW type header*/
+ PR_BOOTMSG("rwtype=%d\n",rwtype);
+ ret += snprintf(buf + ret, PAGE_SIZE - ret, "met-info [000] 0.0: met_emi_rw_cfg: ");
+ if (rwtype == BM_READ_ONLY)
+ ret += snprintf(buf + ret, PAGE_SIZE - ret, "R");
+ else if (rwtype == BM_WRITE_ONLY)
+ ret += snprintf(buf + ret, PAGE_SIZE - ret, "W");
+ else
+ ret += snprintf(buf + ret, PAGE_SIZE - ret, "BOTH");
+
+ for (i = 0; i < 21; i++) {
+ if (ttype_rw_val[i] == BM_TRANS_RW_DEFAULT)
+ ret += snprintf(buf + ret, PAGE_SIZE - ret, ",DEFAULT");
+ else if (ttype_rw_val[i] == BM_TRANS_RW_READONLY)
+ ret += snprintf(buf + ret, PAGE_SIZE - ret, ",R");
+ else if (ttype_rw_val[i] == BM_TRANS_RW_WRITEONLY)
+ ret += snprintf(buf + ret, PAGE_SIZE - ret, ",W");
+ else /*BM_TRANS_RW_RWBOTH*/
+ ret += snprintf(buf + ret, PAGE_SIZE - ret, ",BOTH");
+ }
+ ret += snprintf(buf + ret, PAGE_SIZE - ret, "\n");
+
+ /*ultra header*/
+ ret += snprintf(buf + ret, PAGE_SIZE - ret,
+ "met-info [000] 0.0: met_emi_ultra_filter: %x\n", high_priority_filter);
+
+ /* ttype header */
+ if (ttype17_21_en == BM_TTYPE17_21_ENABLE) {
+ int i = 0;
+ int j = 0;
+
+ /* ttype master list */
+ ret += snprintf(buf + ret, PAGE_SIZE - ret, "met-info [000] 0.0: met_emi_ttype_master_list: ");
+ for (i = 0; i < 21; i++) {
+ for (j = 0; j < ARRAY_SIZE(ttype_master_list_item); j++) {
+ if (ttype_master_val[i] == ttype_master_list_item[j].key) {
+ ret += snprintf(buf + ret, PAGE_SIZE - ret, "%s,", ttype_master_list_item[j].val);
+ }
+ }
+ }
+ /* remove the last comma */
+ snprintf(buf + ret -1, PAGE_SIZE - ret + 1, "\n");
+
+ /* ttype busid list */
+ ret += snprintf(buf + ret, PAGE_SIZE - ret, "met-info [000] 0.0: met_emi_ttype_busid_list: ");
+ for (i = 0; i < 21; i++)
+ ret += snprintf(buf + ret, PAGE_SIZE - ret, "%x,", ttype_busid_val[i]);
+
+ snprintf(buf + ret -1, PAGE_SIZE - ret + 1, "\n");
+
+ /* ttype nbeat list */
+ ret += snprintf(buf + ret, PAGE_SIZE - ret, "met-info [000] 0.0: met_emi_ttype_nbeat_list: ");
+ for (i = 0; i < 21; i++) {
+ for (j = 0; j < ARRAY_SIZE(ttype_nbeat_list_item); j++) {
+ if (ttype_nbeat_val[i] == ttype_nbeat_list_item[j].key) {
+ ret += snprintf(buf + ret, PAGE_SIZE - ret, "%d,", ttype_nbeat_list_item[j].val);
+ }
+ }
+ }
+ snprintf(buf + ret -1, PAGE_SIZE - ret + 1, "\n");
+
+ /* ttype nbyte list */
+ ret += snprintf(buf + ret, PAGE_SIZE - ret, "met-info [000] 0.0: met_emi_ttype_nbyte_list: ");
+ for (i = 0; i < 21; i++) {
+ for (j = 0; j < ARRAY_SIZE(ttype_nbyte_list_item); j++) {
+ if (ttype_nbyte_val[i] == ttype_nbyte_list_item[j].key) {
+ ret += snprintf(buf + ret, PAGE_SIZE - ret, "%d,", ttype_nbyte_list_item[j].val);
+ }
+ }
+ }
+ snprintf(buf + ret -1, PAGE_SIZE - ret + 1, "\n");
+
+ /* ttype burst list */
+ ret += snprintf(buf + ret, PAGE_SIZE - ret, "met-info [000] 0.0: met_emi_ttype_burst_list: ");
+ for (i = 0; i < 21; i++) {
+ for (j = 0; j < ARRAY_SIZE(ttype_burst_list_item); j++) {
+ if (ttype_burst_val[i] == ttype_burst_list_item[j].key) {
+ ret += snprintf(buf + ret, PAGE_SIZE - ret, "%s,", ttype_burst_list_item[j].val);
+ }
+ }
+ }
+ snprintf(buf + ret -1, PAGE_SIZE - ret + 1, "\n");
+
+ }
+ /* ttype enable */
+ ret += snprintf(buf + ret, PAGE_SIZE - ret, "met-info [000] 0.0: met_emi_ttype_enable: %d,%d\n",ttype1_16_en, ttype17_21_en);
+
+
+#if 1 /*SEDA 3.5*/
+
+ ret += snprintf(buf + ret, PAGE_SIZE - ret,
+ "met-info [000] 0.0: met_emi_msel_ext: %x,%x,%x\n",
+ msel_group_ext_val[0] & BM_MASTER_ALL,
+ msel_group_ext_val[4] & BM_MASTER_ALL,
+ msel_group_ext_val[5] & BM_MASTER_ALL);
+
+ ret += snprintf(buf + ret, PAGE_SIZE - ret, "met-info [000] 0.0: met_emi_wsct_rw: ");
+
+ for (i=0;i<WSCT_AMOUNT;i++) {
+ if (wsct_rw_val[i] == BM_WSCT_RW_RWBOTH)
+ ret += snprintf(buf + ret, PAGE_SIZE - ret, "RW,");
+ else if (wsct_rw_val[i] == BM_WSCT_RW_READONLY)
+ ret += snprintf(buf + ret, PAGE_SIZE - ret, "R,");
+ else if (wsct_rw_val[i] == BM_WSCT_RW_WRITEONLY)
+ ret += snprintf(buf + ret, PAGE_SIZE - ret, "W,");
+ else /*disable*/
+ ret += snprintf(buf + ret, PAGE_SIZE - ret, "NONE,");
+ }
+ snprintf(buf + ret -1, PAGE_SIZE - ret + 1, "\n");
+
+
+ ret += snprintf(buf + ret, PAGE_SIZE - ret, "met-info [000] 0.0: met_emi_wsct_HPRI_DIS: ");
+ for (i=0;i<WSCT_AMOUNT;i++) {
+ ret += snprintf(buf + ret, PAGE_SIZE - ret, "%d,",WSCT_HPRI_DIS[i]);
+ }
+ snprintf(buf + ret -1, PAGE_SIZE - ret + 1, "\n");
+
+
+ ret += snprintf(buf + ret, PAGE_SIZE - ret, "met-info [000] 0.0: met_emi_wsct_HPRI_SEL: ");
+ for (i=0;i<WSCT_AMOUNT;i++) {
+ ret += snprintf(buf + ret, PAGE_SIZE - ret, "%x,",WSCT_HPRI_SEL[i]);
+ }
+ snprintf(buf + ret -1, PAGE_SIZE - ret + 1, "\n");
+
+ ret += snprintf(buf + ret, PAGE_SIZE - ret, "met-info [000] 0.0: met_emi_wsct_busid: ");
+ for (i=0;i<WSCT_AMOUNT;i++) {
+ ret += snprintf(buf + ret, PAGE_SIZE - ret, "%x,",wsct_busid_val[i]);
+ }
+ snprintf(buf + ret -1, PAGE_SIZE - ret + 1, "\n");
+
+
+ ret += snprintf(buf + ret, PAGE_SIZE - ret, "met-info [000] 0.0: met_emi_wsct_idMask: ");
+ for (i=0;i<WSCT_AMOUNT;i++) {
+ ret += snprintf(buf + ret, PAGE_SIZE - ret, "%x,",wsct_idMask_val[i]);
+ }
+ snprintf(buf + ret -1, PAGE_SIZE - ret + 1, "\n");
+
+ ret += snprintf(buf + ret, PAGE_SIZE - ret, "met-info [000] 0.0: wsct_chn_rank_sel: ");
+ for (i=0;i<WSCT_AMOUNT;i++) {
+ ret += snprintf(buf + ret, PAGE_SIZE - ret, "%x,",wsct_chn_rank_sel_val[i]);
+ }
+ snprintf(buf + ret -1, PAGE_SIZE - ret + 1, "\n");
+
+ ret += snprintf(buf + ret, PAGE_SIZE - ret, "met-info [000] 0.0: wsct_byte_bnd_dis: ");
+ for (i=0;i<WSCT_AMOUNT;i++) {
+ ret += snprintf(buf + ret, PAGE_SIZE - ret, "%d,",wsct_byte_bnd_dis[i]);
+ }
+ snprintf(buf + ret -1, PAGE_SIZE - ret + 1, "\n");
+
+
+ ret += snprintf(buf + ret, PAGE_SIZE - ret, "met-info [000] 0.0: wsct_byte_low_bnd: ");
+ for (i=0;i<WSCT_AMOUNT;i++) {
+ ret += snprintf(buf + ret, PAGE_SIZE - ret, "%x,",wsct_byte_low_bnd_val[i]);
+ }
+ snprintf(buf + ret -1, PAGE_SIZE - ret + 1, "\n");
+
+ ret += snprintf(buf + ret, PAGE_SIZE - ret, "met-info [000] 0.0: wsct_byte_up_bnd: ");
+ for (i=0;i<WSCT_AMOUNT;i++) {
+ ret += snprintf(buf + ret, PAGE_SIZE - ret, "%x,",wsct_byte_up_bnd_val[i]);
+ }
+ snprintf(buf + ret -1, PAGE_SIZE - ret + 1, "\n");
+
+ ret += snprintf(buf + ret, PAGE_SIZE - ret, "met-info [000] 0.0: tsct_busid_enable: ");
+ for (i=0;i<TSCT_AMOUNT;i++) {
+ ret += snprintf(buf + ret, PAGE_SIZE - ret, "%d,",tsct_busid_enable_val[i]);
+ }
+ snprintf(buf + ret -1, PAGE_SIZE - ret + 1, "\n");
+
+ /***************************** ttype ****************************************/
+ if (ttype17_21_en == BM_TTYPE17_21_ENABLE) {
+
+ ret += snprintf(buf + ret, PAGE_SIZE - ret, "met-info [000] 0.0: TTYPE_HPRI_SEL: ");
+ for (i=0;i<BM_COUNTER_MAX;i++) {
+ ret += snprintf(buf + ret, PAGE_SIZE - ret, "%x,",TTYPE_HPRI_SEL[i]);
+ }
+ snprintf(buf + ret -1, PAGE_SIZE - ret + 1, "\n");
+
+ ret += snprintf(buf + ret, PAGE_SIZE - ret, "met-info [000] 0.0: ttype_idMask: ");
+ for (i=0;i<BM_COUNTER_MAX;i++) {
+ ret += snprintf(buf + ret, PAGE_SIZE - ret, "%x,",ttype_idMask_val[i]);
+ }
+ snprintf(buf + ret -1, PAGE_SIZE - ret + 1, "\n");
+
+ ret += snprintf(buf + ret, PAGE_SIZE - ret, "met-info [000] 0.0: ttype_chn_rank_sel: ");
+ for (i=0;i<BM_COUNTER_MAX;i++) {
+ ret += snprintf(buf + ret, PAGE_SIZE - ret, "%x,",ttype_chn_rank_sel_val[i]);
+ }
+ snprintf(buf + ret -1, PAGE_SIZE - ret + 1, "\n");
+
+ ret += snprintf(buf + ret, PAGE_SIZE - ret, "met-info [000] 0.0: ttype_byte_bnd_dis: ");
+ for (i=0;i<BM_COUNTER_MAX;i++) {
+ ret += snprintf(buf + ret, PAGE_SIZE - ret, "%d,",ttype_byte_bnd_dis[i]);
+ }
+ snprintf(buf + ret -1, PAGE_SIZE - ret + 1, "\n");
+
+ ret += snprintf(buf + ret, PAGE_SIZE - ret, "met-info [000] 0.0: ttype_byte_low_bnd_val: ");
+ for (i=0;i<BM_COUNTER_MAX;i++) {
+ ret += snprintf(buf + ret, PAGE_SIZE - ret, "%x,",ttype_byte_low_bnd_val[i]);
+ }
+ snprintf(buf + ret -1, PAGE_SIZE - ret + 1, "\n");
+
+ ret += snprintf(buf + ret, PAGE_SIZE - ret, "met-info [000] 0.0: ttype_byte_up_bnd_val: ");
+ for (i=0;i<BM_COUNTER_MAX;i++) {
+ ret += snprintf(buf + ret, PAGE_SIZE - ret, "%x,",ttype_byte_up_bnd_val[i]);
+ }
+ snprintf(buf + ret -1, PAGE_SIZE - ret + 1, "\n");
+ }
+#endif
+
+ /*IP version*/
+ ret += snprintf(buf + ret, PAGE_SIZE - ret,
+ "met-info [000] 0.0: DRAMC_VER: %d\n", DRAMC_VER);
+
+ ret += snprintf(buf + ret, PAGE_SIZE - ret,
+ "met-info [000] 0.0: EMI_VER: %d.%d\n", EMI_VER_MAJOR, EMI_VER_MINOR);
+
+#if 1 /* SEDA3.5 header print move to AP side */
+
+ dram_chann_num = MET_EMI_GetDramChannNum();
+
+ if (!get_cur_ddr_ratio_symbol)
+ PR_BOOTMSG("[%s][%d]get_cur_ddr_ratio_symbol = NULL , use the TYPE_LPDDR4 get_cur_ddr_ratio_symbol\n", __func__, __LINE__);
+
+ if (get_ddr_type_symbol) {
+ DRAM_TYPE = get_ddr_type_symbol();
+
+ base_clock_rate = MET_EMI_Get_BaseClock_Rate();
+
+ ret += snprintf(buf + ret, PAGE_SIZE - ret, "met-info [000] 0.0: met_dram_type: %d\n", DRAM_TYPE);
+
+ if ((DRAM_TYPE == 2) || (DRAM_TYPE == 3))
+ ret += snprintf(buf + ret, PAGE_SIZE - ret, "met-info [000] 0.0: met_dram_chann_num_header: %d,%d,%d,%d\n",
+ dram_chann_num, base_clock_rate,
+ DRAM_IO_BUS_WIDTH_LP4, DRAM_DATARATE);
+ else
+ ret += snprintf(buf + ret, PAGE_SIZE - ret, "met-info [000] 0.0: met_dram_chann_num_header: %d,%d,%d,%d\n",
+ dram_chann_num, base_clock_rate,
+ DRAM_IO_BUS_WIDTH_LP3, DRAM_DATARATE);
+ } else
+ METERROR("[%s][%d]get_ddr_type_symbol = NULL , use the TYPE_LPDDR3 setting\n", __func__, __LINE__);
+
+
+ /* met_emi_clockrate */
+ if (get_dram_data_rate_symbol) {
+ dram_data_rate_MHz = get_dram_data_rate_symbol();
+ } else {
+ METERROR("get_dram_data_rate_symbol = NULL\n");
+ dram_data_rate_MHz = 0;
+ }
+
+ ret += snprintf(buf + ret, PAGE_SIZE - ret,
+ "met-info [000] 0.0: met_dram_clockrate: %d\n",
+ dram_data_rate_MHz);
+
+
+ /*dram bank num*/
+ ret += snprintf(buf + ret, PAGE_SIZE - ret,
+ "met-info [000] 0.0: met_dram_rank_num_header: %u,%u\n", MET_EMI_GetDramRankNum(),
+ MET_EMI_GetDramRankNum());
+
+ /* ms_emi header */
+ 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,WACT,DCM_CTRL,TACT,");
+
+ 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,WACT,DCM_CTRL,TACT,");
+
+ 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_emi_tsct_header: ms_emi_tsct,");
+ ret += snprintf(buf + ret, PAGE_SIZE - ret,
+ "tsct1,tsct2,tsct3\n");
+ }
+
+ /*MDCT header*/
+ if (emi_mdct_enable == 1) {
+ ret += snprintf(buf + ret, PAGE_SIZE - ret,
+ "met-info [000] 0.0: ms_emi_mdct_header: ms_emi_mdct,");
+ ret += snprintf(buf + ret, PAGE_SIZE - ret,
+ "RD_ULTRA,RD_MDMCU\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 header */
+ ret += snprintf(buf + ret, PAGE_SIZE - ret,
+ "met-info [000] 0.0: DRAM_DVFS_header: datarate(MHz)\n");
+
+ /*PDIR met_dramc_header*/
+ if (dramc_pdir_enable == 1 && DRAMC_VER >= 2 ) {
+ 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, "freerun_26m_%d,", i);
+ 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");
+ }
+
+ /* DRS header */
+ ret += snprintf(buf + ret, PAGE_SIZE - ret,
+ "met-info [000] 0.0: emi_drs_header: ch0_RANK1_GP(%%),ch0_RANK1_SF(%%),ch0_ALL_SF(%%),ch1_RANK1_GP(%%),ch1_RANK1_SF(%%),ch1_ALL_SF(%%)\n");
+#endif
+
+ return ret;
+}
+
+
+
+static int ondiemet_emi_print_header(char *buf, int len)
+{
+ return emi_print_header(buf, len);
+}
+
+
+
+static void MET_BM_IPI_REGISTER_CB(void)
+{
+ int ret, i;
+ unsigned int rdata;
+ unsigned int ipi_buf[4];
+
+ for (i = 0; i < 4; i++)
+ ipi_buf[i] = 0;
+
+ if (sspm_buf_available == 1) {
+ ipi_buf[0] = MET_MAIN_ID | (MID_EMI << MID_BIT_SHIFT) | MET_ARGU | SET_REGISTER_CB;
+ ret = sspm_ipi_send_sync(IPI_ID_MET, IPI_OPT_WAIT, (void *)ipi_buf, 0, &rdata, 1);
+ }
+}
+
+
+static void MET_BM_IPI_configs(void)
+{
+ int ret, i;
+ unsigned int rdata;
+ unsigned int ipi_buf[4];
+
+ for (i = 0; i < 4; i++)
+ ipi_buf[i] = 0;
+
+ if (sspm_buf_available == 1) {
+ ipi_buf[0] = MET_MAIN_ID | (MID_EMI << MID_BIT_SHIFT) | MET_ARGU | SET_EBM_CONFIGS1;
+ ipi_buf[2] = EMI_VER_MAJOR << 24 | EMI_VER_MINOR << 16 | DRAMC_VER << 8 | 0;
+ ret = sspm_ipi_send_sync(IPI_ID_MET, IPI_OPT_WAIT, (void *)ipi_buf, 0, &rdata, 1);
+ }
+}
+
+
+static void ondiemet_emi_start(void)
+{
+ MET_BM_IPI_REGISTER_CB();
+ if (!emi_inited) {
+ if (MET_BM_Init() != 0) {
+ met_sspm_emi.mode = 0;
+ pr_notice("MET_BM_Init failed!!!\n");
+ return;
+ }
+ emi_inited = 1;
+ }
+ MET_BM_IPI_configs();
+
+ if (do_emi())
+ emi_init();
+
+ ondiemet_module[ONDIEMET_SSPM] |= ID_EMI;
+}
+
+static void ondiemet_emi_stop(void)
+{
+ if (!emi_inited)
+ return;
+
+ if (do_emi())
+ emi_uninit();
+}
+
+
+
+struct metdevice met_sspm_emi = {
+ .name = "emi",
+ .owner = THIS_MODULE,
+ .type = MET_TYPE_BUS,
+ .create_subfs = met_emi_create,
+ .delete_subfs = met_emi_delete,
+ .resume = met_emi_resume,
+#if defined(CONFIG_MTK_TINYSYS_SSPM_SUPPORT) && defined(ONDIEMET_SUPPORT)
+ .ondiemet_start = ondiemet_emi_start,
+ .ondiemet_stop = ondiemet_emi_stop,
+ .ondiemet_print_help = emi_print_help,
+ .ondiemet_print_header = ondiemet_emi_print_header,
+#endif
+ .ondiemet_mode = 1,
+};
+EXPORT_SYMBOL(met_sspm_emi);
\ No newline at end of file
diff --git a/src/devtools/met-driver/4.14/common/met_kernel_symbol.h b/src/devtools/met-driver/4.14/common/met_kernel_symbol.h
new file mode 100644
index 0000000..2eec3ce
--- /dev/null
+++ b/src/devtools/met-driver/4.14/common/met_kernel_symbol.h
@@ -0,0 +1,59 @@
+/*
+ * 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);
+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);
+#ifdef MET_EVENT_POWER_SUPPORT
+extern int met_reg_event_power(void);
+extern int (*met_reg_event_power_symbol)(void);
+extern void met_unreg_event_power(void);
+extern void (*met_unreg_event_power_symbol)(void);
+#endif
+extern void met_arch_setup_dma_ops(struct device *dev);
+extern int met_perf_event_read_local(struct perf_event *ev, u64 *value);
+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.14/common/met_main.c b/src/devtools/met-driver/4.14/common/met_main.c
new file mode 100644
index 0000000..16fa86b
--- /dev/null
+++ b/src/devtools/met-driver/4.14/common/met_main.c
@@ -0,0 +1,400 @@
+/*
+ * 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-a53", "CA53"},
+ {"arm,cortex-a55", "CA55"},
+ {"arm,cortex-a73", "CA73"},
+ {"arm,cortex-a75", "CA75"},
+};
+#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);
+
+#ifdef MET_EVENT_POWER_SUPPORT
+int (*met_reg_event_power_symbol)(void);
+void (*met_unreg_event_power_symbol)(void);
+#endif
+
+void (*met_arch_setup_dma_ops_symbol)(struct device *dev);
+int (*met_perf_event_read_local_symbol)(struct perf_event *ev, u64 *value);
+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);
+
+#ifdef MTK_PLATFORM
+#define _SHOW_MTK_PLATFORM(X) #X
+#define SHOW_MTK_PLATFORM(X) _SHOW_MTK_PLATFORM(X)
+#endif
+
+static int is_platform_name_valid(const char * buf)
+{
+ int len = strlen(buf);
+ int i;
+
+ for (i=0; i<len; i++) {
+ if ((buf[i] == 'm' && buf[i+1] == 't')
+ || (buf[i] == 'M' && buf[i+1] == 'T')
+ || (buf[i] == 'M' && buf[i+1] == 't')
+ || (buf[i] == 'm' && buf[i+1] == 'T')) {
+ return 1;
+ }
+ }
+ return 0;
+}
+
+const char *met_get_platform_name(void)
+{
+ const char default_platform_name[7] = "mtxxxx";
+ int found = 0;
+
+ found = is_platform_name_valid(platform_name);
+
+ if(found == 0){
+#ifdef MTK_PLATFORM
+ const char buf[7] = SHOW_MTK_PLATFORM(MTK_PLATFORM);
+ found = is_platform_name_valid(buf);
+ if(found == 1)
+ platform_name = buf;
+ else
+#endif
+ platform_name = default_platform_name;
+ }
+ 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)))
+ strscpy(cpu_type, met_known_cpu_type[i].abbr_name,sizeof(cpu_type));
+ }
+}
+
+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, j, len;
+ struct device_node *node = NULL, *core_node = NULL;
+ int start_core_id = 0;
+ int cluster_num = 0, cluster_core_num = 0;
+ char cluster_name[16], core_name[16];
+
+ node = of_find_node_by_name(NULL, "cpu-map");
+ if (!node)
+ node = of_find_node_by_name(NULL, "virtual-cpu-map");
+
+ if (node) {
+ cluster_num = of_get_child_count(node);
+ of_node_put(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) {
+
+ j = 0;
+ cluster_core_num = 0;
+ do {
+ snprintf(core_name, sizeof(core_name), "core%d", j);
+ core_node = of_get_child_by_name(node, core_name);
+ if (core_node) {
+ cluster_core_num++;
+ of_node_put(core_node);
+ }
+ j++;
+ } while (core_node);
+ of_node_put(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;
+
+#ifdef MET_EVENT_POWER_SUPPORT
+ if (met_reg_event_power_symbol == NULL)
+ met_reg_event_power_symbol = (void *)symbol_get(met_reg_event_power);
+ if (met_reg_event_power_symbol == NULL)
+ ret = -11;
+
+ if (met_unreg_event_power_symbol == NULL)
+ met_unreg_event_power_symbol = (void *)symbol_get(met_unreg_event_power);
+ if (met_unreg_event_power_symbol == NULL)
+ ret = -12;
+#endif
+
+ 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, "compatible", &platform_name);
+ PR_BOOTMSG("dts property compatible=%s\n", platform_name);
+ }
+ }
+ if (platform_name) {
+ char buf[7];
+ int len = strlen(platform_name);
+ int i;
+ int found = 0;
+
+ for (i=0; i<len; i++) {
+ if ((platform_name[i] == 'm' && platform_name[i+1] == 't')
+ || (platform_name[i] == 'M' && platform_name[i+1] == 'T')
+ || (platform_name[i] == 'M' && platform_name[i+1] == 't')
+ || (platform_name[i] == 'm' && platform_name[i+1] == 'T')) {
+ /* be sure to have 4 chars (chip id) behind 'mt' for copy */
+ if (len - i - 2 >= 4)
+ found = 1;
+ break;
+ }
+ }
+
+ if (found == 1) {
+ memset(buf, 0x0, 7);
+ buf[0] = 'm';
+ buf[1] = 't';
+ strncpy(&buf[2], &platform_name[i+2], 4);
+ met_set_platform(buf, 1);
+ PR_BOOTMSG("Get platform info from dts, platform_name=%s\n", buf);
+ } else {
+#ifdef MTK_PLATFORM
+ memset(buf, 0x0, 7);
+ strcpy(buf, SHOW_MTK_PLATFORM(MTK_PLATFORM));
+ found = is_platform_name_valid((const char *)buf);
+ if(found == 1){
+ PR_BOOTMSG("Get platform info from met_drv Kbuild, platform_name=%s\n", buf);
+ met_set_platform(buf, 1);
+ }
+ else
+#endif
+ {
+ PR_BOOTMSG("Can not get platform info from dts nor met_drv Kbuild, set platform_name=mtxxxx\n");
+ met_set_platform("mtxxxx", 1);
+ }
+ }
+ }
+
+ 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);
+
+#ifdef MET_EVENT_POWER_SUPPORT
+ if (met_reg_event_power_symbol)
+ symbol_put(met_reg_event_power);
+ if (met_unreg_event_power_symbol)
+ symbol_put(met_unreg_event_power);
+#endif
+
+ 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.14/common/met_power.h b/src/devtools/met-driver/4.14/common/met_power.h
new file mode 100644
index 0000000..fc82995
--- /dev/null
+++ b/src/devtools/met-driver/4.14/common/met_power.h
@@ -0,0 +1,63 @@
+/*
+ * * 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_POWER
+#define MET_POWER
+
+enum {
+ _PM_QOS_RESERVED = 0,
+ _PM_QOS_CPU_DMA_LATENCY,
+ _PM_QOS_NETWORK_LATENCY,
+ _PM_QOS_NETWORK_THROUGHPUT,
+ _PM_QOS_MEMORY_BANDWIDTH,
+
+ _PM_QOS_CPU_MEMORY_BANDWIDTH,
+ _PM_QOS_GPU_MEMORY_BANDWIDTH,
+ _PM_QOS_MM_MEMORY_BANDWIDTH,
+ _PM_QOS_OTHER_MEMORY_BANDWIDTH,
+ _PM_QOS_MM0_BANDWIDTH_LIMITER,
+ _PM_QOS_MM1_BANDWIDTH_LIMITER,
+
+ _PM_QOS_DDR_OPP,
+ _PM_QOS_VCORE_OPP,
+ _PM_QOS_SCP_VCORE_REQUEST,
+ _PM_QOS_POWER_MODEL_DDR_REQUEST,
+ _PM_QOS_POWER_MODEL_VCORE_REQUEST,
+ _PM_QOS_VCORE_DVFS_FORCE_OPP,
+
+ _PM_QOS_DISP_FREQ,
+ _PM_QOS_MDP_FREQ,
+ _PM_QOS_VDEC_FREQ,
+ _PM_QOS_VENC_FREQ,
+ _PM_QOS_IMG_FREQ,
+ _PM_QOS_CAM_FREQ,
+ _PM_QOS_VVPU_OPP,
+ _PM_QOS_VMDLA_OPP,
+ _PM_QOS_ISP_HRT_BANDWIDTH,
+ _PM_QOS_APU_MEMORY_BANDWIDTH,
+ /* insert new class ID */
+ _PM_QOS_NUM_CLASSES,
+};
+/* Action requested to pm_qos_update_target */
+enum _pm_qos_req_action {
+ _PM_QOS_ADD_REQ, /* Add a new request */
+ _PM_QOS_UPDATE_REQ, /* Update an existing request */
+ _PM_QOS_REMOVE_REQ /* Remove an existing request */
+};
+
+extern void pm_qos_update_request(int pm_qos_class, s32 value, char *owner);
+extern void pm_qos_update_target(unsigned int action, int prev_value, int curr_value);
+
+#endif /* MET_DRV */
+
diff --git a/src/devtools/met-driver/4.14/common/met_ptpod.c b/src/devtools/met-driver/4.14/common/met_ptpod.c
new file mode 100644
index 0000000..fce982b
--- /dev/null
+++ b/src/devtools/met-driver/4.14/common/met_ptpod.c
@@ -0,0 +1,377 @@
+/*
+ * 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.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/random.h>
+#include <linux/fs.h>
+
+#include "met_drv.h"
+#include "trace.h"
+#include "core_plf_init.h"
+#include "core_plf_trace.h"
+
+static unsigned int MT_GPU_DVFS_IDX = NR_MT_CPU_DVFS;
+static unsigned int g_u4GPUVolt;
+static unsigned int g_u4Volt[NR_MT_CPU_DVFS + 1];
+
+/* g_ap_ptpod: cpu volt from ap or sspm setting
+ * if 1: cpu volt from ap
+ * if 0: cpu volt from sspm */
+static unsigned int g_ap_ptpod;
+
+/* gpu_volt_enable:
+ * if 1: enable gpu volt to output
+ * if 0: disable gpu volt to output */
+static unsigned int gpu_volt_enable = 1;
+
+/* get_volt_by_wq:
+ * if 1: get cpu/gpu volt by workqueue
+ * if 0: get cpu/gpu volt in irq */
+static unsigned int get_volt_by_wq;
+
+static int ptpod_started;
+static struct kobject *kobj_ptpod;
+static struct delayed_work get_volt_dwork;
+
+noinline void ms_ptpod(void)
+{
+ char *SOB, *EOB;
+
+ if (g_ap_ptpod) {
+ MET_TRACE_GETBUF(&SOB, &EOB);
+
+ if (gpu_volt_enable) {
+ g_u4Volt[MT_GPU_DVFS_IDX] = g_u4GPUVolt;
+ EOB = ms_formatD_EOL(EOB, ARRAY_SIZE(g_u4Volt), g_u4Volt);
+ } else
+ EOB = ms_formatD_EOL(EOB, ARRAY_SIZE(g_u4Volt) - 1, g_u4Volt);
+
+ MET_TRACE_PUTBUF(SOB, EOB);
+ } else {
+ if (gpu_volt_enable) {
+ MET_TRACE_GETBUF(&SOB, &EOB);
+ EOB = ms_formatD_EOL(EOB, 1, &g_u4GPUVolt);
+ MET_TRACE_PUTBUF(SOB, EOB);
+ }
+ }
+}
+
+#if 0
+static void ptpod_cpu_voltSampler(enum mt_cpu_dvfs_id id, unsigned int volt)
+{
+ switch (id) {
+ case MT_CPU_DVFS_LL:
+ g_u4CPUVolt_LL = volt;
+ break;
+ case MT_CPU_DVFS_L:
+ g_u4CPUVolt_L = volt;
+ break;
+ case MT_CPU_DVFS_CCI:
+ g_u4CPUVolt_CCI = volt;
+ break;
+ default:
+ return;
+ }
+ ptpod();
+}
+#endif
+
+#if 0
+static void ptpod_gpu_voltSampler(unsigned int a_u4Volt)
+{
+ g_u4GPUVolt = (a_u4Volt+50)/100;
+
+ if (ptpod_started)
+ ptpod();
+}
+#endif
+
+#define PTPOD_CONF_SHOW_IMPLEMENT(var) \
+ do { \
+ int i; \
+ i = snprintf(buf, PAGE_SIZE, "%d\n", var); \
+ return i; \
+ } while (0)
+
+#define PTPOD_CONF_STORE_IMPLEMENT(var) \
+ do { \
+ int value; \
+ if ((n == 0) || (buf == NULL)) \
+ return -EINVAL; \
+ if (kstrtoint(buf, 0, &value) != 0) \
+ return -EINVAL; \
+ if (value == 1) \
+ var = 1; \
+ else \
+ var = 0; \
+ return n; \
+ } while (0)
+
+
+static ssize_t ap_ptpod_enable_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
+{
+ PTPOD_CONF_SHOW_IMPLEMENT(g_ap_ptpod);
+}
+
+static ssize_t ap_ptpod_enable_store(struct kobject *kobj,
+ struct kobj_attribute *attr,
+ const char *buf,
+ size_t n)
+{
+ PTPOD_CONF_STORE_IMPLEMENT(g_ap_ptpod);
+}
+
+static ssize_t gpu_volt_enable_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
+{
+ PTPOD_CONF_SHOW_IMPLEMENT(gpu_volt_enable);
+}
+
+static ssize_t gpu_volt_enable_store(struct kobject *kobj,
+ struct kobj_attribute *attr,
+ const char *buf,
+ size_t n)
+{
+ PTPOD_CONF_STORE_IMPLEMENT(gpu_volt_enable);
+}
+
+static ssize_t get_volt_by_wq_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
+{
+ PTPOD_CONF_SHOW_IMPLEMENT(get_volt_by_wq);
+}
+
+static ssize_t get_volt_by_wq_store(struct kobject *kobj,
+ struct kobj_attribute *attr,
+ const char *buf,
+ size_t n)
+{
+ PTPOD_CONF_STORE_IMPLEMENT(get_volt_by_wq);
+}
+
+static struct kobj_attribute ap_ptpod_enable_attr = __ATTR(ap_ptpod_enable, 0664, ap_ptpod_enable_show, ap_ptpod_enable_store);
+static struct kobj_attribute gpu_volt_enable_attr = __ATTR(gpu_volt_enable, 0664, gpu_volt_enable_show, gpu_volt_enable_store);
+static struct kobj_attribute get_volt_by_wq_attr = __ATTR(get_volt_by_wq, 0664, get_volt_by_wq_show, get_volt_by_wq_store);
+
+/* create ptpod related kobj node */
+#define KOBJ_ATTR_LIST \
+ do { \
+ KOBJ_ATTR_ITEM(ap_ptpod_enable); \
+ KOBJ_ATTR_ITEM(gpu_volt_enable); \
+ KOBJ_ATTR_ITEM(get_volt_by_wq); \
+ } while (0)
+
+static int ptpod_create(struct kobject *parent)
+{
+ int ret = 0;
+
+ kobj_ptpod = parent;
+
+#define KOBJ_ATTR_ITEM(attr_name) \
+ do { \
+ ret = sysfs_create_file(kobj_ptpod, &attr_name ## _attr.attr); \
+ if (ret != 0) { \
+ pr_notice("Failed to create " #attr_name " in sysfs\n"); \
+ return ret; \
+ } \
+ } while (0)
+ KOBJ_ATTR_LIST;
+#undef KOBJ_ATTR_ITEM
+
+ return 0;
+}
+
+static void ptpod_delete(void)
+{
+#define KOBJ_ATTR_ITEM(attr_name) \
+ sysfs_remove_file(kobj_ptpod, &attr_name ## _attr.attr)
+
+ if (kobj_ptpod != NULL) {
+ KOBJ_ATTR_LIST;
+ kobj_ptpod = NULL;
+ }
+#undef KOBJ_ATTR_ITEM
+}
+
+static void update_volt_value(void)
+{
+ int i;
+
+ if (g_ap_ptpod && mt_cpufreq_get_cur_volt_symbol) {
+ /*
+ g_u4CPUVolt_LL = mt_cpufreq_get_cur_volt_symbol(MT_CPU_DVFS_LL)/100;
+ g_u4CPUVolt_L = mt_cpufreq_get_cur_volt_symbol(MT_CPU_DVFS_L)/100;
+ g_u4CPUVolt_CCI = mt_cpufreq_get_cur_volt_symbol(MT_CPU_DVFS_CCI)/100;
+ */
+ for (i = 0; i < NR_MT_CPU_DVFS; i++)
+ g_u4Volt[i] = mt_cpufreq_get_cur_volt_symbol(i) / 100;
+ }
+
+ if (gpu_volt_enable) {
+ if (mt_gpufreq_get_cur_volt_symbol)
+ g_u4GPUVolt = ((mt_gpufreq_get_cur_volt_symbol() + 50) / 100);
+ }
+}
+
+static void get_volt_notify(unsigned long long stamp, int cpu)
+{
+ schedule_delayed_work(&get_volt_dwork, 0);
+}
+
+static void met_ptpod_polling_by_wq(struct work_struct *work)
+{
+ update_volt_value();
+
+ ms_ptpod();
+}
+
+static void met_ptpod_polling(unsigned long long stamp, int cpu)
+{
+ update_volt_value();
+
+ ms_ptpod();
+}
+
+/*
+ * Called from "met-cmd --start"
+ */
+static void ptpod_start(void)
+{
+#if 0
+ met_gpufreq_setvolt_registerCB(ptpod_gpu_voltSampler, kFOR_MET_PTPOD_USE);
+#endif
+
+ if (get_volt_by_wq) {
+ met_ptpod.timed_polling = get_volt_notify;
+
+ INIT_DELAYED_WORK(&get_volt_dwork, met_ptpod_polling_by_wq);
+ } else
+ met_ptpod.timed_polling = met_ptpod_polling;
+
+ update_volt_value();
+
+ ms_ptpod();
+
+#if 0
+ /* register callback */
+ if (mt_cpufreq_setvolt_registerCB_symbol)
+ mt_cpufreq_setvolt_registerCB_symbol(ptpod_cpu_voltSampler);
+#endif
+
+ ptpod_started = 1;
+}
+
+/*
+ * Called from "met-cmd --stop"
+ */
+static void ptpod_stop(void)
+{
+ ptpod_started = 0;
+
+#if 0
+ /* unregister callback */
+ if (mt_cpufreq_setvolt_registerCB_symbol)
+ mt_cpufreq_setvolt_registerCB_symbol(NULL);
+#endif
+
+ if (get_volt_by_wq)
+ cancel_delayed_work_sync(&get_volt_dwork);
+
+ update_volt_value();
+
+ ms_ptpod();
+
+#if 0
+ met_gpufreq_setvolt_registerCB(NULL, kFOR_MET_PTPOD_USE);
+#endif
+}
+
+static char help[] =
+ " --ptpod Measure CPU/GPU voltage\n";
+static int ptpod_print_help(char *buf, int len)
+{
+ return snprintf(buf, PAGE_SIZE, help);
+}
+
+/*
+ * It will be called back when run "met-cmd --extract" and mode is 1
+ */
+static int ptpod_print_header(char *buf, int len)
+{
+ int str_len = 0;
+
+ if (g_ap_ptpod) {
+ int i;
+
+ str_len += snprintf(buf + str_len, PAGE_SIZE - str_len,
+ "met-info [000] 0.0: met_ptpod_header: ");
+
+ for (i = 0; i < NR_MT_CPU_DVFS; i++)
+ {
+#ifdef MT_CPU_DVFS_CCI
+ if( i == MT_CPU_DVFS_CCI )
+ {
+ str_len += snprintf(buf + str_len, PAGE_SIZE - str_len, "CPUVolt_CCI,", i);
+ }
+ else
+ {
+ str_len += snprintf(buf + str_len, PAGE_SIZE - str_len, "CPUVolt_%d,", i);
+ }
+#else
+ str_len += snprintf(buf + str_len, PAGE_SIZE - str_len, "CPUVolt_%d,", i);
+#endif
+ }
+
+ if (gpu_volt_enable)
+ str_len += snprintf(buf + str_len, PAGE_SIZE - str_len, "GPUVolt,");
+
+ buf[str_len-1] = '\n';
+
+ str_len += snprintf(buf + str_len, PAGE_SIZE - str_len,
+ "met-info [000] 0.0: met_ptpod_version: ap\n");
+ } else {
+ if (gpu_volt_enable) {
+ str_len += snprintf(buf + str_len, PAGE_SIZE - str_len,
+ "met-info [000] 0.0: met_ptpod_header: ");
+
+ str_len += snprintf(buf + str_len, PAGE_SIZE - str_len, "GPUVolt\n");
+ }
+
+ str_len += snprintf(buf + str_len, PAGE_SIZE - str_len,
+ "met-info [000] 0.0: met_ptpod_version: sspm\n");
+ }
+
+ if (gpu_volt_enable)
+ str_len += snprintf(buf + str_len, PAGE_SIZE - str_len,
+ "met-info [000] 0.0: met_ptpod_gpu_volt_enable: YES\n");
+ else
+ str_len += snprintf(buf + str_len, PAGE_SIZE - str_len,
+ "met-info [000] 0.0: met_ptpod_gpu_volt_enable: NO\n");
+
+ return str_len;
+}
+
+struct metdevice met_ptpod = {
+ .name = "ptpod",
+ .owner = THIS_MODULE,
+ .type = MET_TYPE_PMU,
+ .cpu_related = 0,
+ .create_subfs = ptpod_create,
+ .delete_subfs = ptpod_delete,
+ .start = ptpod_start,
+ .stop = ptpod_stop,
+ .timed_polling = met_ptpod_polling,
+ .print_help = ptpod_print_help,
+ .print_header = ptpod_print_header,
+};
+EXPORT_SYMBOL(met_ptpod);
diff --git a/src/devtools/met-driver/4.14/common/met_struct.h b/src/devtools/met-driver/4.14/common/met_struct.h
new file mode 100644
index 0000000..a74ff78
--- /dev/null
+++ b/src/devtools/met-driver/4.14/common/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.14/common/met_tag.h b/src/devtools/met-driver/4.14/common/met_tag.h
new file mode 100644
index 0000000..04b5e09
--- /dev/null
+++ b/src/devtools/met-driver/4.14/common/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.14/common/met_tag_ex.c b/src/devtools/met-driver/4.14/common/met_tag_ex.c
new file mode 100644
index 0000000..4413b4e
--- /dev/null
+++ b/src/devtools/met-driver/4.14/common/met_tag_ex.c
@@ -0,0 +1,644 @@
+/*
+ * 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 BUILD_WITH_MET
+
+#ifdef MET_USER_EVENT_SUPPORT
+
+#include <linux/kernel.h>
+#include <linux/mutex.h>
+#include <linux/slab.h>
+#include <linux/mm.h>
+#include <linux/fs.h>
+/* #include <asm/uaccess.h> */
+#include <linux/uaccess.h>
+
+#include "met_drv.h"
+#include "met_tag.h"
+#include "interface.h"
+#include "switch.h"
+#include "met_api_tbl.h"
+
+struct bltable_t bltab;
+
+static int dump_buffer_size;
+static int dump_data_size;
+static int dump_overrun;
+static int dump_overrun_size;
+static int dump_seq_no;
+static void *dump_buffer;
+
+#define OPFLAG_OVERWRITE 0x1
+static unsigned int options_flag;
+
+#define DEVICE_NAME "met_tag"
+
+/* #define ERRF_ENABLE */
+/* #define DEBF_ENABLE */
+
+#ifdef ERRF_ENABLE
+#define MSG_ERR "Error:["DEVICE_NAME"]"
+#define ERRF(args...) pr_debug(MSG_ERR args)
+#else
+#define ERRF(args...)
+#endif
+
+#ifdef DEBF_ENABLE
+#define MSG_IFO "Info :["DEVICE_NAME"]"
+#define DEBF(args...) pr_debug(MSG_IFO args)
+#else
+#define DEBF(args...)
+#endif
+
+static int is_enabled(unsigned int class_id)
+{
+ int i;
+
+ if (bltab.flag == 0)
+ return 1;
+ if (bltab.flag & MET_CLASS_ALL)
+ return 0;
+
+ for (i = 0; i < MAX_EVENT_CLASS; i++) {
+ if ((bltab.flag & (1 << i)) && (bltab.class_id[i] == class_id))
+ return 0;
+ }
+
+ return 1;
+}
+
+noinline int tracing_mark_write(int type, unsigned int class_id,
+ const char *name, unsigned int value,
+ unsigned int value2, unsigned int value3)
+{
+ if (type == TYPE_MET_SUSPEND) {
+ MET_TRACE("C|0|MET_SUSPEND|1");
+ return 0;
+ }
+ if (type == TYPE_MET_RESUME) {
+ MET_TRACE("C|0|MET_SUSPEND|0");
+ return 0;
+ }
+ if (!is_enabled(class_id))
+ return 0;
+ switch (type) {
+ case TYPE_START:
+ MET_TRACE("B|%d|%s\n", class_id, name);
+ break;
+ case TYPE_END:
+ MET_TRACE("E|%s\n", name);
+ break;
+ case TYPE_ONESHOT:
+ MET_TRACE("C|%d|%s|%d\n", class_id, name, value);
+ break;
+ case TYPE_ASYNC_START:
+ MET_TRACE("S|%d|%s|%d\n", class_id, name, value);
+ break;
+ case TYPE_ASYNC_END:
+ MET_TRACE("F|%d|%s|%d\n", class_id, name, value);
+ break;
+ case TYPE_DUMP:
+ MET_TRACE("D|%d|%s|%d|%d|%d\n", class_id, name, value, value2, value3);
+ break;
+ default:
+ return -1;
+ }
+ return 0;
+}
+
+int met_tag_init(void)
+{
+ memset(&bltab, 0, sizeof(struct bltable_t));
+ bltab.flag = MET_CLASS_ALL;
+ mutex_init(&bltab.mlock);
+ return 0;
+}
+
+int met_tag_uninit(void)
+{
+ met_set_dump_buffer_real(0);
+ return 0;
+}
+
+int met_tag_start_real(unsigned int class_id, const char *name)
+{
+ int ret;
+
+ ret = tracing_mark_write(TYPE_START, class_id, name, 0, 0, 0);
+#if 0
+ if ((met_switch.mode & MT_SWITCH_TAGPOLLING)) {
+ /* tag polling only enable when MT_SWITCH_TAGPOLLING is config */
+ force_sample(NULL);
+ }
+#endif
+ return ret;
+}
+EXPORT_SYMBOL(met_tag_start_real);
+
+int met_tag_end_real(unsigned int class_id, const char *name)
+{
+ int ret;
+#if 0
+ if ((met_switch.mode & MT_SWITCH_TAGPOLLING)) {
+ /* tag polling only enable when MT_SWITCH_TAGPOLLING is config */
+ force_sample(NULL);
+ }
+#endif
+ ret = tracing_mark_write(TYPE_END, class_id, name, 0, 0, 0);
+
+ return ret;
+}
+EXPORT_SYMBOL(met_tag_end_real);
+
+int met_tag_async_start_real(unsigned int class_id, const char *name, unsigned int cookie)
+{
+ int ret;
+
+ ret = tracing_mark_write(TYPE_ASYNC_START, class_id, name, cookie, 0, 0);
+#if 0
+ if ((met_switch.mode & MT_SWITCH_TAGPOLLING)) {
+ /* tag polling only enable when MT_SWITCH_TAGPOLLING is config */
+ force_sample(NULL);
+ }
+#endif
+ return ret;
+}
+
+int met_tag_async_end_real(unsigned int class_id, const char *name, unsigned int cookie)
+{
+ int ret;
+
+#if 0
+ if ((met_switch.mode & MT_SWITCH_TAGPOLLING)) {
+ /* tag polling only enable when MT_SWITCH_TAGPOLLING is config */
+ force_sample(NULL);
+ }
+#endif
+ ret = tracing_mark_write(TYPE_ASYNC_END, class_id, name, cookie, 0, 0);
+ return ret;
+}
+
+int met_tag_oneshot_real(unsigned int class_id, const char *name, unsigned int value)
+{
+ int ret;
+
+ ret = tracing_mark_write(TYPE_ONESHOT, class_id, name, value, 0, 0);
+#if 0
+ if ((met_switch.mode & MT_SWITCH_TAGPOLLING)) {
+ /* tag polling only enable when MT_SWITCH_TAGPOLLING is config */
+ force_sample(NULL);
+ }
+#endif
+ return ret;
+}
+EXPORT_SYMBOL(met_tag_oneshot_real);
+
+int met_tag_userdata_real(char *pData)
+{
+ MET_TRACE("%s\n", pData);
+ return 0;
+}
+
+int met_tag_dump_real(unsigned int class_id, const char *name, void *data, unsigned int length)
+{
+ int ret;
+
+ if ((dump_data_size + length + sizeof(int)) > dump_buffer_size) {
+ if (options_flag & OPFLAG_OVERWRITE) {
+ dump_overrun_size = dump_data_size;
+ dump_overrun++;
+ memcpy(dump_buffer, &dump_seq_no, sizeof(int));
+ memcpy(dump_buffer + sizeof(int), data, length);
+ ret = tracing_mark_write(TYPE_DUMP, class_id, name,
+ dump_seq_no++, 0, length + sizeof(int));
+ dump_data_size = length + sizeof(int);
+ } else {
+ ret = tracing_mark_write(TYPE_DUMP, class_id, name, dump_seq_no++, 0, 0);
+ }
+ } else {
+ memcpy(dump_buffer + dump_data_size, &dump_seq_no, sizeof(int));
+ memcpy(dump_buffer + dump_data_size + sizeof(int), data, length);
+ ret = tracing_mark_write(TYPE_DUMP, class_id, name,
+ dump_seq_no++, dump_data_size, length + sizeof(int));
+ dump_data_size += length + sizeof(int);
+ }
+ return ret;
+}
+
+int met_tag_disable_real(unsigned int class_id)
+{
+ int i;
+
+ mutex_lock(&bltab.mlock);
+
+ if (class_id == MET_CLASS_ALL) {
+ bltab.flag |= MET_CLASS_ALL;
+ mutex_unlock(&bltab.mlock);
+ return 0;
+ }
+
+ for (i = 0; i < MAX_EVENT_CLASS; i++) {
+ if ((bltab.flag & (1 << i)) == 0) {
+ bltab.class_id[i] = class_id;
+ bltab.flag |= (1 << i);
+ mutex_unlock(&bltab.mlock);
+ return 0;
+ }
+ }
+
+ mutex_unlock(&bltab.mlock);
+ return -1;
+}
+
+int met_tag_enable_real(unsigned int class_id)
+{
+ int i;
+
+ mutex_lock(&bltab.mlock);
+
+ if (class_id == MET_CLASS_ALL) {
+ bltab.flag &= (~MET_CLASS_ALL);
+ mutex_unlock(&bltab.mlock);
+ return 0;
+ }
+
+ for (i = 0; i < MAX_EVENT_CLASS; i++) {
+ if ((bltab.flag & (1 << i)) && (bltab.class_id[i] == class_id)) {
+ bltab.flag &= (~(1 << i));
+ bltab.class_id[i] = 0;
+ mutex_unlock(&bltab.mlock);
+ return 0;
+ }
+ }
+
+ mutex_unlock(&bltab.mlock);
+ return -1;
+}
+
+int met_set_dump_buffer_real(int size)
+{
+ if (dump_buffer_size && dump_buffer) {
+ free_pages((unsigned long)dump_buffer, get_order(dump_buffer_size));
+ dump_data_size = 0;
+ dump_overrun = 0;
+ dump_overrun_size = 0;
+ dump_seq_no = 0;
+ dump_buffer_size = 0;
+ }
+ /* size is 0 means free dump buffer */
+ if (size == 0)
+ return 0;
+
+ if (size < 0)
+ return -1;
+
+ size = (size + (PAGE_SIZE - 1)) & (~(PAGE_SIZE - 1));
+ dump_buffer = (void *)__get_free_pages(GFP_KERNEL, get_order(size));
+ if (dump_buffer == NULL) {
+ ERRF("can not allocate buffer to copy\n");
+ return -ENOMEM;
+ }
+
+ dump_buffer_size = size;
+ return dump_buffer_size;
+}
+
+int met_save_dump_buffer_real(const char *pathname)
+{
+ int size, ret = 0;
+ struct file *outfp = NULL;
+ mm_segment_t oldfs;
+
+ if (dump_data_size == 0)
+ return 0;
+
+ if (dump_data_size < 0 || dump_overrun_size < 0)
+ return -1;
+
+ if (dump_buffer == NULL || dump_buffer_size <= 0)
+ return -1;
+
+ if (dump_overrun)
+ size = dump_overrun_size;
+ else
+ size = dump_data_size;
+
+ if (size >= dump_buffer_size)
+ return -1;
+
+ oldfs = get_fs();
+ set_fs(KERNEL_DS);
+
+ outfp = filp_open(pathname, O_WRONLY | O_TRUNC | O_CREAT, 0664);
+ if (unlikely(outfp == NULL)) {
+ ERRF("can not open saved file for write\n");
+ return -EIO;
+ }
+
+ ret = vfs_write(outfp, dump_buffer, size, &(outfp->f_pos));
+ if (ret < 0)
+ ERRF("can not write to dump file\n");
+ else {
+ dump_data_size = 0;
+ dump_overrun = 0;
+ dump_overrun_size = 0;
+ dump_seq_no = 0;
+ }
+
+ set_fs(oldfs);
+
+ if (outfp != NULL)
+ filp_close(outfp, NULL);
+
+ return 0;
+}
+
+int met_save_log_real(const char *pathname)
+{
+ int len, ret = 0;
+ struct file *infp = NULL;
+ struct file *outfp = NULL;
+ void *ptr = NULL;
+ mm_segment_t oldfs;
+
+ infp = filp_open("/sys/kernel/debug/tracing/trace", O_RDONLY, 0);
+ if (unlikely(infp == NULL)) {
+ ERRF("can not open trace file for read\n");
+ ret = -1;
+ goto save_out;
+ }
+
+ outfp = filp_open(pathname, O_WRONLY | O_TRUNC | O_CREAT, 0664);
+ if (unlikely(outfp == NULL)) {
+ ERRF("can not open saved file for write\n");
+ ret = -2;
+ goto save_out;
+ }
+
+ ptr = (void *)__get_free_pages(GFP_KERNEL, 2);
+ if (ptr == NULL) {
+ ERRF("can not allocate buffer to copy\n");
+ ret = -3;
+ goto save_out;
+ }
+
+ oldfs = get_fs();
+ set_fs(KERNEL_DS);
+
+ while (1) {
+ len = vfs_read(infp, ptr, PAGE_SIZE << 2, &(infp->f_pos));
+ if (len < 0) {
+ ERRF("can not read from trace file\n");
+ ret = -3;
+ break;
+ } else if (len == 0) {
+ break;
+ }
+
+ ret = vfs_write(outfp, ptr, len, &(outfp->f_pos));
+ if (ret < 0) {
+ ERRF("can not write to saved file\n");
+ break;
+ }
+ }
+
+ set_fs(oldfs);
+
+save_out:
+ if (ptr != NULL)
+ free_pages((unsigned long)ptr, 2);
+ if (infp != NULL)
+ filp_close(infp, NULL);
+ if (outfp != NULL)
+ filp_close(outfp, NULL);
+
+ return ret;
+}
+
+#ifdef BUILD_WITH_MET
+#include <linux/module.h>
+#include <linux/uaccess.h>
+/* =========================================================================== */
+/* misc file nodes */
+/* =========================================================================== */
+static ssize_t enable_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
+{
+ int i;
+
+ i = snprintf(buf, PAGE_SIZE, "%d\n", (bltab.flag >> 31) ? 0 : 1);
+ return i;
+}
+
+static ssize_t enable_store(struct kobject *kobj, struct kobj_attribute *attr, const char *buf,
+ size_t n)
+{
+ int value;
+
+ if ((n == 0) || (buf == NULL))
+ return -EINVAL;
+ if (kstrtoint(buf, 0, &value) != 0)
+ return -EINVAL;
+
+ mutex_lock(&bltab.mlock);
+
+ if (value == 1)
+ bltab.flag &= (~MET_CLASS_ALL);
+ else
+ bltab.flag |= MET_CLASS_ALL;
+
+ mutex_unlock(&bltab.mlock);
+
+ return n;
+}
+
+static struct kobject *kobj_tag;
+static struct kobj_attribute enable_attr = __ATTR(enable, 0664, enable_show, enable_store);
+
+static ssize_t dump_buffer_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
+{
+ int i, size;
+
+ if (dump_overrun)
+ size = dump_overrun_size;
+ else
+ size = dump_data_size;
+
+ i = snprintf(buf, PAGE_SIZE, "Buffer Size (KB)=%d\nData Size (KB)=%d\nOverrun=%d\n",
+ dump_buffer_size >> 10, size >> 10, dump_overrun);
+ return i;
+}
+
+static ssize_t dump_buffer_store(struct kobject *kobj, struct kobj_attribute *attr, const char *buf,
+ size_t n)
+{
+ int ret, value;
+
+ if ((n == 0) || (buf == NULL))
+ return -EINVAL;
+ if (kstrtoint(buf, 0, &value) != 0)
+ return -EINVAL;
+
+ ret = met_set_dump_buffer_real(value << 10);
+
+ if (ret < 0)
+ return ret;
+
+ return n;
+}
+
+static struct kobj_attribute dump_buffer_attr =
+__ATTR(dump_buffer_kb, 0664, dump_buffer_show, dump_buffer_store);
+
+static ssize_t options_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
+{
+ int i = 0;
+
+ buf[0] = 0;
+
+ if (options_flag == 0) {
+ strncat(buf, "none\n", PAGE_SIZE - 1 - i);
+ i += 5;
+ }
+
+ if (options_flag & OPFLAG_OVERWRITE) {
+ strncat(buf, "overwrite\n", PAGE_SIZE - 1 - i);
+ i += 10;
+ }
+
+ return i;
+}
+
+static ssize_t options_store(struct kobject *kobj, struct kobj_attribute *attr, const char *buf,
+ size_t n)
+{
+ if ((n == 0) || (buf == NULL))
+ return -EINVAL;
+
+ if ((n == 1) && (buf[0] == 0xA)) {
+ options_flag = 0;
+ return n;
+ }
+
+ if (strncmp(buf, "overwrite", 9) == 0)
+ options_flag |= OPFLAG_OVERWRITE;
+ else
+ return -EINVAL;
+
+ return n;
+}
+
+static struct kobj_attribute options_attr = __ATTR(options, 0664, options_show, options_store);
+
+int tag_reg(struct file_operations *const fops, struct kobject *kobj)
+{
+ int ret;
+
+ kobj_tag = kobject_create_and_add("tag", kobj);
+ if (kobj_tag == NULL) {
+ ERRF("can not create kobject: kobj_bus\n");
+ return -1;
+ }
+
+ ret = sysfs_create_file(kobj_tag, &enable_attr.attr);
+ if (ret != 0) {
+ ERRF("Failed to create enable in sysfs\n");
+ kobject_del(kobj_tag);
+ kobject_put(kobj_tag);
+ kobj_tag = NULL;
+ return ret;
+ }
+
+ ret = sysfs_create_file(kobj_tag, &dump_buffer_attr.attr);
+ if (ret != 0) {
+ ERRF("Failed to create dump_buffer in sysfs\n");
+ sysfs_remove_file(kobj_tag, &enable_attr.attr);
+ kobject_del(kobj_tag);
+ kobject_put(kobj_tag);
+ kobj_tag = NULL;
+ return ret;
+ }
+
+ ret = sysfs_create_file(kobj_tag, &options_attr.attr);
+ if (ret != 0) {
+ ERRF("Failed to create options in sysfs\n");
+ sysfs_remove_file(kobj_tag, &enable_attr.attr);
+ sysfs_remove_file(kobj_tag, &dump_buffer_attr.attr);
+ kobject_del(kobj_tag);
+ kobject_put(kobj_tag);
+ kobj_tag = NULL;
+ return ret;
+ }
+
+ met_tag_init();
+ met_ext_api.met_tag_start = met_tag_start_real;
+ met_ext_api.met_tag_end = met_tag_end_real;
+ met_ext_api.met_tag_async_start = met_tag_async_start_real;
+ met_ext_api.met_tag_async_end = met_tag_async_end_real;
+ met_ext_api.met_tag_oneshot = met_tag_oneshot_real;
+ met_ext_api.met_tag_userdata = met_tag_userdata_real;
+ met_ext_api.met_tag_dump = met_tag_dump_real;
+ met_ext_api.met_tag_disable = met_tag_disable_real;
+ met_ext_api.met_tag_enable = met_tag_enable_real;
+ met_ext_api.met_set_dump_buffer = met_set_dump_buffer_real;
+ met_ext_api.met_save_dump_buffer = met_save_dump_buffer_real;
+ met_ext_api.met_save_log = met_save_log_real;
+ met_ext_api.met_sched_switch = met_sched_switch;
+ return 0;
+}
+
+int tag_unreg(void)
+{
+ met_ext_api.met_tag_start = NULL;
+ met_ext_api.met_tag_end = NULL;
+ met_ext_api.met_tag_async_start = NULL;
+ met_ext_api.met_tag_async_end = NULL;
+ met_ext_api.met_tag_oneshot = NULL;
+ met_ext_api.met_tag_userdata = NULL;
+ met_ext_api.met_tag_dump = NULL;
+ met_ext_api.met_tag_disable = NULL;
+ met_ext_api.met_tag_enable = NULL;
+ met_ext_api.met_set_dump_buffer = NULL;
+ met_ext_api.met_save_dump_buffer = NULL;
+ met_ext_api.met_save_log = NULL;
+ met_ext_api.met_show_bw_limiter = NULL;
+ met_ext_api.met_reg_bw_limiter = NULL;
+ met_ext_api.met_show_clk_tree = NULL;
+ met_ext_api.met_reg_clk_tree = NULL;
+ met_ext_api.met_sched_switch = NULL;
+ met_tag_uninit();
+ sysfs_remove_file(kobj_tag, &enable_attr.attr);
+ sysfs_remove_file(kobj_tag, &dump_buffer_attr.attr);
+ sysfs_remove_file(kobj_tag, &options_attr.attr);
+ kobject_del(kobj_tag);
+ kobject_put(kobj_tag);
+ kobj_tag = NULL;
+ return 0;
+}
+
+#endif /* BUILD_WITH_MET */
+
+#else /* not MET_USER_EVENT_SUPPORT */
+
+#ifdef BUILD_WITH_MET
+int tag_reg(void *p, void *q)
+{
+ return 0;
+}
+
+int tag_unreg(void)
+{
+ return 0;
+}
+#endif
+
+#endif /* MET_USER_EVENT_SUPPORT */
diff --git a/src/devtools/met-driver/4.14/common/met_vcoredvfs.c b/src/devtools/met-driver/4.14/common/met_vcoredvfs.c
new file mode 100644
index 0000000..560ef67
--- /dev/null
+++ b/src/devtools/met-driver/4.14/common/met_vcoredvfs.c
@@ -0,0 +1,314 @@
+/*
+ * 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/sched.h>
+#include <linux/device.h>
+#include <linux/module.h>
+#include <linux/dma-mapping.h>
+
+#include <helio-dvfsrc.h>
+
+#define MET_USER_EVENT_SUPPORT
+#include "met_drv.h"
+#include "trace.h"
+
+#include "core_plf_init.h"
+#include "core_plf_trace.h"
+
+/*======================================================================*/
+/* Global variable definitions */
+/*======================================================================*/
+
+/* Global variables */
+struct metdevice met_vcoredvfs;
+int polling_mode = 1;
+
+/* external symbols */
+#define DEFAULT_INFO_NUM 3
+#define DEFAULT_SRC_NUM 1
+
+char *default_info_name[DEFAULT_INFO_NUM] = {
+ "OPP",
+ "VOLT",
+ "FREQ"
+};
+
+char *default_src_name[DEFAULT_SRC_NUM] = {
+ "MD2SPM"
+};
+
+unsigned int opp_info[DEFAULT_INFO_NUM];
+unsigned int src_req[DEFAULT_SRC_NUM];
+
+static int met_vcorefs_get_num_opp(void)
+{
+ if (vcorefs_get_num_opp_symbol)
+ return vcorefs_get_num_opp_symbol();
+ else
+ return 0;
+}
+
+static int met_vcorefs_get_opp_info_num(void)
+{
+ if (vcorefs_get_opp_info_num_symbol)
+ return vcorefs_get_opp_info_num_symbol();
+ else
+ return DEFAULT_INFO_NUM;
+}
+
+
+static int met_vcorefs_get_src_req_num(void)
+{
+ if (vcorefs_get_src_req_num_symbol)
+ return vcorefs_get_src_req_num_symbol();
+ else
+ return DEFAULT_SRC_NUM;
+}
+
+
+static char **met_vcorefs_get_opp_info_name(void)
+{
+ if (vcorefs_get_opp_info_name_symbol)
+ return vcorefs_get_opp_info_name_symbol();
+ else
+ return default_info_name;
+}
+
+static char **met_vcorefs_get_src_req_name(void)
+{
+ if (vcorefs_get_src_req_name_symbol)
+ return vcorefs_get_src_req_name_symbol();
+ else
+ return default_src_name;
+}
+
+static unsigned int *met_vcorefs_get_opp_info(void)
+{
+ if (vcorefs_get_opp_info_symbol)
+ return vcorefs_get_opp_info_symbol();
+
+ return opp_info;
+}
+
+static unsigned int *met_vcorefs_get_src_req(void)
+{
+ if (vcorefs_get_src_req_symbol)
+ return vcorefs_get_src_req_symbol();
+
+ return src_req;
+}
+
+
+/*======================================================================*/
+/* File Node definitions */
+/*======================================================================*/
+
+static struct kobject *kobj_met_vcoredvfs;
+
+static ssize_t vcorefs_polling_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf);
+static ssize_t vcorefs_polling_store(struct kobject *kobj,
+ struct kobj_attribute *attr, const char *buf, size_t n);
+static struct kobj_attribute vcorefs_polling_attr =
+__ATTR(vcorefs_polling, 0664, vcorefs_polling_show, vcorefs_polling_store);
+
+/*======================================================================*/
+/* Utilities */
+/*======================================================================*/
+
+static inline int do_vcoredvfs(void)
+{
+ return met_vcoredvfs.mode;
+}
+
+/*======================================================================*/
+/* Data Output */
+/*======================================================================*/
+
+noinline void vcorefs(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 vcorefs_kicker(unsigned char cnt, 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_vcorefs(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);
+}
+
+/*======================================================================*/
+/* Callback functions */
+/*======================================================================*/
+void vcoredvfs_irq(int opp)
+{
+ int num;
+ unsigned int *out;
+
+ num = met_vcorefs_get_opp_info_num();
+ out = met_vcorefs_get_opp_info();
+
+ vcorefs(num, out);
+}
+
+/*======================================================================*/
+/* File Node Operations */
+/*======================================================================*/
+static ssize_t vcorefs_polling_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
+{
+ return snprintf(buf, PAGE_SIZE, "%d\n", polling_mode);
+}
+
+static ssize_t vcorefs_polling_store(struct kobject *kobj,
+ struct kobj_attribute *attr, const char *buf, size_t n)
+{
+ int value;
+
+ if ((n == 0) || (buf == NULL))
+ return -EINVAL;
+
+ if (kstrtoint(buf, 0, &value) != 0)
+ return -EINVAL;
+
+ if (value < 0)
+ return -EINVAL;
+
+ polling_mode = value;
+
+ return n;
+}
+
+/*======================================================================*/
+/* MET Device Operations */
+/*======================================================================*/
+static int met_vcoredvfs_create(struct kobject *parent)
+{
+ int ret = 0;
+
+ kobj_met_vcoredvfs = parent;
+
+ ret = sysfs_create_file(kobj_met_vcoredvfs, &vcorefs_polling_attr.attr);
+ if (ret != 0) {
+ pr_debug("Failed to create vcoredvfs in sysfs\n");
+ return ret;
+ }
+
+ return ret;
+}
+
+static void met_vcoredvfs_delete(void)
+{
+ sysfs_remove_file(kobj_met_vcoredvfs, &vcorefs_polling_attr.attr);
+}
+
+static void met_vcoredvfs_start(void)
+{
+ vcoredvfs_irq(-1);
+}
+
+static void met_vcoredvfs_stop(void)
+{
+ vcoredvfs_irq(-1);
+}
+
+static void met_vcoredvfs_polling(unsigned long long stamp, int cpu)
+{
+ int num;
+ unsigned int *out;
+
+ if (!do_vcoredvfs())
+ return;
+
+ /* vcorefs opp information */
+ if (polling_mode)
+ vcoredvfs_irq(-1);
+
+ /* vcorefs source request */
+ num = met_vcorefs_get_src_req_num();
+ out = met_vcorefs_get_src_req();
+
+ ms_vcorefs(num, out);
+}
+
+static const char help[] =
+ " --vcoredvfs monitor VCORE DVFS\n";
+static int vcoredvfs_print_help(char *buf, int len)
+{
+ return snprintf(buf, PAGE_SIZE, help);
+}
+
+static const char header_dvfs[] = "met-info [000] 0.0: met_vcorefs_header:";
+
+static const char header_ms_dvfs[] = "met-info [000] 0.0: ms_vcorefs_header:";
+
+static int vcoredvfs_print_header(char *buf, int len)
+{
+ int ret = 0;
+ int idx = 0;
+ int num = 0;
+ char **header;
+
+ ret = snprintf(buf, PAGE_SIZE, "met-info [000] 0.0: met_vcorefs_cfg: NUM_OPP:%d\n", met_vcorefs_get_num_opp());
+
+ /* opp information */
+ num = met_vcorefs_get_opp_info_num();
+ header = met_vcorefs_get_opp_info_name();
+
+ ret += snprintf(buf + ret, PAGE_SIZE, header_dvfs);
+ for (idx = 0; idx < num; idx++)
+ ret += snprintf(buf + ret, PAGE_SIZE - ret, "%s,", header[idx]);
+ buf[strlen(buf)-1] = '\n';
+
+ /* source requests */
+ num = met_vcorefs_get_src_req_num();
+ header = met_vcorefs_get_src_req_name();
+
+ ret += snprintf(buf + ret, PAGE_SIZE - ret, header_ms_dvfs);
+ for (idx = 0; idx < num; idx++)
+ ret += snprintf(buf + ret, PAGE_SIZE - ret, "%s,", header[idx]);
+ buf[strlen(buf)-1] = '\n';
+
+ met_vcoredvfs.mode = 0;
+
+ return ret;
+}
+
+struct metdevice met_vcoredvfs = {
+ .name = "vcoredvfs",
+ .owner = THIS_MODULE,
+ .type = MET_TYPE_BUS,
+ .create_subfs = met_vcoredvfs_create,
+ .delete_subfs = met_vcoredvfs_delete,
+ .cpu_related = 0,
+ .start = met_vcoredvfs_start,
+ .stop = met_vcoredvfs_stop,
+ .polling_interval = 1, /* ms */
+ .timed_polling = met_vcoredvfs_polling,
+ .print_help = vcoredvfs_print_help,
+ .print_header = vcoredvfs_print_header,
+};
diff --git a/src/devtools/met-driver/4.14/common/met_wall_time.c b/src/devtools/met-driver/4.14/common/met_wall_time.c
new file mode 100644
index 0000000..ac7e18e
--- /dev/null
+++ b/src/devtools/met-driver/4.14/common/met_wall_time.c
@@ -0,0 +1,256 @@
+/*
+ * 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/module.h>
+#include <linux/sched.h>
+#include <asm/div64.h>
+#include <linux/types.h>
+#include <linux/kallsyms.h>
+
+#define MET_USER_EVENT_SUPPORT
+#include "met_drv.h"
+#include "trace.h"
+#include "interface.h"
+
+#define MODE_CUSOM_CLKSRC 2
+struct metdevice met_wall_time;
+
+/** */
+/* How to add a new clocksource: */
+/* */
+/* 1. add constant for new clocksource in #define-macro */
+/* 2. declare new weakref function */
+/* 3. implement handler functions: */
+/* (1) clksrc_attr_t::*ready: */
+/* check if ... */
+/* (i) clocksource correctly working */
+/* (ii) weakref function is not null */
+/* (2) clksrc_attr_t::*get_cnt: read clocksource from weakref function */
+/* 4. place attrs of new clocksource into clksrc_attr_tb */
+/* 5. update DEFAULT_CLKSRC_STR */
+/* 6. update help message */
+/** */
+
+#define __SYS_TIMER 0x0
+#define __GPT1 0x1
+#define __GPT2 0x2
+#define __GPT3 0x3
+#define __GPT4 0x4
+#define __GPT5 0x5
+#define __GPT6 0x6
+
+#define DEFAULT_CLKSRC_STR "SYS_TIMER"
+
+extern u64 met_arch_counter_get_cntvct(void);
+u64 (*met_arch_counter_get_cntvct_symbol)(void);
+
+int __sys_timer_get_cnt(u8 clksrc, u64 *cycles)
+{
+ if (met_arch_counter_get_cntvct_symbol)
+ *cycles = met_arch_counter_get_cntvct_symbol();
+ return 0;
+}
+
+
+struct clksrc_attr_t {
+ u8 clksrc;
+ const char *clksrc_str;
+ /* checks if clksrc/get_cnt function is working/available */
+ int (*clksrc_ready)(u8 clksrc);
+ int (*clksrc_get_cnt)(u8 clksrc, u64 *cycles);
+};
+
+struct clksrc_attr_t clksrc_attr_tb[] = {
+ {__SYS_TIMER, "SYS_TIMER", NULL, __sys_timer_get_cnt},
+ /* {__GPT1, "GPT1", __gpt_timer_ready, __gpt_timer_get_cnt}, */
+ /* {__GPT2, "GPT2", __gpt_timer_ready, __gpt_timer_get_cnt}, */
+ /* {__GPT3, "GPT3", __gpt_timer_ready, __gpt_timer_get_cnt}, */
+ /* {__GPT4, "GPT4", __gpt_timer_ready, __gpt_timer_get_cnt}, */
+ /* {__GPT5, "GPT5", __gpt_timer_ready, __gpt_timer_get_cnt}, */
+ /* {__GPT6, "GPT6", __gpt_timer_ready, __gpt_timer_get_cnt}, */
+};
+
+static const struct clksrc_attr_t *lookup_clksrc_attr_tb(const char *clksrc_str, int len);
+static const struct clksrc_attr_t *wall_time_attr;
+
+/* definitions for auto-sampling of working freq. of clocksource */
+/* maximum tolerable error percentage(%) of sampled clock freq */
+#define FREQ_ERR_PERCENT 3
+
+/* expected working freq. of clocksources */
+static const u32 freq_level[] = { 32768, 13000000, 26000000 };
+
+static u32 lookup_freq_level(u32 freq);
+
+/* flag indicating whether sampling is on-going */
+static u32 do_sample;
+static u32 freq;
+static u64 start_us_ts;
+static u64 start_wall_time;
+/* end definitions for sampling of freq. */
+
+static void wall_time_start(void)
+{
+ met_arch_counter_get_cntvct_symbol = (void *)symbol_get(met_arch_counter_get_cntvct);
+
+ if (met_wall_time.mode != MODE_CUSOM_CLKSRC) {
+ wall_time_attr = lookup_clksrc_attr_tb(DEFAULT_CLKSRC_STR,
+ strlen(DEFAULT_CLKSRC_STR));
+ }
+
+ freq = 0;
+ do_sample = 1;
+
+ if (wall_time_attr) {
+
+ /* XXX: always use CPU 0 */
+ start_us_ts = cpu_clock(0);
+ wall_time_attr->clksrc_get_cnt(wall_time_attr->clksrc, &start_wall_time);
+
+ /* us_ts = ap_ts/1000; */
+ do_div(start_us_ts, 1000);
+ }
+}
+
+noinline void met_ap_wall_time(unsigned long long ts, int cpu)
+{
+ u64 ap_ts;
+ u64 us_ts;
+ u64 sec;
+ u64 usec;
+ u64 cycles;
+ u64 f;
+
+ if (wall_time_attr) {
+
+ wall_time_attr->clksrc_get_cnt(wall_time_attr->clksrc, &cycles);
+ ap_ts = cpu_clock(cpu);
+
+ us_ts = ap_ts;
+ do_div(us_ts, 1000); /* us_ts = ap_ts/1000; */
+
+ sec = us_ts;
+ usec = do_div(sec, 1000000); /* sec = us_ts/1000000; usec = us_ts%1000000; */
+ MET_TRACE("TS.APTS=%llu.%06llu WCLK=%llu\n", (unsigned long long)sec,
+ (unsigned long long)usec, (unsigned long long)cycles);
+
+ if (do_sample) {
+
+ do_sample = 0;
+
+ f = (cycles - start_wall_time) * 1000000;
+ do_div(f, us_ts - start_us_ts);
+
+ /* don't worry about the u64 -> u32 assignment, */
+ /* sampled wall-clock freq is expected to be below 2^32-1 */
+ freq = lookup_freq_level(f);
+
+ /* debug message */
+ /* MET_TRACE("wall_time debug: result: %u," */
+ /* "start cycle: %llu, end cycle: %llu, cycle diff: %llu," */
+ /* "start us: %llu, end us: %llu, us diff: %llu", */
+ /* f, */
+ /* start_wall_time, cycles, cycles - start_wall_time, */
+ /* start_us_ts, us_ts, us_ts - start_us_ts); */
+
+ if (freq != 0)
+ met_tag_oneshot_real(33880, "_WCLK_FREQ_", freq);
+ }
+ }
+}
+
+static const char help[] =
+" --wall_time output wall-clock syncing info in system timer\n";
+/* " --wall_time=SYS_TIMER|GPT[1-6] output wall-clock syncing info in custom clocksource\n"; */
+
+static int wall_time_print_help(char *buf, int len)
+{
+ return snprintf(buf, PAGE_SIZE, help);
+}
+
+static const char *header =
+"met-info [000] 0.0: WCLK: %d\n"
+"met-info [000] 0.0: clocksource: %s\n";
+
+static int wall_time_print_header(char *buf, int len)
+{
+ return snprintf(buf, len, header,
+ freq == 0 ? -1 : freq,
+ wall_time_attr ? wall_time_attr->clksrc_str : "NONE");
+}
+
+static int wall_time_process_argument(const char *arg, int len)
+{
+ /* reset wall-time clocksource */
+ wall_time_attr = lookup_clksrc_attr_tb(arg, len);
+
+ if (!wall_time_attr) {
+ met_wall_time.mode = 0;
+ return -1;
+ }
+
+ met_wall_time.mode = MODE_CUSOM_CLKSRC;
+ return 0;
+}
+
+static const struct clksrc_attr_t *lookup_clksrc_attr_tb(const char *clksrc_str, int len)
+{
+ int i;
+ const struct clksrc_attr_t *attr;
+ int tb_nmemb = sizeof(clksrc_attr_tb) / sizeof(*clksrc_attr_tb);
+
+ for (i = 0; i < tb_nmemb; i++) {
+
+ attr = clksrc_attr_tb + i;
+
+ if (strlen(attr->clksrc_str) == len &&
+ strncmp(clksrc_str, attr->clksrc_str, len) == 0) {
+ return attr;
+ }
+ }
+
+ return NULL;
+}
+
+static u32 lookup_freq_level(u32 freq)
+{
+
+ int ii;
+ int freq_nmemb = sizeof(freq_level) / sizeof(*freq_level);
+ u32 fdiff;
+
+ for (ii = 0; ii < freq_nmemb; ii++) {
+ fdiff = freq_level[ii] > freq ? freq_level[ii] - freq : freq - freq_level[ii];
+ if (fdiff < freq_level[ii] * FREQ_ERR_PERCENT / 100)
+ return freq_level[ii];
+ }
+
+ return 0;
+}
+
+struct metdevice met_wall_time = {
+ .name = "wall_time",
+ .owner = THIS_MODULE,
+ .type = MET_TYPE_BUS,
+ .cpu_related = 0,
+ .start = wall_time_start,
+ .polling_interval = 1000,
+ .timed_polling = met_ap_wall_time,
+ .print_help = wall_time_print_help,
+ .print_header = wall_time_print_header,
+ .process_argument = wall_time_process_argument,
+};
+EXPORT_SYMBOL(met_wall_time);
diff --git a/src/devtools/met-driver/4.14/common/mips_pmu_hw.c b/src/devtools/met-driver/4.14/common/mips_pmu_hw.c
new file mode 100644
index 0000000..19e836b
--- /dev/null
+++ b/src/devtools/met-driver/4.14/common/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.14/common/mips_pmu_name.h b/src/devtools/met-driver/4.14/common/mips_pmu_name.h
new file mode 100644
index 0000000..d7ea26a
--- /dev/null
+++ b/src/devtools/met-driver/4.14/common/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.14/common/mtk_emi_bm.c b/src/devtools/met-driver/4.14/common/mtk_emi_bm.c
new file mode 100644
index 0000000..3c61470
--- /dev/null
+++ b/src/devtools/met-driver/4.14/common/mtk_emi_bm.c
@@ -0,0 +1,253 @@
+/*
+ * 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/of.h>
+#include <linux/of_address.h>
+#include <asm/io.h>
+#include <mt-plat/sync_write.h>
+#include <mt-plat/mtk_io.h>
+/* #include "mtk_typedefs.h" */
+#include "core_plf_init.h"
+#include "mtk_emi_bm.h"
+#include "met_drv.h"
+#include "interface.h"
+#if defined(CONFIG_MTK_TINYSYS_SSPM_SUPPORT) && defined(ONDIEMET_SUPPORT)
+#include "sspm/ondiemet_sspm.h"
+#endif
+
+#undef DEBUG
+
+#define emi_readl readl
+#define emi_reg_sync_writel mt_reg_sync_writel
+
+#define MASK_MASTER 0xFF
+#define MASK_TRANS_TYPE 0xFF
+
+static void __iomem *BaseAddrEMI;
+const unsigned int emi_config[] = {
+ EMI_BMEN,
+ EMI_MSEL,
+ EMI_MSEL2,
+ EMI_MSEL3,
+ EMI_MSEL4,
+ EMI_MSEL5,
+ EMI_MSEL6,
+ EMI_MSEL7,
+ EMI_MSEL8,
+ EMI_MSEL9,
+ EMI_MSEL10,
+ EMI_BMID0,
+ EMI_BMID1,
+ EMI_BMID2,
+ EMI_BMID3,
+ EMI_BMID4,
+ EMI_BMID5,
+ EMI_BMID6,
+ EMI_BMID7,
+ EMI_BMID8,
+ EMI_BMID9,
+ EMI_BMID10,
+ EMI_BMEN1,
+ EMI_BMEN2,
+ EMI_BMRW0,
+ EMI_BMRW1
+};
+#define EMI_CONFIG_MX_NR (sizeof(emi_config)/sizeof(unsigned int))
+static unsigned int emi_config_val[EMI_CONFIG_MX_NR];
+
+int MET_BM_Init(void)
+{
+ /*emi*/
+ if (mt_cen_emi_base_get_symbol) {
+ BaseAddrEMI = mt_cen_emi_base_get_symbol();
+ } else {
+ pr_debug("mt_cen_emi_base_get_symbol = NULL\n");
+ PR_BOOTMSG_ONCE("mt_cen_emi_base_get_symbol = NULL\n");
+ BaseAddrEMI = 0;
+ }
+
+ if (BaseAddrEMI == 0) {
+ pr_debug("BaseAddrEMI = 0\n");
+ PR_BOOTMSG_ONCE("BaseAddrEMI = 0\n");
+ return -1;
+ }
+ pr_debug("MET EMI: map emi to %p\n", BaseAddrEMI);
+ PR_BOOTMSG("MET EMI: map emi to %p\n", BaseAddrEMI);
+
+ return 0;
+}
+
+void MET_BM_DeInit(void)
+{
+}
+
+void MET_BM_SaveCfg(void)
+{
+ int i;
+
+ for (i = 0; i < EMI_CONFIG_MX_NR; i++)
+ emi_config_val[i] = emi_readl(IOMEM(ADDR_EMI + emi_config[i]));
+}
+
+void MET_BM_RestoreCfg(void)
+{
+ int i;
+
+ for (i = 0; i < EMI_CONFIG_MX_NR; i++)
+ emi_reg_sync_writel(emi_config_val[i], ADDR_EMI + emi_config[i]);
+}
+
+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);
+
+
+ value = emi_readl(IOMEM(ADDR_EMI + addr)) & ~(iMask << ((counter_num % 2) * 16));
+
+
+ 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)
+{
+
+ 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_SetbusID_En(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;
+
+ if (enable == 0) {
+
+ value = (emi_readl(IOMEM(ADDR_EMI + EMI_BMEN2))
+ & ~(1 << (counter_num - 1)));
+ } else {
+
+ 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;
+}
+
+int MET_BM_SetbusID(const unsigned int counter_num,
+ const unsigned int id)
+{
+ unsigned int value, addr, shift_num;
+
+ if ((counter_num < 1 || counter_num > BM_COUNTER_MAX))
+ return BM_ERR_WRONG_REQ;
+
+
+ addr = EMI_BMID0 + (counter_num - 1) / 2 * 4;
+ shift_num = ((counter_num - 1) % 2) * 16;
+
+ value = emi_readl(IOMEM(ADDR_EMI + addr)) & ~(EMI_BMID_MASK << shift_num);
+
+
+ if (id <= 0xffff)
+ value |= id << shift_num;
+
+ emi_reg_sync_writel(value, ADDR_EMI + addr);
+
+ 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);
+ if (enable == 1)
+ value |= (0x2 << 24);
+
+ emi_reg_sync_writel(value, ADDR_EMI + EMI_BMEN2);
+
+ return BM_REQ_OK;
+}
diff --git a/src/devtools/met-driver/4.14/common/mtk_emi_bm.h b/src/devtools/met-driver/4.14/common/mtk_emi_bm.h
new file mode 100644
index 0000000..43295be
--- /dev/null
+++ b/src/devtools/met-driver/4.14/common/mtk_emi_bm.h
@@ -0,0 +1,151 @@
+/*
+ * 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_MET_EMI_BM_H__
+#define __MT_MET_EMI_BM_H__
+
+
+#define ADDR_EMI ((unsigned long) BaseAddrEMI)
+
+/*========================================================*/
+/*EMI configuration by project*/
+/*Change config start*/
+/*========================================================*/
+#define _GP_1_Default (_M0 | _M1)
+#define _GP_2_Default (_M2 | _M5)
+#define _GP_3_Default (_M6 | _M7)
+
+
+/*========================================================*/
+/*Change config end*/
+/*========================================================*/
+
+
+#define _M0 (0x01)
+#define _M1 (0x02)
+#define _M2 (0x04)
+#define _M3 (0x08)
+#define _M4 (0x10)
+#define _M5 (0x20)
+#define _M6 (0x40)
+#define _M7 (0x80)
+#define _ALL (0xFF)
+
+enum BM_RW_Type {
+ BM_BOTH_READ_WRITE,
+ BM_READ_ONLY,
+ BM_WRITE_ONLY
+};
+
+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
+};
+
+
+#define EMI_BMID_MASK (0xFFFF)
+#define BM_COUNTER_MAX (21)
+
+#define BM_REQ_OK (0)
+#define BM_ERR_WRONG_REQ (-1)
+#define BM_ERR_OVERRUN (-2)
+
+#define BM_TTYPE1_16_ENABLE (0)
+#define BM_TTYPE1_16_DISABLE (-1)
+#define BM_TTYPE17_21_ENABLE (0)
+#define BM_TTYPE17_21_DISABLE (-1)
+
+#if defined(CONFIG_MTK_TINYSYS_SSPM_SUPPORT) && defined(ONDIEMET_SUPPORT)
+enum BM_EMI_IPI_Type {
+ SET_BASE_EMI = 0x0,
+ SET_EBM_CONFIGS1 = 0x7,
+ SET_EBM_CONFIGS2 = 0x8,
+ SET_REGISTER_CB = 0x9,
+};
+#endif
+
+#define EMI_OFF 0x0000
+#define EMI_BMEN (0x400-EMI_OFF)
+#define EMI_MSEL (0x440-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)
+
+
+extern int MET_BM_Init(void);
+extern void MET_BM_DeInit(void);
+extern void MET_BM_SaveCfg(void);
+extern void MET_BM_RestoreCfg(void);
+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_SetbusID_En(const unsigned int counter_num,
+ const unsigned int enable);
+extern int MET_BM_SetbusID(const unsigned int counter_num,
+ const unsigned int id);
+extern int MET_BM_SetUltraHighFilter(const unsigned int counter_num, const unsigned int enable);
+extern int MET_BM_SetLatencyCounter(unsigned int enable);
+#endif /* !__MT_MET_EMI_BM_H__ */
diff --git a/src/devtools/met-driver/4.14/common/mtk_emi_bm_35.c b/src/devtools/met-driver/4.14/common/mtk_emi_bm_35.c
new file mode 100644
index 0000000..d3945f1
--- /dev/null
+++ b/src/devtools/met-driver/4.14/common/mtk_emi_bm_35.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 <linux/kernel.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/io.h>
+#include <mt-plat/sync_write.h>
+#include <mt-plat/mtk_io.h>
+#include "mtk_typedefs.h"
+#include "core_plf_init.h"
+#include "mtk_emi_bm_35.h"
+
+#include "met_drv.h"
+#include "interface.h"
+
+#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 int dram_chann_num;
+static void __iomem *BaseAddrEMI;
+static void __iomem *BaseAddrCHN_EMI[2];
+
+static int dramc0_dcm_enable;
+static int dramc1_dcm_enable;
+
+#define CH0_MISC_CG_CTRL0 (((unsigned long) BaseAddrDDRPHY_AO[0]) + 0x284)
+#define CH1_MISC_CG_CTRL0 (((unsigned long) BaseAddrDDRPHY_AO[1]) + 0x284)
+const unsigned int emi_config[] = {
+ EMI_BMEN,
+ EMI_MSEL,
+ EMI_MSEL2,
+ EMI_MSEL3,
+ EMI_MSEL4,
+ EMI_MSEL5,
+ EMI_MSEL6,
+ EMI_MSEL7,
+ EMI_MSEL8,
+ EMI_MSEL9,
+ EMI_MSEL10,
+ EMI_BMID0,
+ EMI_BMID1,
+ EMI_BMID2,
+ EMI_BMID3,
+ EMI_BMID4,
+ EMI_BMID5,
+ EMI_BMID6,
+ EMI_BMID7,
+ EMI_BMID8,
+ EMI_BMID9,
+ EMI_BMID10,
+ EMI_BMEN1,
+ EMI_BMEN2,
+ EMI_BMRW0,
+ EMI_BMRW1,
+ EMI_DBWA,
+ EMI_DBWB,
+ EMI_DBWC,
+ EMI_DBWD,
+ EMI_DBWE,
+ EMI_DBWF,
+ EMI_DBWI,
+ EMI_DBWJ,
+ EMI_DBWK,
+ EMI_DBWA_2ND,
+ EMI_DBWB_2ND,
+ EMI_DBWC_2ND,
+ EMI_DBWD_2ND,
+ EMI_DBWE_2ND,
+ EMI_DBWF_2ND,
+ EMI_TTYPE1_CONA,
+ EMI_TTYPE1_CONB,
+ EMI_TTYPE2_CONA,
+ EMI_TTYPE2_CONB,
+ EMI_TTYPE3_CONA,
+ EMI_TTYPE3_CONB,
+ EMI_TTYPE4_CONA,
+ EMI_TTYPE4_CONB,
+ EMI_TTYPE5_CONA,
+ EMI_TTYPE5_CONB,
+ EMI_TTYPE6_CONA,
+ EMI_TTYPE6_CONB,
+ EMI_TTYPE7_CONA,
+ EMI_TTYPE7_CONB,
+ EMI_TTYPE8_CONA,
+ EMI_TTYPE8_CONB,
+ EMI_TTYPE9_CONA,
+ EMI_TTYPE9_CONB,
+ EMI_TTYPE10_CONA,
+ EMI_TTYPE10_CONB,
+ EMI_TTYPE11_CONA,
+ EMI_TTYPE11_CONB,
+ EMI_TTYPE12_CONA,
+ EMI_TTYPE12_CONB,
+ EMI_TTYPE13_CONA,
+ EMI_TTYPE13_CONB,
+ EMI_TTYPE14_CONA,
+ EMI_TTYPE14_CONB,
+ EMI_TTYPE15_CONA,
+ EMI_TTYPE15_CONB,
+ EMI_TTYPE16_CONA,
+ EMI_TTYPE16_CONB,
+ EMI_TTYPE17_CONA,
+ EMI_TTYPE17_CONB,
+ EMI_TTYPE18_CONA,
+ EMI_TTYPE18_CONB,
+ EMI_TTYPE19_CONA,
+ EMI_TTYPE19_CONB,
+ EMI_TTYPE20_CONA,
+ EMI_TTYPE20_CONB,
+ EMI_TTYPE21_CONA,
+ EMI_TTYPE21_CONB
+};
+#define EMI_CONFIG_MX_NR (sizeof(emi_config)/sizeof(unsigned int))
+static unsigned int emi_config_val[EMI_CONFIG_MX_NR];
+
+
+
+static inline void MET_REG_BCLR(unsigned long reg, u32 shift)
+{
+ unsigned int read_val = 0;
+
+ read_val = emi_readl(IOMEM(reg));
+ emi_reg_sync_writel(read_val & (~((1 << shift) & 0xFFFFFFFF)), reg);
+}
+
+
+int MET_BM_Init(void)
+{
+ /*emi*/
+ if (mt_cen_emi_base_get_symbol) {
+ BaseAddrEMI = mt_cen_emi_base_get_symbol();
+ } else {
+ pr_debug("mt_cen_emi_base_get_symbol = NULL\n");
+ PR_BOOTMSG_ONCE("mt_cen_emi_base_get_symbol = NULL\n");
+ BaseAddrEMI = 0;
+ }
+
+ if (BaseAddrEMI == 0) {
+ pr_debug("BaseAddrEMI = 0\n");
+ PR_BOOTMSG_ONCE("BaseAddrEMI = 0\n");
+ return -1;
+ }
+ pr_debug("MET EMI: map emi to %p\n", BaseAddrEMI);
+ PR_BOOTMSG("MET EMI: map emi to %p\n", BaseAddrEMI);
+
+ return 0;
+}
+
+
+void MET_BM_DeInit(void)
+{
+}
+
+
+void MET_BM_SaveCfg(void)
+{
+ int i;
+
+ for (i = 0; i < EMI_CONFIG_MX_NR; i++)
+ emi_config_val[i] = emi_readl(IOMEM(ADDR_EMI + emi_config[i]));
+}
+
+
+void MET_BM_RestoreCfg(void)
+{
+ int i;
+
+ for (i = 0; i < EMI_CONFIG_MX_NR; i++)
+ emi_reg_sync_writel(emi_config_val[i], ADDR_EMI + emi_config[i]);
+}
+
+
+
+
+
+
+
+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_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)
+{
+
+ 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_SetbusID_En(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;
+
+ if (enable == 0) {
+ /* clear EMI ID selection Enabling SEL_ID_EN */
+ value = (emi_readl(IOMEM(ADDR_EMI + EMI_BMEN2))
+ & ~(1 << (counter_num - 1)));
+ } else {
+ /* enable EMI ID selection Enabling SEL_ID_EN */
+ 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;
+}
+
+
+int MET_BM_SetbusID(const unsigned int counter_num,
+ const unsigned int id)
+{
+ unsigned int value, addr, shift_num;
+
+ if ((counter_num < 1 || counter_num > BM_COUNTER_MAX))
+ return BM_ERR_WRONG_REQ;
+
+ /* offset of EMI_BMIDx register */
+ addr = EMI_BMID0 + (counter_num - 1) / 2 * 4;
+ 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 */
+ if (id <= 0xffff) /*bigger then 0xff_ff : no select busid in master, reset busid as 0*/
+ value |= id << shift_num;
+
+ emi_reg_sync_writel(value, ADDR_EMI + addr);
+
+ 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;
+}
+
+
+
+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;
+}
+
+
+unsigned int MET_EMI_GetDramRankNum(void)
+{
+ int dual_rank = 0;
+
+ if (BaseAddrEMI) {
+ dual_rank = emi_readl(IOMEM(ADDR_EMI + EMI_CONA));
+ dual_rank = ((dual_rank >> 17) & RANK_MASK);
+ } else {
+ return DUAL_RANK;
+ }
+
+ if (dual_rank == DISABLE_DUAL_RANK_MODE)
+ return ONE_RANK;
+ else /* default return dual rank */
+ return DUAL_RANK;
+}
+
+
+unsigned int MET_EMI_GetDramRankNum_CHN1(void)
+{
+ int dual_rank = 0;
+
+ if (BaseAddrEMI) {
+ dual_rank = emi_readl(IOMEM(ADDR_EMI + EMI_CONA));
+ dual_rank = ((dual_rank >> 16) & RANK_MASK);
+ } else {
+ return DUAL_RANK;
+ }
+
+ if (dual_rank == DISABLE_DUAL_RANK_MODE)
+ return ONE_RANK;
+ else /* default return dual rank */
+ return DUAL_RANK;
+}
+
+
+
+unsigned int MET_EMI_Get_BaseClock_Rate(void)
+{
+ unsigned int DRAM_TYPE;
+
+ if (get_cur_ddr_ratio_symbol)
+ return get_cur_ddr_ratio_symbol();
+ else {
+
+ if (get_ddr_type_symbol) {
+ DRAM_TYPE = get_ddr_type_symbol();
+
+ if ((DRAM_TYPE == 2) || (DRAM_TYPE == 3))
+ return DRAM_EMI_BASECLOCK_RATE_LP4;
+ else
+ return DRAM_EMI_BASECLOCK_RATE_LP3;
+
+ } else {
+ return DRAM_EMI_BASECLOCK_RATE_LP4;
+ }
+ }
+}
+
+/* For SEDA3.5 wsct setting*/
+/* EMI_DBWX[15:8], X=A~F (SEL_MASTER) */
+/* RW: EMI_DBWX[1:0], X=A~F */
+int MET_BM_SetWSCT_master_rw(unsigned int *master , unsigned int *rw)
+{
+ unsigned int value, addr;
+ int i;
+
+ const unsigned int Mask_master = 0xFF;
+ const unsigned int offset_master = 8;
+
+ const unsigned int Mask_rw = 0x3;
+ const unsigned int offset_rw = 0;
+
+ for (i=0; i<WSCT_AMOUNT; i++) {
+ addr = EMI_DBWA + i*4;
+ value = emi_readl(IOMEM(ADDR_EMI + addr));
+
+ value = (value & ~(Mask_master << offset_master)) | ((*(master+i) & Mask_master) << offset_master);
+ value = (value & ~(Mask_rw << offset_rw)) | ((*(rw+i) & Mask_rw) << offset_rw);
+
+
+ emi_reg_sync_writel(value, ADDR_EMI + addr);
+ }
+
+ return BM_REQ_OK;
+}
+
+int MET_BM_SetWSCT_high_priority(unsigned int *disable, unsigned int *select)
+{
+ unsigned int value, addr;
+ int i;
+
+ const unsigned int Mask_disable = 0x1;
+ const unsigned int offset_disable = 2;
+
+ const unsigned int Mask_select = 0xF;
+ const unsigned int offset_select = 28;
+
+ for (i=0;i<WSCT_AMOUNT;i++) {
+ addr = EMI_DBWA + i*4;
+ value = emi_readl(IOMEM(ADDR_EMI + addr));
+
+ value = (value & ~(Mask_disable << offset_disable)) | ((*(disable+i) & Mask_disable) << offset_disable);
+ value = (value & ~(Mask_select << offset_select)) | ((*(select+i) & Mask_select) << offset_select);
+
+ emi_reg_sync_writel(value, ADDR_EMI + addr);
+ }
+ return BM_REQ_OK;
+}
+
+/* busid enbale: EMI_DBWX[3], X=A~F */
+/* busid sel: EMI_DBWX[28:16], X=A~F (SEL_ID_TMP) */
+/* busid mask : EMI_DBWY[12:0] 或 EMI_DBWY[28:16], Y=I~K (SEL_ID_MSK) */
+int MET_BM_SetWSCT_busid_idmask(unsigned int *busid, unsigned int *idMask)
+{
+ unsigned int value, addr;
+ unsigned int enable_tmp, busid_tmp, idmask_tmp;
+ int i;
+
+ const unsigned int Mask_busid = 0x1FFF;
+ const unsigned int offset_busid = 16;
+
+ const unsigned int Mask_enable = 0x1;
+ const unsigned int offset_enable = 3;
+
+ const unsigned int Mask_idMask = 0x1FFF;
+ const unsigned int offset_idMask_even = 0;
+ const unsigned int offset_idMask_odd = 16;
+
+ for (i=0;i<WSCT_AMOUNT;i++) {
+
+ /*enable, SEL_ID_TMP*/
+ if (*(busid+i)>0xffff) {
+ enable_tmp = 0;
+ busid_tmp = 0x1FFF;
+ idmask_tmp = 0x1FFF;
+ }
+ else {
+ enable_tmp = 1;
+ busid_tmp = *(busid+i) & Mask_busid;
+ idmask_tmp = *(idMask+i) & Mask_idMask;
+ }
+
+
+ addr = EMI_DBWA + i*4;
+ value = emi_readl(IOMEM(ADDR_EMI + addr));
+
+ value = (value & ~(Mask_busid << offset_busid)) | (busid_tmp << offset_busid);
+ value = (value & ~(Mask_enable << offset_enable)) | (enable_tmp << offset_enable);
+
+ emi_reg_sync_writel(value, ADDR_EMI + addr);
+
+ /*SEL_ID_MSK*/
+ addr = EMI_DBWI + (i/2)*4;
+
+ value = emi_readl(IOMEM(ADDR_EMI + addr));
+
+ if (i%2==0)
+ value = (value & ~(Mask_idMask << offset_idMask_even)) | (idmask_tmp << offset_idMask_even);
+ else
+ value = (value & ~(Mask_idMask << offset_idMask_odd)) | (idmask_tmp << offset_idMask_odd);
+
+ emi_reg_sync_writel(value, ADDR_EMI + addr);
+ }
+
+
+ return BM_REQ_OK;
+}
+
+
+int MET_BM_SetWSCT_chn_rank_sel(unsigned int *chn_rank_sel)
+{
+ unsigned int value, addr;
+ int i;
+
+ const unsigned int Mask = 0xF;
+ const unsigned int offset = 12;
+
+
+ for (i=0;i<WSCT_AMOUNT;i++) {
+ addr = EMI_DBWA_2ND + i*4;
+ value = emi_readl(IOMEM(ADDR_EMI + addr));
+
+ value = (value & ~(Mask << offset)) | ((*(chn_rank_sel+i) & Mask) << offset);
+
+ emi_reg_sync_writel(value, ADDR_EMI + addr);
+ }
+ return BM_REQ_OK;
+}
+
+int MET_BM_SetWSCT_burst_range(unsigned int *bnd_dis, unsigned int *low_bnd, unsigned int *up_bnd)
+{
+ unsigned int value, addr;
+ int i;
+
+ const unsigned int Mask_dis = 0x1, Mask_low_bnd = 0x1FF, Mask_up_bnd = 0x1FF;
+ const unsigned int offset_dis = 4, offset_low_bnd = 16 , offset_up_bnd = 0 ;
+
+
+ for (i=0;i<WSCT_AMOUNT;i++) {
+
+ addr = EMI_DBWA + i*4;
+ value = emi_readl(IOMEM(ADDR_EMI + addr));
+
+ value = (value & ~(Mask_dis << offset_dis)) | ((*(bnd_dis+i) & Mask_dis) << offset_dis);
+
+ emi_reg_sync_writel(value, ADDR_EMI + addr);
+
+
+ addr = EMI_DBWA_2ND + i*4;
+ value = emi_readl(IOMEM(ADDR_EMI + addr));
+
+ value = (value & ~(Mask_low_bnd << offset_low_bnd)) | ((*(low_bnd+i) & Mask_low_bnd) << offset_low_bnd);
+ value = (value & ~(Mask_up_bnd << offset_up_bnd)) | ((*(up_bnd+i) & Mask_up_bnd) << offset_up_bnd);
+
+ emi_reg_sync_writel(value, ADDR_EMI + addr);
+ }
+ return BM_REQ_OK;
+
+}
+
+int MET_BM_SetTSCT_busid_enable(unsigned int *enable)
+{
+ //MET_BM_Set_WsctTsct_id_sel
+
+ int i;
+
+ for (i=0;i<TSCT_AMOUNT;i++) {
+ MET_BM_Set_WsctTsct_id_sel(i, *(enable+i));
+ }
+
+ return BM_REQ_OK;
+}
+
+//use the origin together, MET_BM_SetUltraHighFilter()
+/* EMI_TTYPEN_CONA [23:20], N=1~21 (HPRI_SEL) */
+int MET_BM_SetTtype_high_priority_sel(unsigned int _high_priority_filter, unsigned int *select)
+{
+ int i;
+ unsigned int enable;
+ unsigned int value, addr;
+
+ const unsigned int Mask_sel = 0xF;
+ const unsigned int offset_sel = 20;
+
+ 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);
+
+ /* ultra level select */
+ addr = EMI_TTYPE1_CONA + i*8;
+ value = emi_readl(IOMEM(ADDR_EMI + addr));
+
+ value = (value & ~(Mask_sel << offset_sel)) | ((*(select+i) & Mask_sel) << offset_sel);
+
+ emi_reg_sync_writel(value, ADDR_EMI + addr);
+
+ }
+
+ return BM_REQ_OK;
+}
+
+
+//always call this API to init the reg
+//related API, MET_BM_SetbusID, MET_BM_SetbusID_En
+int MET_BM_SetTtype_busid_idmask(unsigned int *busid, unsigned int *idMask, int _ttype1_16_en, int _ttype17_21_en)
+{
+ int i;
+ unsigned int value, addr;
+
+ const unsigned int Mask_idMask = 0x1FFF;
+ const unsigned int offset_idMask = 0;
+
+ if (_ttype1_16_en != BM_TTYPE1_16_ENABLE) {
+ /* mask set 0x1FFF , busid set disable*/
+ for (i = 1; i <= 16; i++) {
+ *(busid + i - 1) = 0xfffff;
+ *(idMask + i - 1) = 0x1FFF;
+ }
+ }
+
+ if (_ttype17_21_en != BM_TTYPE17_21_ENABLE) {
+ for (i = 17; i <= 21; i++) {
+ *(busid + i - 1) = 0xfffff;
+ *(idMask + i - 1) = 0x1FFF;
+ }
+ }
+
+ for (i = 1; i <= BM_COUNTER_MAX; i++) {
+ MET_BM_SetbusID(i, *(busid + i - 1));
+ MET_BM_SetbusID_En(i, ( *(busid + i - 1) > 0xffff) ? 0 : 1);
+
+ /* set idMask */
+ addr = EMI_TTYPE1_CONA + (i-1)*8;
+ value = emi_readl(IOMEM(ADDR_EMI + addr));
+
+ value = (value & ~(Mask_idMask << offset_idMask)) | ((*(idMask+i-1) & Mask_idMask) << offset_idMask);
+
+ emi_reg_sync_writel(value, ADDR_EMI + addr);
+
+ }
+
+ return BM_REQ_OK;
+}
+
+
+int MET_BM_SetTtype_chn_rank_sel(unsigned int *chn_rank_sel)
+{
+ unsigned int value, addr;
+ int i;
+
+ const unsigned int Mask = 0xF;
+ const unsigned int offset = 16;
+
+
+ for (i=0;i<BM_COUNTER_MAX;i++) {
+ addr = EMI_TTYPE1_CONA + i*8;
+ value = emi_readl(IOMEM(ADDR_EMI + addr));
+
+ value = (value & ~(Mask << offset)) | ((*(chn_rank_sel+i) & Mask) << offset);
+
+ emi_reg_sync_writel(value, ADDR_EMI + addr);
+ }
+ return BM_REQ_OK;
+}
+
+int MET_BM_SetTtype_burst_range(unsigned int *bnd_dis, unsigned int *low_bnd, unsigned int *up_bnd)
+{
+ unsigned int value, addr;
+ int i;
+
+ const unsigned int Mask_dis = 0x1, Mask_low_bnd = 0x1FF, Mask_up_bnd = 0x1FF;
+ const unsigned int offset_dis = 24, offset_low_bnd = 16 , offset_up_bnd = 0 ;
+
+
+ for (i=0;i<BM_COUNTER_MAX;i++) {
+
+ /* set dis bit */
+ addr = EMI_TTYPE1_CONA + i*8;
+ value = emi_readl(IOMEM(ADDR_EMI + addr));
+
+ value = (value & ~(Mask_dis << offset_dis)) | ((*(bnd_dis+i) & Mask_dis) << offset_dis);
+
+ emi_reg_sync_writel(value, ADDR_EMI + addr);
+
+
+ addr = EMI_TTYPE1_CONB + i*8;
+ value = emi_readl(IOMEM(ADDR_EMI + addr));
+
+ value = (value & ~(Mask_low_bnd << offset_low_bnd)) | ((*(low_bnd+i) & Mask_low_bnd) << offset_low_bnd);
+ value = (value & ~(Mask_up_bnd << offset_up_bnd)) | ((*(up_bnd+i) & Mask_up_bnd) << offset_up_bnd);
+
+ emi_reg_sync_writel(value, ADDR_EMI + addr);
+ }
+ return BM_REQ_OK;
+}
\ No newline at end of file
diff --git a/src/devtools/met-driver/4.14/common/mtk_emi_bm_35.h b/src/devtools/met-driver/4.14/common/mtk_emi_bm_35.h
new file mode 100644
index 0000000..bae1307
--- /dev/null
+++ b/src/devtools/met-driver/4.14/common/mtk_emi_bm_35.h
@@ -0,0 +1,301 @@
+/*
+ * 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_MET_EMI_BM_H__
+#define __MT_MET_EMI_BM_H__
+
+#define EMI_VER_MAJOR 3
+#define EMI_VER_MINOR 5
+
+#define FILE_NODE_DATA_LEN 512
+#define WSCT_AMOUNT 6
+#define TSCT_AMOUNT 3
+
+
+#define DRAM_EMI_BASECLOCK_RATE_LP4 4
+#define DRAM_EMI_BASECLOCK_RATE_LP3 2
+
+#define DRAM_IO_BUS_WIDTH_LP4 16
+#define DRAM_IO_BUS_WIDTH_LP3 32
+
+#define DRAM_DATARATE 2
+
+#define ADDR_EMI ((unsigned long)BaseAddrEMI)
+
+
+#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)
+
+
+enum BM_RW_Type {
+ BM_BOTH_READ_WRITE,
+ BM_READ_ONLY,
+ BM_WRITE_ONLY
+};
+
+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
+};
+
+enum {
+ BM_WSCT_RW_DISABLE = 0x0,
+ BM_WSCT_RW_READONLY,
+ BM_WSCT_RW_WRITEONLY,
+ BM_WSCT_RW_RWBOTH
+};
+
+/*coda busid 12bit, but HW support 16 bit*/
+#define EMI_BMID_MASK (0xFFFF)
+#define BM_COUNTER_MAX (21)
+
+enum {
+ BUS_MON_EN_SHIFT = 0,
+ BUS_MON_PAUSE_SHIFT = 1,
+ BUS_MON_IDLE_SHIFT = 3,
+ BC_OVERRUN_SHIFT = 8,
+ DRAMC_CG_SHIFT = 9,
+};
+
+#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)
+
+/* EMI Rank configuration */
+enum {
+ DISABLE_DUAL_RANK_MODE = 0,
+ ENABLE_DUAL_RANK_MODE,
+};
+
+#define RANK_MASK 0x1
+#define ONE_RANK 1
+#define DUAL_RANK 2
+
+
+#if defined(CONFIG_MTK_TINYSYS_SSPM_SUPPORT) && defined(ONDIEMET_SUPPORT)
+enum BM_EMI_IPI_Type {
+ SET_BASE_EMI = 0x0,
+ SET_EBM_CONFIGS1 = 0x7,
+ SET_EBM_CONFIGS2 = 0x8,
+ SET_REGISTER_CB = 0x9,
+};
+#endif
+
+
+#define EMI_OFF 0x0000
+#define EMI_CONA (0x000-EMI_OFF)
+#define EMI_CONH (0x038-EMI_OFF)
+#define EMI_CONH_2ND (0x03C-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_MSEL (0x440 - 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)
+
+
+/* SEDA 3.5 New! reg*/
+/* For WSCT setting*/
+#define EMI_DBWA (0xF00 - EMI_OFF)
+#define EMI_DBWB (0xF04 - EMI_OFF)
+#define EMI_DBWC (0xF08 - EMI_OFF)
+#define EMI_DBWD (0xF0C - EMI_OFF)
+#define EMI_DBWE (0xF10 - EMI_OFF)
+#define EMI_DBWF (0xF14 - EMI_OFF)
+
+
+#define EMI_DBWA_2ND (0xF2C - EMI_OFF)
+#define EMI_DBWB_2ND (0xF30 - EMI_OFF)
+#define EMI_DBWC_2ND (0xF34 - EMI_OFF)
+#define EMI_DBWD_2ND (0xF38 - EMI_OFF)
+#define EMI_DBWE_2ND (0xF3C - EMI_OFF)
+#define EMI_DBWF_2ND (0xF40 - EMI_OFF)
+
+#define EMI_DBWI (0xF20 - EMI_OFF) /* SEL_ID_MSK*/
+#define EMI_DBWJ (0xF24 - EMI_OFF)
+#define EMI_DBWK (0xF28 - EMI_OFF)
+
+/* For Ttype setting */
+#define EMI_TTYPE1_CONA (0xF50 - EMI_OFF)
+#define EMI_TTYPE1_CONB (0xF54 - EMI_OFF)
+#define EMI_TTYPE2_CONA (0xF58 - EMI_OFF)
+#define EMI_TTYPE2_CONB (0xF5C - EMI_OFF)
+#define EMI_TTYPE3_CONA (0xF60 - EMI_OFF)
+#define EMI_TTYPE3_CONB (0xF64 - EMI_OFF)
+#define EMI_TTYPE4_CONA (0xF68 - EMI_OFF)
+#define EMI_TTYPE4_CONB (0xF6C - EMI_OFF)
+#define EMI_TTYPE5_CONA (0xF70 - EMI_OFF)
+#define EMI_TTYPE5_CONB (0xF74 - EMI_OFF)
+#define EMI_TTYPE6_CONA (0xF78 - EMI_OFF)
+#define EMI_TTYPE6_CONB (0xF7C - EMI_OFF)
+#define EMI_TTYPE7_CONA (0xF80 - EMI_OFF)
+#define EMI_TTYPE7_CONB (0xF84 - EMI_OFF)
+#define EMI_TTYPE8_CONA (0xF88 - EMI_OFF)
+#define EMI_TTYPE8_CONB (0xF8C - EMI_OFF)
+#define EMI_TTYPE9_CONA (0xF90 - EMI_OFF)
+#define EMI_TTYPE9_CONB (0xF94 - EMI_OFF)
+#define EMI_TTYPE10_CONA (0xF98 - EMI_OFF)
+#define EMI_TTYPE10_CONB (0xF9C - EMI_OFF)
+#define EMI_TTYPE11_CONA (0xFA0 - EMI_OFF)
+#define EMI_TTYPE11_CONB (0xFA4 - EMI_OFF)
+#define EMI_TTYPE12_CONA (0xFA8 - EMI_OFF)
+#define EMI_TTYPE12_CONB (0xFAC - EMI_OFF)
+#define EMI_TTYPE13_CONA (0xFB0 - EMI_OFF)
+#define EMI_TTYPE13_CONB (0xFB4 - EMI_OFF)
+#define EMI_TTYPE14_CONA (0xFB8 - EMI_OFF)
+#define EMI_TTYPE14_CONB (0xFBC - EMI_OFF)
+#define EMI_TTYPE15_CONA (0xFC0 - EMI_OFF)
+#define EMI_TTYPE15_CONB (0xFC4 - EMI_OFF)
+#define EMI_TTYPE16_CONA (0xFC8 - EMI_OFF)
+#define EMI_TTYPE16_CONB (0xFCC - EMI_OFF)
+#define EMI_TTYPE17_CONA (0xFD0 - EMI_OFF)
+#define EMI_TTYPE17_CONB (0xFD4 - EMI_OFF)
+#define EMI_TTYPE18_CONA (0xFD8 - EMI_OFF)
+#define EMI_TTYPE18_CONB (0xFDC - EMI_OFF)
+#define EMI_TTYPE19_CONA (0xFE0 - EMI_OFF)
+#define EMI_TTYPE19_CONB (0xFE4 - EMI_OFF)
+#define EMI_TTYPE20_CONA (0xFE8 - EMI_OFF)
+#define EMI_TTYPE20_CONB (0xFEC - EMI_OFF)
+#define EMI_TTYPE21_CONA (0xFF0 - EMI_OFF)
+#define EMI_TTYPE21_CONB (0xFF4 - EMI_OFF)
+
+
+
+extern int MET_BM_Init(void);
+extern void MET_BM_DeInit(void);
+extern void MET_BM_SaveCfg(void);
+extern void MET_BM_RestoreCfg(void);
+
+
+
+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_SetbusID_En(const unsigned int counter_num,
+ const unsigned int enable);
+extern int MET_BM_SetbusID(const unsigned int counter_num,
+ const unsigned int id);
+extern int MET_BM_SetUltraHighFilter(const unsigned int counter_num, const unsigned int enable);
+extern int MET_BM_SetLatencyCounter(unsigned int enable);
+extern void MET_BM_SetReadWriteType(const unsigned int ReadWriteType);
+
+extern unsigned int MET_EMI_GetDramRankNum(void);
+extern unsigned int MET_EMI_GetDramRankNum_CHN1(void);
+
+
+unsigned int MET_EMI_GetDramChannNum(void);
+
+
+/* SEDA 3.5 NEW */
+extern int MET_BM_SetWSCT_master_rw(unsigned int *master , unsigned int *rw);
+extern int MET_BM_SetWSCT_high_priority(unsigned int *disable, unsigned int *select);
+extern int MET_BM_SetWSCT_busid_idmask(unsigned int *busid, unsigned int *idMask);
+extern int MET_BM_SetWSCT_chn_rank_sel(unsigned int *chn_rank_sel);
+extern int MET_BM_SetWSCT_burst_range(unsigned int *bnd_dis, unsigned int *low_bnd, unsigned int *up_bnd);
+extern int MET_BM_SetTSCT_busid_enable(unsigned int *enable);
+extern int MET_BM_SetTtype_high_priority_sel(unsigned int _high_priority_filter, unsigned int *select);
+extern int MET_BM_SetTtype_busid_idmask(unsigned int *busid, unsigned int *idMask, int _ttype1_16_en, int _ttype17_21_en);
+extern int MET_BM_SetTtype_chn_rank_sel(unsigned int *chn_rank_sel);
+extern int MET_BM_SetTtype_burst_range(unsigned int *bnd_dis, unsigned int *low_bnd, unsigned int *up_bnd);
+extern unsigned int MET_EMI_Get_BaseClock_Rate(void);
+
+#endif /* !__MT_MET_EMI_BM_H__ */
diff --git a/src/devtools/met-driver/4.14/common/mtk_gpu_metmonitor.c b/src/devtools/met-driver/4.14/common/mtk_gpu_metmonitor.c
new file mode 100644
index 0000000..fea3388
--- /dev/null
+++ b/src/devtools/met-driver/4.14/common/mtk_gpu_metmonitor.c
@@ -0,0 +1,877 @@
+/*
+ * 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/io.h>
+#include <linux/hrtimer.h>
+
+#include "met_drv.h"
+#include "trace.h"
+#include "interface.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;
+
+static unsigned int output_header_pmu_len;
+static unsigned int output_pmu_str_len;
+
+noinline void GPU_Loading(unsigned char cnt, unsigned int *value)
+{
+ switch (cnt) {
+ case 1:
+ MET_TRACE("%u\n", value[0]);
+ break;
+ case 2:
+ MET_TRACE("%u,%u\n", value[0], value[1]);
+ break;
+ case 3:
+ MET_TRACE("%u,%u,%u\n", value[0], value[1], value[2]);
+ break;
+ case 4:
+ MET_TRACE("%u,%u,%u,%u\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, 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)
+{
+ unsigned int pu4Value[eMET_GPU_PROFILE_CNT];
+ unsigned long u4Index = 0;
+ unsigned int loading = 0;
+ int count = 0;
+
+ memset(pu4Value, 0x00, 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);
+ }
+}
+#endif
+
+static void gpu_monitor_start(void)
+{
+ if (mtk_get_gpu_loading_symbol)
+ g_u4AvailableInfo |= (1 << eMET_GPU_LOADING);
+ 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);
+
+#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,");
+
+ 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");
+
+ 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
+ */
+#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)];
+
+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);
+}
+
+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);
+}
+
+static void gpu_dvfs(void)
+{
+ unsigned int freq = 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;
+
+ freq = mt_gpufreq_get_cur_freq_symbol ? mt_gpufreq_get_cur_freq_symbol() : 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);
+ GPU_DVFS(freq, ThermalLimit, CustomBoost, CustomUpbound);
+
+ /* 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);
+ }
+ }
+}
+
+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();
+
+ /* reset status */
+ gpu_dvfs_type_prev = 0;
+ gpu_dvfs_type_freq_prev = 0;
+}
+
+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: ");
+ ret += snprintf(buf+ret, PAGE_SIZE-ret,
+ "Freq(kHz),ThermalLimit(kHz),CustomBoost,CustomUpbound\n");
+
+ 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");
+
+ 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;
+
+#if 0
+ if (mtk_get_gpu_memory_usage_symbol(&u4Value))
+ g_u4MemProfileIsOn = 1;
+#endif
+ 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\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,
+};
+
+/*
+ * 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);
+ }
+
+ /* init state */
+ met_gpu_pmu.header_read_again = 0;
+ output_header_pmu_len = 0;
+ output_pmu_str_len = 0;
+
+ 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 1
+ /* 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)
+{
+ if(output_header_pmu_len == 0){
+ len = snprintf(buf, PAGE_SIZE, "%s", header_pmu);
+ met_gpu_pmu.header_read_again = 1;
+ output_header_pmu_len = len;
+ }
+ else{
+ if( (strlen(pmu_str) - output_pmu_str_len) > PAGE_SIZE ){
+ char output_buf[PAGE_SIZE/4];
+
+ strncpy(output_buf, pmu_str+output_pmu_str_len, PAGE_SIZE/4);
+ len = snprintf(buf, PAGE_SIZE, "%s", output_buf);
+ output_pmu_str_len += len;
+ }
+ else{
+ len = snprintf(buf, PAGE_SIZE, "%s\n", pmu_str+output_pmu_str_len);
+
+ /* reset state */
+ met_gpu_pmu.header_read_again = 0;
+ output_header_pmu_len = 0;
+ output_pmu_str_len = 0;
+ }
+ }
+
+ 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,
+};
+
+/*
+ * GPU stall counters
+ */
+#ifdef MET_GPU_STALL_MONITOR
+static void __iomem *io_addr_gpu_stall;
+
+static int gpu_stall_create_subfs(struct kobject *parent)
+{
+ io_addr_gpu_stall = ioremap(IO_ADDR_GPU_STALL, IO_SIZE_GPU_STALL);
+ if (!io_addr_gpu_stall) {
+ PR_BOOTMSG("Failed to init GPU stall counters!!\n");
+ return -ENODEV;
+ }
+
+ return 0;
+}
+
+static void gpu_stall_delete_subfs(void)
+{
+ if (io_addr_gpu_stall) {
+ iounmap(io_addr_gpu_stall);
+ io_addr_gpu_stall = NULL;
+ }
+}
+
+static void gpu_stall_start(void)
+{
+ writel(0x00010001, io_addr_gpu_stall+OFFSET_STALL_GPU_M0_CHECK);
+ writel(0x00010001, io_addr_gpu_stall+OFFSET_STALL_GPU_M1_CHECK);
+ writel(0x00010001, io_addr_gpu_stall+OFFSET_STALL_GPU_M0_EMI_CHECK);
+ writel(0x00010001, io_addr_gpu_stall+OFFSET_STALL_GPU_M1_EMI_CHECK);
+}
+
+static void gpu_stall_stop(void)
+{
+ writel(0x00000000, io_addr_gpu_stall+OFFSET_STALL_GPU_M0_CHECK);
+ writel(0x00000000, io_addr_gpu_stall+OFFSET_STALL_GPU_M1_CHECK);
+ writel(0x00000000, io_addr_gpu_stall+OFFSET_STALL_GPU_M0_EMI_CHECK);
+ writel(0x00000000, io_addr_gpu_stall+OFFSET_STALL_GPU_M1_EMI_CHECK);
+}
+
+noinline void GPU_STALL_RAW(void)
+{
+ unsigned int stall_counters[4];
+ char *SOB, *EOB;
+
+ stall_counters[0] = (unsigned int)readl(io_addr_gpu_stall+OFFSET_STALL_GPU_M0_CHECK);
+ stall_counters[1] = (unsigned int)readl(io_addr_gpu_stall+OFFSET_STALL_GPU_M1_CHECK);
+ stall_counters[2] = (unsigned int)readl(io_addr_gpu_stall+OFFSET_STALL_GPU_M0_EMI_CHECK);
+ stall_counters[3] = (unsigned int)readl(io_addr_gpu_stall+OFFSET_STALL_GPU_M1_EMI_CHECK);
+
+ MET_TRACE_GETBUF(&SOB, &EOB);
+ EOB = ms_formatH(EOB, ARRAY_SIZE(stall_counters), stall_counters);
+ MET_TRACE_PUTBUF(SOB, EOB);
+}
+
+static void gpu_stall_timed_polling(unsigned long long stamp, int cpu)
+{
+ GPU_STALL_RAW();
+}
+
+static char g_pComGPUStallHeader[] =
+ "met-info [000] 0.0: met_gpu_stall_header: M0_STATUS_1,M1_STATUS_1,M0_STATUS_2,M1_STATUS_2\n";
+static int gpu_stall_print_header(char *buf, int len)
+{
+ return snprintf(buf, PAGE_SIZE, g_pComGPUStallHeader);
+}
+
+struct metdevice met_gpu_stall = {
+ .name = "gpu-stall",
+ .owner = THIS_MODULE,
+ .type = MET_TYPE_BUS,
+ .cpu_related = 0,
+ .create_subfs = gpu_stall_create_subfs,
+ .delete_subfs = gpu_stall_delete_subfs,
+ .start = gpu_stall_start,
+ .stop = gpu_stall_stop,
+ .mode = 0,
+ .polling_interval = 1, /* ms */
+ .timed_polling = gpu_stall_timed_polling,
+ .print_header = gpu_stall_print_header,
+};
+#endif /* MET_GPU_STALL_MONITOR */
diff --git a/src/devtools/met-driver/4.14/common/mtk_gpu_metmonitor.h b/src/devtools/met-driver/4.14/common/mtk_gpu_metmonitor.h
new file mode 100644
index 0000000..069c534
--- /dev/null
+++ b/src/devtools/met-driver/4.14/common/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.14/common/mtk_typedefs.h b/src/devtools/met-driver/4.14/common/mtk_typedefs.h
new file mode 100644
index 0000000..4424479
--- /dev/null
+++ b/src/devtools/met-driver/4.14/common/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.14/common/ondiemet.c b/src/devtools/met-driver/4.14/common/ondiemet.c
new file mode 100644
index 0000000..5247fa7
--- /dev/null
+++ b/src/devtools/met-driver/4.14/common/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.14/common/ondiemet.h b/src/devtools/met-driver/4.14/common/ondiemet.h
new file mode 100644
index 0000000..3fff604
--- /dev/null
+++ b/src/devtools/met-driver/4.14/common/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.14/common/ondiemet_log.c b/src/devtools/met-driver/4.14/common/ondiemet_log.c
new file mode 100644
index 0000000..fd1ca31
--- /dev/null
+++ b/src/devtools/met-driver/4.14/common/ondiemet_log.c
@@ -0,0 +1,517 @@
+/*
+ * 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;
+ current->flags |= PF_NOFREEZE;
+ 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.14/common/ondiemet_log.h b/src/devtools/met-driver/4.14/common/ondiemet_log.h
new file mode 100644
index 0000000..cfe8be9
--- /dev/null
+++ b/src/devtools/met-driver/4.14/common/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.14/common/power.c b/src/devtools/met-driver/4.14/common/power.c
new file mode 100644
index 0000000..c57d907
--- /dev/null
+++ b/src/devtools/met-driver/4.14/common/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.14/common/power.h b/src/devtools/met-driver/4.14/common/power.h
new file mode 100644
index 0000000..8a0e8f0
--- /dev/null
+++ b/src/devtools/met-driver/4.14/common/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.14/common/sampler.c b/src/devtools/met-driver/4.14/common/sampler.c
new file mode 100644
index 0000000..e47ef19
--- /dev/null
+++ b/src/devtools/met-driver/4.14/common/sampler.c
@@ -0,0 +1,649 @@
+/*
+ * 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 "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 preferred_cpu_list[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
+
+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) {
+ *(this_cpu_ptr(c->polling_count)) = 0;
+ 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();
+ }
+ *(this_cpu_ptr(c->polling_count)) = 0;
+ }
+}
+
+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;
+ struct metdevice *c;
+
+ 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) {
+ /*pr_info("%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);
+
+#ifdef CONFIG_CPU_FREQ
+ force_power_log(cpu);
+#endif
+ list_for_each_entry(c, &met_list, list) {
+ if (c->cpu_state_notify)
+ c->cpu_state_notify(cpu, action);
+ }
+ break;
+
+ case MET_CPU_OFFLINE:
+ list_for_each_entry(c, &met_list, list) {
+ if (c->cpu_state_notify)
+ c->cpu_state_notify(cpu, action);
+ }
+
+ online_cpu_map &= ~(1 << cpu);
+ dbg_met_tag_oneshot(0, "met_offline cpu", cpu);
+ if (cpu == curr_polling_cpu) {
+ /* pr_info("%s, %d: curr_polling_cpu %d is down\n",
+ * __func__, __LINE__, curr_polling_cpu);
+ */
+ preferred_polling_cpu = calc_preferred_polling_cpu(online_cpu_map);
+ /* pr_info("%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)
+ /* pr_info("%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);
+
+ met_cpu_ptr = &per_cpu(met_cpu, cpu);
+ dw = &met_cpu_ptr->dwork;
+ cancel_delayed_work_sync(dw);
+
+ /* sync_samples(cpu); */
+ break;
+ default:
+ list_for_each_entry(c, &met_list, list) {
+ if (c->cpu_state_notify)
+ c->cpu_state_notify(cpu, action);
+ }
+ }
+
+ 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;
+
+ 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->cpu_related) && (c->mode) && (c->uniq_start))
+ c->uniq_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();
+ else if ((c->cpu_related) && (c->mode) && (c->uniq_start))
+ c->uniq_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);
+ for_each_online_cpu(cpu) {
+ met_smp_call_function_single_symbol(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);
+ online_cpu_map = 0;
+ for_each_online_cpu(cpu) {
+ online_cpu_map |= (1 << cpu);
+ }
+
+ for_each_online_cpu(cpu) {
+ met_smp_call_function_single_symbol(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) {
+ if (c->ondiemet_mode == 0) {
+ if ((!(c->cpu_related)) && (c->mode) && (c->stop))
+ c->stop();
+ else if ((c->cpu_related) && (c->mode) && (c->uniq_stop))
+ c->uniq_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();
+ else if ((c->cpu_related) && (c->mode) && (c->uniq_stop))
+ c->uniq_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.14/common/sampler.h b/src/devtools/met-driver/4.14/common/sampler.h
new file mode 100644
index 0000000..6089c2d
--- /dev/null
+++ b/src/devtools/met-driver/4.14/common/sampler.h
@@ -0,0 +1,52 @@
+/*
+ * 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);
+
+#ifdef CONFIG_CPU_FREQ
+#include "power.h"
+#endif
+
+#endif /* _SAMPLER_H_ */
diff --git a/src/devtools/met-driver/4.14/common/spmtwam/ap/met_spmtwam.c b/src/devtools/met-driver/4.14/common/spmtwam/ap/met_spmtwam.c
new file mode 100644
index 0000000..7ce45ed
--- /dev/null
+++ b/src/devtools/met-driver/4.14/common/spmtwam/ap/met_spmtwam.c
@@ -0,0 +1,690 @@
+/*
+ * 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/random.h>
+#include <linux/fs.h>
+#include <linux/ctype.h>
+
+#include <mtk_spm.h>
+#include "met_drv.h"
+#include "trace.h"
+#include "core_plf_init.h"
+#include "interface.h"
+#include "met_spmtwam.h"
+
+/* #define SPM_TWAM_DEBUG */
+
+#define INFRA_FMEM_DCM_BASE 0x1020E000
+#define INFRA_FMEM_DCM_SIZE 0x1000
+#define FMEM_MUX_ADDR_OFFSET 0x200
+#define FMEM_MUX_VALUE 0x780
+#define TWAM_DBG_SIG_BASE 0x0D0A0000
+#define TWAM_DBG_SIG_SIZE 0x100
+#define TWAM_DBG_SIG_OFFSET 0x94
+
+
+struct metdevice met_spmtwam;
+static struct kobject *kobj_spmtwam;
+/* static void __iomem *fmem_dcm_base; */
+static void __iomem *twam_dbg_signal_base;
+static struct met_spmtwam_para spmtwam_para[MAX_EVENT_COUNT];
+
+#ifdef SPM_TWAM_DEBUG
+static unsigned int debug_signal_val;
+#endif
+
+static struct twam_sig twamsig;
+static struct twam_sig montype; /* b'00: rising, b'01: falling, b'10: high, b'11: low */
+static struct twam_sig dbgout;
+static int used_count;
+static int start;
+static bool twam_clock_mode = TWAM_SPEED_MODE; /* true:speed mode, false:normal mode */
+static unsigned int window_len = 1300000; /* 50 ms in 26 MHz */
+static unsigned int idle_sel;
+
+#define MONTYPE_SHOW_IMPLEMENT(num) \
+ do { \
+ int i; \
+ i = snprintf(buf, PAGE_SIZE, "%d\n", montype.sig ## num); \
+ return i; \
+ } while (0)
+
+#define MONTYPE_STORE_IMPLEMENT(num) \
+ do { \
+ int value; \
+ if ((n == 0) || (buf == NULL)) \
+ return -EINVAL; \
+ if (kstrtoint(buf, 10, &value) != 0) \
+ return -EINVAL; \
+ if (value < 0 || value > 3) \
+ return -EINVAL; \
+ montype.sig ## num= value; \
+ return n; \
+ } while (0)
+
+#define DBGOUT_SHOW_IMPLEMENT(num) \
+ do { \
+ int i; \
+ i = snprintf(buf, PAGE_SIZE, "%d\n", dbgout.sig ## num); \
+ return i; \
+ } while (0)
+
+#define DBGOUT_STORE_IMPLEMENT(num) \
+ do { \
+ int value; \
+ if ((n == 0) || (buf == NULL)) \
+ return -EINVAL; \
+ if (kstrtoint(buf, 10, &value) != 0) \
+ return -EINVAL; \
+ if (value < 0 || value > 127) \
+ return -EINVAL; \
+ dbgout.sig ## num = value; \
+ return n; \
+ } while (0)
+
+
+static ssize_t montype0_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
+{
+ MONTYPE_SHOW_IMPLEMENT(0);
+}
+
+static ssize_t montype0_store(struct kobject *kobj,
+ struct kobj_attribute *attr,
+ const char *buf,
+ size_t n)
+{
+ MONTYPE_STORE_IMPLEMENT(0);
+}
+
+static ssize_t montype1_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
+{
+ MONTYPE_SHOW_IMPLEMENT(1);
+}
+
+static ssize_t montype1_store(struct kobject *kobj,
+ struct kobj_attribute *attr,
+ const char *buf,
+ size_t n)
+{
+ MONTYPE_STORE_IMPLEMENT(1);
+}
+
+static ssize_t montype2_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
+{
+ MONTYPE_SHOW_IMPLEMENT(2);
+}
+
+static ssize_t montype2_store(struct kobject *kobj,
+ struct kobj_attribute *attr,
+ const char *buf,
+ size_t n)
+{
+ MONTYPE_STORE_IMPLEMENT(2);
+}
+
+static ssize_t montype3_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
+{
+ MONTYPE_SHOW_IMPLEMENT(3);
+}
+
+static ssize_t montype3_store(struct kobject *kobj,
+ struct kobj_attribute *attr,
+ const char *buf,
+ size_t n)
+{
+ MONTYPE_STORE_IMPLEMENT(3);
+}
+
+static ssize_t window_len_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
+{
+ int i;
+
+ i = snprintf(buf, PAGE_SIZE, "%d\n", window_len);
+
+ return i;
+}
+
+static ssize_t window_len_store(struct kobject *kobj,
+ struct kobj_attribute *attr,
+ const char *buf,
+ size_t n)
+{
+ int value;
+
+ if ((n == 0) || (buf == NULL))
+ return -EINVAL;
+
+ if (kstrtoint(buf, 10, &value) != 0)
+ return -EINVAL;
+
+ if (value < 0)
+ return -EINVAL;
+
+ window_len = value;
+
+ return n;
+}
+
+static ssize_t dbgout0_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
+{
+ DBGOUT_SHOW_IMPLEMENT(0);
+}
+
+static ssize_t dbgout0_store(struct kobject *kobj,
+ struct kobj_attribute *attr,
+ const char *buf,
+ size_t n)
+{
+ DBGOUT_STORE_IMPLEMENT(0);
+}
+
+static ssize_t dbgout1_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
+{
+ DBGOUT_SHOW_IMPLEMENT(1);
+}
+
+static ssize_t dbgout1_store(struct kobject *kobj,
+ struct kobj_attribute *attr,
+ const char *buf,
+ size_t n)
+{
+ DBGOUT_STORE_IMPLEMENT(1);
+}
+
+static ssize_t dbgout2_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
+{
+ DBGOUT_SHOW_IMPLEMENT(2);
+}
+
+static ssize_t dbgout2_store(struct kobject *kobj,
+ struct kobj_attribute *attr,
+ const char *buf,
+ size_t n)
+{
+ DBGOUT_STORE_IMPLEMENT(2);
+}
+
+static ssize_t dbgout3_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
+{
+ DBGOUT_SHOW_IMPLEMENT(3);
+}
+
+static ssize_t dbgout3_store(struct kobject *kobj,
+ struct kobj_attribute *attr,
+ const char *buf,
+ size_t n)
+{
+ DBGOUT_STORE_IMPLEMENT(3);
+}
+
+#ifdef SPM_TWAM_DEBUG
+/* extern void *mt_spm_base_get(void); */
+static ssize_t debug_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
+{
+ int ret;
+
+ ret = snprintf(buf, PAGE_SIZE, "0x%x\n", debug_signal_val);
+ ret += snprintf(buf+ret, PAGE_SIZE-ret, "%d, %d, %d, %d\n",
+ twam_sig_size[0], twam_sig_size[1], twam_sig_size[2], twam_sig_size[3]);
+/* ret += snprintf(buf+ret, PAGE_SIZE-ret, "spm_base_addr: %p\n", mt_spm_base_get()); */
+
+ return ret;
+}
+static struct kobj_attribute debug_attr = __ATTR_RO(debug);
+#endif
+
+static struct kobj_attribute montype0_attr = __ATTR(montype0, 0664, montype0_show, montype0_store);
+static struct kobj_attribute montype1_attr = __ATTR(montype1, 0664, montype1_show, montype1_store);
+static struct kobj_attribute montype2_attr = __ATTR(montype2, 0664, montype2_show, montype2_store);
+static struct kobj_attribute montype3_attr = __ATTR(montype3, 0664, montype3_show, montype3_store);
+static struct kobj_attribute window_len_attr = __ATTR(window_len, 0664, window_len_show, window_len_store);
+static struct kobj_attribute dbgout0_attr = __ATTR(dbgout0, 0664, dbgout0_show, dbgout0_store);
+static struct kobj_attribute dbgout1_attr = __ATTR(dbgout1, 0664, dbgout1_show, dbgout1_store);
+static struct kobj_attribute dbgout2_attr = __ATTR(dbgout2, 0664, dbgout2_show, dbgout2_store);
+static struct kobj_attribute dbgout3_attr = __ATTR(dbgout3, 0664, dbgout3_show, dbgout3_store);
+
+
+/* create spmtwam related kobj node */
+#define KOBJ_ATTR_LIST \
+ do { \
+ KOBJ_ATTR_ITEM(montype0); \
+ KOBJ_ATTR_ITEM(montype1); \
+ KOBJ_ATTR_ITEM(montype2); \
+ KOBJ_ATTR_ITEM(montype3); \
+ KOBJ_ATTR_ITEM(window_len); \
+ KOBJ_ATTR_ITEM(dbgout0); \
+ KOBJ_ATTR_ITEM(dbgout1); \
+ KOBJ_ATTR_ITEM(dbgout2); \
+ KOBJ_ATTR_ITEM(dbgout3); \
+ } while (0)
+
+static int met_spmtwam_create(struct kobject *parent)
+{
+ int ret = 0;
+
+ kobj_spmtwam = parent;
+
+#define KOBJ_ATTR_ITEM(attr_name) \
+ do { \
+ ret = sysfs_create_file(kobj_spmtwam, &attr_name ## _attr.attr); \
+ if (ret != 0) { \
+ pr_notice("Failed to create " #attr_name " in sysfs\n"); \
+ return ret; \
+ } \
+ } while (0)
+ KOBJ_ATTR_LIST;
+#undef KOBJ_ATTR_ITEM
+
+#ifdef SPM_TWAM_DEBUG
+ ret = sysfs_create_file(kobj_spmtwam, &debug_attr.attr);
+ if (ret != 0) {
+ pr_debug("Failed to create debug in sysfs\n");
+ return ret;
+ }
+#endif
+
+ /* init. */
+ montype.sig0 = 0x2;
+ montype.sig1 = 0x2;
+ montype.sig2 = 0x2;
+ montype.sig3 = 0x2;
+
+#if 0
+ dbgout.sig0 = 87;
+ dbgout.sig1 = 89;
+ dbgout.sig2 = 91;
+ dbgout.sig3 = 106;
+#endif
+
+ return ret;
+}
+
+static void met_spmtwam_delete(void)
+{
+#define KOBJ_ATTR_ITEM(attr_name) \
+ sysfs_remove_file(kobj_spmtwam, &attr_name ## _attr.attr)
+
+ if (kobj_spmtwam != NULL) {
+ KOBJ_ATTR_LIST;
+ kobj_spmtwam = NULL;
+ }
+#undef KOBJ_ATTR_ITEM
+
+#ifdef SPM_TWAM_DEBUG
+ sysfs_remove_file(kobj_spmtwam, &debug_attr.attr);
+#endif
+}
+
+void ms_spmtwam(struct twam_sig *ts)
+{
+ switch (used_count) {
+ case 1:
+ MET_TRACE(MP_FMT1,
+ (ts->sig0));
+ break;
+ case 2:
+ MET_TRACE(MP_FMT2,
+ (ts->sig0),
+ (ts->sig1));
+ break;
+ case 3:
+ MET_TRACE(MP_FMT3,
+ (ts->sig0),
+ (ts->sig1),
+ (ts->sig2));
+ break;
+ case 4:
+ MET_TRACE(MP_FMT4,
+ (ts->sig0),
+ (ts->sig1),
+ (ts->sig2),
+ (ts->sig3));
+ break;
+ default:
+ MET_SPMTWAM_ERR("No assign profile event\n");
+ break;
+ }
+}
+
+static int reset_driver_stat(void)
+{
+ met_spmtwam.mode = 0;
+ used_count = 0;
+ start = 0;
+ return 0;
+}
+
+void spm_twam_enable_debug_out(struct twam_sig *sig, void *addr)
+{
+ int value = 0;
+
+ value |= ((1 << 31) | (sig->sig3 << 24) | (sig->sig2 << 16) | (sig->sig1 << 8) | (sig->sig0 << 0));
+
+#ifdef SPM_TWAM_DEBUG
+ debug_signal_val = value;
+#endif
+
+ writel(value, addr);
+}
+
+void spm_twam_disable_debug_out(void *addr)
+{
+ unsigned int value = 0;
+
+ value = readl(addr);
+ value &= (((unsigned int)(1 << 31)) - 1);
+
+#ifdef SPM_TWAM_DEBUG
+ debug_signal_val = value;
+#endif
+
+ writel(value, addr);
+}
+
+/*
+ * Called from "met-cmd --start"
+ */
+static void spmtwam_start(void)
+{
+ if (idle_sel == 3) {
+#if 0
+ /* swithc idle signal D id0 pimux to fmem */
+ if (fmem_dcm_base == NULL) {
+ fmem_dcm_base = ioremap_nocache(INFRA_FMEM_DCM_BASE, INFRA_FMEM_DCM_SIZE);
+ if (!fmem_dcm_base) {
+ pr_debug("fmem_dcm_base ioremap fail...");
+ return;
+ }
+
+ writel(FMEM_MUX_VALUE, (fmem_dcm_base + FMEM_MUX_ADDR_OFFSET));
+ }
+#endif
+ } else if (idle_sel == 2) {
+ /* debug signal mapping */
+ if (twam_dbg_signal_base == NULL) {
+ twam_dbg_signal_base = ioremap_nocache(TWAM_DBG_SIG_BASE, TWAM_DBG_SIG_SIZE);
+ if (!twam_dbg_signal_base) {
+ pr_debug("twam_dbg_signal_base ioremap fail...");
+ return;
+ }
+ }
+
+ spm_twam_enable_debug_out(&dbgout, (twam_dbg_signal_base + TWAM_DBG_SIG_OFFSET));
+ }
+
+ if (spm_twam_set_mon_type_symbol)
+ spm_twam_set_mon_type_symbol(&montype);
+ else {
+ MET_SPMTWAM_ERR("spm_twam_set_mon_type_symbol is NULL\n");
+ return;
+ }
+
+ if (spm_twam_set_window_length_symbol)
+ spm_twam_set_window_length_symbol(window_len);
+ else {
+ MET_SPMTWAM_ERR("spm_twam_set_window_length_symbol is NULL\n");
+ return;
+ }
+
+ if (spm_twam_set_idle_select_symbol)
+ spm_twam_set_idle_select_symbol(idle_sel);
+ else {
+ MET_SPMTWAM_ERR("spm_twam_set_idle_select_symbol is NULL\n");
+ return;
+ }
+
+ if (spm_twam_register_handler_symbol)
+ spm_twam_register_handler_symbol(ms_spmtwam);
+ else {
+ MET_SPMTWAM_ERR("spm_twam_register_handler_symbol is NULL\n");
+ return;
+ }
+
+ if (spm_twam_enable_monitor_symbol)
+ spm_twam_enable_monitor_symbol(&twamsig, twam_clock_mode);
+ else {
+ MET_SPMTWAM_ERR("spm_twam_enable_monitor_symbol is NULL\n");
+ return;
+ }
+
+ start = 1;
+}
+
+/*
+ * Called from "met-cmd --stop"
+ */
+static void spmtwam_stop(void)
+{
+ if (idle_sel == 3) {
+#if 0
+ if (fmem_dcm_base)
+ iounmap(fmem_dcm_base);
+#endif
+ } else if (idle_sel == 2) {
+ spm_twam_disable_debug_out(twam_dbg_signal_base + TWAM_DBG_SIG_OFFSET);
+
+ if (twam_dbg_signal_base) {
+ iounmap(twam_dbg_signal_base);
+ twam_dbg_signal_base = NULL;
+ }
+ }
+
+ if (spm_twam_register_handler_symbol)
+ spm_twam_register_handler_symbol(NULL);
+ else {
+ MET_SPMTWAM_ERR("spm_twam_register_handler_symbol is NULL\n");
+ return;
+ }
+
+ if (spm_twam_disable_monitor_symbol)
+ spm_twam_disable_monitor_symbol();
+ else {
+ MET_SPMTWAM_ERR("spm_twam_disable_monitor_symbol is NULL\n");
+ return;
+ }
+}
+
+static const char header[] = "met-info [000] 0.0: ms_spmtwam_header: ";
+
+/*
+ * It will be called back when run "met-cmd --extract" and mode is 1
+ */
+static int spmtwam_print_header(char *buf, int len)
+{
+ int i, total_size;
+ char idle_sig;
+ unsigned int event;
+
+ total_size = snprintf(buf, PAGE_SIZE, header);
+
+ for (i = 0; i < used_count; i++) {
+ idle_sig = spmtwam_para[i].idle_sig;
+ event = spmtwam_para[i].event;
+
+ total_size += snprintf(buf + total_size, PAGE_SIZE - total_size,
+ "signal_%c_%02u,", idle_sig, event);
+ }
+
+ /* cut the last comma */
+ buf[total_size - 1] = '\n';
+
+ total_size += snprintf(buf + total_size, PAGE_SIZE - total_size, "met-info [000] 0.0: spmtwam_clock_mode: %s\n",
+ twam_clock_mode == TWAM_SPEED_MODE ? "speed" : "normal");
+
+#ifdef SPMTWAM_SINGLE_IDLE_SIGNAL
+ total_size += snprintf(buf + total_size, PAGE_SIZE - total_size, "met-info [000] 0.0: spmtwam_idle_signal_support: %s\n",
+ TWAM_SINGLE_IDLE_SIGNAL);
+#endif
+
+#ifdef SPMTWAM_MULTIPLE_IDLE_SIGNAL
+ total_size += snprintf(buf + total_size, PAGE_SIZE - total_size, "met-info [000] 0.0: spmtwam_idle_signal_support: %s\n",
+ TWAM_MULTIPLE_IDLE_SIGNAL);
+#endif
+
+ reset_driver_stat();
+ return total_size;
+}
+
+static int assign_slot(char idle_sig, unsigned int event)
+{
+ int i;
+ int sig2int;
+
+ if (used_count == MAX_EVENT_COUNT) {
+ PR_BOOTMSG("%s exceed max used event count\n", MET_SPMTWAM_TAG);
+ return -1;
+ }
+
+ /* check duplicated */
+ for (i = 0; i < used_count; i++) {
+ if ((spmtwam_para[i].idle_sig == idle_sig) &&
+ (spmtwam_para[i].event == event)) {
+ PR_BOOTMSG("%s input duplicated event %u\n", MET_SPMTWAM_TAG, event);
+ return -2;
+ }
+ }
+
+ /* check idle_sig range in a~d or A~D */
+ if (tolower(idle_sig) < 'a' || tolower(idle_sig) > 'd') {
+ PR_BOOTMSG("%s input idle signal %c is not in a~d range\n",
+ MET_SPMTWAM_TAG, idle_sig);
+ return -3;
+ }
+
+ /* check event no */
+ if (event > MAX_TWAM_EVENT_COUNT) {
+ PR_BOOTMSG("%s input event %u exceed max twam event %u\n",
+ MET_SPMTWAM_TAG, event, MAX_TWAM_EVENT_COUNT);
+ return -4;
+ }
+
+#ifdef SPMTWAM_SINGLE_IDLE_SIGNAL
+ if (used_count > 0) {
+ for (i = 0; i < used_count; i++) {
+ if (idle_sig != spmtwam_para[i].idle_sig) {
+ PR_BOOTMSG("%s %c idle signal is defferent previous, only support one idle signal\n",
+ MET_SPMTWAM_TAG, idle_sig);
+ return -5;
+ }
+ }
+ }
+#endif
+
+ spmtwam_para[used_count].idle_sig = idle_sig;
+ spmtwam_para[used_count].event = event;
+
+ sig2int = (int) (tolower(idle_sig) - 'a');
+#ifdef SPMTWAM_SINGLE_IDLE_SIGNAL
+ idle_sel = sig2int;
+#endif
+
+ switch (used_count) {
+ case 0:
+ twamsig.sig0 = event;
+ break;
+ case 1:
+ twamsig.sig1 = event;
+ break;
+ case 2:
+ twamsig.sig2 = event;
+ break;
+ case 3:
+ twamsig.sig3 = event;
+ break;
+ }
+
+ used_count++;
+
+ return 0;
+}
+
+static char help[] =
+ " --spmtwam=clock:[speed|normal] default is normal\n"
+ " normal mode monitors 4 channels\n"
+ " speed mode monitors 4 channels\n"
+ " --spmtwam=signal:selx\n"
+ " signal= a ~ d for idle signal A~D select\n"
+ " selx= 0 ~ 31 for for channel event\n";
+
+/*
+ * Called from "met-cmd --help"
+ */
+static int spmtwam_print_help(char *buf, int len)
+{
+ return snprintf(buf, PAGE_SIZE, help);
+}
+
+static int spmtwam_process_argument(const char *arg, int len)
+{
+ if (start == 1)
+ reset_driver_stat();
+
+ if (strncmp(arg, "clock:", 6) == 0) {
+ if (strncmp(&(arg[6]), "speed", 5) == 0) {
+ twam_clock_mode = TWAM_SPEED_MODE;
+ } else if (strncmp(&(arg[6]), "normal", 6) == 0) {
+ twam_clock_mode = TWAM_NORMAL_MODE;
+ } else {
+ PR_BOOTMSG("%s unknown clock mode\n", MET_SPMTWAM_TAG);
+
+ goto error;
+ }
+ } else {
+ char signal;
+ int event;
+ int ret;
+
+ if (len < 3) {
+ PR_BOOTMSG("%s input parameter is too short !!!\n", MET_SPMTWAM_TAG);
+ goto error;
+ }
+
+ /*
+ * parse arguments
+ */
+ ret = sscanf(arg, "%c:%u", &signal, &event);
+ if (ret < 2) {
+ PR_BOOTMSG("%s input parameter is wrong format !!!\n", MET_SPMTWAM_TAG);
+ goto error;
+ }
+
+ if (assign_slot(signal, event) < 0) {
+ goto error;
+ }
+ }
+
+ met_spmtwam.mode = 1;
+ return 0;
+
+error:
+ reset_driver_stat();
+ return -1;
+}
+
+struct metdevice met_spmtwam = {
+ .name = "spmtwam",
+ .owner = THIS_MODULE,
+ .type = MET_TYPE_BUS,
+ .create_subfs = met_spmtwam_create,
+ .delete_subfs = met_spmtwam_delete,
+ .cpu_related = 0,
+ .start = spmtwam_start,
+ .stop = spmtwam_stop,
+ .reset = reset_driver_stat,
+ .print_help = spmtwam_print_help,
+ .print_header = spmtwam_print_header,
+ .process_argument = spmtwam_process_argument
+};
+EXPORT_SYMBOL(met_spmtwam);
diff --git a/src/devtools/met-driver/4.14/common/spmtwam/include/met_spmtwam.h b/src/devtools/met-driver/4.14/common/spmtwam/include/met_spmtwam.h
new file mode 100644
index 0000000..d889cfd
--- /dev/null
+++ b/src/devtools/met-driver/4.14/common/spmtwam/include/met_spmtwam.h
@@ -0,0 +1,30 @@
+#ifndef __MET_SPMTWAM_H__
+#define __MET_SPMTWAM_H__
+
+#define MET_SPMTWAM_TAG "[met_spmtwam]"
+#define MET_SPMTWAM_ERR(format, ...) \
+ do { \
+ MET_TRACE(MET_SPMTWAM_TAG format, ##__VA_ARGS__); \
+ PR_BOOTMSG(MET_SPMTWAM_TAG format, ##__VA_ARGS__); \
+ } while (0)
+
+#define TWAM_ENABLE true
+#define TWAM_DISABLE false
+#define TWAM_SPEED_MODE true
+#define TWAM_NORMAL_MODE false
+#define TWAM_DEBUG_SIG_ENABLE 1
+#define TWAM_DEBUG_SIG_DISABLE 0
+#define TWAM_SINGLE_IDLE_SIGNAL "single"
+#define TWAM_MULTIPLE_IDLE_SIGNAL "multiple"
+
+struct met_spmtwam_para {
+ char idle_sig;
+ int event;
+};
+
+
+/* event counters by HW spec */
+#define MAX_EVENT_COUNT 4
+#define MAX_TWAM_EVENT_COUNT 32
+
+#endif
diff --git a/src/devtools/met-driver/4.14/common/spmtwam/sspm/met_spmtwam.c b/src/devtools/met-driver/4.14/common/spmtwam/sspm/met_spmtwam.c
new file mode 100644
index 0000000..0542fff
--- /dev/null
+++ b/src/devtools/met-driver/4.14/common/spmtwam/sspm/met_spmtwam.c
@@ -0,0 +1,477 @@
+/*
+ * 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/random.h>
+#include <linux/fs.h>
+#include <linux/ctype.h>
+
+#include <mtk_spm.h>
+#include "met_drv.h"
+#include "trace.h"
+#include "core_plf_init.h"
+#include "interface.h"
+#include "met_spmtwam.h"
+
+
+struct metdevice met_spmtwam;
+static struct kobject *kobj_spmtwam;
+
+static struct twam_cfg twam_config;
+static struct met_spmtwam_para spmtwam_para[MAX_EVENT_COUNT];
+
+static unsigned int twam_dbg_enable = TWAM_DEBUG_SIG_DISABLE;
+static bool twam_clock_mode = TWAM_SPEED_MODE; /* true:speed mode, false:normal mode */
+static unsigned int window_len = 1300000; /* 50 ms in 26 MHz */
+
+static int used_count;
+static int start;
+
+
+#define MONTYPE_SHOW_IMPLEMENT(cfg) \
+ do { \
+ int i; \
+ i = snprintf(buf, PAGE_SIZE, "%d\n", cfg.monitor_type); \
+ return i; \
+ } while (0)
+
+#define MONTYPE_STORE_IMPLEMENT(cfg) \
+ do { \
+ int value; \
+ if ((n == 0) || (buf == NULL)) \
+ return -EINVAL; \
+ if (kstrtoint(buf, 10, &value) != 0) \
+ return -EINVAL; \
+ if (value < 0 || value > 3) \
+ return -EINVAL; \
+ cfg.monitor_type = value; \
+ return n; \
+ } while (0)
+
+static ssize_t montype0_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
+{
+ MONTYPE_SHOW_IMPLEMENT(twam_config.byte[0]);
+}
+
+static ssize_t montype0_store(struct kobject *kobj,
+ struct kobj_attribute *attr,
+ const char *buf,
+ size_t n)
+{
+ MONTYPE_STORE_IMPLEMENT(twam_config.byte[0]);
+}
+
+static ssize_t montype1_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
+{
+ MONTYPE_SHOW_IMPLEMENT(twam_config.byte[1]);
+}
+
+static ssize_t montype1_store(struct kobject *kobj,
+ struct kobj_attribute *attr,
+ const char *buf,
+ size_t n)
+{
+ MONTYPE_STORE_IMPLEMENT(twam_config.byte[1]);
+}
+
+static ssize_t montype2_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
+{
+ MONTYPE_SHOW_IMPLEMENT(twam_config.byte[2]);
+}
+
+static ssize_t montype2_store(struct kobject *kobj,
+ struct kobj_attribute *attr,
+ const char *buf,
+ size_t n)
+{
+ MONTYPE_STORE_IMPLEMENT(twam_config.byte[2]);
+}
+
+static ssize_t montype3_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
+{
+ MONTYPE_SHOW_IMPLEMENT(twam_config.byte[3]);
+}
+
+static ssize_t montype3_store(struct kobject *kobj,
+ struct kobj_attribute *attr,
+ const char *buf,
+ size_t n)
+{
+ MONTYPE_STORE_IMPLEMENT(twam_config.byte[3]);
+}
+
+static ssize_t window_len_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
+{
+ int i;
+
+ i = snprintf(buf, PAGE_SIZE, "%d\n", window_len);
+
+ return i;
+}
+
+static ssize_t window_len_store(struct kobject *kobj,
+ struct kobj_attribute *attr,
+ const char *buf,
+ size_t n)
+{
+ int value;
+
+ if ((n == 0) || (buf == NULL))
+ return -EINVAL;
+
+ if (kstrtoint(buf, 10, &value) != 0)
+ return -EINVAL;
+
+ if (value < 0)
+ return -EINVAL;
+
+ window_len = value;
+
+ return n;
+}
+
+static struct kobj_attribute montype0_attr = __ATTR(montype0, 0664, montype0_show, montype0_store);
+static struct kobj_attribute montype1_attr = __ATTR(montype1, 0664, montype1_show, montype1_store);
+static struct kobj_attribute montype2_attr = __ATTR(montype2, 0664, montype2_show, montype2_store);
+static struct kobj_attribute montype3_attr = __ATTR(montype3, 0664, montype3_show, montype3_store);
+static struct kobj_attribute window_len_attr = __ATTR(window_len, 0664, window_len_show, window_len_store);
+
+
+/* create spmtwam related kobj node */
+#define KOBJ_ATTR_LIST \
+ do { \
+ KOBJ_ATTR_ITEM(montype0); \
+ KOBJ_ATTR_ITEM(montype1); \
+ KOBJ_ATTR_ITEM(montype2); \
+ KOBJ_ATTR_ITEM(montype3); \
+ KOBJ_ATTR_ITEM(window_len); \
+ } while (0)
+
+static int met_spmtwam_create(struct kobject *parent)
+{
+ int i;
+ int ret = 0;
+
+ kobj_spmtwam = parent;
+
+#define KOBJ_ATTR_ITEM(attr_name) \
+ do { \
+ ret = sysfs_create_file(kobj_spmtwam, &attr_name ## _attr.attr); \
+ if (ret != 0) { \
+ pr_notice("Failed to create " #attr_name " in sysfs\n"); \
+ return ret; \
+ } \
+ } while (0)
+ KOBJ_ATTR_LIST;
+#undef KOBJ_ATTR_ITEM
+
+ /* init. */
+ for (i = 0; i < MAX_EVENT_COUNT; i++)
+ twam_config.byte[i].monitor_type = 0x2;
+
+ return ret;
+}
+
+static void met_spmtwam_delete(void)
+{
+#define KOBJ_ATTR_ITEM(attr_name) \
+ sysfs_remove_file(kobj_spmtwam, &attr_name ## _attr.attr)
+
+ if (kobj_spmtwam != NULL) {
+ KOBJ_ATTR_LIST;
+ kobj_spmtwam = NULL;
+ }
+#undef KOBJ_ATTR_ITEM
+}
+
+void ms_spmtwam(struct twam_cfg *cfg, struct twam_select *twam_sel)
+{
+ switch (used_count) {
+ case 1:
+ MET_TRACE(MP_FMT1,
+ (cfg->byte[0].id));
+ break;
+ case 2:
+ MET_TRACE(MP_FMT2,
+ (cfg->byte[0].id),
+ (cfg->byte[1].id));
+ break;
+ case 3:
+ MET_TRACE(MP_FMT3,
+ (cfg->byte[0].id),
+ (cfg->byte[1].id),
+ (cfg->byte[2].id));
+ break;
+ case 4:
+ MET_TRACE(MP_FMT4,
+ (cfg->byte[0].id),
+ (cfg->byte[1].id),
+ (cfg->byte[2].id),
+ (cfg->byte[3].id));
+ break;
+ default:
+ MET_SPMTWAM_ERR("No assign profile event\n");
+ break;
+ }
+}
+
+static int reset_driver_stat(void)
+{
+ met_spmtwam.mode = 0;
+ used_count = 0;
+ start = 0;
+
+ return 0;
+}
+
+#if 0
+void sspm_twam_debug(void)
+{
+/*
+ PR_BOOTMSG("[MET_TWAM] byte0 idle=%d, event=%d, type=%d \n",twam_config.byte[0].signal,twam_config.byte[0].id,twam_config.byte[0].monitor_type);
+ PR_BOOTMSG("[MET_TWAM] byte1 idle=%d, event=%d, type=%d \n",twam_config.byte[1].signal,twam_config.byte[1].id,twam_config.byte[1].monitor_type);
+ PR_BOOTMSG("[MET_TWAM] byte2 idle=%d, event=%d, type=%d \n",twam_config.byte[2].signal,twam_config.byte[2].id,twam_config.byte[2].monitor_type);
+ PR_BOOTMSG("[MET_TWAM] byte3 idle=%d, event=%d, type=%d \n",twam_config.byte[3].signal,twam_config.byte[3].id,twam_config.byte[3].monitor_type);
+ PR_BOOTMSG("[MET_TWAM] twam_clock_mode=%d, window_len=%d \n",twam_clock_mode, window_len);
+*/
+}
+#endif
+
+/*
+ * Called from "met-cmd --start"
+ */
+static void spmtwam_start(void)
+{
+ if (spm_twam_met_enable_symbol) {
+ if (true == spm_twam_met_enable_symbol()) {
+ if (spm_twam_enable_monitor_symbol)
+ spm_twam_enable_monitor_symbol(TWAM_DISABLE, TWAM_DEBUG_SIG_DISABLE, NULL);
+ else {
+ MET_SPMTWAM_ERR("spm_twam_enable_monitor_symbol is NULL\n");
+ return;
+ }
+ }
+ } else {
+ MET_SPMTWAM_ERR("spm_twam_met_enable_symbol is NULL\n");
+ return;
+ }
+
+ if (spm_twam_config_channel_symbol)
+ spm_twam_config_channel_symbol(&twam_config, twam_clock_mode, window_len);
+ else {
+ MET_SPMTWAM_ERR("spm_twam_config_channel_symbol is NULL\n");
+ return;
+ }
+
+ if (spm_twam_enable_monitor_symbol) {
+ spm_twam_enable_monitor_symbol(TWAM_ENABLE, twam_dbg_enable, ms_spmtwam);
+ /* sspm_twam_debug(); */
+ } else {
+ MET_SPMTWAM_ERR("spm_twam_enable_monitor_symbol is NULL\n");
+ return;
+ }
+
+ start = 1;
+}
+
+/*
+ * Called from "met-cmd --stop"
+ */
+static void spmtwam_stop(void)
+{
+ if (spm_twam_enable_monitor_symbol)
+ spm_twam_enable_monitor_symbol(TWAM_DISABLE, TWAM_DEBUG_SIG_DISABLE, NULL);
+ else {
+ MET_SPMTWAM_ERR("spm_twam_enable_monitor_symbol is NULL\n");
+ return;
+ }
+}
+
+static const char header[] = "met-info [000] 0.0: ms_spmtwam_header: ";
+
+/*
+ * It will be called back when run "met-cmd --extract" and mode is 1
+ */
+static int spmtwam_print_header(char *buf, int len)
+{
+ int i, total_size;
+ char idle_sig;
+ unsigned int event;
+
+ total_size = snprintf(buf, PAGE_SIZE, header);
+
+ for (i = 0; i < used_count; i++) {
+ idle_sig = spmtwam_para[i].idle_sig;
+ event = spmtwam_para[i].event;
+
+ total_size += snprintf(buf + total_size, PAGE_SIZE - total_size,
+ "signal_%c_%02u,", idle_sig, event);
+ }
+
+ /* cut the last comma */
+ buf[total_size - 1] = '\n';
+
+ total_size += snprintf(buf + total_size, PAGE_SIZE - total_size, "met-info [000] 0.0: spmtwam_clock_mode: %s\n",
+ twam_clock_mode == TWAM_SPEED_MODE ? "speed" : "normal");
+
+#ifdef SPMTWAM_SINGLE_IDLE_SIGNAL
+ total_size += snprintf(buf + total_size, PAGE_SIZE - total_size, "met-info [000] 0.0: spmtwam_idle_signal_support: %s\n",
+ TWAM_SINGLE_IDLE_SIGNAL);
+#endif
+
+#ifdef SPMTWAM_MULTIPLE_IDLE_SIGNAL
+ total_size += snprintf(buf + total_size, PAGE_SIZE - total_size, "met-info [000] 0.0: spmtwam_idle_signal_support: %s\n",
+ TWAM_MULTIPLE_IDLE_SIGNAL);
+#endif
+
+ reset_driver_stat();
+ return total_size;
+}
+
+static int assign_slot_sspm_twam(char idle_sig, unsigned int event)
+{
+ int i;
+ int sig2int;
+
+ if (used_count == MAX_EVENT_COUNT) {
+ PR_BOOTMSG("%s exceed max used event count\n", MET_SPMTWAM_TAG);
+ return -1;
+ }
+
+ /* check duplicated */
+ for (i = 0; i < used_count; i++) {
+ if ((spmtwam_para[i].idle_sig == idle_sig) &&
+ (spmtwam_para[i].event == event)) {
+ PR_BOOTMSG("%s input duplicated event %u\n", MET_SPMTWAM_TAG, event);
+ return -2;
+ }
+ }
+
+ /* check idle_sig range in a~d or A~D */
+ if (tolower(idle_sig) < 'a' || tolower(idle_sig) > 'd') {
+ PR_BOOTMSG("%s input idle signal %c is not in a~d range\n",
+ MET_SPMTWAM_TAG, idle_sig);
+ return -3;
+ }
+
+ /* check event no */
+ if (event > MAX_TWAM_EVENT_COUNT) {
+ PR_BOOTMSG("%s input event %u exceed max twam event %u\n",
+ MET_SPMTWAM_TAG, event, MAX_TWAM_EVENT_COUNT);
+ return -4;
+ }
+
+#ifdef SPMTWAM_SINGLE_IDLE_SIGNAL
+ if (used_count > 0) {
+ for (i = 0; i < used_count; i++) {
+ if (idle_sig != spmtwam_para[i].idle_sig) {
+ PR_BOOTMSG("%s %c idle signal is defferent previous, only support one idle signal\n",
+ MET_SPMTWAM_TAG, idle_sig);
+ return -5;
+ }
+ }
+ }
+#endif
+
+ spmtwam_para[used_count].idle_sig = idle_sig;
+ spmtwam_para[used_count].event = event;
+
+ sig2int = (int) (tolower(idle_sig) - 'a');
+
+ twam_config.byte[used_count].id = event;
+ twam_config.byte[used_count].signal = sig2int;
+
+ used_count++;
+
+ return 0;
+}
+
+static char help[] =
+ " --spmtwam=clock:[speed|normal] default is normal\n"
+ " normal mode monitors 4 channels\n"
+ " speed mode monitors 4 channels\n"
+ " --spmtwam=signal:selx\n"
+ " signal= a ~ d for idle signal A~D select\n"
+ " selx= 0 ~ 31 for for channel event\n";
+
+/*
+ * Called from "met-cmd --help"
+ */
+static int spmtwam_print_help(char *buf, int len)
+{
+ return snprintf(buf, PAGE_SIZE, help);
+}
+
+static int spmtwam_process_argument(const char *arg, int len)
+{
+ if (start == 1)
+ reset_driver_stat();
+
+ if (strncmp(arg, "clock:", 6) == 0) {
+ if (strncmp(&(arg[6]), "speed", 5) == 0) {
+ twam_clock_mode = TWAM_SPEED_MODE;
+ } else if (strncmp(&(arg[6]), "normal", 6) == 0) {
+ twam_clock_mode = TWAM_NORMAL_MODE;
+ } else {
+ PR_BOOTMSG("%s unknown clock mode\n", MET_SPMTWAM_TAG);
+
+ goto error;
+ }
+ } else {
+ char signal;
+ int event;
+ int ret;
+
+ if (len < 3) {
+ PR_BOOTMSG("%s input parameter is too short !!!\n", MET_SPMTWAM_TAG);
+ goto error;
+ }
+
+ /*
+ * parse arguments
+ */
+ ret = sscanf(arg, "%c:%u", &signal, &event);
+ if (ret < 2) {
+ PR_BOOTMSG("%s input parameter is wrong format !!!\n", MET_SPMTWAM_TAG);
+ goto error;
+ }
+
+ if (assign_slot_sspm_twam(signal, event) < 0) {
+ goto error;
+ }
+ }
+
+ met_spmtwam.mode = 1;
+ return 0;
+
+error:
+ reset_driver_stat();
+ return -1;
+}
+
+struct metdevice met_spmtwam = {
+ .name = "spmtwam",
+ .owner = THIS_MODULE,
+ .type = MET_TYPE_BUS,
+ .create_subfs = met_spmtwam_create,
+ .delete_subfs = met_spmtwam_delete,
+ .cpu_related = 0,
+ .start = spmtwam_start,
+ .stop = spmtwam_stop,
+ .reset = reset_driver_stat,
+ .print_help = spmtwam_print_help,
+ .print_header = spmtwam_print_header,
+ .process_argument = spmtwam_process_argument
+};
+EXPORT_SYMBOL(met_spmtwam);
diff --git a/src/devtools/met-driver/4.14/common/sspm/ondiemet_sspm.c b/src/devtools/met-driver/4.14/common/sspm/ondiemet_sspm.c
new file mode 100644
index 0000000..8c5711b
--- /dev/null
+++ b/src/devtools/met-driver/4.14/common/sspm/ondiemet_sspm.c
@@ -0,0 +1,494 @@
+/*
+ * 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 */
+
+#define MET_USER_EVENT_SUPPORT
+#include "met_drv.h"
+
+#if defined(CONFIG_MTK_TINYSYS_SSPM_SUPPORT) && defined(ONDIEMET_SUPPORT)
+#include "ondiemet_sspm.h"
+
+#if defined(CONFIG_MTK_GMO_RAM_OPTIMIZE) || defined(CONFIG_MTK_MET_MEM_ALLOC)
+#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 = 500;
+
+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;
+
+#if defined(CONFIG_MTK_GMO_RAM_OPTIMIZE) || defined(CONFIG_MTK_MET_MEM_ALLOC)
+#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) {
+#if defined(CONFIG_MTK_GMO_RAM_OPTIMIZE) || defined(CONFIG_MTK_MET_MEM_ALLOC)
+#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 && ONDIEMET_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 && ONDIEMET_SUPPORT */
diff --git a/src/devtools/met-driver/4.14/common/sspm/ondiemet_sspm.h b/src/devtools/met-driver/4.14/common/sspm/ondiemet_sspm.h
new file mode 100644
index 0000000..64dfcbd
--- /dev/null
+++ b/src/devtools/met-driver/4.14/common/sspm/ondiemet_sspm.h
@@ -0,0 +1,148 @@
+/*
+ * 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
+
+#if defined(CONFIG_MTK_TINYSYS_SSPM_SUPPORT) && defined(ONDIEMET_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_MET_TAG,
+ MID_TS,
+ MID_TS_ISR,
+ MID_TS_LAST,
+ MID_TS_ISR_LAST,
+ MID_SRAM_INFO,
+ MID_MET_STOP,
+ MID_IOP_MON,
+ MID_CPU_INFO_MAPPING,
+ MID_SMI,
+
+ 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)
+#define ID_CPU_INFO_MAPPING (1 << MID_CPU_INFO_MAPPING)
+#define ID_SMI (1 << MID_SMI)
+
+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 && ONDIEMET_SUPPORT */
+#endif /* __ONDIEMET_SSPM_H */
diff --git a/src/devtools/met-driver/4.14/common/sspm/sspm_common.c b/src/devtools/met-driver/4.14/common/sspm/sspm_common.c
new file mode 100644
index 0000000..b3e1066
--- /dev/null
+++ b/src/devtools/met-driver/4.14/common/sspm/sspm_common.c
@@ -0,0 +1,266 @@
+#include <linux/string.h>
+#include <linux/slab.h>
+#include <linux/kernel.h>
+
+#include "met_drv.h"
+#include "ondiemet_sspm.h"
+
+
+struct sspm_met_evnet_header {
+ unsigned int rts_event_id;
+ char *rts_event_name;
+ char *chart_line_name;
+ char *key_list;
+};
+
+
+enum {
+ #ifdef MET_SSPM_RTS_EVNET
+ #undef MET_SSPM_RTS_EVNET
+ #endif
+ #define MET_SSPM_RTS_EVNET(rts_event_id, key_list) rts_event_id,
+ #include "met_sspm_rts_event.h"
+
+ /**********************/
+ CUR_MET_RTS_EVENT_NUM,
+ MAX_MET_RTS_EVENT_NUM = 128
+};
+
+
+struct sspm_met_evnet_header met_evnet_header[MAX_MET_RTS_EVENT_NUM] = {
+ #ifdef MET_SSPM_RTS_EVNET
+ #undef MET_SSPM_RTS_EVNET
+ #endif
+ #define MET_SSPM_RTS_EVNET(rts_event_id, key_list) {rts_event_id, #rts_event_id, #rts_event_id, key_list},
+ #include "met_sspm_rts_event.h"
+};
+
+
+static void ondiemet_sspm_start(void);
+static void ondiemet_sspm_stop(void);
+static int ondiemet_sspm_print_help(char *buf, int len);
+static int ondiemet_sspm_process_argument(const char *arg, int len);
+static int ondiemet_sspm_print_header(char *buf, int len);
+
+
+static unsigned int event_id_flag0;
+static unsigned int event_id_flag1;
+static unsigned int event_id_flag2;
+static unsigned int event_id_flag3;
+static char *update_rts_event_tbl[MAX_MET_RTS_EVENT_NUM];
+static char sspm_help[] = " --sspm_common=rts_event_name\n";
+static char header[] = "met-info [000] 0.0: sspm_common_header: ";
+
+struct metdevice met_sspm_common = {
+ .name = "sspm_common",
+ .owner = THIS_MODULE,
+ .type = MET_TYPE_BUS,
+ .cpu_related = 0,
+ .ondiemet_mode = 1,
+ .ondiemet_start = ondiemet_sspm_start,
+ .ondiemet_stop = ondiemet_sspm_stop,
+ .ondiemet_process_argument = ondiemet_sspm_process_argument,
+ .ondiemet_print_help = ondiemet_sspm_print_help,
+ .ondiemet_print_header = ondiemet_sspm_print_header,
+};
+
+
+static int ondiemet_sspm_print_help(char *buf, int len)
+{
+ return snprintf(buf, PAGE_SIZE, sspm_help);
+}
+
+
+static int ondiemet_sspm_print_header(char *buf, int len)
+{
+ int i;
+ int write_len;
+ int flag = 0;
+ static int is_dump_header = 0;
+ static int read_idx = 0;
+
+ len = 0;
+ met_sspm_common.header_read_again = 0;
+ if (is_dump_header == 0) {
+ len = snprintf(buf, PAGE_SIZE, "%s", header);
+ is_dump_header = 1;
+ }
+
+ for (i=read_idx; i<MAX_MET_RTS_EVENT_NUM; i++) {
+ if (met_evnet_header[i].chart_line_name) {
+ if (i <32) {
+ flag = 1<<i;
+ flag = event_id_flag0 & flag;
+ } else if (i >=32 && i < 64) {
+ flag = 1<<(i-32);
+ flag = event_id_flag1 & flag;
+ } else if (i >=64 && i < 96) {
+ flag = 1<<(i-64);
+ flag = event_id_flag2 & flag;
+ } else if (i >=96 && i < 128) {
+ flag = 1<<(i-96);
+ flag = event_id_flag3 & flag;
+ }
+ if (flag == 0)
+ continue;
+
+ write_len = strlen(met_evnet_header[i].chart_line_name) + strlen(met_evnet_header[i].key_list) + 3;
+ if ((len+write_len) < PAGE_SIZE) {
+ len += snprintf(buf+len, PAGE_SIZE-len, "%u,%s,%s;",
+ met_evnet_header[i].rts_event_id,
+ met_evnet_header[i].chart_line_name,
+ met_evnet_header[i].key_list);
+ } else {
+ met_sspm_common.header_read_again = 1;
+ read_idx = i;
+ return len;
+ }
+ }
+ }
+
+ if (i == MAX_MET_RTS_EVENT_NUM) {
+ is_dump_header = 0;
+ read_idx = 0;
+ buf[len-1] = '\n';
+ for (i=0; i<MAX_MET_RTS_EVENT_NUM; i++) {
+ if (update_rts_event_tbl[i]) {
+ kfree(update_rts_event_tbl[i]);
+ update_rts_event_tbl[i] = NULL;
+ }
+ }
+ met_sspm_common.mode = 0;
+ event_id_flag0 = 0;
+ event_id_flag1 = 0;
+ event_id_flag2 = 0;
+ event_id_flag3 = 0;
+ }
+
+ return len;
+}
+
+
+static void ondiemet_sspm_start(void)
+{
+ if (sspm_buf_available == 0)
+ return ;
+
+ /* ID_COMMON = 1<<MID_COMMON */
+ ondiemet_module[ONDIEMET_SSPM] |= ID_COMMON;
+}
+
+
+static void ondiemet_sspm_stop(void)
+{
+ if (sspm_buf_available == 0)
+ return ;
+}
+
+
+static void update_event_id_flag(int event_id)
+{
+ unsigned int ipi_buf[4];
+ unsigned int rdata;
+ unsigned int res;
+
+ if (sspm_buf_available == 0)
+ return ;
+
+ /* main func ID: bit[31-24]; sub func ID: bit[23-18]; argu 0: bit[17-0]
+ #define MET_MAIN_ID_MASK 0xff000000
+ #define FUNC_BIT_SHIFT 18
+ #define MET_ARGU (4 << FUNC_BIT_SHIFT)
+ #define MID_BIT_SHIFT 9
+ */
+ if (event_id <32) {
+ event_id_flag0 |= 1<<event_id;
+ ipi_buf[0] = MET_MAIN_ID | MET_ARGU | MID_COMMON<<MID_BIT_SHIFT | 1;
+ ipi_buf[1] = 0;
+ ipi_buf[2] = event_id_flag0;
+ ipi_buf[3] = 0;
+ res = sspm_ipi_send_sync(IPI_ID_MET, IPI_OPT_WAIT, (void *)ipi_buf, 0, &rdata, 1);
+ } else if (event_id >=32 && event_id < 64) {
+ event_id_flag1 |= 1<<(event_id-32);
+ ipi_buf[0] = MET_MAIN_ID | MET_ARGU | MID_COMMON<<MID_BIT_SHIFT | 1;
+ ipi_buf[1] = 1;
+ ipi_buf[2] = event_id_flag1;
+ ipi_buf[3] = 0;
+ res = sspm_ipi_send_sync(IPI_ID_MET, IPI_OPT_WAIT, (void *)ipi_buf, 0, &rdata, 1);
+ } else if (event_id >=64 && event_id < 96) {
+ event_id_flag2 |= 1<<(event_id-64);
+ ipi_buf[0] = MET_MAIN_ID | MET_ARGU | MID_COMMON<<MID_BIT_SHIFT | 1;
+ ipi_buf[1] = 2;
+ ipi_buf[2] = event_id_flag2;
+ ipi_buf[3] = 0;
+ res = sspm_ipi_send_sync(IPI_ID_MET, IPI_OPT_WAIT, (void *)ipi_buf, 0, &rdata, 1);
+ } else if (event_id >=96 && event_id < 128) {
+ event_id_flag3 = 1<<(event_id-96);
+ ipi_buf[0] |= MET_MAIN_ID | MET_ARGU | MID_COMMON<<MID_BIT_SHIFT | 1;
+ ipi_buf[1] = 3;
+ ipi_buf[2] = event_id_flag3;
+ ipi_buf[3] = 0;
+ res = sspm_ipi_send_sync(IPI_ID_MET, IPI_OPT_WAIT, (void *)ipi_buf, 0, &rdata, 1);
+ }
+ met_sspm_common.mode = 1;
+}
+
+
+static char *strdup(const char *s)
+{
+ char *p = kmalloc(strlen(s) + 1, GFP_KERNEL);
+
+ if (p)
+ strcpy(p, s);
+ return p;
+}
+
+
+static int ondiemet_sspm_process_argument(const char *arg, int len)
+{
+ int i;
+ int rts_event_id = -1;
+ int res;
+ char *line;
+ char *token;
+
+ for (i=0; met_evnet_header[i].rts_event_name && i<MAX_MET_RTS_EVENT_NUM; i++) {
+ if (strcmp(met_evnet_header[i].rts_event_name, arg) == 0) {
+ rts_event_id = i;
+ break;
+ }
+ }
+ if (strstarts(arg, "update_rts_event_tbl")) {
+ char *ptr;
+
+ /* update_rts_event_tbl=rts_event_id;rts_event_name;chart_line_name;key_list*/
+ line = strdup(arg);
+ if (line == NULL)
+ return -1;
+ ptr = line;
+ token = strsep(&line, "=");
+
+ /* rts_event_id, */
+ token = strsep(&line, ";");
+ res = kstrtoint(token, 10, &rts_event_id);
+ met_evnet_header[rts_event_id].rts_event_id = rts_event_id;
+
+ /* rts_event_name */
+ token = strsep(&line, ";");
+ met_evnet_header[rts_event_id].rts_event_name = token;
+
+ /* chart_line_name */
+ token = strsep(&line, ";");
+ met_evnet_header[rts_event_id].chart_line_name = token;
+
+ /* key_list */
+ token = strsep(&line, ";\n");
+ met_evnet_header[rts_event_id].key_list = token;
+
+ update_rts_event_tbl[rts_event_id] = ptr;
+ }
+
+ if (rts_event_id >=0)
+ update_event_id_flag(rts_event_id);
+
+ return 0;
+}
+EXPORT_SYMBOL(met_sspm_common);
diff --git a/src/devtools/met-driver/4.14/common/sspm/sspm_ipi_handle.c b/src/devtools/met-driver/4.14/common/sspm/sspm_ipi_handle.c
new file mode 100644
index 0000000..161d752
--- /dev/null
+++ b/src/devtools/met-driver/4.14/common/sspm/sspm_ipi_handle.c
@@ -0,0 +1,156 @@
+/*
+ * 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/kthread.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/wait.h>
+#include <linux/signal.h>
+#include <linux/semaphore.h>
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/syscalls.h>
+#include <linux/file.h>
+#include <linux/fs.h>
+#include <linux/fcntl.h>
+/* #include <asm/uaccess.h> */
+#include <linux/uaccess.h>
+#include <linux/delay.h>
+
+#include "ondiemet_sspm.h"
+#include "ondiemet_log.h"
+#include "interface.h"
+
+static struct ipi_action ondiemet_sspm_isr;
+static uint32_t log_size;
+static uint32_t recv_buf[4];
+static struct task_struct *ondiemet_sspm_recv_task;
+static int sspm_ipi_thread_started;
+int sspm_buffer_dumping;
+int sspm_recv_thread_comp;
+
+void log_done_cb(const void *p)
+{
+ uint32_t ret;
+ uint32_t rdata = 0;
+ uint32_t ipi_buf[4];
+ uint32_t opt = (p != NULL);
+
+ if (opt == 0) {
+ ipi_buf[0] = MET_MAIN_ID | MET_RESP_AP2MD;
+ ipi_buf[1] = MET_DUMP_BUFFER;
+ 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);
+ }
+}
+
+int ondiemet_sspm_recv_thread(void *data)
+{
+ uint32_t rdata = 0, cmd, ret;
+ uint32_t ridx, widx, wlen;
+
+ ondiemet_sspm_isr.data = (void *)recv_buf;
+ ret = sspm_ipi_recv_registration(IPI_ID_TST1, &ondiemet_sspm_isr);
+ do {
+ sspm_ipi_recv_wait(IPI_ID_TST1);
+ if (sspm_recv_thread_comp == 1) {
+ while (!kthread_should_stop())
+ ;
+ return 0;
+ }
+ cmd = recv_buf[0] & MET_SUB_ID_MASK;
+ switch (cmd) {
+ case MET_DUMP_BUFFER: /* mbox 1: start index; 2: size */
+ sspm_buffer_dumping = 1;
+ ridx = recv_buf[1];
+ widx = recv_buf[2];
+ log_size = recv_buf[3];
+ sspm_ipi_send_ack(IPI_ID_TST1, &rdata);
+ if (widx < ridx) { /* wrapping occurs */
+ wlen = log_size - ridx;
+ ondiemet_log_req_enq((char *)(ondiemet_sspm_log_virt_addr) + (ridx << 2),
+ wlen * 4, log_done_cb, (void *)1);
+ ondiemet_log_req_enq((char *)(ondiemet_sspm_log_virt_addr),
+ widx * 4, log_done_cb, (void *)0);
+ } else {
+ wlen = widx - ridx;
+ ondiemet_log_req_enq((char *)(ondiemet_sspm_log_virt_addr) + (ridx << 2),
+ wlen * 4, log_done_cb, (void *)0);
+ }
+ break;
+ case MET_CLOSE_FILE: /* no argument */
+ /* do close file */
+ ridx = recv_buf[1];
+ widx = recv_buf[2];
+ met_sspm_log_discard = recv_buf[3];
+ if (widx < ridx) { /* wrapping occurs */
+ wlen = log_size - ridx;
+ ondiemet_log_req_enq((char *)(ondiemet_sspm_log_virt_addr) + (ridx << 2),
+ wlen * 4, log_done_cb, (void *)1);
+ ondiemet_log_req_enq((char *)(ondiemet_sspm_log_virt_addr),
+ widx * 4, log_done_cb, (void *)1);
+ } else {
+ wlen = widx - ridx;
+ ondiemet_log_req_enq((char *)(ondiemet_sspm_log_virt_addr) + (ridx << 2),
+ wlen * 4, log_done_cb, (void *)1);
+ }
+ ret = ondiemet_log_manager_stop();
+ /* pr_debug("MET_CLOSE_FILE: ret=%d log_discard=%d\n", ret, met_sspm_log_discard); */
+ sspm_ipi_send_ack(IPI_ID_TST1, &rdata);
+ if (sspm_run_mode == SSPM_RUN_CONTINUOUS) {
+ /* clear the memory */
+ memset_io((void *)ondiemet_sspm_log_virt_addr, 0,
+ ondiemet_sspm_log_size);
+ /* re-start ondiemet again */
+ sspm_start();
+ }
+ break;
+ case MET_RESP_MD2AP:
+ sspm_ipi_send_ack(IPI_ID_TST1, &rdata);
+ sspm_buffer_dumping = 0;
+ break;
+ default:
+ sspm_ipi_send_ack(IPI_ID_TST1, &rdata);
+ break;
+ }
+ } while (!kthread_should_stop());
+ return 0;
+}
+
+void start_sspm_ipi_recv_thread(void)
+{
+ if (sspm_ipi_thread_started != 1) {
+ sspm_recv_thread_comp = 0;
+ ondiemet_sspm_recv_task =
+ kthread_run(ondiemet_sspm_recv_thread, NULL, "ondiemet_sspm_recv");
+ if (IS_ERR(ondiemet_sspm_recv_task))
+ pr_debug("MET: Can not create ondiemet_sspm_recv\n");
+ else
+ sspm_ipi_thread_started = 1;
+ }
+}
+
+void stop_sspm_ipi_recv_thread(void)
+{
+ if (ondiemet_sspm_recv_task) {
+ sspm_recv_thread_comp = 1;
+ sspm_ipi_recv_complete(IPI_ID_TST1);
+ kthread_stop(ondiemet_sspm_recv_task);
+ ondiemet_sspm_recv_task = NULL;
+ sspm_ipi_thread_started = 0;
+ sspm_ipi_recv_unregistration(IPI_ID_TST1);
+ }
+}
+
diff --git a/src/devtools/met-driver/4.14/common/sspm/sspm_met_smi.c b/src/devtools/met-driver/4.14/common/sspm/sspm_met_smi.c
new file mode 100644
index 0000000..f4e185b
--- /dev/null
+++ b/src/devtools/met-driver/4.14/common/sspm/sspm_met_smi.c
@@ -0,0 +1,529 @@
+/*
+ * 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/string.h>
+#include <linux/ctype.h>
+#include <mt-plat/sync_write.h>
+#include <mt-plat/mtk_io.h>
+
+#include "met_drv.h"
+#include "trace.h"
+
+#include "mtk_typedefs.h"
+#include "sspm_mtk_smi.h"
+#include "sspm_met_smi.h"
+#include "sspm_met_smi_name.h"
+#include "core_plf_trace.h"
+#include "core_plf_init.h"
+#include "interface.h"
+#include "sspm/ondiemet_sspm.h"
+
+#define MET_SMI_DEBUG 1
+#define MET_SMI_BUF_SIZE 128
+#define MET_SMI_DEBUGBUF_SIZE 512
+#define NPORT_IN_PM 4
+
+// SMI Encode -- Master
+// bit15~bit16
+#define MET_SMI_BIT_REQ_LARB 15
+// bit13~bit14
+#define MET_SMI_BIT_REQ_COMM 13
+// bit12:Parallel Mode */
+#define MET_SMI_BIT_PM 12
+// bit9~bit8:Destination */
+#define MET_SMI_BIT_DST 8
+/* bit5~bit4:Request Type */
+#define MET_SMI_BIT_REQ 4
+/* bit3~bit0:Master */
+#define MET_SMI_BIT_MASTER 0
+
+// SMI Encode -- Metadata
+/* bit6~bit5:RW */
+#define MET_SMI_BIT_RW 5
+/* bit4~bit0:Port */
+#define MET_SMI_BIT_PORT 0
+
+/*
+*declare smi: larb0-larbn:
+*real define table in met_smi_name.h
+*/
+/*MET_SMI_DESC_DEFINE();*/
+/**/
+
+/*======================================================================*/
+/* Global variable definitions */
+/*======================================================================*/
+#define MAX_CONFIG_ARRAY_SIZE 20
+struct metdevice met_sspm_smi;
+struct met_smi_conf smi_conf_array[MAX_CONFIG_ARRAY_SIZE];
+int smi_array_index;
+
+//static unsigned int smi_met_larb_number = SMI_LARB_NUMBER;
+static int count = SMI_LARB_NUMBER + SMI_COMM_NUMBER;
+static struct kobject *kobj_smi;
+
+/* Request type */
+static unsigned int larb_req_type = SMI_REQ_ALL;
+static unsigned int comm_req_type = SMI_REQ_ALL;
+
+/* Parallel mode */
+static unsigned int parallel_mode;
+
+/* Read/Write type in parallel mode */
+static int comm_pm_rw_type[SMI_COMM_NUMBER][NPORT_IN_PM];
+/* Error message */
+static char err_msg[MET_SMI_BUF_SIZE];
+static char debug_msg[MET_SMI_DEBUGBUF_SIZE];
+
+/*======================================================================*/
+/* KOBJ Declarations */
+/*======================================================================*/
+/* KOBJ: larb_req_type */
+DECLARE_KOBJ_ATTR_INT(larb_req_type, larb_req_type);
+
+/* KOBJ : comm_req_type */
+DECLARE_KOBJ_ATTR_INT(comm_req_type, comm_req_type);
+
+/* KOBJ : enable_parallel_mode */
+DECLARE_KOBJ_ATTR_INT(enable_parallel_mode, parallel_mode);
+
+/* KOBJ : pm_rwtypeX */
+DECLARE_KOBJ_ATTR_STR_LIST_ITEM(
+ pm_rwtype,
+ KOBJ_ITEM_LIST(
+ {SMI_READ_ONLY, "READ"},
+ {SMI_WRITE_ONLY, "WRITE"}
+ )
+);
+DECLARE_KOBJ_ATTR_STR_LIST(pm_rwtype1, comm_pm_rw_type[0][0], pm_rwtype);
+DECLARE_KOBJ_ATTR_STR_LIST(pm_rwtype2, comm_pm_rw_type[0][1], pm_rwtype);
+DECLARE_KOBJ_ATTR_STR_LIST(pm_rwtype3, comm_pm_rw_type[0][2], pm_rwtype);
+DECLARE_KOBJ_ATTR_STR_LIST(pm_rwtype4, comm_pm_rw_type[0][3], pm_rwtype);
+
+/* KOBJ : count */
+DECLARE_KOBJ_ATTR_RO_INT(count, count);
+
+/* KOBJ : err_msg */
+DECLARE_KOBJ_ATTR_RO_STR(err_msg, err_msg);
+
+#define KOBJ_ATTR_LIST \
+do { \
+ KOBJ_ATTR_ITEM(larb_req_type); \
+ KOBJ_ATTR_ITEM(comm_req_type); \
+ KOBJ_ATTR_ITEM(enable_parallel_mode); \
+ KOBJ_ATTR_ITEM(pm_rwtype1); \
+ KOBJ_ATTR_ITEM(pm_rwtype2); \
+ KOBJ_ATTR_ITEM(pm_rwtype3); \
+ KOBJ_ATTR_ITEM(pm_rwtype4); \
+ KOBJ_ATTR_ITEM(count); \
+ KOBJ_ATTR_ITEM(err_msg); \
+} while (0)
+
+/*======================================================================*/
+/* SMI Operations */
+/*======================================================================*/
+static void met_smi_debug(char *debug_log)
+{
+ MET_TRACE("%s", debug_log);
+}
+
+static int do_smi(void)
+{
+ return met_sspm_smi.mode;
+}
+
+static void smi_init_value(void)
+{
+ int i;
+
+ smi_array_index = 0;
+ for (i = 0; i < MAX_CONFIG_ARRAY_SIZE; i++) {
+ smi_conf_array[i].master = 0;
+ smi_conf_array[i].port[0] = -1;
+ smi_conf_array[i].port[1] = -1;
+ smi_conf_array[i].port[2] = -1;
+ smi_conf_array[i].port[3] = -1;
+ smi_conf_array[i].rwtype[0] = SMI_RW_ALL;
+ smi_conf_array[i].rwtype[1] = SMI_RW_ALL;
+ smi_conf_array[i].rwtype[2] = SMI_RW_ALL;
+ smi_conf_array[i].rwtype[3] = SMI_RW_ALL;
+ smi_conf_array[i].desttype = SMI_DEST_EMI;
+ smi_conf_array[i].reqtype = SMI_REQ_ALL;
+ }
+}
+
+static int smi_init(void)
+{
+ int i;
+
+ if (smi_array_index < MAX_CONFIG_ARRAY_SIZE) {
+ for (i = 0; i < (smi_array_index + 1); i++) {
+ snprintf(debug_msg, MET_SMI_DEBUGBUF_SIZE,
+ "===SMI config: parallel_mode = %d, master = %d, port0 = %d, port1 = %d, port2 = %d, port3 = %d, rwtype0 = %d, rwtype1 = %d, rwtype2 = %d, rwtype3 = %d, desttype = %d, reqtype(larb) = %d, reqtype(comm) = %d\n",
+ parallel_mode,
+ smi_conf_array[i].master,
+ smi_conf_array[i].port[0],
+ smi_conf_array[i].port[1],
+ smi_conf_array[i].port[2],
+ smi_conf_array[i].port[3],
+ smi_conf_array[i].rwtype[0],
+ smi_conf_array[i].rwtype[1],
+ smi_conf_array[i].rwtype[2],
+ smi_conf_array[i].rwtype[3],
+ smi_conf_array[i].desttype,
+ smi_conf_array[i].reqtype >> MET_SMI_BIT_REQ_LARB,
+ smi_conf_array[i].reqtype >> MET_SMI_BIT_REQ_COMM);
+ met_smi_debug(debug_msg);
+ }
+ } else {
+ met_smi_debug("smi_init() FAIL : smi_array_index overflow\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+static int met_smi_create(struct kobject *parent)
+{
+#define KOBJ_ATTR_ITEM(attr_name) \
+do { \
+ ret = sysfs_create_file(kobj_smi, &attr_name##_attr.attr); \
+ if (ret != 0) { \
+ pr_debug("Failed to create " #attr_name " in sysfs\n"); \
+ return ret; \
+ } \
+} while (0)
+
+ int j;
+ int ret = 0;
+
+ pr_debug(" met_smi_create\n met_smi_create\n met_smi_create\n met_smi_create\n met_smi_create\n met_smi_create\n met_smi_create\n met_smi_create\n");
+
+ /* Init. */
+
+ smi_init_value();
+
+ larb_req_type = SMI_REQ_ALL;
+ comm_req_type = SMI_REQ_ALL;
+ parallel_mode = 0;
+
+ for (j = 0; j < NPORT_IN_PM; j++)
+ comm_pm_rw_type[0][j] = SMI_READ_ONLY;
+
+ kobj_smi = parent;
+
+ KOBJ_ATTR_LIST;
+
+ return ret;
+
+#undef KOBJ_ATTR_ITEM
+}
+
+void met_smi_delete(void)
+{
+#define KOBJ_ATTR_ITEM(attr_name) \
+ sysfs_remove_file(kobj_smi, &attr_name##_attr.attr)
+
+
+ if (kobj_smi != NULL) {
+ KOBJ_ATTR_LIST;
+ kobj_smi = NULL;
+ }
+
+#undef KOBJ_ATTR_ITEM
+}
+
+static void met_smi_start(void)
+{
+ if (do_smi()) {
+ if (smi_init() != 0) {
+ met_sspm_smi.mode = 0;
+ smi_init_value();
+ return;
+ }
+ }
+}
+
+static void met_smi_stop(void)
+{
+ int j;
+
+ if (do_smi()) {
+
+ /* Reset */
+ smi_init_value();
+
+ larb_req_type = SMI_REQ_ALL;
+ comm_req_type = SMI_REQ_ALL;
+ parallel_mode = 0;
+
+ for (j = 0; j < NPORT_IN_PM; j++)
+ comm_pm_rw_type[0][j] = SMI_READ_ONLY;
+
+
+ met_sspm_smi.mode = 0;
+ }
+ return ;
+}
+
+static char help[] =
+" --smi=master:port:rw:dest:bus monitor specified SMI banwidth\n"
+" --smi=master:p1[:p2][:p3][:p4] monitor parallel mode\n";
+
+static int smi_print_help(char *buf, int len)
+{
+ return snprintf(buf, PAGE_SIZE, "%s", help);
+}
+
+static int get_num(const char *__restrict__ dc, int *pValue)
+{
+ int value = 0, digit;
+ int i = 0;
+
+ while (((*dc) >= '0') && ((*dc) <= '9')) {
+ digit = (int)(*dc - '0');
+ value = value * 10 + digit;
+ dc++;
+ i++;
+ }
+
+ if (i == 0)
+ return 0;
+ *pValue = value;
+
+ return i;
+}
+
+/*
+ * There are serveal cases as follows:
+ *
+ * 1. "met-cmd --start --smi=master:port:rwtype:desttype:bustype" => Can assign multiple master
+ * 2. "met-cmd --start --smi=master:port[:port1][:port2][:port3]" ==> parael mode
+ *
+ */
+static int smi_process_argument(const char *__restrict__ arg, int len)
+{
+ int args[5];
+ int i, array_index;
+ int idx;
+ unsigned int smi_conf_index = 0;
+ struct met_smi_conf smi_conf;
+
+ uint32_t ipi_buf[4];
+ uint32_t ret;
+ uint32_t rdata;
+ uint16_t sspm_master = 0;
+ uint32_t sspm_meta = 0;
+
+ if (len < 3)
+ return -1;
+
+ /*reset local config structure*/
+ memset(err_msg, 0, MET_SMI_BUF_SIZE);
+ for (i = 0; i < 4; i++) {
+ smi_conf.port[i] = -1;
+ smi_conf.rwtype[i] = SMI_RW_ALL;
+ }
+ smi_conf.master = 0;
+ smi_conf.reqtype = SMI_REQ_ALL;
+ smi_conf.desttype = SMI_DEST_EMI;
+
+ if (met_sspm_smi.mode != 0 && met_sspm_smi.mode != 1)
+ return -1;
+
+ /*
+ * parse arguments
+ * arg[0] = master
+ * arg[1] = port or port1
+ * arg[2] = rwtype or port2
+ * arg[3] = desttype or port3
+ * arg[4] = bustype or port4
+ */
+ for (i = 0; i < ARRAY_SIZE(args); i++)
+ args[i] = -1;
+ idx = 0;
+ for (i = 0; i < ARRAY_SIZE(args); i++) {
+ ret = get_num(&(arg[idx]), &(args[i]));
+ if (ret == 0)
+ break;
+ idx += ret;
+ if (arg[idx] != ':')
+ break;
+ idx++;
+ }
+
+ pr_debug("===SMI process argu: args[0](%d), args[1](%d), args[2](%d), args[3](%d), args[4](%d)\n",
+ args[0],
+ args[1],
+ args[2],
+ args[3],
+ args[4]);
+
+ /*fill local config structure*/
+ smi_conf.master = args[0];
+ smi_conf.reqtype = (larb_req_type << MET_SMI_BIT_REQ_LARB) | (comm_req_type << MET_SMI_BIT_REQ_COMM);
+ if (parallel_mode == 0) { /*legacy mode*/
+ smi_conf.rwtype[0] = args[2];
+ smi_conf.desttype = args[3];
+ smi_conf.port[0] = args[1];
+ } else { /*parallel mode*/
+ smi_conf.desttype = SMI_DEST_EMI;
+ for (i = 0; i < 4; i++) {
+ if (args[i+1] < 0)
+ break;
+ smi_conf.port[i] = args[i+1];
+ smi_conf.rwtype[i] = comm_pm_rw_type[0][i];
+ }
+ }
+
+/*debug log to ftrace*/
+#ifdef MET_SMI_DEBUG
+ snprintf(debug_msg, MET_SMI_DEBUGBUF_SIZE,
+ "(argu)===SMI process argu Master[%d]: parallel_mode = %d, master = %d, port0 = %d, port1 = %d, port2 = %d, port3 = %d, rwtype0 = %d, rwtype1 = %d, rwtype2 = %d, rwtype3 = %d, desttype = %d, reqtype(larb) = %d, reqtype(comm) = %d\n",
+ args[0],
+ parallel_mode,
+ smi_conf.master,
+ smi_conf.port[0],
+ smi_conf.port[1],
+ smi_conf.port[2],
+ smi_conf.port[3],
+ smi_conf.rwtype[0],
+ smi_conf.rwtype[1],
+ smi_conf.rwtype[2],
+ smi_conf.rwtype[3],
+ smi_conf.desttype,
+ (smi_conf.reqtype >> MET_SMI_BIT_REQ_LARB) & 0x3,
+ (smi_conf.reqtype >> MET_SMI_BIT_REQ_COMM) & 0x3);
+ met_smi_debug(debug_msg);
+#endif
+
+ /*find the empty conf_array*/
+ for (i = 0; i < MAX_CONFIG_ARRAY_SIZE; i++) {
+ if ((smi_conf_array[i].master == smi_conf.master) && (smi_conf_array[i].port[0] != -1))
+ break;
+ }
+ if (i >= MAX_CONFIG_ARRAY_SIZE) {
+ if (smi_conf_array[0].port[0] == -1) {
+ smi_array_index = 0;
+ array_index = 0;
+ } else {
+ smi_array_index = smi_array_index + 1;
+ array_index = smi_array_index;
+ }
+ } else {
+ array_index = i;
+ }
+
+ if ((smi_array_index >= MAX_CONFIG_ARRAY_SIZE) || (array_index >= MAX_CONFIG_ARRAY_SIZE)) {
+ snprintf(err_msg, MET_SMI_BUF_SIZE,
+ "===Setting Master[%d]: check smi_array_index=%d, array_index=%d overflow (> %d)\n",
+ args[0], smi_array_index, array_index, MAX_CONFIG_ARRAY_SIZE);
+ return -1;
+ }
+
+ smi_conf_array[array_index].master = smi_conf.master;
+
+
+ if (parallel_mode == 0) { /* Legacy mode */
+ smi_conf_array[array_index].port[0] = smi_conf.port[0];
+ } else { /* Parallel mode */
+ for (i = 0; i < NPORT_IN_PM; i++) {
+ if (smi_conf_array[array_index].port[i] == -1)
+ smi_conf_array[array_index].port[i] = smi_conf.port[smi_conf_index++];
+ }
+ }
+ smi_conf_array[array_index].rwtype[0] = smi_conf.rwtype[0];
+ smi_conf_array[array_index].rwtype[1] = smi_conf.rwtype[1];
+ smi_conf_array[array_index].rwtype[2] = smi_conf.rwtype[2];
+ smi_conf_array[array_index].rwtype[3] = smi_conf.rwtype[3];
+ smi_conf_array[array_index].desttype = smi_conf.desttype;
+ smi_conf_array[array_index].reqtype = smi_conf.reqtype;
+
+ pr_debug("===SMI process argu Master[%d]: parallel_mode = %d, master = %d, port0 = %d, port1 = %d, port2 = %d, port3 = %d, rwtype0 = %d, rwtype1 = %d, rwtype2 = %d, rwtype3 = %d, desttype = %d, reqtype(larb) = %d, reqtype(comm) = %d\n",
+ args[0],
+ parallel_mode,
+ smi_conf.master,
+ smi_conf.port[0],
+ smi_conf.port[1],
+ smi_conf.port[2],
+ smi_conf.port[3],
+ smi_conf.rwtype[0],
+ smi_conf.rwtype[1],
+ smi_conf.rwtype[2],
+ smi_conf.rwtype[3],
+ smi_conf.desttype,
+ (smi_conf.reqtype >> MET_SMI_BIT_REQ_LARB) & 0x3,
+ (smi_conf.reqtype >> MET_SMI_BIT_REQ_COMM) & 0x3);
+
+ // Encode SMI config: Master (request format from SMI driver)
+ sspm_master = sspm_master | (smi_conf_array[array_index].master << MET_SMI_BIT_MASTER);
+ sspm_master = sspm_master | 0 << MET_SMI_BIT_REQ; //reqtype value will be update in sspm side
+ sspm_master = sspm_master | (smi_conf_array[array_index].desttype << MET_SMI_BIT_DST);
+ sspm_master = sspm_master | (parallel_mode << MET_SMI_BIT_PM);
+ // Extrace info for larb and comm reqestType since of unable to recognize master belong to larb or comm.
+ // BIT13~BIT14: comm reqtype
+ // BIT15~BIT16: larb reqtype
+ sspm_master = sspm_master | smi_conf_array[array_index].reqtype;
+
+
+ // Encode SMI config: Meta (request format from SMI driver)
+ // Encode 4 Metadata into 1 unsigned int
+ // BIT0~BIT4: port
+ // BIT5~BIT6: rwtype
+ if(parallel_mode == 0){
+ sspm_meta = sspm_meta | (smi_conf_array[array_index].port[0] << MET_SMI_BIT_PORT);
+ sspm_meta = sspm_meta | (smi_conf_array[array_index].rwtype[0] << MET_SMI_BIT_RW);
+ }
+ else{
+ for(i = 0; i < 4; i++){
+ if(smi_conf_array[array_index].port[i] == 0xFFFFFFFF){
+ smi_conf_array[array_index].port[i] = USHRT_MAX;
+ }
+ sspm_meta = sspm_meta | (smi_conf_array[array_index].port[i] << (MET_SMI_BIT_PORT+8*i));
+ sspm_meta = sspm_meta | (smi_conf_array[array_index].rwtype[i] << (MET_SMI_BIT_RW+8*i));
+ }
+ }
+
+ // Transfer to SSPM side
+ if (sspm_buf_available == 1)
+ {
+ ipi_buf[0] = MET_MAIN_ID | MET_ARGU | MID_SMI << MID_BIT_SHIFT | 1;
+ ipi_buf[1] = sspm_master;
+ ipi_buf[2] = sspm_meta;
+ ipi_buf[3] = 0;
+ ret = sspm_ipi_send_sync(IPI_ID_MET, IPI_OPT_WAIT, (void *)ipi_buf, 0, &rdata, 1);
+
+ /* Set mode */
+ met_sspm_smi.mode = 1;
+ ondiemet_module[ONDIEMET_SSPM] |= ID_CPU_INFO_MAPPING;
+ }
+
+ return 0;
+}
+
+struct metdevice met_sspm_smi = {
+ .name = "smi",
+ .owner = THIS_MODULE,
+ .type = MET_TYPE_BUS,
+ .create_subfs = met_smi_create,
+ .delete_subfs = met_smi_delete,
+ .cpu_related = 0,
+ .ondiemet_mode = 1,
+ .ondiemet_start = met_smi_start,
+ .ondiemet_stop = met_smi_stop,
+ .ondiemet_process_argument = smi_process_argument,
+ .ondiemet_print_help = smi_print_help,
+};
+
diff --git a/src/devtools/met-driver/4.14/common/sspm/sspm_met_smi.h b/src/devtools/met-driver/4.14/common/sspm/sspm_met_smi.h
new file mode 100644
index 0000000..918a5a5
--- /dev/null
+++ b/src/devtools/met-driver/4.14/common/sspm/sspm_met_smi.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 _SMI_H_
+#define _SMI_H_
+
+#include <linux/device.h>
+
+struct met_smi {
+ int mode;
+ int master;
+ unsigned int port;
+ unsigned int rwtype; /* 0 for R+W, 1 for read, 2 for write */
+ unsigned int desttype; /* 0 for EMI+internal mem, 1 for EMI, 3 for internal mem */
+ unsigned int bustype; /* 0 for GMC, 1 for AXI */
+ /* unsigned long requesttype;// 0:All, 1:ultra high, 2:pre-ultrahigh, 3:normal. */
+ struct kobject *kobj_bus_smi;
+};
+
+struct smi_cfg {
+ int master;
+ unsigned int port;
+ unsigned int rwtype; /* 0 for R+W, 1 for read, 2 for write */
+ unsigned int desttype; /* 0 for EMI+internal mem, 1 for EMI, 3 for internal mem */
+ unsigned int bustype; /*0 for GMC, 1 for AXI */
+ /*unsigned long requesttype;// 0:All, 1:ultra high, 2:pre-ultrahigh, 3:normal. */
+};
+
+struct smi_mon_con {
+ unsigned int requesttype; /* 0:All, 1:ultra high, 2:pre-ultrahigh, 3:normal. */
+};
+
+/* ====================== SMI/EMI Interface ================================ */
+
+struct met_smi_conf {
+ unsigned int master; /*Ex : Whitney: 0~8 for larb0~larb8, 9 for common larb*/
+ int port[4]; /* port select : [0] only for legacy mode, [0~3] ports for parallel mode, -1 no select*/
+ unsigned int reqtype; /* Selects request type : 0 for all,1 for ultra,2 for preultra,3 for normal*/
+ unsigned int rwtype[4]; /* Selects read/write: 0 for R+W, 1 for read, 2 for write;*/
+ /* [0] for legacy and parallel larb0~8, [0~3] for parallel mode common*/
+ unsigned int desttype; /* Selects destination: 0 and 3 for all memory, 1 for External,2 for internal*/
+};
+
+#endif /* _SMI_H_ */
diff --git a/src/devtools/met-driver/4.14/common/sspm/sspm_met_smi_name.h b/src/devtools/met-driver/4.14/common/sspm/sspm_met_smi_name.h
new file mode 100644
index 0000000..fd9197e
--- /dev/null
+++ b/src/devtools/met-driver/4.14/common/sspm/sspm_met_smi_name.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 _SMI_NAME_H_
+#define _SMI_NAME_H_
+#include "mtk_smi.h"
+
+enum SMI_DEST {
+ SMI_DEST_ALL = 0,
+ SMI_DEST_EMI = 1,
+ SMI_DEST_INTERNAL = 2,
+ SMI_DEST_NONE = 9
+};
+
+enum SMI_RW {
+ SMI_RW_ALL = 0,
+ SMI_READ_ONLY = 1,
+ SMI_WRITE_ONLY = 2,
+ SMI_RW_RESPECTIVE = 3,
+ SMI_RW_NONE = 9
+};
+
+enum SMI_BUS {
+ SMI_BUS_GMC = 0,
+ SMI_BUS_AXI = 1,
+ SMI_BUS_NONE = 9
+};
+
+enum SMI_REQUEST {
+ SMI_REQ_ALL = 0,
+ SMI_REQ_ULTRA = 1,
+ SMI_REQ_PREULTRA = 2,
+ SMI_NORMAL_ULTRA = 3,
+ SMI_REQ_NONE = 9
+};
+
+struct smi_desc {
+ unsigned long port;
+ enum SMI_DEST desttype;
+ enum SMI_RW rwtype;
+ enum SMI_BUS bustype;
+};
+#endif /* _SMI_NAME_H_ */
diff --git a/src/devtools/met-driver/4.14/common/sspm/sspm_mtk_smi.h b/src/devtools/met-driver/4.14/common/sspm/sspm_mtk_smi.h
new file mode 100644
index 0000000..9e76a0b
--- /dev/null
+++ b/src/devtools/met-driver/4.14/common/sspm/sspm_mtk_smi.h
@@ -0,0 +1,133 @@
+/*
+ * 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_SMI_H__
+#define __MT_SMI_H__
+
+/* the default value, but the real number will get from symbol function*/
+#define SMI_LARB_NUMBER 9
+#define SMI_COMM_NUMBER 1
+#define SMI_LARB_MONITOR_NUMBER 1
+#define SMI_COMM_MONITOR_NUMBER 1
+
+#define SMI_REQ_OK (0)
+#define SMI_ERR_WRONG_REQ (-1)
+#define SMI_ERR_OVERRUN (-2)
+
+#define SMI_IOMEM_ADDR(b, off) ((void __iomem *)(((unsigned long)b)+off))
+
+#define SMI_LARB_MON_ENA(b) SMI_IOMEM_ADDR((b), 0x400)
+#define SMI_LARB_MON_CLR(b) SMI_IOMEM_ADDR((b), 0x404)
+#define SMI_LARB_MON_PORT(b) SMI_IOMEM_ADDR((b), 0x408)
+#define SMI_LARB_MON_CON(b) SMI_IOMEM_ADDR((b), 0x40C)
+
+#define SMI_LARB_MON_ACT_CNT(b) SMI_IOMEM_ADDR((b), 0x410)
+#define SMI_LARB_MON_REQ_CNT(b) SMI_IOMEM_ADDR((b), 0x414)
+#define SMI_LARB_MON_BEA_CNT(b) SMI_IOMEM_ADDR((b), 0x418)
+#define SMI_LARB_MON_BYT_CNT(b) SMI_IOMEM_ADDR((b), 0x41C)
+#define SMI_LARB_MON_CP_CNT(b) SMI_IOMEM_ADDR((b), 0x420)
+#define SMI_LARB_MON_DP_CNT(b) SMI_IOMEM_ADDR((b), 0x424)
+#define SMI_LARB_MON_OSTD_CNT(b) SMI_IOMEM_ADDR((b), 0x428)
+#define SMI_LARB_MON_CP_MAX(b) SMI_IOMEM_ADDR((b), 0x430)
+#define SMI_LARB_MON_OSTD_MAX(b) SMI_IOMEM_ADDR((b), 0x434)
+
+#define SMI_COMM_MON_ENA(b) SMI_IOMEM_ADDR((b), 0x1A0)
+#define SMI_COMM_MON_CLR(b) SMI_IOMEM_ADDR((b), 0x1A4)
+#define SMI_COMM_MON_TYPE(b) SMI_IOMEM_ADDR((b), 0x1AC)
+#define SMI_COMM_MON_CON(b) SMI_IOMEM_ADDR((b), 0x1B0)
+#define SMI_COMM_MON_ACT_CNT(b) SMI_IOMEM_ADDR((b), 0x1C0)
+#define SMI_COMM_MON_REQ_CNT(b) SMI_IOMEM_ADDR((b), 0x1C4)
+#define SMI_COMM_MON_OSTD_CNT(b) SMI_IOMEM_ADDR((b), 0x1C8)
+#define SMI_COMM_MON_BEA_CNT(b) SMI_IOMEM_ADDR((b), 0x1CC)
+#define SMI_COMM_MON_BYT_CNT(b) SMI_IOMEM_ADDR((b), 0x1D0)
+#define SMI_COMM_MON_CP_CNT(b) SMI_IOMEM_ADDR((b), 0x1D4)
+#define SMI_COMM_MON_DP_CNT(b) SMI_IOMEM_ADDR((b), 0x1D8)
+#define SMI_COMM_MON_CP_MAX(b) SMI_IOMEM_ADDR((b), 0x1DC)
+#define SMI_COMM_MON_OSTD_MAX(b) SMI_IOMEM_ADDR((b), 0x1E0)
+
+
+/*ondiemet smi ipi command*/
+enum MET_SMI_IPI_Type {
+ SMI_DRIVER_INITIAL_VALUE = 0x0,
+ SMI_DRIVER_RESET_VALUE,
+ SET_BASE_SMI,
+ SMI_ASSIGN_PORT_START,
+ SMI_ASSIGN_PORT_I,
+ SMI_ASSIGN_PORT_II,
+ SMI_ASSIGN_PORT_III,
+ SMI_ASSIGN_PORT_END,
+};
+
+
+
+
+void MET_SMI_IPI_baseaddr(void);
+int MET_SMI_Init(void);
+void MET_SMI_Fini(void);
+void MET_SMI_Enable(int larbno);
+void MET_SMI_Disable(int larbno);
+void MET_SMI_Pause(int larbno);
+void MET_SMI_Clear(int larbno);
+int MET_SMI_PowerOn(unsigned int master);
+void MET_SMI_PowerOff(unsigned int master);
+int MET_SMI_LARB_SetCfg(int larbno,
+ unsigned int pm,
+ unsigned int reqtype, unsigned int rwtype, unsigned int dsttype);
+int MET_SMI_LARB_SetPortNo(int larbno, unsigned int idx, unsigned int port);
+int MET_SMI_COMM_SetCfg(int commonno, unsigned int pm, unsigned int reqtype);
+int MET_SMI_COMM_SetPortNo(int commonno, unsigned int idx, unsigned int port);
+int MET_SMI_COMM_SetRWType(int commonno, unsigned int idx, unsigned int rw);
+
+/* config */
+int MET_SMI_GetEna(int larbno);
+int MET_SMI_GetClr(int larbno);
+int MET_SMI_GetPortNo(int larbno);
+int MET_SMI_GetCon(int larbno);
+
+/* cnt */
+int MET_SMI_GetActiveCnt(int larbno);
+int MET_SMI_GetRequestCnt(int larbno);
+int MET_SMI_GetBeatCnt(int larbno);
+int MET_SMI_GetByteCnt(int larbno);
+int MET_SMI_GetCPCnt(int larbno);
+int MET_SMI_GetDPCnt(int larbno);
+int MET_SMI_GetOSTDCnt(int larbno);
+int MET_SMI_GetCP_MAX(int larbno);
+int MET_SMI_GetOSTD_MAX(int larbno);
+
+/* common */
+void MET_SMI_Comm_Init(void);
+void MET_SMI_Comm_Enable(int commonno);
+void MET_SMI_Comm_Disable(int commonno);
+void MET_SMI_Pause(int commonno);
+void MET_SMI_Comm_Clear(int commonno);
+
+/* common config */
+int MET_SMI_Comm_GetEna(int commonno);
+int MET_SMI_Comm_GetClr(int commonno);
+int MET_SMI_Comm_GetType(int commonno);
+int MET_SMI_Comm_GetCon(int commonno);
+
+/* cnt */
+int MET_SMI_Comm_GetPortNo(int commonno);
+int MET_SMI_Comm_GetActiveCnt(int commonno);
+int MET_SMI_Comm_GetRequestCnt(int commonno);
+int MET_SMI_Comm_GetBeatCnt(int commonno);
+int MET_SMI_Comm_GetByteCnt(int commonno);
+int MET_SMI_Comm_GetCPCnt(int commonno);
+int MET_SMI_Comm_GetDPCnt(int commonno);
+int MET_SMI_Comm_GetOSTDCnt(int commonno);
+int MET_SMI_Comm_GetCP_MAX(int commonno);
+int MET_SMI_Comm_GetOSTD_MAX(int commonno);
+
+#endif /* __MT_SMI_H__ */
diff --git a/src/devtools/met-driver/4.14/common/sspm/sspm_walltime.c b/src/devtools/met-driver/4.14/common/sspm/sspm_walltime.c
new file mode 100644
index 0000000..cc12b8b
--- /dev/null
+++ b/src/devtools/met-driver/4.14/common/sspm/sspm_walltime.c
@@ -0,0 +1,52 @@
+/*
+ * 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.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/random.h>
+#include <linux/fs.h>
+
+#include "met_drv.h"
+#include "trace.h"
+/* #include "plf_init.h" */
+#include "sspm/ondiemet_sspm.h"
+
+static void sspm_walltime_start(void)
+{
+ ondiemet_module[ONDIEMET_SSPM] |= ID_WALL_TIME;
+}
+
+static void sspm_walltime_stop(void)
+{
+}
+
+static const char sspm_walltime_header[] = "met-info [000] 0.0: sspm WCLK: freq\n";
+
+static int sspm_walltime_print_header(char *buf, int len)
+{
+ return snprintf(buf, PAGE_SIZE, sspm_walltime_header);
+}
+
+struct metdevice met_sspm_walltime = {
+ .name = "sspm_wall_time",
+ .owner = THIS_MODULE,
+ .type = MET_TYPE_BUS,
+ .cpu_related = 0,
+ .ondiemet_mode = 0,
+ .print_header = sspm_walltime_print_header,
+ .ondiemet_start = sspm_walltime_start,
+ .ondiemet_stop = sspm_walltime_stop,
+ .ondiemet_print_header = sspm_walltime_print_header,
+};
+EXPORT_SYMBOL(met_sspm_walltime);
diff --git a/src/devtools/met-driver/4.14/common/stat.c b/src/devtools/met-driver/4.14/common/stat.c
new file mode 100644
index 0000000..af7b738
--- /dev/null
+++ b/src/devtools/met-driver/4.14/common/stat.c
@@ -0,0 +1,198 @@
+/*
+ * 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/cpu.h>
+#include <linux/cpumask.h>
+/* #include <linux/fs.h> */
+#include <linux/init.h>
+#include <linux/version.h>
+#include <linux/interrupt.h>
+#include <linux/kernel_stat.h>
+/* #include <linux/proc_fs.h> */
+#include <linux/sched.h>
+/* #include <linux/seq_file.h> */
+/* #include <linux/slab.h> */
+#include <linux/time.h>
+#include <linux/irqnr.h>
+#include <linux/vmalloc.h>
+/* #include <asm/cputime.h> */
+/* #include <asm-generic/cputime.h> */
+#include <linux/tick.h>
+#include <linux/jiffies.h>
+
+#include <asm/page.h>
+#include <linux/slab.h>
+
+#include "stat.h"
+#include "met_drv.h"
+#include "trace.h"
+
+#define MS_STAT_FMT "%5lu.%06lu"
+#define FMTLX7 ",%llx,%llx,%llx,%llx,%llx,%llx,%llx\n"
+#define FMTLX10 ",%llx,%llx,%llx,%llx,%llx,%llx,%llx,%llx,%llx,%llx\n"
+
+
+static DEFINE_PER_CPU(int, cpu_status);
+
+
+/* void ms_st(unsigned long long timestamp, unsigned char cnt, unsigned int *value) */
+noinline void ms_st(unsigned long long timestamp, unsigned char cnt, u64 *value)
+{
+ unsigned long nano_rem = do_div(timestamp, 1000000000);
+
+ switch (cnt) {
+ case 10:
+ MET_TRACE(MS_STAT_FMT FMTLX10, (unsigned long)(timestamp), (nano_rem/1000),
+ value[0], value[1], value[2], value[3], value[4],
+ value[5], value[6], value[7], value[8], value[9]);
+ break;
+ case 7:
+ MET_TRACE(MS_STAT_FMT FMTLX7, (unsigned long)(timestamp), (nano_rem/1000),
+ value[0], value[1], value[2], value[3], value[4],
+ value[5], value[6]);
+ break;
+ }
+}
+
+static void met_stat_start(void)
+{
+ int cpu = raw_smp_processor_id();
+
+ if (get_ctrl_flags() & 1)
+ met_stat.mode = 0;
+
+ per_cpu(cpu_status, cpu) = MET_CPU_ONLINE;
+}
+
+static void met_stat_stop(void)
+{
+}
+
+static int do_stat(void)
+{
+ return met_stat.mode;
+}
+
+u64 met_usecs_to_cputime64(u64 n)
+{
+#if (NSEC_PER_SEC % HZ) == 0
+ /* Common case, HZ = 100, 128, 200, 250, 256, 500, 512, 1000 etc. */
+ return div_u64(n, NSEC_PER_SEC / HZ);
+#elif (HZ % 512) == 0
+ /* overflow after 292 years if HZ = 1024 */
+ return div_u64(n * HZ / 512, NSEC_PER_SEC / 512);
+#else
+ /*
+ * Generic case - optimized for cases where HZ is a multiple of 3.
+ * overflow after 64.99 years, exact for HZ = 60, 72, 90, 120 etc.
+ */
+ return div_u64(n * 9, (9ull * NSEC_PER_SEC + HZ / 2) / HZ);
+#endif
+}
+
+static u64 get_idle_time(int cpu)
+{
+ u64 idle, idle_time = get_cpu_idle_time_us(cpu, NULL);
+
+ if (idle_time == -1ULL) {
+ /* !NO_HZ so we can rely on cpustat.idle */
+ idle = kcpustat_cpu(cpu).cpustat[CPUTIME_IDLE];
+ } else
+ idle = met_usecs_to_cputime64(idle_time);
+
+ return idle;
+}
+
+static u64 get_iowait_time(int cpu)
+{
+ u64 iowait, iowait_time = get_cpu_iowait_time_us(cpu, NULL);
+
+ if (iowait_time == -1ULL) {
+ /* !NO_HZ so we can rely on cpustat.iowait */
+ iowait = kcpustat_cpu(cpu).cpustat[CPUTIME_IOWAIT];
+ } else
+ iowait = met_usecs_to_cputime64(iowait_time);
+
+ return iowait;
+}
+
+
+static unsigned int stat_os_polling(u64 *value, int i)
+{
+ int j = -1;
+
+ /* Copy values here to work around gcc-2.95.3, gcc-2.96 */
+ value[++j] = jiffies_64_to_clock_t(kcpustat_cpu(i).cpustat[CPUTIME_USER]); /* user */
+ value[++j] = jiffies_64_to_clock_t(kcpustat_cpu(i).cpustat[CPUTIME_NICE]); /* nice */
+ value[++j] = jiffies_64_to_clock_t(kcpustat_cpu(i).cpustat[CPUTIME_SYSTEM]); /* system */
+ value[++j] = jiffies_64_to_clock_t(get_idle_time(i)); /* idle */
+ value[++j] = jiffies_64_to_clock_t(get_iowait_time(i)); /* iowait */
+ value[++j] = jiffies_64_to_clock_t(kcpustat_cpu(i).cpustat[CPUTIME_IRQ]); /* irq */
+ value[++j] = jiffies_64_to_clock_t(kcpustat_cpu(i).cpustat[CPUTIME_SOFTIRQ]); /* softirq */
+ value[++j] = jiffies_64_to_clock_t(kcpustat_cpu(i).cpustat[CPUTIME_STEAL]); /* steal */
+ value[++j] = jiffies_64_to_clock_t(kcpustat_cpu(i).cpustat[CPUTIME_GUEST]); /* guest */
+ value[++j] = jiffies_64_to_clock_t(kcpustat_cpu(i).cpustat[CPUTIME_GUEST_NICE]); /* guest_nice */
+
+ return j + 1;
+}
+
+static void met_stat_polling(unsigned long long stamp, int cpu)
+{
+ unsigned char count;
+ u64 value[10] = {0};
+
+ if (per_cpu(cpu_status, cpu) != MET_CPU_ONLINE)
+ return;
+
+ /* return; */
+ if (do_stat()) {
+ count = stat_os_polling(value, cpu);
+ if (count)
+ ms_st(stamp, count, value);
+ }
+}
+
+static const char header[] =
+ "met-info [000] 0.0: met_st_header: user,nice,system,idle,iowait,irq,softirq,steal,guest,guest_nice\n";
+
+static const char help[] = " --stat monitor stat\n";
+
+
+static int met_stat_print_help(char *buf, int len)
+{
+ return snprintf(buf, PAGE_SIZE, help);
+}
+
+static int met_stat_print_header(char *buf, int len)
+{
+ return snprintf(buf, PAGE_SIZE, header);
+}
+
+static void met_stat_cpu_state_notify(long cpu, unsigned long action)
+{
+ per_cpu(cpu_status, cpu) = action;
+}
+
+
+struct metdevice met_stat = {
+ .name = "stat",
+ .type = MET_TYPE_PMU,
+ .cpu_related = 1,
+ .start = met_stat_start,
+ .stop = met_stat_stop,
+ .polling_interval = 30,
+ .timed_polling = met_stat_polling,
+ .print_help = met_stat_print_help,
+ .print_header = met_stat_print_header,
+ .cpu_state_notify = met_stat_cpu_state_notify,
+};
diff --git a/src/devtools/met-driver/4.14/common/stat.h b/src/devtools/met-driver/4.14/common/stat.h
new file mode 100644
index 0000000..8aabc5c
--- /dev/null
+++ b/src/devtools/met-driver/4.14/common/stat.h
@@ -0,0 +1,30 @@
+/*
+ * 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 _STAT_H_
+#define _STAT_H_
+
+#include <linux/device.h>
+
+extern struct metdevice met_stat;
+
+int stat_reg(struct kobject *parent);
+void stat_unreg(void);
+
+void stat_start(void);
+void stat_stop(void);
+void stat_polling(unsigned long long stamp, int cpu);
+
+unsigned int get_ctrl_flags(void);
+
+#endif /* _STAT_H_ */
diff --git a/src/devtools/met-driver/4.14/common/switch.c b/src/devtools/met-driver/4.14/common/switch.c
new file mode 100644
index 0000000..f634e16
--- /dev/null
+++ b/src/devtools/met-driver/4.14/common/switch.c
@@ -0,0 +1,302 @@
+/*
+ * 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/percpu.h> */
+#include <trace/events/sched.h>
+#include <linux/module.h>
+#include <trace/events/irq.h>
+#include <trace/events/power.h>
+
+#include "interface.h"
+#include "met_drv.h"
+#include "cpu_pmu.h"
+#include "switch.h"
+#include "sampler.h"
+#include "met_kernel_symbol.h"
+/* #include "trace.h" */
+
+/*
+ * IRQ_TIRGGER and CPU_IDLE_TRIGGER
+ */
+/* #define IRQ_TRIGGER */
+/* #define CPU_IDLE_TRIGGER */
+
+static DEFINE_PER_CPU(unsigned int, first_log);
+
+#ifdef __aarch64__
+/* #include <asm/compat.h> */
+#include <linux/compat.h>
+#endif
+
+noinline void mt_switch(struct task_struct *prev, struct task_struct *next)
+{
+ int cpu;
+ int prev_state = 0, next_state = 0;
+
+#ifdef __aarch64__
+ prev_state = !(is_compat_thread(task_thread_info(prev)));
+ next_state = !(is_compat_thread(task_thread_info(next)));
+#endif
+
+ cpu = smp_processor_id();
+ if (per_cpu(first_log, cpu)) {
+ MET_TRACE("%d, %d, %d, %d\n", prev->pid, prev_state, next->pid, next_state);
+ per_cpu(first_log, cpu) = 0;
+ }
+ else if (prev_state != next_state)
+ MET_TRACE("%d, %d, %d, %d\n", prev->pid, prev_state, next->pid, next_state);
+}
+
+
+#if 0 /* move to kernel space */
+MET_DEFINE_PROBE(sched_switch,
+ TP_PROTO(bool preempt, struct task_struct *prev, struct task_struct *next))
+{
+ /* speedup sched_switch callback handle */
+ if (met_switch.mode == 0)
+ return;
+
+ if (met_switch.mode & MT_SWITCH_EVENT_TIMER)
+ met_event_timer_notify();
+
+ if (met_switch.mode & MT_SWITCH_64_32BIT)
+ mt_switch(prev, next);
+
+ if (met_switch.mode & MT_SWITCH_SCHEDSWITCH) {
+ if (get_pmu_profiling_version() == 1)
+ cpupmu_polling(0, smp_processor_id());
+#ifdef MET_SUPPORT_CPUPMU_V2
+ else if (get_pmu_profiling_version() == 2)
+ cpupmu_polling_v2(0, smp_processor_id());
+#endif
+ }
+}
+#endif
+
+void met_sched_switch(struct task_struct *prev, struct task_struct *next)
+{
+ /* speedup sched_switch callback handle */
+ if (met_switch.mode == 0)
+ return;
+
+ if (met_switch.mode & MT_SWITCH_EVENT_TIMER)
+ met_event_timer_notify();
+
+ if (met_switch.mode & MT_SWITCH_64_32BIT)
+ mt_switch(prev, next);
+
+ /* met_perf_cpupmu_status: 0: stop, others: polling */
+ if ((met_switch.mode & MT_SWITCH_SCHEDSWITCH) && met_perf_cpupmu_status)
+ met_perf_cpupmu_polling(0, smp_processor_id());
+}
+
+#ifdef IRQ_TRIGGER
+MET_DEFINE_PROBE(irq_handler_entry, TP_PROTO(int irq, struct irqaction *action))
+{
+ if (met_switch.mode & MT_SWITCH_EVENT_TIMER) {
+ met_event_timer_notify();
+ return;
+ }
+}
+#endif
+
+#ifdef CPU_IDLE_TRIGGER
+MET_DEFINE_PROBE(cpu_idle, TP_PROTO(unsigned int state, unsigned int cpu_id))
+{
+ if (met_switch.mode & MT_SWITCH_EVENT_TIMER) {
+ met_event_timer_notify();
+ return;
+ }
+}
+#endif
+
+#ifdef MET_ANYTIME
+/*
+ * create related subfs file node
+ */
+
+static ssize_t default_on_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
+{
+ return snprintf(buf, PAGE_SIZE, "1\n");
+}
+
+static struct kobj_attribute default_on_attr = __ATTR(default_on, 0664, default_on_show, NULL);
+static struct kobject *kobj_cpu;
+#endif
+
+static int met_switch_create_subfs(struct kobject *parent)
+{
+ int ret = 0;
+
+ /* register tracepoints */
+#if 0
+ if (MET_REGISTER_TRACE(sched_switch)) {
+ pr_debug("can not register callback of sched_switch\n");
+ return -ENODEV;
+ }
+#else
+ if (met_reg_switch_symbol)
+ ret = met_reg_switch_symbol();
+#endif
+#ifdef CPU_IDLE_TRIGGER
+ if (MET_REGISTER_TRACE(cpu_idle)) {
+ pr_debug("can not register callback of irq_handler_entry\n");
+ return -ENODEV;
+ }
+#endif
+#ifdef IRQ_TRIGGER
+ if (MET_REGISTER_TRACE(irq_handler_entry)) {
+ pr_debug("can not register callback of irq_handler_entry\n");
+ return -ENODEV;
+ }
+#endif
+
+#ifdef MET_ANYTIME
+ /*
+ * to create default_on file node
+ * let user space can know we can support MET default on
+ */
+ kobj_cpu = parent;
+ ret = sysfs_create_file(kobj_cpu, &default_on_attr.attr);
+ if (ret != 0) {
+ pr_debug("Failed to create default_on in sysfs\n");
+ return -1;
+ }
+#endif
+
+ return ret;
+}
+
+
+static void met_switch_delete_subfs(void)
+{
+#ifdef MET_ANYTIME
+ if (kobj_cpu != NULL) {
+ sysfs_remove_file(kobj_cpu, &default_on_attr.attr);
+ kobj_cpu = NULL;
+ }
+#endif
+#ifdef IRQ_TRIGGER
+ MET_UNREGISTER_TRACE(irq_handler_entry);
+#endif
+#ifdef CPU_IDLE_TRIGGER
+ MET_UNREGISTER_TRACE(cpu_idle);
+#endif
+#if 0
+ MET_UNREGISTER_TRACE(sched_switch);
+#else
+ if (met_unreg_switch_symbol)
+ met_unreg_switch_symbol();
+#endif
+
+}
+
+
+static void (*cpu_timed_polling)(unsigned long long stamp, int cpu);
+/* static void (*cpu_tagged_polling)(unsigned long long stamp, int cpu); */
+
+static void met_switch_start(void)
+{
+ int cpu;
+
+ if (met_switch.mode & MT_SWITCH_SCHEDSWITCH) {
+ cpu_timed_polling = met_cpupmu.timed_polling;
+ /* cpu_tagged_polling = met_cpupmu.tagged_polling; */
+ met_cpupmu.timed_polling = NULL;
+ /* met_cpupmu.tagged_polling = NULL; */
+ }
+
+ for_each_possible_cpu(cpu) {
+ per_cpu(first_log, cpu) = 1;
+ }
+
+}
+
+
+static void met_switch_stop(void)
+{
+ int cpu;
+
+ if (met_switch.mode & MT_SWITCH_SCHEDSWITCH) {
+ met_cpupmu.timed_polling = cpu_timed_polling;
+ /* met_cpupmu.tagged_polling = cpu_tagged_polling; */
+ }
+
+ for_each_possible_cpu(cpu) {
+ per_cpu(first_log, cpu) = 0;
+ }
+
+}
+
+
+static int met_switch_process_argument(const char *arg, int len)
+{
+ unsigned int value;
+ /*ex: mxitem is 0x0005, max value should be (5-1) + (5-2) = 0x100 + 0x11 = 7 */
+ unsigned int max_value = ((MT_SWITCH_MX_ITEM * 2) - 3);
+
+
+ if (met_parse_num(arg, &value, len) < 0)
+ goto arg_switch_exit;
+
+ if ((value < 1) || (value > max_value))
+ goto arg_switch_exit;
+
+ met_switch.mode = value;
+ return 0;
+
+arg_switch_exit:
+ met_switch.mode = 0;
+ return -EINVAL;
+}
+
+static const char header[] =
+ "met-info [000] 0.0: met_switch_header: prev_pid,prev_state,next_pid,next_state\n";
+
+static const char help[] =
+" --switch=mode mode:0x1 - output CPUPMU whenever sched_switch\n"
+" mode:0x2 - output Aarch 32/64 state whenever state changed (no CPUPMU)\n"
+" mode:0x4 - force output count at tag_start/tag_end\n"
+" mode:0x8 - task switch timer\n"
+" mode:0xF - mode 0x1 + 0x2 + 04 + 08\n";
+
+static int met_switch_print_help(char *buf, int len)
+{
+ return snprintf(buf, PAGE_SIZE, help);
+}
+
+static int met_switch_print_header(char *buf, int len)
+{
+ int ret = 0;
+
+ ret =
+ snprintf(buf, PAGE_SIZE, "met-info [000] 0.0: mp_cpu_switch_base: %d\n",
+ met_switch.mode);
+ if (met_switch.mode & MT_SWITCH_64_32BIT)
+ ret += snprintf(buf + ret, PAGE_SIZE, header);
+
+ return ret;
+}
+
+
+struct metdevice met_switch = {
+ .name = "switch",
+ .type = MET_TYPE_PMU,
+ .create_subfs = met_switch_create_subfs,
+ .delete_subfs = met_switch_delete_subfs,
+ .start = met_switch_start,
+ .stop = met_switch_stop,
+ .process_argument = met_switch_process_argument,
+ .print_help = met_switch_print_help,
+ .print_header = met_switch_print_header,
+};
diff --git a/src/devtools/met-driver/4.14/common/switch.h b/src/devtools/met-driver/4.14/common/switch.h
new file mode 100644
index 0000000..14397d7
--- /dev/null
+++ b/src/devtools/met-driver/4.14/common/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.14/common/trace.h b/src/devtools/met-driver/4.14/common/trace.h
new file mode 100644
index 0000000..f259b7a
--- /dev/null
+++ b/src/devtools/met-driver/4.14/common/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.14/common/trace_event.c b/src/devtools/met-driver/4.14/common/trace_event.c
new file mode 100644
index 0000000..203708c
--- /dev/null
+++ b/src/devtools/met-driver/4.14/common/trace_event.c
@@ -0,0 +1,274 @@
+/*
+ * 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 "interface.h"
+#include "met_drv.h"
+
+#ifdef CONFIG_GPU_TRACEPOINTS
+#include <trace/events/gpu.h>
+
+#define show_secs_from_ns(ns) \
+ ({ \
+ u64 t = ns + (NSEC_PER_USEC / 2); \
+ do_div(t, NSEC_PER_SEC); \
+ t; \
+ })
+
+#define show_usecs_from_ns(ns) \
+ ({ \
+ u64 t = ns + (NSEC_PER_USEC / 2) ; \
+ u32 rem; \
+ do_div(t, NSEC_PER_USEC); \
+ rem = do_div(t, USEC_PER_SEC); \
+ })
+
+static int event_gpu_registered;
+static int event_gpu_enabled;
+
+noinline void gpu_sched_switch(const char *gpu_name, u64 timestamp,
+ u32 next_ctx_id, s32 next_prio, u32 next_job_id)
+{
+ MET_TRACE("gpu_name=%s ts=%llu.%06lu next_ctx_id=%lu next_prio=%ld next_job_id=%lu\n",
+ gpu_name,
+ (unsigned long long)show_secs_from_ns(timestamp),
+ (unsigned long)show_usecs_from_ns(timestamp),
+ (unsigned long)next_ctx_id, (long)next_prio, (unsigned long)next_job_id);
+}
+
+MET_DEFINE_PROBE(gpu_sched_switch, TP_PROTO(const char *gpu_name, u64 timestamp,
+ u32 next_ctx_id, s32 next_prio, u32 next_job_id))
+{
+ gpu_sched_switch(gpu_name, timestamp, next_ctx_id, next_prio, next_job_id);
+}
+
+noinline void gpu_job_enqueue(u32 ctx_id, u32 job_id, const char *type)
+{
+ MET_TRACE("ctx_id=%lu job_id=%lu type=%s",
+ (unsigned long)ctx_id, (unsigned long)job_id, type);
+}
+
+MET_DEFINE_PROBE(gpu_job_enqueue, TP_PROTO(u32 ctx_id, u32 job_id, const char *type))
+{
+ gpu_job_enqueue(ctx_id, job_id, type);
+}
+#endif
+
+
+#ifdef MET_EVENT_POWER_SUPPORT
+#include "met_power.h"
+#include "met_kernel_symbol.h"
+
+static int event_power_registered;
+static int event_power_enabled;
+
+const char *
+met_trace_print_symbols_seq(char* pclass_name, unsigned long val,
+ const struct trace_print_flags *symbol_array)
+{
+ int i;
+ size_t new_fsize=0;
+ char _buf[32];
+ const char *ret = pclass_name;
+
+ for (i = 0; symbol_array[i].name; i++) {
+
+ if (val != symbol_array[i].mask)
+ continue;
+
+ new_fsize = sprintf(pclass_name, symbol_array[i].name, strlen(symbol_array[i].name));
+ break;
+ }
+
+ if (new_fsize == 0) {
+ snprintf(_buf, 32, "0x%lx", val);
+ new_fsize = sprintf(pclass_name, _buf, strlen(_buf));
+ }
+
+ return ret;
+}
+
+#define __print_symbolic(pclass_name, value, symbol_array...) \
+ ({ \
+ static const struct trace_print_flags symbols[] = \
+ { symbol_array, { -1, NULL }}; \
+ met_trace_print_symbols_seq(pclass_name, value, symbols); \
+ })
+
+#ifdef pm_qos_update_request
+#undef pm_qos_update_request
+#endif
+void pm_qos_update_request(int pm_qos_class, s32 value, char *owner)
+{
+ char class_name[64];
+ MET_TRACE("pm_qos_class=%s value=%d owner=%s\n",
+ __print_symbolic(class_name, pm_qos_class,
+ { _PM_QOS_CPU_DMA_LATENCY, "CPU_DMA_LATENCY" },
+ { _PM_QOS_NETWORK_LATENCY, "NETWORK_LATENCY" },
+ { _PM_QOS_NETWORK_THROUGHPUT, "NETWORK_THROUGHPUT" }),
+ value, owner);
+}
+//#endif
+
+#ifdef pm_qos_update_target
+#undef pm_qos_update_target
+#endif
+void pm_qos_update_target(unsigned int action, int prev_value, int curr_value)
+{
+ char class_name[64];
+
+ MET_TRACE("action=%s prev_value=%d curr_value=%d\n",
+ __print_symbolic(class_name, action,
+ { _PM_QOS_ADD_REQ, "ADD_REQ" },
+ { _PM_QOS_UPDATE_REQ, "UPDATE_REQ" },
+ { _PM_QOS_REMOVE_REQ, "REMOVE_REQ" }),
+ prev_value, curr_value);
+}
+#endif
+//#endif
+
+static int reset_driver_stat(void)
+{
+#ifdef CONFIG_GPU_TRACEPOINTS
+ event_gpu_enabled = 0;
+#endif
+#ifdef MET_EVENT_POWER_SUPPORT
+ event_power_enabled = 0;
+#endif
+
+ met_trace_event.mode = 0;
+ return 0;
+}
+
+
+
+static void met_event_start(void)
+{
+#ifdef CONFIG_GPU_TRACEPOINTS
+ /* register trace event for gpu */
+ do {
+ if (!event_gpu_enabled)
+ break;
+ if (MET_REGISTER_TRACE(gpu_sched_switch)) {
+ pr_debug("can not register callback of gpu_sched_switch\n");
+ break;
+ }
+ if (MET_REGISTER_TRACE(gpu_job_enqueue)) {
+ pr_debug("can not register callback of gpu_job_enqueue\n");
+ MET_UNREGISTER_TRACE(gpu_sched_switch);
+ break;
+ }
+ event_gpu_registered = 1;
+ } while (0);
+#endif
+
+#ifdef MET_EVENT_POWER_SUPPORT
+ /* register trace event for power */
+ do {
+ if (!event_power_enabled)
+ break;
+ if (met_reg_event_power_symbol)
+ if (met_reg_event_power_symbol()) {
+ pr_debug("can not register callback of met_reg_event_power\n");
+ break;
+ }
+ event_power_registered = 1;
+ } while (0);
+#endif
+
+}
+
+static void met_event_stop(void)
+{
+#ifdef CONFIG_GPU_TRACEPOINTS
+ /* unregister trace event for gpu */
+ if (event_gpu_registered) {
+ MET_UNREGISTER_TRACE(gpu_job_enqueue);
+ MET_UNREGISTER_TRACE(gpu_sched_switch);
+ event_gpu_registered = 0;
+ }
+#endif
+
+#ifdef MET_EVENT_POWER_SUPPORT
+ /* unregister trace event for power */
+ if (event_power_registered) {
+ if (met_unreg_event_power_symbol)
+ met_unreg_event_power_symbol();
+ event_power_registered = 0;
+ }
+#endif
+}
+
+static int met_event_process_argument(const char *arg, int len)
+{
+ int ret = -1;
+
+#ifdef CONFIG_GPU_TRACEPOINTS
+ if (strcasecmp(arg, "gpu") == 0) {
+ event_gpu_enabled = 1;
+ met_trace_event.mode = 1;
+ ret = 0;
+ }
+#endif
+#ifdef MET_EVENT_POWER_SUPPORT
+ if (strcasecmp(arg, "power") == 0) {
+ event_power_enabled = 1;
+ met_trace_event.mode = 1;
+ ret = 0;
+ }
+#endif
+ return ret;
+}
+
+static const char help[] = "\b"
+#ifdef CONFIG_GPU_TRACEPOINTS
+ " --event=gpu output gpu trace events\n"
+#endif
+#ifdef MET_EVENT_POWER_SUPPORT
+ " --event=power output pmqos trace events\n"
+#endif
+ ;
+
+static int met_event_print_help(char *buf, int len)
+{
+ return snprintf(buf, PAGE_SIZE, help);
+}
+
+static const char header[] =
+ "met-info [000] 0.0: met_ftrace_event:"
+#ifdef CONFIG_GPU_TRACEPOINTS
+ " gpu:gpu_sched_switch gpu:gpu_job_enqueue"
+#endif
+#ifdef MET_EVENT_POWER_SUPPORT
+ " power:pm_qos_update_request power:pm_qos_update_target"
+#endif
+ "\n";
+
+static int met_event_print_header(char *buf, int len)
+{
+ int ret;
+
+ ret = snprintf(buf, PAGE_SIZE, header);
+ return ret;
+}
+
+struct metdevice met_trace_event = {
+ .name = "event",
+ .type = MET_TYPE_PMU,
+ .start = met_event_start,
+ .stop = met_event_stop,
+ .reset = reset_driver_stat,
+ .process_argument = met_event_process_argument,
+ .print_help = met_event_print_help,
+ .print_header = met_event_print_header,
+};
diff --git a/src/devtools/met-driver/4.14/common/util.c b/src/devtools/met-driver/4.14/common/util.c
new file mode 100644
index 0000000..051a3bd
--- /dev/null
+++ b/src/devtools/met-driver/4.14/common/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.14/common/util.h b/src/devtools/met-driver/4.14/common/util.h
new file mode 100644
index 0000000..5730376
--- /dev/null
+++ b/src/devtools/met-driver/4.14/common/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.14/common/v6_pmu_hw.c b/src/devtools/met-driver/4.14/common/v6_pmu_hw.c
new file mode 100644
index 0000000..24b133c
--- /dev/null
+++ b/src/devtools/met-driver/4.14/common/v6_pmu_hw.c
@@ -0,0 +1,242 @@
+/*
+ * 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 "cpu_pmu.h"
+
+/*******************************
+ * ARM v6 operations *
+ *******************************/
+#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);
+}
+
+/***********************************
+ * MET ARM v6 operations *
+ ***********************************/
+enum ARM_TYPE {
+ ARM1136 = 0xB36,
+ ARM1156 = 0xB56,
+ ARM1176 = 0xB76,
+ CHIP_UNKNOWN = 0xFFF
+};
+
+struct chip_pmu {
+ enum ARM_TYPE type;
+};
+
+static struct chip_pmu chips[] = {
+ {ARM1136},
+ {ARM1156},
+ {ARM1176},
+};
+
+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;
+ }
+
+ 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",
+ .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 < ARRAY_SIZE(chips); i++)
+ if (chips[i].type == typeid)
+ break;
+
+ if (i == ARRAY_SIZE(chips))
+ return NULL;
+
+ armv6_pmu.nr_cnt = 3;
+
+ return &armv6_pmu;
+}
diff --git a/src/devtools/met-driver/4.14/common/v6_pmu_hw.h b/src/devtools/met-driver/4.14/common/v6_pmu_hw.h
new file mode 100644
index 0000000..a532f13
--- /dev/null
+++ b/src/devtools/met-driver/4.14/common/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.14/common/v7_pmu_hw.c b/src/devtools/met-driver/4.14/common/v7_pmu_hw.c
new file mode 100644
index 0000000..931d25e
--- /dev/null
+++ b/src/devtools/met-driver/4.14/common/v7_pmu_hw.c
@@ -0,0 +1,269 @@
+/*
+ * 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 "cpu_pmu.h"
+#include "v6_pmu_hw.h"
+
+/*******************************
+ * ARM v7 operations *
+ *******************************/
+#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 */
+
+static unsigned int 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 */
+}
+
+/***********************************
+ * MET ARM v7 operations *
+ ***********************************/
+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;
+};
+
+static struct chip_pmu chips[] = {
+ {CORTEX_A7},
+ {CORTEX_A9},
+ {CORTEX_A12},
+ {CORTEX_A15},
+ {CORTEX_A17},
+ {CORTEX_A53},
+ {CORTEX_A57},
+};
+
+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;
+ }
+
+ 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;
+}
+
+static struct met_pmu pmus[MXNR_CPU][MXNR_PMU_EVENTS];
+
+struct cpu_pmu_hw armv7_pmu = {
+ .name = "armv7_pmu",
+ .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;
+
+ type = (enum ARM_TYPE)armv7_get_ic();
+ for (i = 0; i < ARRAY_SIZE(chips); i++)
+ if (chips[i].type == type)
+ break;
+
+ if (i < ARRAY_SIZE(chips)) {
+ armv7_pmu.nr_cnt = armv7_pmu_hw_get_counters() + 1;
+ pmu = &armv7_pmu;
+ } else
+ pmu = v6_cpu_pmu_hw_init(type);
+
+ if (pmu == NULL)
+ return NULL;
+
+ for (i = 0; i < MXNR_CPU; i++) {
+ pmu->event_count[i] = pmu->nr_cnt;
+ pmu->pmu[i] = pmus[i];
+ }
+
+ return pmu;
+}
diff --git a/src/devtools/met-driver/4.14/common/v8_dsu_hw.c b/src/devtools/met-driver/4.14/common/v8_dsu_hw.c
new file mode 100644
index 0000000..0b58c9a
--- /dev/null
+++ b/src/devtools/met-driver/4.14/common/v8_dsu_hw.c
@@ -0,0 +1,56 @@
+/*
+ * 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/cpu.h>
+#include "met_kernel_symbol.h"
+#include "cpu_dsu.h"
+
+//dsu support 6 event
+#define DSU_EVENT_MAX_CNT 6
+
+static int armv8_dsu_hw_check_event(struct met_dsu *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;
+ }
+ return 0;
+}
+
+
+static struct met_dsu pmus[MXNR_DSU_EVENTS];
+
+struct cpu_dsu_hw armv8_dsu = {
+ .name = "armv8_dsu",
+ .check_event = armv8_dsu_hw_check_event,
+};
+
+static void init_dsu(void)
+{
+ armv8_dsu.event_count = DSU_EVENT_MAX_CNT;
+}
+
+struct cpu_dsu_hw *cpu_dsu_hw_init(void)
+{
+
+ init_dsu();
+ armv8_dsu.pmu = pmus;
+ return &armv8_dsu;
+}
diff --git a/src/devtools/met-driver/4.14/common/v8_pmu_hw.c b/src/devtools/met-driver/4.14/common/v8_pmu_hw.c
new file mode 100644
index 0000000..ebfd249
--- /dev/null
+++ b/src/devtools/met-driver/4.14/common/v8_pmu_hw.c
@@ -0,0 +1,259 @@
+/*
+ * 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/cpu.h>
+#include "met_kernel_symbol.h"
+#include "cpu_pmu.h"
+
+/*******************************
+ * ARM v8 operations *
+ *******************************/
+/*
+ * 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
+
+static inline void armv8_pmu_counter_select(unsigned int idx)
+{
+ asm volatile ("msr pmselr_el0, %0"::"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, %0"::"r" (type));
+}
+
+static inline unsigned int armv8_pmu_read_count(unsigned int idx)
+{
+ unsigned int value;
+
+ if (idx == 31) {
+ asm volatile ("mrs %0, pmccntr_el0":"=r" (value));
+ } else {
+ armv8_pmu_counter_select(idx);
+ asm volatile ("mrs %0, pmxevcntr_el0":"=r" (value));
+ }
+ return value;
+}
+
+static inline void armv8_pmu_enable_count(unsigned int idx)
+{
+ asm volatile ("msr pmcntenset_el0, %0"::"r" (1 << idx));
+}
+
+static inline void armv8_pmu_disable_count(unsigned int idx)
+{
+ asm volatile ("msr pmcntenclr_el0, %0"::"r" (1 << idx));
+}
+
+static inline void armv8_pmu_enable_intr(unsigned int idx)
+{
+ asm volatile ("msr pmintenset_el1, %0"::"r" (1 << idx));
+}
+
+static inline void armv8_pmu_disable_intr(unsigned int idx)
+{
+ asm volatile ("msr pmintenclr_el1, %0"::"r" (1 << idx));
+ isb();
+ asm volatile ("msr pmovsclr_el0, %0"::"r" (1 << idx));
+ isb();
+}
+
+static inline unsigned int armv8_pmu_overflow(void)
+{
+ unsigned int val;
+
+ asm volatile ("mrs %0, pmovsclr_el0":"=r" (val)); /* read */
+ val &= ARMV8_OVSR_MASK;
+ asm volatile ("mrs %0, pmovsclr_el0"::"r" (val));
+ return val;
+}
+
+static inline unsigned int armv8_pmu_control_read(void)
+{
+ unsigned int val;
+
+ asm volatile ("mrs %0, 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, %0"::"r" (val));
+}
+
+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 */
+}
+
+/***********************************
+ * MET ARM v8 operations *
+ ***********************************/
+enum ARM_TYPE {
+ CORTEX_A53 = 0xD03,
+ CORTEX_A35 = 0xD04,
+ CORTEX_A55 = 0xD05,
+ CORTEX_A57 = 0xD07,
+ CORTEX_A72 = 0xD08,
+ CORTEX_A73 = 0xD09,
+ CORTEX_A75 = 0xD0A,
+ CHIP_UNKNOWN = 0xFFF
+};
+
+struct chip_pmu {
+ enum ARM_TYPE type;
+ unsigned int event_count;
+};
+
+static struct chip_pmu chips[] = {
+ {CORTEX_A35, 6+1},
+ {CORTEX_A53, 6+1},
+ {CORTEX_A55, 6+1},
+ {CORTEX_A57, 6+1},
+ {CORTEX_A72, 6+1},
+ {CORTEX_A73, 6+1},
+ {CORTEX_A75, 6+1},
+};
+
+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;
+ }
+
+ 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;
+}
+
+static struct met_pmu pmus[MXNR_CPU][MXNR_PMU_EVENTS];
+
+struct cpu_pmu_hw armv8_pmu = {
+ .name = "armv8_pmu",
+ .check_event = armv8_pmu_hw_check_event,
+ .start = armv8_pmu_hw_start,
+ .stop = armv8_pmu_hw_stop,
+ .polling = armv8_pmu_hw_polling,
+};
+
+static void init_pmus(void)
+{
+ int cpu;
+ int i;
+
+ for_each_possible_cpu(cpu) {
+ struct cpuinfo_arm64 *cpuinfo;
+ if (cpu >= MXNR_CPU)
+ continue;
+ met_get_cpuinfo_symbol(cpu, &cpuinfo);
+ /* PR_BOOTMSG("CPU[%d]: reg_midr = %x\n", cpu, cpuinfo->reg_midr); */
+ for (i = 0; i < ARRAY_SIZE(chips); i++) {
+ if (chips[i].type == (cpuinfo->reg_midr & 0xffff) >> 4) {
+ armv8_pmu.event_count[cpu] = chips[i].event_count;
+ break;
+ }
+ }
+ }
+}
+
+struct cpu_pmu_hw *cpu_pmu_hw_init(void)
+{
+ int cpu;
+
+ init_pmus();
+ for (cpu = 0; cpu < MXNR_CPU; cpu++)
+ armv8_pmu.pmu[cpu] = pmus[cpu];
+
+ return &armv8_pmu;
+}
diff --git a/src/devtools/met-driver/4.14/common/version.h b/src/devtools/met-driver/4.14/common/version.h
new file mode 100644
index 0000000..ae304ec
--- /dev/null
+++ b/src/devtools/met-driver/4.14/common/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.2.0"