[Feature]add MT2731_MP2_MR2_SVN388 baseline version
Change-Id: Ief04314834b31e27effab435d3ca8ba33b499059
diff --git a/src/devtools/met-driver/4.19/mt2712/mips_pmu_hw.c b/src/devtools/met-driver/4.19/mt2712/mips_pmu_hw.c
new file mode 100644
index 0000000..19e836b
--- /dev/null
+++ b/src/devtools/met-driver/4.19/mt2712/mips_pmu_hw.c
@@ -0,0 +1,438 @@
+/*
+ * Copyright (C) 2018 MediaTek Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <asm/system.h>
+#include <linux/smp.h>
+
+#include "cpu_pmu.h"
+#include "mips_pmu_name.h"
+
+struct chip_pmu {
+ enum cpu_type_enum type;
+ struct pmu_desc **desc;
+ void *refptr;
+ const char *cpu_name;
+ unsigned int pmu_desc_size;
+ unsigned int max_hw_events;
+ unsigned int max_reg_count;
+};
+
+struct pmu_desc *mips_pmu_desc[MIPS_MAX_HWEVENTS];
+
+static struct chip_pmu chips[] = {
+ {CPU_1004K, mips_pmu_desc, (void *)mips_1004k_pmu_desc, "MIPS_1004K",
+ MIPS_1004K_PMU_DESC_SIZE, MIPS_1004K_PMU_DESC_COUNT, PMU_1004K_MAX_HW_REGS},
+};
+
+static struct chip_pmu chip_unknown = { CPU_UNKNOWN, NULL, NULL, "Unknown CPU", 0, 0, 0 };
+
+#define CHIP_PMU_COUNT (sizeof(chips) / sizeof(struct chip_pmu))
+static struct chip_pmu *chip;
+#define M_CONFIG1_PC (1 << 4)
+
+#define M_PERFCTL_EXL (1 << 0)
+#define M_PERFCTL_KERNEL (1 << 1)
+#define M_PERFCTL_SUPERVISOR (1 << 2)
+#define M_PERFCTL_USER (1 << 3)
+#define M_PERFCTL_INTERRUPT_ENABLE (1 << 4)
+#define M_PERFCTL_EVENT(event) (((event) & 0x3ff) << 5)
+#define M_PERFCTL_VPEID(vpe) ((vpe) << 16)
+
+#ifdef CONFIG_CPU_BMIPS5000
+#define M_PERFCTL_MT_EN(filter) 0
+#else /* !CONFIG_CPU_BMIPS5000 */
+#define M_PERFCTL_MT_EN(filter) ((filter) << 20)
+#endif /* CONFIG_CPU_BMIPS5000 */
+
+#define M_TC_EN_ALL M_PERFCTL_MT_EN(0)
+#define M_TC_EN_VPE M_PERFCTL_MT_EN(1)
+#define M_TC_EN_TC M_PERFCTL_MT_EN(2)
+#define M_PERFCTL_TCID(tcid) ((tcid) << 22)
+#define M_PERFCTL_WIDE (1 << 30)
+#define M_PERFCTL_MORE (1 << 31)
+#define M_PERFCTL_TC (1 << 30)
+
+#define M_PERFCTL_COUNT_EVENT_WHENEVER (M_PERFCTL_EXL | \
+ M_PERFCTL_KERNEL | \
+ M_PERFCTL_USER | \
+ M_PERFCTL_SUPERVISOR | \
+ M_PERFCTL_INTERRUPT_ENABLE)
+
+#ifdef CONFIG_MIPS_MT_SMP
+#define M_PERFCTL_CONFIG_MASK 0x3fff801f
+#else
+#define M_PERFCTL_CONFIG_MASK 0x1f
+#endif
+#define M_PERFCTL_EVENT_MASK 0xfe0
+
+#define vpe_id() 0
+
+/* To get current TCID*/
+#define read_c0_tcbind() __read_32bit_c0_register($2, 2)
+
+struct cpu_hw_events {
+ unsigned int config_base[MIPS_MAX_HWEVENTS];
+ unsigned int saved_ctrl[MIPS_MAX_HWEVENTS];
+};
+
+DEFINE_PER_CPU(struct cpu_hw_events, cpu_hw_events) = {
+ .config_base = {
+ 0, 0, 0, 0}, .saved_ctrl = {
+0, 0, 0, 0},};
+
+static enum cpu_type_enum mips_get_ic(void)
+{
+ unsigned int value = current_cpu_type();
+
+ /* pr_debug("ic value: %X\n", value); */
+ return value;
+}
+
+static int __n_counters(void)
+{
+ if (!(read_c0_config1() & M_CONFIG1_PC))
+ return 0;
+ if (!(read_c0_perfctrl0() & M_PERFCTL_MORE))
+ return 1;
+ if (!(read_c0_perfctrl1() & M_PERFCTL_MORE))
+ return 2;
+ if (!(read_c0_perfctrl2() & M_PERFCTL_MORE))
+ return 3;
+
+ return 4;
+}
+
+static int n_counters(void)
+{
+ int counters;
+
+ switch (current_cpu_type()) {
+ case CPU_R10000:
+ counters = 2;
+ break;
+ case CPU_R12000:
+ case CPU_R14000:
+ counters = 4;
+ break;
+ default:
+ counters = __n_counters();
+ break;
+ }
+
+ return counters;
+}
+
+static int mips_pmu_hw_get_counters(void)
+{
+ int count = n_counters();
+
+ /* pr_debug("pmu hw event nr: %d\n", count); */
+ return count;
+}
+
+static unsigned int mipsxx_pmu_swizzle_perf_idx(unsigned int idx)
+{
+ if (vpe_id() == 1)
+ idx = (idx + 2) & 3;
+ return idx;
+}
+
+static void mipsxx_pmu_write_counter(unsigned int idx, u64 val)
+{
+ idx = mipsxx_pmu_swizzle_perf_idx(idx);
+
+ switch (idx) {
+ case 0:
+ write_c0_perfcntr0(val);
+ return;
+ case 1:
+ write_c0_perfcntr1(val);
+ return;
+ case 2:
+ write_c0_perfcntr2(val);
+ return;
+ case 3:
+ write_c0_perfcntr3(val);
+ return;
+ }
+}
+
+static u64 mipsxx_pmu_read_counter(unsigned int idx)
+{
+ idx = mipsxx_pmu_swizzle_perf_idx(idx);
+
+ switch (idx) {
+ case 0:
+ /*
+ * The counters are unsigned, we must cast to truncate
+ * off the high bits.
+ */
+ return (u32) read_c0_perfcntr0();
+ case 1:
+ return (u32) read_c0_perfcntr1();
+ case 2:
+ return (u32) read_c0_perfcntr2();
+ case 3:
+ return (u32) read_c0_perfcntr3();
+ default:
+ WARN_ONCE(1, "Invalid performance counter number (%d)\n", idx);
+ return 0;
+ }
+}
+
+
+static unsigned int mipsxx_pmu_read_control(unsigned int idx)
+{
+ idx = mipsxx_pmu_swizzle_perf_idx(idx);
+
+ switch (idx) {
+ case 0:
+ return read_c0_perfctrl0();
+ case 1:
+ return read_c0_perfctrl1();
+ case 2:
+ return read_c0_perfctrl2();
+ case 3:
+ return read_c0_perfctrl3();
+ default:
+ WARN_ONCE(1, "Invalid performance counter number (%d)\n", idx);
+ return 0;
+ }
+}
+
+static void mipsxx_pmu_write_control(unsigned int idx, unsigned int val)
+{
+ idx = mipsxx_pmu_swizzle_perf_idx(idx);
+
+ switch (idx) {
+ case 0:
+ write_c0_perfctrl0(val);
+ return;
+ case 1:
+ write_c0_perfctrl1(val);
+ return;
+ case 2:
+ write_c0_perfctrl2(val);
+ return;
+ case 3:
+ write_c0_perfctrl3(val);
+ return;
+ }
+}
+
+static int mipsxx_pmu_get_vpeid(void)
+{
+ return read_c0_tcbind() & 0xF;
+}
+
+static void mipsxx_pmu_reset_counters(int idx)
+{
+ switch (idx) {
+ case 3:
+ mipsxx_pmu_write_control(3, 0);
+ mipsxx_pmu_write_counter(3, 0);
+ break;
+ case 2:
+ mipsxx_pmu_write_control(2, 0);
+ mipsxx_pmu_write_counter(2, 0);
+ break;
+ case 1:
+ mipsxx_pmu_write_control(1, 0);
+ mipsxx_pmu_write_counter(1, 0);
+ break;
+ case 0:
+ mipsxx_pmu_write_control(0, 0);
+ mipsxx_pmu_write_counter(0, 0);
+ break;
+ }
+}
+
+static void mipsxx_pmu_enable_event(int idx, int event)
+{
+ struct cpu_hw_events *cpuc = this_cpu_ptr(&cpu_hw_events);
+ unsigned long flags;
+
+ WARN_ON(idx < 0 || idx >= chip->max_hw_events);
+ cpuc->saved_ctrl[idx] = M_PERFCTL_EVENT(event & 0xff) |
+ M_PERFCTL_VPEID(mipsxx_pmu_get_vpeid()) |
+ (cpuc->config_base[idx] & M_PERFCTL_CONFIG_MASK);
+#ifdef CONFIG_CPU_BMIPS5000
+ /* if (IS_ENABLED(CONFIG_CPU_BMIPS5000)) */
+ /* enable the counter for the calling thread */
+ cpuc->saved_ctrl[idx] |= (1 << (12 + vpe_id())) | M_PERFCTL_TC;
+#endif
+ /*
+ * To enable pmu count
+ */
+ local_irq_save(flags);
+ mipsxx_pmu_write_control(idx, cpuc->saved_ctrl[idx]);
+ local_irq_restore(flags);
+}
+
+static void mipsxx_pmu_disable_event(int idx)
+{
+ struct cpu_hw_events *cpuc = this_cpu_ptr(&cpu_hw_events);
+ unsigned long flags;
+
+ /* WARN_ON(idx < 0 || idx >= mipspmu.num_counters); */
+ WARN_ON(idx < 0 || idx >= chip->max_hw_events);
+
+ local_irq_save(flags);
+ cpuc->saved_ctrl[idx] = mipsxx_pmu_read_control(idx) & ~M_PERFCTL_COUNT_EVENT_WHENEVER;
+ mipsxx_pmu_write_control(idx, cpuc->saved_ctrl[idx]);
+ local_irq_restore(flags);
+}
+
+static int mips_pmu_hw_get_event_desc(int idx, int event, char *event_desc)
+{
+ int i;
+
+ if (event_desc == NULL) {
+ pr_debug("event_desc is NULL\n");
+ return -1;
+ }
+
+ for (i = 0; i < chip->max_reg_count; i++) {
+ if (chip->desc[idx][i].event == event) {
+ strncpy(event_desc, chip->desc[idx][i].name, MXSIZE_PMU_DESC - 1);
+ break;
+ }
+ }
+ if (i == chip->max_reg_count)
+ return -1;
+
+ return 0;
+}
+
+
+static int mips_pmu_hw_check_event(struct met_pmu *pmu, int idx, int event)
+{
+ int i;
+
+ /* to check index over run */
+ if (!chip)
+ return -1;
+
+ if (idx >= chip->max_hw_events)
+ return -1;
+
+ for (i = 0; i < chip->max_reg_count; i++) {
+ if (chip->desc[idx][i].event == event)
+ break;
+ }
+ if (i == chip->max_reg_count)
+ return -1;
+
+ return 0;
+}
+
+static void mips_pmu_hw_start(struct met_pmu *pmu, int count)
+{
+ int i;
+ int generic = count - 1;
+ struct cpu_hw_events *cpuc = this_cpu_ptr(&cpu_hw_events);
+
+ /* pr_debug("hw_start generic: %d\n", generic); */
+ for (i = 0; i < generic; i++) {
+ /* init config */
+ cpuc->config_base[i] = 0;
+ cpuc->config_base[i] |= M_TC_EN_VPE;
+ cpuc->config_base[i] |= M_PERFCTL_USER;
+ cpuc->config_base[i] |= M_PERFCTL_KERNEL;
+ cpuc->config_base[i] |= M_PERFCTL_EXL;
+ cpuc->config_base[i] |= M_PERFCTL_SUPERVISOR;
+ cpuc->config_base[i] &= M_PERFCTL_CONFIG_MASK;
+ /**/ mipsxx_pmu_reset_counters(i);
+ if (pmu[i].mode == MODE_POLLING)
+ mipsxx_pmu_enable_event(i, pmu[i].event);
+ }
+ if (pmu[count - 1].mode == MODE_POLLING)
+ pr_debug("%s %d BUG!!! index over run!!\n", __func__, __LINE__);
+}
+
+static void mips_pmu_hw_stop(int count)
+{
+ int idx = 0;
+ int generic = count - 1;
+ /* pr_debug("reset %d\n", generic); */
+ for (idx = 0; idx < generic; idx++) {
+ mipsxx_pmu_reset_counters(idx);
+ mipsxx_pmu_disable_event(idx);
+ }
+}
+
+
+static unsigned int mips_pmu_hw_polling(struct met_pmu *pmu, int count, unsigned int *pmu_value)
+{
+ int i, cnt = 0;
+ int generic = count - 1;
+
+ for (i = 0; i < generic; i++) {
+ if (pmu[i].mode == MODE_POLLING) {
+ pmu_value[cnt] = mipsxx_pmu_read_counter(i);
+ cnt++;
+ mipsxx_pmu_reset_counters(i);
+ mipsxx_pmu_enable_event(i, pmu[i].event);
+ }
+ }
+ if (pmu[count - 1].mode == MODE_POLLING) {
+ pr_debug("%s %d BUG!!! index over run!!\n", __func__, __LINE__);
+ pmu_value[cnt] = 0xFFFF;
+ cnt++;
+ }
+
+ return cnt;
+}
+
+
+
+struct cpu_pmu_hw mips_pmu = {
+ .name = "mips_pmu",
+ .get_event_desc = mips_pmu_hw_get_event_desc,
+ .check_event = mips_pmu_hw_check_event,
+ .start = mips_pmu_hw_start,
+ .stop = mips_pmu_hw_stop,
+ .polling = mips_pmu_hw_polling,
+};
+
+struct cpu_pmu_hw *cpu_pmu_hw_init(void)
+{
+ int i = 0;
+ enum cpu_type_enum type;
+ int pmu_hw_count = 0;
+
+ type = mips_get_ic();
+
+ if (CPU_UNKNOWN == type || CPU_LAST == type) {
+ chip = &chip_unknown;
+ return NULL;
+ }
+ for (i = 0; i < CHIP_PMU_COUNT; i++) {
+ if (chips[i].type == type) {
+ chip = &(chips[i]);
+ break;
+ }
+ }
+ if (i == CHIP_PMU_COUNT) {
+ chip = &chip_unknown;
+ return NULL;
+ }
+
+ pmu_hw_count = mips_pmu_hw_get_counters();
+ for (i = 0; i < pmu_hw_count; i++)
+ chip->desc[i] = chip->refptr + (chip->pmu_desc_size * i);
+
+ mips_pmu.nr_cnt = pmu_hw_count + 1;
+ mips_pmu.cpu_name = chip->cpu_name;
+ return &mips_pmu;
+}