perf: aarch32: support arm pmu [1/1]
authorHanjie Lin <hanjie.lin@amlogic.com>
Mon, 15 Oct 2018 11:27:49 +0000 (19:27 +0800)
committerJianxin Pan <jianxin.pan@amlogic.com>
Thu, 18 Oct 2018 01:46:36 +0000 (18:46 -0700)
PD#SWPL-631

Problem:
32bit_kernel CtsSimpleperfTestCases modules 39 fail

Solution:
modify dts for arm pmu
port pmu interrupt issue fixup code
http://scgit.amlogic.com:8080/#/c/47552/

Verify:
p212 perf test ok

Change-Id: Icd31d645c8af0ca64e898251ffe8dbb0469e47e7
Signed-off-by: Hanjie Lin <hanjie.lin@amlogic.com>
arch/arm/boot/dts/amlogic/mesonaxg.dtsi
arch/arm/boot/dts/amlogic/mesong12a.dtsi
arch/arm/boot/dts/amlogic/mesongxl.dtsi
arch/arm/boot/dts/amlogic/mesontxlx.dtsi
arch/arm/include/asm/hardirq.h
arch/arm/include/asm/perf_event.h
arch/arm/kernel/perf_event_v7.c
arch/arm/kernel/smp.c

index b7e32f4..63b8bd3 100644 (file)
                bit_resolution=<0>;
        };
        arm_pmu {
-               compatible = "arm,armv8-pmuv3";
+               compatible = "arm,cortex-a15-pmu";
                interrupts = <0 137 4>;
                reg = <0xff634400 0x1000>;
 
index d339a84..8e611df 100644 (file)
                bit_resolution=<0>;
        };
        arm_pmu {
-               compatible = "arm,armv8-pmuv3";
+               compatible = "arm,cortex-a15-pmu";
                interrupts = <0 137 4>;
                reg = <0xff634400 0x1000>;
 
index 88f08e4..4252865 100644 (file)
                };
 
        arm_pmu {
-               compatible = "arm,armv8-pmuv3";
+               compatible = "arm,cortex-a15-pmu";
                interrupts = <0 137 4>;
                reg = <0xc8834400 0x1000>;
 
index 9989091..b847018 100644 (file)
        };
 
        arm_pmu {
-               compatible = "arm,armv8-pmuv3";
+               compatible = "arm,cortex-a15-pmu";
                interrupts = <0 137 4>;
                reg = <0xff634400 0x1000>;
 
index 3d7351c..f81292d 100644 (file)
@@ -5,7 +5,11 @@
 #include <linux/threads.h>
 #include <asm/irq.h>
 
+#ifdef CONFIG_AMLOGIC_MODIFY
+#define NR_IPI 8
+#else
 #define NR_IPI 7
+#endif
 
 typedef struct {
        unsigned int __softirq_pending;
index 4f9dec4..0142b04 100644 (file)
 #ifndef __ARM_PERF_EVENT_H__
 #define __ARM_PERF_EVENT_H__
 
+#ifdef CONFIG_AMLOGIC_MODIFY
+#include <linux/hrtimer.h>
+#endif
+
 #ifdef CONFIG_PERF_EVENTS
 struct pt_regs;
 extern unsigned long perf_instruction_pointer(struct pt_regs *regs);
@@ -26,4 +30,50 @@ extern unsigned long perf_misc_flags(struct pt_regs *regs);
        (regs)->ARM_cpsr = SVC_MODE; \
 }
 
+#ifdef CONFIG_AMLOGIC_MODIFY
+
+extern void armv8pmu_handle_irq_ipi(void);
+
+struct amlpmu_fixup_cpuinfo {
+       int irq_num;
+
+       int fix_done;
+
+       unsigned long irq_cnt;
+       unsigned long empty_irq_cnt;
+
+       unsigned long irq_time;
+       unsigned long empty_irq_time;
+
+       unsigned long last_irq_cnt;
+       unsigned long last_empty_irq_cnt;
+
+       unsigned long last_irq_time;
+       unsigned long last_empty_irq_time;
+};
+
+struct amlpmu_fixup_context {
+       struct amlpmu_fixup_cpuinfo __percpu *cpuinfo;
+
+       /* struct arm_pmu */
+       void *dev;
+
+       /* sys_cpu_status0 reg */
+       unsigned int *sys_cpu_status0;
+
+       /*
+        * In main pmu irq route wait for other cpu fix done may cause lockup,
+        * when lockup we disable main irq for a while.
+        * relax_timer will enable main irq again.
+        */
+       struct hrtimer relax_timer;
+
+       /* dts prop */
+       unsigned int sys_cpu_status0_offset;
+       unsigned int sys_cpu_status0_pmuirq_mask;
+       unsigned int relax_timer_ns;
+       unsigned int max_wait_cnt;
+};
+#endif
+
 #endif /* __ARM_PERF_EVENT_H__ */
index b942349..e811beb 100644 (file)
 
 #ifdef CONFIG_CPU_V7
 
+#ifdef CONFIG_AMLOGIC_MODIFY
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+#endif
+
 #include <asm/cp15.h>
 #include <asm/cputype.h>
 #include <asm/irq_regs.h>
 #include <linux/perf/arm_pmu.h>
 #include <linux/platform_device.h>
 
+#ifdef CONFIG_AMLOGIC_MODIFY
+#include <linux/of.h>
+#include <linux/of_irq.h>
+#include <linux/of_device.h>
+#include <linux/irq.h>
+#include <asm/irq.h>
+#include <linux/interrupt.h>
+#include <linux/irqdesc.h>
+#include <linux/of_address.h>
+#include <linux/delay.h>
+#endif
+
+
 /*
  * Common ARMv7 event types
  *
@@ -945,6 +962,164 @@ static void armv7pmu_disable_event(struct perf_event *event)
        raw_spin_unlock_irqrestore(&events->pmu_lock, flags);
 }
 
+#ifdef CONFIG_AMLOGIC_MODIFY
+static struct amlpmu_fixup_context amlpmu_fixup_ctx;
+
+static enum hrtimer_restart amlpmu_relax_timer_func(struct hrtimer *timer)
+{
+       struct amlpmu_fixup_cpuinfo *ci;
+
+       ci = per_cpu_ptr(amlpmu_fixup_ctx.cpuinfo, 0);
+
+       pr_alert("enable cpu0_irq %d again, irq cnt = %lu\n",
+               ci->irq_num,
+               ci->irq_cnt);
+       enable_irq(ci->irq_num);
+
+       return HRTIMER_NORESTART;
+}
+
+
+static void amlpmu_relax_timer_start(int other_cpu)
+{
+       struct amlpmu_fixup_cpuinfo *ci;
+       int cpu;
+
+       cpu = smp_processor_id();
+       WARN_ON(cpu != 0);
+
+       ci = per_cpu_ptr(amlpmu_fixup_ctx.cpuinfo, 0);
+
+       pr_alert("wait cpu %d fixup done timeout, main cpu irq cnt = %lu\n",
+                       other_cpu,
+                       ci->irq_cnt);
+
+       if (hrtimer_active(&amlpmu_fixup_ctx.relax_timer)) {
+               pr_alert("relax_timer already active, return!\n");
+               return;
+       }
+
+       disable_irq_nosync(ci->irq_num);
+
+       hrtimer_start(&amlpmu_fixup_ctx.relax_timer,
+               ns_to_ktime(amlpmu_fixup_ctx.relax_timer_ns),
+               HRTIMER_MODE_REL);
+}
+
+static irqreturn_t armv7pmu_handle_irq(int irq_num, void *dev);
+
+void armv8pmu_handle_irq_ipi(void)
+{
+       int cpu = smp_processor_id();
+
+       WARN_ON(cpu == 0);
+       WARN_ON(!amlpmu_fixup_ctx.dev);
+
+       armv7pmu_handle_irq(-1, amlpmu_fixup_ctx.dev);
+}
+
+static int aml_pmu_fix(void)
+{
+       int i;
+       int cpu;
+       int pmuirq_val;
+       struct amlpmu_fixup_cpuinfo *ci;
+
+       int max_wait_cnt = amlpmu_fixup_ctx.max_wait_cnt;
+
+       pmuirq_val = readl(amlpmu_fixup_ctx.sys_cpu_status0);
+       pmuirq_val &= amlpmu_fixup_ctx.sys_cpu_status0_pmuirq_mask;
+
+       for (cpu = 0; cpu < num_possible_cpus(); cpu++) {
+               if (pmuirq_val & (1<<cpu)) {
+                       if (cpu == 0) {
+                               pr_debug("cpu0 shouldn't fix pmuirq = 0x%x\n",
+                                       pmuirq_val);
+                       } else {
+                               pr_debug("fix pmu irq cpu %d, pmuirq = 0x%x\n",
+                                       cpu,
+                                       pmuirq_val);
+
+                               ci = per_cpu_ptr(amlpmu_fixup_ctx.cpuinfo,
+                                       cpu);
+
+                               ci->fix_done = 0;
+
+                               /* aml pmu IPI will set fix_done to 1 */
+                               mb();
+
+                               smp_send_aml_pmu(cpu);
+
+                               for (i = 0; i < max_wait_cnt; i++) {
+                                       if (READ_ONCE(ci->fix_done))
+                                               break;
+
+                                       udelay(1);
+                               }
+
+                               if (i == amlpmu_fixup_ctx.max_wait_cnt)
+                                       amlpmu_relax_timer_start(cpu);
+
+                               return 0;
+                       }
+               }
+       }
+
+       return 1;
+}
+
+static void aml_pmu_fix_stat_account(int is_empty_irq)
+{
+       int freq;
+       unsigned long time = jiffies;
+       struct amlpmu_fixup_cpuinfo *ci;
+
+       ci = this_cpu_ptr(amlpmu_fixup_ctx.cpuinfo);
+
+       ci->irq_cnt++;
+       ci->irq_time = time;
+       if (!ci->last_irq_cnt) {
+               ci->last_irq_cnt = ci->irq_cnt;
+               ci->last_irq_time = ci->irq_time;
+       }
+
+       if (is_empty_irq) {
+               ci->empty_irq_cnt++;
+               ci->empty_irq_time = time;
+               if (!ci->last_empty_irq_cnt) {
+                       ci->last_empty_irq_cnt = ci->empty_irq_cnt;
+                       ci->last_empty_irq_time = ci->empty_irq_time;
+               }
+       }
+
+       if (time_after(ci->irq_time, ci->last_irq_time + HZ)) {
+               freq = ci->irq_cnt - ci->last_irq_cnt;
+               freq = freq * HZ / (ci->irq_time - ci->last_irq_time);
+               pr_debug("irq_cnt = %lu, irq_last_cnt = %lu, freq = %d\n",
+                       ci->irq_cnt,
+                       ci->last_irq_cnt,
+                       freq);
+
+               ci->last_irq_cnt = ci->irq_cnt;
+               ci->last_irq_time = ci->irq_time;
+       }
+
+       if (is_empty_irq &&
+               time_after(ci->empty_irq_time, ci->last_empty_irq_time + HZ)) {
+
+               freq = ci->empty_irq_cnt - ci->last_empty_irq_cnt;
+               freq *= HZ;
+               freq /= (ci->empty_irq_time - ci->last_empty_irq_time);
+               pr_debug("empty_irq_cnt = %lu, freq = %d\n",
+                       ci->empty_irq_cnt,
+                       freq);
+
+               ci->last_empty_irq_cnt = ci->empty_irq_cnt;
+               ci->last_empty_irq_time = ci->empty_irq_time;
+       }
+}
+#endif
+
 static irqreturn_t armv7pmu_handle_irq(int irq_num, void *dev)
 {
        u32 pmnc;
@@ -954,17 +1129,47 @@ static irqreturn_t armv7pmu_handle_irq(int irq_num, void *dev)
        struct pt_regs *regs;
        int idx;
 
+#ifdef CONFIG_AMLOGIC_MODIFY
+       int cpu;
+       int is_empty_irq = 0;
+       struct amlpmu_fixup_cpuinfo *ci;
+
+       ci = this_cpu_ptr(amlpmu_fixup_ctx.cpuinfo);
+       ci->irq_num = irq_num;
+       amlpmu_fixup_ctx.dev = dev;
+       cpu = smp_processor_id();
+#endif
+
        /*
         * Get and reset the IRQ flags
         */
        pmnc = armv7_pmnc_getreset_flags();
 
+#ifdef CONFIG_AMLOGIC_MODIFY
+       ci->fix_done = 1;
+#endif
+
        /*
         * Did an overflow occur?
         */
+#ifdef CONFIG_AMLOGIC_MODIFY
+       if (!armv7_pmnc_has_overflowed(pmnc)) {
+               is_empty_irq = 1;
+
+               if (cpu == 0)
+                       is_empty_irq = aml_pmu_fix();
+       }
+
+       aml_pmu_fix_stat_account(is_empty_irq);
+
+       /* txlx have some empty pmu irqs, so return IRQ_HANDLED */
+       if (is_empty_irq)
+               return IRQ_HANDLED;
+#else
        if (!armv7_pmnc_has_overflowed(pmnc))
                return IRQ_NONE;
 
+#endif
        /*
         * Handle the counter(s) overflow(s)
         */
@@ -2019,9 +2224,88 @@ static const struct pmu_probe_info armv7_pmu_probe_table[] = {
        { /* sentinel value */ }
 };
 
+#ifdef CONFIG_AMLOGIC_MODIFY
+static int amlpmu_fixup_init(struct platform_device *pdev)
+{
+       int ret;
+       void __iomem *base;
+
+       amlpmu_fixup_ctx.cpuinfo = __alloc_percpu(
+               sizeof(struct amlpmu_fixup_cpuinfo), 2 * sizeof(void *));
+       if (!amlpmu_fixup_ctx.cpuinfo) {
+               pr_err("alloc percpu failed\n");
+               return -ENOMEM;
+       }
+
+       base = of_iomap(pdev->dev.of_node, 0);
+       if (IS_ERR(base)) {
+               pr_err("of_iomap() failed, base = %p\n", base);
+               return PTR_ERR(base);
+       }
+
+       ret = of_property_read_u32(pdev->dev.of_node,
+               "sys_cpu_status0_offset",
+               &amlpmu_fixup_ctx.sys_cpu_status0_offset);
+       if (ret) {
+               pr_err("read sys_cpu_status0_offset failed, ret = %d\n", ret);
+               return 1;
+       }
+       pr_debug("sys_cpu_status0_offset = 0x%0x\n",
+               amlpmu_fixup_ctx.sys_cpu_status0_offset);
+
+       ret = of_property_read_u32(pdev->dev.of_node,
+               "sys_cpu_status0_pmuirq_mask",
+               &amlpmu_fixup_ctx.sys_cpu_status0_pmuirq_mask);
+       if (ret) {
+               pr_err("read sys_cpu_status0_pmuirq_mask failed, ret = %d\n",
+                       ret);
+               return 1;
+       }
+       pr_debug("sys_cpu_status0_pmuirq_mask = 0x%0x\n",
+               amlpmu_fixup_ctx.sys_cpu_status0_pmuirq_mask);
+
+
+       ret = of_property_read_u32(pdev->dev.of_node,
+               "relax_timer_ns",
+               &amlpmu_fixup_ctx.relax_timer_ns);
+       if (ret) {
+               pr_err("read prop relax_timer_ns failed, ret = %d\n", ret);
+               return 1;
+       }
+       pr_debug("relax_timer_ns = %u\n", amlpmu_fixup_ctx.relax_timer_ns);
+
+
+       ret = of_property_read_u32(pdev->dev.of_node,
+               "max_wait_cnt",
+               &amlpmu_fixup_ctx.max_wait_cnt);
+       if (ret) {
+               pr_err("read prop max_wait_cnt failed, ret = %d\n", ret);
+               return 1;
+       }
+       pr_debug("max_wait_cnt = %u\n", amlpmu_fixup_ctx.max_wait_cnt);
+
+
+       base += (amlpmu_fixup_ctx.sys_cpu_status0_offset << 2);
+       amlpmu_fixup_ctx.sys_cpu_status0 = base;
+       pr_debug("sys_cpu_status0 = %p\n", amlpmu_fixup_ctx.sys_cpu_status0);
+
+
+       hrtimer_init(&amlpmu_fixup_ctx.relax_timer,
+               CLOCK_MONOTONIC,
+               HRTIMER_MODE_REL);
+       amlpmu_fixup_ctx.relax_timer.function = amlpmu_relax_timer_func;
+
+       return 0;
+}
+#endif
 
 static int armv7_pmu_device_probe(struct platform_device *pdev)
 {
+#ifdef CONFIG_AMLOGIC_MODIFY
+       if (amlpmu_fixup_init(pdev))
+               return 1;
+#endif
+
        return arm_pmu_device_probe(pdev, armv7_pmu_of_device_ids,
                                    armv7_pmu_probe_table);
 }
index 7dd14e8..f75378c 100644 (file)
 #include <asm/mach/arch.h>
 #include <asm/mpu.h>
 
+#ifdef CONFIG_AMLOGIC_MODIFY
+#include <asm/perf_event.h>
+#endif
+
 #define CREATE_TRACE_POINTS
 #include <trace/events/ipi.h>
 
@@ -72,6 +76,9 @@ enum ipi_msg_type {
        IPI_CPU_STOP,
        IPI_IRQ_WORK,
        IPI_COMPLETION,
+       #ifdef CONFIG_AMLOGIC_MODIFY
+       IPI_AML_PMU,
+       #endif
        IPI_CPU_BACKTRACE,
        /*
         * SGI8-15 can be reserved by secure firmware, and thus may
@@ -482,6 +489,9 @@ static const char *ipi_types[NR_IPI] __tracepoint_string = {
        S(IPI_CPU_STOP, "CPU stop interrupts"),
        S(IPI_IRQ_WORK, "IRQ work interrupts"),
        S(IPI_COMPLETION, "completion interrupts"),
+#ifdef CONFIG_AMLOGIC_MODIFY
+       S(IPI_AML_PMU, "AML pmu cross interrupts"),
+#endif
 };
 
 static void smp_cross_call(const struct cpumask *target, unsigned int ipinr)
@@ -490,6 +500,13 @@ static void smp_cross_call(const struct cpumask *target, unsigned int ipinr)
        __smp_cross_call(target, ipinr);
 }
 
+#ifdef CONFIG_AMLOGIC_MODIFY
+void smp_send_aml_pmu(int cpu)
+{
+       smp_cross_call(cpumask_of(cpu), IPI_AML_PMU);
+}
+#endif
+
 void show_ipi_list(struct seq_file *p, int prec)
 {
        unsigned int cpu, i;
@@ -651,6 +668,12 @@ void handle_IPI(int ipinr, struct pt_regs *regs)
                printk_nmi_exit();
                break;
 
+#ifdef CONFIG_AMLOGIC_MODIFY
+       case IPI_AML_PMU:
+               armv8pmu_handle_irq_ipi();
+               break;
+#endif
+
        default:
                pr_crit("CPU%u: Unknown IPI message 0x%x\n",
                        cpu, ipinr);