From 33bf6bb6d18d5e60f9b4fa347c7805d724b0275f Mon Sep 17 00:00:00 2001 From: Hanjie Lin Date: Fri, 14 Dec 2018 19:39:58 +0800 Subject: [PATCH] perf_event: aml pmu interrupts routing on g12b [1/1] PD#SWPL-3088 Problem: g12b big-little cluster is different from other SoC with pmu interrupts and registers. software modifications must adapt to the difference. Solution: modify Verify: u200 w400 Change-Id: If9217c1025dff5c17d51790f8c216e31b7d6532b Signed-off-by: Hanjie Lin --- arch/arm/boot/dts/amlogic/mesonaxg.dtsi | 19 +- arch/arm/boot/dts/amlogic/mesong12a.dtsi | 18 +- arch/arm/boot/dts/amlogic/mesong12b.dtsi | 16 +- arch/arm/boot/dts/amlogic/mesongxl.dtsi | 17 +- .../arm/boot/dts/amlogic/mesongxl_sei210.dtsi | 17 +- arch/arm/boot/dts/amlogic/mesongxm.dtsi | 17 +- arch/arm/boot/dts/amlogic/mesontl1.dtsi | 15 +- arch/arm/boot/dts/amlogic/mesontxl.dtsi | 15 +- arch/arm/boot/dts/amlogic/mesontxlx.dtsi | 17 +- arch/arm/include/asm/hardirq.h | 4 - arch/arm/include/asm/perf_event.h | 47 -- arch/arm/kernel/perf_event_v7.c | 286 +--------- arch/arm/kernel/smp.c | 23 - arch/arm64/boot/dts/amlogic/mesonaxg.dtsi | 19 +- arch/arm64/boot/dts/amlogic/mesong12a.dtsi | 22 +- arch/arm64/boot/dts/amlogic/mesong12b.dtsi | 12 +- arch/arm64/boot/dts/amlogic/mesongxl.dtsi | 18 +- .../boot/dts/amlogic/mesongxl_sei210.dtsi | 13 +- arch/arm64/boot/dts/amlogic/mesongxm.dtsi | 13 +- arch/arm64/boot/dts/amlogic/mesontl1.dtsi | 15 +- arch/arm64/boot/dts/amlogic/mesontxl.dtsi | 13 +- arch/arm64/boot/dts/amlogic/mesontxlx.dtsi | 17 +- arch/arm64/include/asm/hardirq.h | 4 - arch/arm64/include/asm/perf_event.h | 44 -- arch/arm64/kernel/perf_event.c | 285 +--------- arch/arm64/kernel/smp.c | 25 - drivers/perf/arm_pmu.c | 518 ++++++++++++++++++ include/linux/perf/arm_pmu.h | 93 ++++ include/linux/smp.h | 7 - 29 files changed, 772 insertions(+), 857 deletions(-) diff --git a/arch/arm/boot/dts/amlogic/mesonaxg.dtsi b/arch/arm/boot/dts/amlogic/mesonaxg.dtsi index 0aa846dc48d2..3d67b2da266b 100644 --- a/arch/arm/boot/dts/amlogic/mesonaxg.dtsi +++ b/arch/arm/boot/dts/amlogic/mesonaxg.dtsi @@ -119,24 +119,19 @@ bit_mode=<12>; bit_resolution=<0>; }; + arm_pmu { compatible = "arm,cortex-a15-pmu"; - interrupts = <0 137 4>; - reg = <0xff634400 0x1000>; - - /* addr = base + offset << 2 */ - sys_cpu_status0_offset = <0xa0>; - - sys_cpu_status0_pmuirq_mask = <0xf>; - + /* clusterb-enabled; */ + interrupts = ; + reg = <0xff634680 0x4>; + cpumasks = <0xf>; /* default 10ms */ - relax_timer_ns = <10000000>; - + relax-timer-ns = <10000000>; /* default 10000us */ - max_wait_cnt = <10000>; + max-wait-cnt = <10000>; }; - gic: interrupt-controller@2c001000 { compatible = "arm,cortex-a15-gic", "arm,cortex-a9-gic"; #interrupt-cells = <3>; diff --git a/arch/arm/boot/dts/amlogic/mesong12a.dtsi b/arch/arm/boot/dts/amlogic/mesong12a.dtsi index 7282425de64e..d75f4bde2803 100644 --- a/arch/arm/boot/dts/amlogic/mesong12a.dtsi +++ b/arch/arm/boot/dts/amlogic/mesong12a.dtsi @@ -155,21 +155,17 @@ bit_mode=<12>; bit_resolution=<0>; }; + arm_pmu { compatible = "arm,cortex-a15-pmu"; - interrupts = <0 137 4>; - reg = <0xff634400 0x1000>; - - /* addr = base + offset << 2 */ - sys_cpu_status0_offset = <0xa0>; - - sys_cpu_status0_pmuirq_mask = <0xf>; - + /* clusterb-enabled; */ + interrupts = ; + reg = <0xff634680 0x4>; + cpumasks = <0xf>; /* default 10ms */ - relax_timer_ns = <10000000>; - + relax-timer-ns = <10000000>; /* default 10000us */ - max_wait_cnt = <10000>; + max-wait-cnt = <10000>; }; gic: interrupt-controller@2c001000 { diff --git a/arch/arm/boot/dts/amlogic/mesong12b.dtsi b/arch/arm/boot/dts/amlogic/mesong12b.dtsi index cfca75cbef31..876b1e76e131 100644 --- a/arch/arm/boot/dts/amlogic/mesong12b.dtsi +++ b/arch/arm/boot/dts/amlogic/mesong12b.dtsi @@ -196,7 +196,7 @@ }; timer { - compatible = "arm,armv8-timer"; + compatible = "arm,armv7-timer"; interrupts = , , , @@ -215,9 +215,19 @@ bit_mode=<12>; bit_resolution=<0>; }; + arm_pmu { - compatible = "arm,armv8-pmuv3"; - interrupts = <0 137 4>; + compatible = "arm,cortex-a15-pmu"; + clusterb-enabled; + interrupts = , + ; + reg = <0xff634680 0x4>, + <0xff6347c0 0x04>; + cpumasks = <0x3 0x3C>; + /* default 10ms */ + relax-timer-ns = <10000000>; + /* default 10000us */ + max-wait-cnt = <10000>; }; gic: interrupt-controller@2c001000 { diff --git a/arch/arm/boot/dts/amlogic/mesongxl.dtsi b/arch/arm/boot/dts/amlogic/mesongxl.dtsi index 982bae263190..d35d3b4d7723 100644 --- a/arch/arm/boot/dts/amlogic/mesongxl.dtsi +++ b/arch/arm/boot/dts/amlogic/mesongxl.dtsi @@ -134,19 +134,14 @@ arm_pmu { compatible = "arm,cortex-a15-pmu"; - interrupts = <0 137 4>; - reg = <0xc8834400 0x1000>; - - /* addr = base + offset << 2 */ - sys_cpu_status0_offset = <0xa0>; - - sys_cpu_status0_pmuirq_mask = <0xf>; - + /* clusterb-enabled; */ + interrupts = ; + reg = <0xc8834400 0x4>; + cpumasks = <0xf>; /* default 10ms */ - relax_timer_ns = <10000000>; - + relax-timer-ns = <10000000>; /* default 10000us */ - max_wait_cnt = <10000>; + max-wait-cnt = <10000>; }; gic: interrupt-controller@2c001000 { diff --git a/arch/arm/boot/dts/amlogic/mesongxl_sei210.dtsi b/arch/arm/boot/dts/amlogic/mesongxl_sei210.dtsi index 9f4e451d488d..53d782398b99 100644 --- a/arch/arm/boot/dts/amlogic/mesongxl_sei210.dtsi +++ b/arch/arm/boot/dts/amlogic/mesongxl_sei210.dtsi @@ -115,7 +115,7 @@ }; timer { - compatible = "arm,armv8-timer"; + compatible = "arm,armv7-timer"; interrupts = , , , @@ -133,12 +133,17 @@ bit_mode=<12>; bit_resolution=<0>; }; + arm_pmu { - compatible = "arm,armv8-pmuv3"; - interrupts = <0 137 4>, - <0 138 4>, - <0 153 4>, - <0 154 4>; + compatible = "arm,cortex-a15-pmu"; + /* clusterb-enabled; */ + interrupts = ; + reg = <0xc8834400 0x4>; + cpumasks = <0xf>; + /* default 10ms */ + relax-timer-ns = <10000000>; + /* default 10000us */ + max-wait-cnt = <10000>; }; gic: interrupt-controller@2c001000 { diff --git a/arch/arm/boot/dts/amlogic/mesongxm.dtsi b/arch/arm/boot/dts/amlogic/mesongxm.dtsi index 0a0fb4458925..5544ca943c6e 100644 --- a/arch/arm/boot/dts/amlogic/mesongxm.dtsi +++ b/arch/arm/boot/dts/amlogic/mesongxm.dtsi @@ -205,7 +205,7 @@ }; timer { - compatible = "arm,armv8-timer"; + compatible = "arm,armv7-timer"; interrupts = , , , @@ -223,12 +223,17 @@ bit_mode=<12>; bit_resolution=<0>; }; + arm_pmu { - compatible = "arm,armv8-pmuv3"; - interrupts = <0 137 4>, - <0 138 4>, - <0 153 4>, - <0 154 4>; + compatible = "arm,cortex-a15-pmu"; + /* clusterb-enabled; */ + interrupts = ; + reg = <0xc8834400 0x4>; + cpumasks = <0xf>; + /* default 10ms */ + relax-timer-ns = <10000000>; + /* default 10000us */ + max-wait-cnt = <10000>; }; gic: interrupt-controller@2c001000 { diff --git a/arch/arm/boot/dts/amlogic/mesontl1.dtsi b/arch/arm/boot/dts/amlogic/mesontl1.dtsi index 4d9a95000b4c..ac54aaee166a 100644 --- a/arch/arm/boot/dts/amlogic/mesontl1.dtsi +++ b/arch/arm/boot/dts/amlogic/mesontl1.dtsi @@ -135,19 +135,14 @@ arm_pmu { compatible = "arm,cortex-a15-pmu"; + /* clusterb-enabled; */ interrupts = ; - reg = <0xff634400 0x1000>; - - /* addr = base + offset << 2 */ - sys_cpu_status0_offset = <0xa0>; - - sys_cpu_status0_pmuirq_mask = <0xf>; - + reg = <0xff634680 0x4>; + cpumasks = <0xf>; /* default 10ms */ - relax_timer_ns = <10000000>; - + relax-timer-ns = <10000000>; /* default 10000us */ - max_wait_cnt = <10000>; + max-wait-cnt = <10000>; }; gic: interrupt-controller@2c001000 { diff --git a/arch/arm/boot/dts/amlogic/mesontxl.dtsi b/arch/arm/boot/dts/amlogic/mesontxl.dtsi index ed9fd929a4a2..4a9bc6595553 100644 --- a/arch/arm/boot/dts/amlogic/mesontxl.dtsi +++ b/arch/arm/boot/dts/amlogic/mesontxl.dtsi @@ -139,17 +139,16 @@ bit_resolution = <0>; }; - pmu { + arm_pmu { compatible = "arm,cortex-a15-pmu"; - interrupts = <0 137 4>; - reg = <0xc8834400 0x1000>; - /* addr = base + offset << 2 */ - sys_cpu_status0_offset = <0xa0>; - sys_cpu_status0_pmuirq_mask = <0xf>; + /* clusterb-enabled; */ + interrupts = ; + reg = <0xc8834400 0x4>; + cpumasks = <0xf>; /* default 10ms */ - relax_timer_ns = <10000000>; + relax-timer-ns = <10000000>; /* default 10000us */ - max_wait_cnt = <10000>; + max-wait-cnt = <10000>; }; psci { diff --git a/arch/arm/boot/dts/amlogic/mesontxlx.dtsi b/arch/arm/boot/dts/amlogic/mesontxlx.dtsi index 8228a79b8d92..3521f63c08c7 100644 --- a/arch/arm/boot/dts/amlogic/mesontxlx.dtsi +++ b/arch/arm/boot/dts/amlogic/mesontxlx.dtsi @@ -134,19 +134,14 @@ arm_pmu { compatible = "arm,cortex-a15-pmu"; - interrupts = <0 137 4>; - reg = <0xff634400 0x1000>; - - /* addr = base + offset << 2 */ - sys_cpu_status0_offset = <0xa0>; - - sys_cpu_status0_pmuirq_mask = <0xf>; - + /* clusterb-enabled; */ + interrupts = ; + reg = <0xff634680 0x4>; + cpumasks = <0xf>; /* default 10ms */ - relax_timer_ns = <10000000>; - + relax-timer-ns = <10000000>; /* default 10000us */ - max_wait_cnt = <10000>; + max-wait-cnt = <10000>; }; gic: interrupt-controller@2c001000 { diff --git a/arch/arm/include/asm/hardirq.h b/arch/arm/include/asm/hardirq.h index f81292dbc46e..3d7351c844aa 100644 --- a/arch/arm/include/asm/hardirq.h +++ b/arch/arm/include/asm/hardirq.h @@ -5,11 +5,7 @@ #include #include -#ifdef CONFIG_AMLOGIC_MODIFY -#define NR_IPI 8 -#else #define NR_IPI 7 -#endif typedef struct { unsigned int __softirq_pending; diff --git a/arch/arm/include/asm/perf_event.h b/arch/arm/include/asm/perf_event.h index 0142b04a4a0f..2501bd0977e6 100644 --- a/arch/arm/include/asm/perf_event.h +++ b/arch/arm/include/asm/perf_event.h @@ -29,51 +29,4 @@ extern unsigned long perf_misc_flags(struct pt_regs *regs); (regs)->ARM_sp = current_stack_pointer; \ (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__ */ diff --git a/arch/arm/kernel/perf_event_v7.c b/arch/arm/kernel/perf_event_v7.c index e811beb08e87..836efdda3d80 100644 --- a/arch/arm/kernel/perf_event_v7.c +++ b/arch/arm/kernel/perf_event_v7.c @@ -18,10 +18,6 @@ #ifdef CONFIG_CPU_V7 -#ifdef CONFIG_AMLOGIC_MODIFY -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt -#endif - #include #include #include @@ -32,19 +28,6 @@ #include #include -#ifdef CONFIG_AMLOGIC_MODIFY -#include -#include -#include -#include -#include -#include -#include -#include -#include -#endif - - /* * Common ARMv7 event types * @@ -963,160 +946,13 @@ static void armv7pmu_disable_event(struct perf_event *event) } #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); -} +#include static irqreturn_t armv7pmu_handle_irq(int irq_num, void *dev); -void armv8pmu_handle_irq_ipi(void) +void amlpmu_handle_irq_ipi(void *arg) { - 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<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; - } + armv7pmu_handle_irq(-1, amlpmu_ctx.pmu); } #endif @@ -1129,47 +965,25 @@ 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 - + /* amlpmu have routed the interrupt successfully, return IRQ_HANDLED */ + if (amlpmu_handle_irq(cpu_pmu, + irq_num, + armv7_pmnc_has_overflowed(pmnc))) + return IRQ_HANDLED; +#else /* * 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) */ @@ -2224,88 +2038,8 @@ 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); } diff --git a/arch/arm/kernel/smp.c b/arch/arm/kernel/smp.c index f75378cff21a..7dd14e8395e6 100644 --- a/arch/arm/kernel/smp.c +++ b/arch/arm/kernel/smp.c @@ -48,10 +48,6 @@ #include #include -#ifdef CONFIG_AMLOGIC_MODIFY -#include -#endif - #define CREATE_TRACE_POINTS #include @@ -76,9 +72,6 @@ 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 @@ -489,9 +482,6 @@ 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) @@ -500,13 +490,6 @@ 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; @@ -668,12 +651,6 @@ 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); diff --git a/arch/arm64/boot/dts/amlogic/mesonaxg.dtsi b/arch/arm64/boot/dts/amlogic/mesonaxg.dtsi index 318bb4d8caa9..ee04ae39a005 100644 --- a/arch/arm64/boot/dts/amlogic/mesonaxg.dtsi +++ b/arch/arm64/boot/dts/amlogic/mesonaxg.dtsi @@ -119,24 +119,19 @@ bit_mode=<12>; bit_resolution=<0>; }; + arm_pmu { compatible = "arm,armv8-pmuv3"; - interrupts = <0 137 4>; - reg = <0x0 0xff634400 0 0x1000>; - - /* addr = base + offset << 2 */ - sys_cpu_status0_offset = <0xa0>; - - sys_cpu_status0_pmuirq_mask = <0xf>; - + /* clusterb-enabled; */ + interrupts = ; + reg = <0x0 0xff634680 0x0 0x4>; + cpumasks = <0xf>; /* default 10ms */ - relax_timer_ns = <10000000>; - + relax-timer-ns = <10000000>; /* default 10000us */ - max_wait_cnt = <10000>; + max-wait-cnt = <10000>; }; - gic: interrupt-controller@2c001000 { compatible = "arm,cortex-a15-gic", "arm,cortex-a9-gic"; #interrupt-cells = <3>; diff --git a/arch/arm64/boot/dts/amlogic/mesong12a.dtsi b/arch/arm64/boot/dts/amlogic/mesong12a.dtsi index 9d7bf3706514..e24fe0445e92 100644 --- a/arch/arm64/boot/dts/amlogic/mesong12a.dtsi +++ b/arch/arm64/boot/dts/amlogic/mesong12a.dtsi @@ -155,21 +155,17 @@ bit_mode=<12>; bit_resolution=<0>; }; + arm_pmu { - compatible = "arm,armv8-pmuv3"; - interrupts = <0 137 4>; - reg = <0x0 0xff634400 0 0x1000>; - - /* addr = base + offset << 2 */ - sys_cpu_status0_offset = <0xa0>; - - sys_cpu_status0_pmuirq_mask = <0xf>; - + compatible = "arm,cortex-a15-pmu"; + /* clusterb-enabled; */ + interrupts = ; + reg = <0xff634680 0x4>; + cpumasks = <0xf>; /* default 10ms */ - relax_timer_ns = <10000000>; - + relax-timer-ns = <10000000>; /* default 10000us */ - max_wait_cnt = <10000>; + max-wait-cnt = <10000>; }; gic: interrupt-controller@2c001000 { @@ -1749,7 +1745,7 @@ max-duty-cycle = <1250>; /* Voltage Duty-Cycle */ voltage-table = <1022000 0>, - <1011000 3>, + <1011000 3>, <1001000 6>, <991000 10>, <981000 13>, diff --git a/arch/arm64/boot/dts/amlogic/mesong12b.dtsi b/arch/arm64/boot/dts/amlogic/mesong12b.dtsi index 4ddb213b5ffd..97a7f17aa327 100644 --- a/arch/arm64/boot/dts/amlogic/mesong12b.dtsi +++ b/arch/arm64/boot/dts/amlogic/mesong12b.dtsi @@ -215,9 +215,19 @@ bit_mode=<12>; bit_resolution=<0>; }; + arm_pmu { compatible = "arm,armv8-pmuv3"; - interrupts = <0 137 4>; + clusterb-enabled; + interrupts = , + ; + reg = <0x0 0xff634680 0x0 0x4>, + <0x0 0xff6347c0 0x0 0x04>; + cpumasks = <0x3 0x3C>; + /* default 10ms */ + relax-timer-ns = <10000000>; + /* default 10000us */ + max-wait-cnt = <10000>; }; gic: interrupt-controller@2c001000 { diff --git a/arch/arm64/boot/dts/amlogic/mesongxl.dtsi b/arch/arm64/boot/dts/amlogic/mesongxl.dtsi index af7117d097b4..244efa26fb60 100644 --- a/arch/arm64/boot/dts/amlogic/mesongxl.dtsi +++ b/arch/arm64/boot/dts/amlogic/mesongxl.dtsi @@ -119,21 +119,17 @@ bit_mode=<12>; bit_resolution=<0>; }; + arm_pmu { compatible = "arm,armv8-pmuv3"; - interrupts = <0 137 4>; - reg = <0x0 0xc8834400 0 0x1000>; - - /* addr = base + offset << 2 */ - sys_cpu_status0_offset = <0xa0>; - - sys_cpu_status0_pmuirq_mask = <0xf>; - + /* clusterb-enabled; */ + interrupts = ; + reg = <0x0 0xc8834400 0x0 0x4>; + cpumasks = <0xf>; /* default 10ms */ - relax_timer_ns = <10000000>; - + relax-timer-ns = <10000000>; /* default 10000us */ - max_wait_cnt = <10000>; + max-wait-cnt = <10000>; }; gic: interrupt-controller@2c001000 { diff --git a/arch/arm64/boot/dts/amlogic/mesongxl_sei210.dtsi b/arch/arm64/boot/dts/amlogic/mesongxl_sei210.dtsi index a66605342869..ef80ea74d5a3 100644 --- a/arch/arm64/boot/dts/amlogic/mesongxl_sei210.dtsi +++ b/arch/arm64/boot/dts/amlogic/mesongxl_sei210.dtsi @@ -133,12 +133,17 @@ bit_mode=<12>; bit_resolution=<0>; }; + arm_pmu { compatible = "arm,armv8-pmuv3"; - interrupts = <0 137 4>, - <0 138 4>, - <0 153 4>, - <0 154 4>; + /* clusterb-enabled; */ + interrupts = ; + reg = <0x0 0xc8834400 0x0 0x4>; + cpumasks = <0xf>; + /* default 10ms */ + relax-timer-ns = <10000000>; + /* default 10000us */ + max-wait-cnt = <10000>; }; gic: interrupt-controller@2c001000 { diff --git a/arch/arm64/boot/dts/amlogic/mesongxm.dtsi b/arch/arm64/boot/dts/amlogic/mesongxm.dtsi index 177907cdc2e7..3c66f01cdf43 100644 --- a/arch/arm64/boot/dts/amlogic/mesongxm.dtsi +++ b/arch/arm64/boot/dts/amlogic/mesongxm.dtsi @@ -223,12 +223,17 @@ bit_mode=<12>; bit_resolution=<0>; }; + arm_pmu { compatible = "arm,armv8-pmuv3"; - interrupts = <0 137 4>, - <0 138 4>, - <0 153 4>, - <0 154 4>; + /* clusterb-enabled; */ + interrupts = ; + reg = <0x0 0xc8834400 0x0 0x4>; + cpumasks = <0xf>; + /* default 10ms */ + relax-timer-ns = <10000000>; + /* default 10000us */ + max-wait-cnt = <10000>; }; gic: interrupt-controller@2c001000 { diff --git a/arch/arm64/boot/dts/amlogic/mesontl1.dtsi b/arch/arm64/boot/dts/amlogic/mesontl1.dtsi index 65c212efd999..a1b5a2eed712 100644 --- a/arch/arm64/boot/dts/amlogic/mesontl1.dtsi +++ b/arch/arm64/boot/dts/amlogic/mesontl1.dtsi @@ -135,19 +135,14 @@ arm_pmu { compatible = "arm,armv8-pmuv3"; + /* clusterb-enabled; */ interrupts = ; - reg = <0x0 0xff634400 0x0 0x1000>; - - /* addr = base + offset << 2 */ - sys_cpu_status0_offset = <0xa0>; - - sys_cpu_status0_pmuirq_mask = <0xf>; - + reg = <0x0 0xff634680 0x0 0x4>; + cpumasks = <0xf>; /* default 10ms */ - relax_timer_ns = <10000000>; - + relax-timer-ns = <10000000>; /* default 10000us */ - max_wait_cnt = <10000>; + max-wait-cnt = <10000>; }; gic: interrupt-controller@2c001000 { diff --git a/arch/arm64/boot/dts/amlogic/mesontxl.dtsi b/arch/arm64/boot/dts/amlogic/mesontxl.dtsi index 9d90ba8118f8..7b89d397ab2a 100644 --- a/arch/arm64/boot/dts/amlogic/mesontxl.dtsi +++ b/arch/arm64/boot/dts/amlogic/mesontxl.dtsi @@ -141,15 +141,14 @@ arm_pmu { compatible = "arm,armv8-pmuv3"; - interrupts = <0 137 4>; - reg = <0x0 0xc8834400 0 0x1000>; - /* addr = base + offset << 2 */ - sys_cpu_status0_offset = <0xa0>; - sys_cpu_status0_pmuirq_mask = <0xf>; + /* clusterb-enabled; */ + interrupts = ; + reg = <0x0 0xc8834400 0x0 0x4>; + cpumasks = <0xf>; /* default 10ms */ - relax_timer_ns = <10000000>; + relax-timer-ns = <10000000>; /* default 10000us */ - max_wait_cnt = <10000>; + max-wait-cnt = <10000>; }; psci { diff --git a/arch/arm64/boot/dts/amlogic/mesontxlx.dtsi b/arch/arm64/boot/dts/amlogic/mesontxlx.dtsi index 9f19b46c2826..bd7a87dca258 100644 --- a/arch/arm64/boot/dts/amlogic/mesontxlx.dtsi +++ b/arch/arm64/boot/dts/amlogic/mesontxlx.dtsi @@ -134,19 +134,14 @@ arm_pmu { compatible = "arm,armv8-pmuv3"; - interrupts = <0 137 4>; - reg = <0x0 0xff634400 0 0x1000>; - - /* addr = base + offset << 2 */ - sys_cpu_status0_offset = <0xa0>; - - sys_cpu_status0_pmuirq_mask = <0xf>; - + /* clusterb-enabled; */ + interrupts = ; + reg = <0x0 0xff634680 0x0 0x4>; + cpumasks = <0xf>; /* default 10ms */ - relax_timer_ns = <10000000>; - + relax-timer-ns = <10000000>; /* default 10000us */ - max_wait_cnt = <10000>; + max-wait-cnt = <10000>; }; gic: interrupt-controller@2c001000 { diff --git a/arch/arm64/include/asm/hardirq.h b/arch/arm64/include/asm/hardirq.h index 9745a1d52418..8740297dac77 100644 --- a/arch/arm64/include/asm/hardirq.h +++ b/arch/arm64/include/asm/hardirq.h @@ -20,11 +20,7 @@ #include #include -#ifdef CONFIG_AMLOGIC_MODIFY -#define NR_IPI 7 -#else #define NR_IPI 6 -#endif typedef struct { unsigned int __softirq_pending; diff --git a/arch/arm64/include/asm/perf_event.h b/arch/arm64/include/asm/perf_event.h index 11185b42b060..b963c3bbd437 100644 --- a/arch/arm64/include/asm/perf_event.h +++ b/arch/arm64/include/asm/perf_event.h @@ -92,50 +92,6 @@ extern unsigned long perf_misc_flags(struct pt_regs *regs); } -#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 diff --git a/arch/arm64/kernel/perf_event.c b/arch/arm64/kernel/perf_event.c index 77d8aa0c1b54..83348602dab8 100644 --- a/arch/arm64/kernel/perf_event.c +++ b/arch/arm64/kernel/perf_event.c @@ -19,10 +19,6 @@ * along with this program. If not, see . */ -#ifdef CONFIG_AMLOGIC_MODIFY -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt -#endif - #include #include #include @@ -34,18 +30,6 @@ #include -#ifdef CONFIG_AMLOGIC_MODIFY -#include -#include -#include -#include -#include -#include -#include -#include -#include -#endif - /* * ARMv8 PMUv3 Performance Events handling code. * Common event types (some are defined in asm/perf_event.h). @@ -766,161 +750,13 @@ static void armv8pmu_disable_event(struct perf_event *event) } #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); -} +#include static irqreturn_t armv8pmu_handle_irq(int irq_num, void *dev); -void armv8pmu_handle_irq_ipi(void) +void amlpmu_handle_irq_ipi(void *arg) { - int cpu = smp_processor_id(); - - WARN_ON(cpu == 0); - WARN_ON(!amlpmu_fixup_ctx.dev); - - armv8pmu_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<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; - } + armv8pmu_handle_irq(-1, amlpmu_ctx.pmu); } #endif @@ -933,42 +769,21 @@ static irqreturn_t armv8pmu_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 */ pmovsr = armv8pmu_getreset_flags(); #ifdef CONFIG_AMLOGIC_MODIFY - ci->fix_done = 1; -#endif + /* amlpmu have routed the interrupt already, so return IRQ_HANDLED */ + if (amlpmu_handle_irq(cpu_pmu, + irq_num, + armv8pmu_has_overflowed(pmovsr))) + return IRQ_HANDLED; +#else /* * Did an overflow occur? */ -#ifdef CONFIG_AMLOGIC_MODIFY - if (!armv8pmu_has_overflowed(pmovsr)) { - 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 (!armv8pmu_has_overflowed(pmovsr)) return IRQ_NONE; #endif @@ -1014,9 +829,6 @@ static irqreturn_t armv8pmu_handle_irq(int irq_num, void *dev) return IRQ_HANDLED; } - - - static void armv8pmu_start(struct arm_pmu *cpu_pmu) { unsigned long flags; @@ -1308,87 +1120,8 @@ static const struct pmu_probe_info armv8_pmu_probe_table[] = { }; -#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 armv8_pmu_device_probe(struct platform_device *pdev) { -#ifdef CONFIG_AMLOGIC_MODIFY - if (amlpmu_fixup_init(pdev)) - return 1; -#endif if (acpi_disabled) return arm_pmu_device_probe(pdev, armv8_pmu_of_device_ids, NULL); diff --git a/arch/arm64/kernel/smp.c b/arch/arm64/kernel/smp.c index 0185f898dced..d034e17211ae 100644 --- a/arch/arm64/kernel/smp.c +++ b/arch/arm64/kernel/smp.c @@ -55,10 +55,6 @@ #include #include -#ifdef CONFIG_AMLOGIC_MODIFY -#include -#endif - #ifdef CONFIG_AMLOGIC_VMAP #include #endif @@ -84,12 +80,7 @@ enum ipi_msg_type { IPI_CPU_STOP, IPI_TIMER, IPI_IRQ_WORK, -#ifdef CONFIG_AMLOGIC_MODIFY - IPI_WAKEUP, - IPI_AML_PMU -#else IPI_WAKEUP -#endif }; #ifdef CONFIG_ARM64_VHE @@ -775,9 +766,6 @@ static const char *ipi_types[NR_IPI] __tracepoint_string = { S(IPI_TIMER, "Timer broadcast interrupts"), S(IPI_IRQ_WORK, "IRQ work interrupts"), S(IPI_WAKEUP, "CPU wake-up 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) @@ -786,13 +774,6 @@ 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; @@ -911,12 +892,6 @@ void handle_IPI(int ipinr, struct pt_regs *regs) break; #endif -#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); break; diff --git a/drivers/perf/arm_pmu.c b/drivers/perf/arm_pmu.c index f51759da76f2..9fd30396ea45 100644 --- a/drivers/perf/arm_pmu.c +++ b/drivers/perf/arm_pmu.c @@ -1013,6 +1013,517 @@ static int of_pmu_irq_cfg(struct arm_pmu *pmu) return 0; } + +#ifdef CONFIG_AMLOGIC_MODIFY +#include +#include + +struct amlpmu_context amlpmu_ctx; + +static enum hrtimer_restart amlpmu_relax_timer_func(struct hrtimer *timer) +{ + struct amlpmu_context *ctx = &amlpmu_ctx; + struct amlpmu_cpuinfo *ci; + + ci = per_cpu_ptr(ctx->cpuinfo, 0); + + pr_info("enable cpu0_irq %d again, irq cnt = %lu\n", + ci->irq_num, + ci->valid_irq_cnt); + enable_irq(ci->irq_num); + + return HRTIMER_NORESTART; +} + +void amlpmu_relax_timer_start(int other_cpu) +{ + struct amlpmu_cpuinfo *ci; + int cpu; + struct amlpmu_context *ctx = &amlpmu_ctx; + + cpu = smp_processor_id(); + WARN_ON(cpu != 0); + + ci = per_cpu_ptr(ctx->cpuinfo, 0); + + pr_warn("wait cpu %d fixup done timeout, main cpu irq cnt = %lu\n", + other_cpu, + ci->valid_irq_cnt); + + if (hrtimer_active(&ctx->relax_timer)) { + pr_alert("relax_timer already active, return!\n"); + return; + } + + disable_irq_nosync(ci->irq_num); + + hrtimer_start(&ctx->relax_timer, + ns_to_ktime(ctx->relax_timer_ns), + HRTIMER_MODE_REL); +} + +static void amlpmu_fix_setup_affinity(int irq) +{ + int cluster_index = 0; + int cpu; + int affinity_cpu = -1; + struct amlpmu_cpuinfo *ci = NULL; + struct amlpmu_context *ctx = &amlpmu_ctx; + s64 latest_next_stamp = S64_MAX; + + if (irq == ctx->irqs[0]) + cluster_index = 0; + else if (ctx->clusterb_enabled && irq == + ctx->irqs[1]) + cluster_index = 1; + else { + pr_err("amlpmu_fix_setup_affinity() bad irq = %d\n", irq); + return; + } + + /* + * find latest next_predicted_stamp cpu for affinity cpu + * if no cpu have predict time, select first cpu of cpumask + * todo: + * - if a cpu predict failed for continuous N times, + * try add some punishment. + * - if no cpu have predicted time, try recently most used cpu + * for affinity + * - try to keep and promote prediction accuracy + */ + for_each_cpu_and(cpu, + &ctx->cpumasks[cluster_index], + cpu_possible_mask) { + ci = per_cpu_ptr(ctx->cpuinfo, cpu); + //pr_info("cpu = %d, ci->next_predicted_stamp.tv64 = %lld\n", + // cpu, ci->next_predicted_stamp.tv64); + if (ci->next_predicted_stamp.tv64 && + ci->next_predicted_stamp.tv64 < latest_next_stamp) { + latest_next_stamp = ci->next_predicted_stamp.tv64; + affinity_cpu = cpu; + } + } + + if (affinity_cpu == -1) { + affinity_cpu = cpumask_first(&ctx->cpumasks[cluster_index]); + pr_debug("used first cpu: %d, cluster: 0x%lx\n", + affinity_cpu, + *cpumask_bits(&ctx->cpumasks[cluster_index])); + } else + pr_debug("find affinity cpu: %d, next_predicted_stamp: %lld\n", + affinity_cpu, + latest_next_stamp); + + if (irq_set_affinity(irq, cpumask_of(affinity_cpu))) + pr_err("irq_set_affinity() failed irq: %d, affinity_cpu: %d\n", + irq, + affinity_cpu); +} + +/* + * on pmu interrupt generated cpu, @irq_num is valid + * on other cpus(called by AML_PMU_IPI), @irq_num is -1 + */ +static int amlpmu_irq_fix(int irq_num) +{ + int i; + int cpu; + int cur_cpu; + int pmuirq_val; + int cluster_index = 0; + int fix_success = 0; + struct amlpmu_cpuinfo *ci; + struct amlpmu_context *ctx = &amlpmu_ctx; + struct call_single_data csd_stack; + int max_wait_cnt = ctx->max_wait_cnt; + + csd_stack.func = amlpmu_handle_irq_ipi; + csd_stack.info = NULL; + + cur_cpu = smp_processor_id(); + + if (irq_num == ctx->irqs[0]) + cluster_index = 0; + else if (ctx->clusterb_enabled && irq_num == ctx->irqs[1]) + cluster_index = 1; + else { + pr_err("amlpmu_irq_fix() bad irq = %d\n", irq_num); + return fix_success; + } + + if (!cpumask_test_cpu(cur_cpu, &ctx->cpumasks[cluster_index])) { + pr_warn("amlpmu_irq_fix() cur_cpu %d not in cluster: 0x%lx\n", + cur_cpu, + *cpumask_bits(&ctx->cpumasks[cluster_index])); + } + + pmuirq_val = readl(ctx->regs[cluster_index]); + pmuirq_val &= 0xf; + pmuirq_val <<= ctx->first_cpus[cluster_index]; + + pr_debug("amlpmu_irq_fix() val=0x%0x, first_cpu=%d, cluster=0x%lx\n", + readl(ctx->regs[cluster_index]), + ctx->first_cpus[cluster_index], + *cpumask_bits(&ctx->cpumasks[cluster_index])); + + for_each_cpu_and(cpu, + &ctx->cpumasks[cluster_index], + cpu_possible_mask) { + if (pmuirq_val & (1<cpuinfo, cpu); + WRITE_ONCE(ci->fix_done, 0); + WRITE_ONCE(ci->fix_overflowed, 0); + + csd_stack.flags = 0; + smp_call_function_single_async(cpu, &csd_stack); + + for (i = 0; i < max_wait_cnt; i++) { + if (READ_ONCE(ci->fix_done)) + break; + + udelay(1); + } + + if (i == ctx->max_wait_cnt) { + pr_err("wait for cpu %d done timeout\n", + cpu); + //amlpmu_relax_timer_start(cpu); + } + + if (READ_ONCE(ci->fix_overflowed)) + fix_success++; + } + } + + return fix_success; +} + +static void amlpmu_update_stats(int irq_num, + int has_overflowed, + int fix_success) +{ + int freq; + int i; + ktime_t stamp; + unsigned long time = jiffies; + struct amlpmu_cpuinfo *ci; + struct amlpmu_context *ctx = &amlpmu_ctx; + + ci = this_cpu_ptr(ctx->cpuinfo); + + if (!has_overflowed && !fix_success) { + pr_debug("empty_irq_cnt: %lu\n", ci->empty_irq_cnt); + ci->empty_irq_cnt++; + ci->empty_irq_time = time; + } + + if (fix_success) { + /* send IPI success */ + pr_debug("fix_irq_cnt: %lu, fix_success = %d\n", + ci->fix_irq_cnt, + fix_success); + ci->fix_irq_cnt++; + ci->fix_irq_time = time; + } + + if (has_overflowed) { + ci->valid_irq_cnt++; + ci->valid_irq_time = time; + + stamp = ktime_get(); + ci->stamp_deltas[ci->valid_irq_cnt % MAX_DELTA_CNT] = + stamp.tv64 - ci->last_stamp.tv64; + ci->last_stamp = stamp; + + /* update avg_delta if it's valid */ + ci->avg_delta = 0; + for (i = 0; i < MAX_DELTA_CNT; i++) + ci->avg_delta += ci->stamp_deltas[i]; + + ci->avg_delta /= MAX_DELTA_CNT; + for (i = 0; i < MAX_DELTA_CNT; i++) { + if (ci->stamp_deltas[i] > ci->avg_delta * 3/2 || + ci->stamp_deltas[i] < ci->avg_delta / 2) { + ci->avg_delta = 0; + break; + } + } + if (ci->avg_delta) + ci->next_predicted_stamp.tv64 = + ci->last_stamp.tv64 + ci->avg_delta; + else + ci->next_predicted_stamp.tv64 = 0; + + pr_debug("irq_num = %d, valid_irq_cnt = %lu\n", + irq_num, + ci->valid_irq_cnt); + pr_debug("cur_delta = %lld, avg_delta = %lld, next = %lld\n", + ci->stamp_deltas[ci->valid_irq_cnt % MAX_DELTA_CNT], + ci->avg_delta, + ci->next_predicted_stamp.tv64); + } + + if (time_after(ci->valid_irq_time, ci->last_valid_irq_time + 2*HZ)) { + freq = ci->empty_irq_cnt - ci->last_empty_irq_cnt; + freq *= HZ; + freq /= (ci->empty_irq_time - ci->last_empty_irq_time); + pr_info("######## empty_irq_cnt: %lu - %lu = %lu, freq = %d\n", + ci->empty_irq_cnt, + ci->last_empty_irq_cnt, + ci->empty_irq_cnt - ci->last_empty_irq_cnt, + freq); + + ci->last_empty_irq_cnt = ci->empty_irq_cnt; + ci->last_empty_irq_time = ci->empty_irq_time; + + + freq = ci->fix_irq_cnt - ci->last_fix_irq_cnt; + freq *= HZ; + freq /= (ci->fix_irq_time - ci->last_fix_irq_time); + pr_info("######## fix_irq_cnt: %lu - %lu = %lu, freq = %d\n", + ci->fix_irq_cnt, + ci->last_fix_irq_cnt, + ci->fix_irq_cnt - ci->last_fix_irq_cnt, + freq); + + ci->last_fix_irq_cnt = ci->fix_irq_cnt; + ci->last_fix_irq_time = ci->fix_irq_time; + + + freq = ci->valid_irq_cnt - ci->last_valid_irq_cnt; + freq *= HZ; + freq /= (ci->valid_irq_time - ci->last_valid_irq_time); + pr_info("######## valid_irq_cnt: %lu - %lu = %lu, freq = %d\n", + ci->valid_irq_cnt, + ci->last_valid_irq_cnt, + ci->valid_irq_cnt - ci->last_valid_irq_cnt, + freq); + + ci->last_valid_irq_cnt = ci->valid_irq_cnt; + ci->last_valid_irq_time = ci->valid_irq_time; + } +} + +int amlpmu_handle_irq(struct arm_pmu *cpu_pmu, int irq_num, int has_overflowed) +{ + int cpu; + int fix_success = 0; + struct amlpmu_cpuinfo *ci; + struct amlpmu_context *ctx = &amlpmu_ctx; + + ci = this_cpu_ptr(ctx->cpuinfo); + ci->irq_num = irq_num; + cpu = smp_processor_id(); + + pr_debug("amlpmu_handle_irq() irq_num = %d, overflowed = %d\n", + irq_num, has_overflowed); + + /* + * if current cpu is not overflowed, it's possible some other + * cpus caused the pmu interrupt. + * so if current cpu is interrupt generated cpu(irq_num != -1), + * call aml_pmu_fix() try to send IPI to other cpus and waiting + * for fix_done. + */ + if (!has_overflowed && irq_num != -1) + fix_success = amlpmu_irq_fix(irq_num); + + /* + * valid_irq, fix_irq and empty_irq status + * avg_delta time account to predict next interrupt time + */ + amlpmu_update_stats(irq_num, has_overflowed, fix_success); + + /* + * armv*pmu_getreset_flags() will clear interrupt. If current + * interrupt is IPI fix(irq_num = -1), interrupt generated cpu + * now is waiting for ci->fix_done=1(clear interrupt). + * we must set ci->fix_done to 1 after amlpmu_stat_account(), + * because interrupt generated cpu need this predict time info + * to setup interrupt affinity. + */ + if (irq_num == -1) { + WRITE_ONCE(ci->fix_overflowed, has_overflowed); + /* fix_overflowed must before fix_done */ + mb(); + WRITE_ONCE(ci->fix_done, 1); + } + + /* only interrupt generated cpu need setup affinity */ + if (irq_num != -1) + amlpmu_fix_setup_affinity(irq_num); + + /* + * when a pmu interrupt generated, if current cpu is not + * overflowed and some other cpus succeed in handling the + * interrupt by IPIs return true. + */ + return !has_overflowed && fix_success; +} + +static int amlpmu_init(struct platform_device *pdev, struct arm_pmu *pmu) +{ + int cpu; + int ret = 0; + int irq; + u32 cpumasks[MAX_CLUSTER_NR] = {0}; + struct amlpmu_context *ctx = &amlpmu_ctx; + struct amlpmu_cpuinfo *ci; + + memset(ctx, 0, sizeof(*ctx)); + + ctx->cpuinfo = __alloc_percpu_gfp( + sizeof(struct amlpmu_cpuinfo), + SMP_CACHE_BYTES, + GFP_KERNEL | __GFP_ZERO); + if (!ctx->cpuinfo) { + pr_err("alloc percpu failed\n"); + ret = -ENOMEM; + goto free; + } + + for_each_possible_cpu(cpu) { + ci = per_cpu_ptr(ctx->cpuinfo, cpu); + ci->last_valid_irq_time = INITIAL_JIFFIES; + ci->last_fix_irq_time = INITIAL_JIFFIES; + ci->last_empty_irq_time = INITIAL_JIFFIES; + } + + ctx->pmu = pmu; + + if (of_property_read_bool(pdev->dev.of_node, "clusterb-enabled")) + ctx->clusterb_enabled = 1; + + pr_info("clusterb_enabled = %d\n", ctx->clusterb_enabled); + + ret = of_property_read_u32_array(pdev->dev.of_node, + "cpumasks", + cpumasks, + ctx->clusterb_enabled ? MAX_CLUSTER_NR : 1); + if (ret) { + pr_err("read prop cpumasks failed, ret = %d\n", ret); + ret = -EINVAL; + goto free; + } + pr_info("cpumasks 0x%0x, 0x%0x\n", cpumasks[0], cpumasks[1]); + + ret = of_property_read_u32(pdev->dev.of_node, + "relax-timer-ns", + &ctx->relax_timer_ns); + if (ret) { + pr_err("read prop relax-timer-ns failed, ret = %d\n", ret); + ret = -EINVAL; + goto free; + } + + ret = of_property_read_u32(pdev->dev.of_node, + "max-wait-cnt", + &ctx->max_wait_cnt); + if (ret) { + pr_err("read prop max-wait-cnt failed, ret = %d\n", ret); + ret = -EINVAL; + goto free; + } + + irq = platform_get_irq(pdev, 0); + if (irq < 0) { + pr_err("get clusterA irq failed, %d\n", irq); + ret = -EINVAL; + goto free; + } + ctx->irqs[0] = irq; + pr_info("cluster A irq = %d\n", irq); + + ctx->regs[0] = of_iomap(pdev->dev.of_node, 0); + if (IS_ERR(ctx->regs[0])) { + pr_err("of_iomap() clusterA failed, base = %p\n", ctx->regs[0]); + ret = PTR_ERR(ctx->regs[0]); + goto free; + } + + cpumask_clear(&ctx->cpumasks[0]); + memcpy(cpumask_bits(&ctx->cpumasks[0]), + &cpumasks[0], + sizeof(cpumasks[0])); + if (!cpumask_intersects(&ctx->cpumasks[0], cpu_possible_mask)) { + pr_err("bad cpumasks[0] 0x%x\n", cpumasks[0]); + ret = -EINVAL; + goto free; + } + ctx->first_cpus[0] = cpumask_first(&ctx->cpumasks[0]); + + amlpmu_fix_setup_affinity(ctx->irqs[0]); + + hrtimer_init(&ctx->relax_timer, + CLOCK_MONOTONIC, + HRTIMER_MODE_REL); + ctx->relax_timer.function = amlpmu_relax_timer_func; + + if (!ctx->clusterb_enabled) + return 0; + + irq = platform_get_irq(pdev, 1); + if (irq < 0) { + pr_err("get clusterB irq failed, %d\n", irq); + ret = -EINVAL; + goto free; + } + ctx->irqs[1] = irq; + pr_info("cluster B irq = %d\n", irq); + + + ctx->regs[1] = of_iomap(pdev->dev.of_node, 1); + if (IS_ERR(ctx->regs[1])) { + pr_err("of_iomap() clusterA failed, base = %p\n", ctx->regs[1]); + ret = PTR_ERR(ctx->regs[1]); + goto free; + } + + cpumask_clear(&ctx->cpumasks[1]); + memcpy(cpumask_bits(&ctx->cpumasks[1]), + &cpumasks[1], + sizeof(cpumasks[1])); + if (!cpumask_intersects(&ctx->cpumasks[1], cpu_possible_mask)) { + pr_err("bad cpumasks[1] 0x%x\n", cpumasks[1]); + ret = -EINVAL; + goto free; + } else if (cpumask_intersects(&ctx->cpumasks[0], &ctx->cpumasks[1])) { + pr_err("cpumasks intersect 0x%x : 0x%x\n", + cpumasks[0], + cpumasks[1]); + ret = -EINVAL; + goto free; + } + ctx->first_cpus[1] = cpumask_first(&ctx->cpumasks[1]); + + amlpmu_fix_setup_affinity(ctx->irqs[1]); + + return 0; + +free: + if (ctx->cpuinfo) + free_percpu(ctx->cpuinfo); + + if (ctx->regs[0]) + iounmap(ctx->regs[0]); + + if (ctx->regs[1]) + iounmap(ctx->regs[1]); + + return ret; +} + +#endif + int arm_pmu_device_probe(struct platform_device *pdev, const struct of_device_id *of_table, const struct pmu_probe_info *probe_table) @@ -1029,6 +1540,13 @@ int arm_pmu_device_probe(struct platform_device *pdev, return -ENOMEM; } +#ifdef CONFIG_AMLOGIC_MODIFY + if (amlpmu_init(pdev, pmu)) { + pr_err("amlpmu_init() failed\n"); + return 1; + } +#endif + armpmu_init(pmu); pmu->plat_device = pdev; diff --git a/include/linux/perf/arm_pmu.h b/include/linux/perf/arm_pmu.h index 8462da266089..e3c724ba22fd 100644 --- a/include/linux/perf/arm_pmu.h +++ b/include/linux/perf/arm_pmu.h @@ -162,6 +162,99 @@ int arm_pmu_device_probe(struct platform_device *pdev, #define ARMV8_PMU_PDEV_NAME "armv8-pmu" +#ifdef CONFIG_AMLOGIC_MODIFY +#define MAX_DELTA_CNT 4 +struct amlpmu_cpuinfo { + int irq_num; + + /* + * In interrupt generated cpu(affinity cpu) + * If pmu no overflowed, then we need to send IPI to some other cpus to + * fix it. And before send IPI, set corresponding cpu's fix_done and + * fix_overflowed to zero, in corresponding cpu's IPI interrupt will set + * fix_done to inform source cpu and if indeed pmu overflowed then also + * set fix_overflowed to 1, then inerrupt generated cpu can feel that. + */ + int fix_done; + int fix_overflowed; + + /* for interrupt affinity prediction */ + ktime_t last_stamp; + s64 stamp_deltas[MAX_DELTA_CNT]; + s64 avg_delta; + ktime_t next_predicted_stamp; + + /* + * irq state account of this cpu + * + * - valid_irq_cnt: + * valid irq cnt.(pmu overflow happened) + * - fix_irq_cnt: + * when this cpu is pmu interrupt generated affinity cpu, a pmu + * interrupt if cpu affinity predict failed so no pmu overflow + * happened and succeeded send IPI to other cpu, then it's a send + * fix irq. So the lower is better. + * - empty_irq_cnt: + * when this cpu is pmu interrupt generated affinity cpu, a pmu + * interrupt that no overflow happened and also no fix IPI sended to + * other cpus, then it's a empty irq. + * when this cpu is not affinity cpu, a IPI interrupt(pmu fix from + * affinity cpu) that no pmu overflow happened, it's a empty irq. + * + * attention: + * A interrupt can be a valid_irq and also a fix_irq. + */ + unsigned long valid_irq_cnt; + unsigned long fix_irq_cnt; + unsigned long empty_irq_cnt; + + unsigned long valid_irq_time; + unsigned long fix_irq_time; + unsigned long empty_irq_time; + + unsigned long last_valid_irq_cnt; + unsigned long last_fix_irq_cnt; + unsigned long last_empty_irq_cnt; + + unsigned long last_valid_irq_time; + unsigned long last_fix_irq_time; + unsigned long last_empty_irq_time; +}; + + +#define MAX_CLUSTER_NR 2 +struct amlpmu_context { + struct amlpmu_cpuinfo __percpu *cpuinfo; + + /* struct arm_pmu */ + struct arm_pmu *pmu; + + int clusterb_enabled; + + unsigned int __iomem *regs[MAX_CLUSTER_NR]; + int irqs[MAX_CLUSTER_NR]; + struct cpumask cpumasks[MAX_CLUSTER_NR]; + int first_cpus[MAX_CLUSTER_NR]; + + /* + * 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; + + unsigned int relax_timer_ns; + unsigned int max_wait_cnt; +}; + +extern struct amlpmu_context amlpmu_ctx; + +int amlpmu_handle_irq(struct arm_pmu *cpu_pmu, int irq_num, int has_overflowed); + +/* defined int arch/arm(64)/kernel/perf_event(_v7).c */ +void amlpmu_handle_irq_ipi(void *arg); +#endif + #endif /* CONFIG_ARM_PMU */ #endif /* __ARM_PMU_H__ */ diff --git a/include/linux/smp.h b/include/linux/smp.h index c9b5fb366422..acba97e0eeab 100644 --- a/include/linux/smp.h +++ b/include/linux/smp.h @@ -73,13 +73,6 @@ extern void smp_send_stop(void); */ extern void smp_send_reschedule(int cpu); -#ifdef CONFIG_AMLOGIC_MODIFY -/* - * sends a 'aml pmu' event to another CPU: - */ -extern void smp_send_aml_pmu(int cpu); -#endif - /* * Prepare machine for booting other CPUs. */