#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);
(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__ */
#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
*
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;
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)
*/
{ /* 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);
}