Merge tag 'lsk-v4.4-17.07-android' of git://git.linaro.org/kernel/linux-linaro-stable.git
[firefly-linux-kernel-4.4.55.git] / drivers / perf / arm_pmu.c
index be3755c973e96d4c6ee48f0459c751ba6d225665..04827692ef4fe098a05a7473c2fe21eb5361e1ea 100644 (file)
@@ -13,6 +13,7 @@
 
 #include <linux/bitmap.h>
 #include <linux/cpumask.h>
+#include <linux/cpu_pm.h>
 #include <linux/export.h>
 #include <linux/kernel.h>
 #include <linux/of_device.h>
@@ -610,7 +611,8 @@ static void cpu_pmu_free_irq(struct arm_pmu *cpu_pmu)
 
        irq = platform_get_irq(pmu_device, 0);
        if (irq >= 0 && irq_is_percpu(irq)) {
-               on_each_cpu(cpu_pmu_disable_percpu_irq, &irq, 1);
+               on_each_cpu_mask(&cpu_pmu->supported_cpus,
+                                cpu_pmu_disable_percpu_irq, &irq, 1);
                free_percpu_irq(irq, &hw_events->percpu_pmu);
        } else {
                for (i = 0; i < irqs; ++i) {
@@ -652,7 +654,9 @@ static int cpu_pmu_request_irq(struct arm_pmu *cpu_pmu, irq_handler_t handler)
                                irq);
                        return err;
                }
-               on_each_cpu(cpu_pmu_enable_percpu_irq, &irq, 1);
+
+               on_each_cpu_mask(&cpu_pmu->supported_cpus,
+                                cpu_pmu_enable_percpu_irq, &irq, 1);
        } else {
                for (i = 0; i < irqs; ++i) {
                        int cpu = i;
@@ -718,6 +722,104 @@ static int cpu_pmu_notify(struct notifier_block *b, unsigned long action,
        return NOTIFY_OK;
 }
 
+#ifdef CONFIG_CPU_PM
+static void cpu_pm_pmu_setup(struct arm_pmu *armpmu, unsigned long cmd)
+{
+       struct pmu_hw_events *hw_events = this_cpu_ptr(armpmu->hw_events);
+       struct perf_event *event;
+       int idx;
+
+       for (idx = 0; idx < armpmu->num_events; idx++) {
+               /*
+                * If the counter is not used skip it, there is no
+                * need of stopping/restarting it.
+                */
+               if (!test_bit(idx, hw_events->used_mask))
+                       continue;
+
+               event = hw_events->events[idx];
+
+               switch (cmd) {
+               case CPU_PM_ENTER:
+                       /*
+                        * Stop and update the counter
+                        */
+                       armpmu_stop(event, PERF_EF_UPDATE);
+                       break;
+               case CPU_PM_EXIT:
+               case CPU_PM_ENTER_FAILED:
+                        /*
+                         * Restore and enable the counter.
+                         * armpmu_start() indirectly calls
+                         *
+                         * perf_event_update_userpage()
+                         *
+                         * that requires RCU read locking to be functional,
+                         * wrap the call within RCU_NONIDLE to make the
+                         * RCU subsystem aware this cpu is not idle from
+                         * an RCU perspective for the armpmu_start() call
+                         * duration.
+                         */
+                       RCU_NONIDLE(armpmu_start(event, PERF_EF_RELOAD));
+                       break;
+               default:
+                       break;
+               }
+       }
+}
+
+static int cpu_pm_pmu_notify(struct notifier_block *b, unsigned long cmd,
+                            void *v)
+{
+       struct arm_pmu *armpmu = container_of(b, struct arm_pmu, cpu_pm_nb);
+       struct pmu_hw_events *hw_events = this_cpu_ptr(armpmu->hw_events);
+       int enabled = bitmap_weight(hw_events->used_mask, armpmu->num_events);
+
+       if (!cpumask_test_cpu(smp_processor_id(), &armpmu->supported_cpus))
+               return NOTIFY_DONE;
+
+       /*
+        * Always reset the PMU registers on power-up even if
+        * there are no events running.
+        */
+       if (cmd == CPU_PM_EXIT && armpmu->reset)
+               armpmu->reset(armpmu);
+
+       if (!enabled)
+               return NOTIFY_OK;
+
+       switch (cmd) {
+       case CPU_PM_ENTER:
+               armpmu->stop(armpmu);
+               cpu_pm_pmu_setup(armpmu, cmd);
+               break;
+       case CPU_PM_EXIT:
+               cpu_pm_pmu_setup(armpmu, cmd);
+       case CPU_PM_ENTER_FAILED:
+               armpmu->start(armpmu);
+               break;
+       default:
+               return NOTIFY_DONE;
+       }
+
+       return NOTIFY_OK;
+}
+
+static int cpu_pm_pmu_register(struct arm_pmu *cpu_pmu)
+{
+       cpu_pmu->cpu_pm_nb.notifier_call = cpu_pm_pmu_notify;
+       return cpu_pm_register_notifier(&cpu_pmu->cpu_pm_nb);
+}
+
+static void cpu_pm_pmu_unregister(struct arm_pmu *cpu_pmu)
+{
+       cpu_pm_unregister_notifier(&cpu_pmu->cpu_pm_nb);
+}
+#else
+static inline int cpu_pm_pmu_register(struct arm_pmu *cpu_pmu) { return 0; }
+static inline void cpu_pm_pmu_unregister(struct arm_pmu *cpu_pmu) { }
+#endif
+
 static int cpu_pmu_init(struct arm_pmu *cpu_pmu)
 {
        int err;
@@ -733,6 +835,10 @@ static int cpu_pmu_init(struct arm_pmu *cpu_pmu)
        if (err)
                goto out_hw_events;
 
+       err = cpu_pm_pmu_register(cpu_pmu);
+       if (err)
+               goto out_unregister;
+
        for_each_possible_cpu(cpu) {
                struct pmu_hw_events *events = per_cpu_ptr(cpu_hw_events, cpu);
                raw_spin_lock_init(&events->pmu_lock);
@@ -754,6 +860,8 @@ static int cpu_pmu_init(struct arm_pmu *cpu_pmu)
 
        return 0;
 
+out_unregister:
+       unregister_cpu_notifier(&cpu_pmu->hotplug_nb);
 out_hw_events:
        free_percpu(cpu_hw_events);
        return err;
@@ -761,6 +869,7 @@ out_hw_events:
 
 static void cpu_pmu_destroy(struct arm_pmu *cpu_pmu)
 {
+       cpu_pm_pmu_unregister(cpu_pmu);
        unregister_cpu_notifier(&cpu_pmu->hotplug_nb);
        free_percpu(cpu_pmu->hw_events);
 }
@@ -815,6 +924,7 @@ static int of_pmu_irq_cfg(struct arm_pmu *pmu)
                        if (i > 0 && spi != using_spi) {
                                pr_err("PPI/SPI IRQ type mismatch for %s!\n",
                                        dn->name);
+                               of_node_put(dn);
                                kfree(irqs);
                                return -EINVAL;
                        }
@@ -858,9 +968,23 @@ static int of_pmu_irq_cfg(struct arm_pmu *pmu)
                i++;
        } while (1);
 
-       /* If we didn't manage to parse anything, claim to support all CPUs */
-       if (cpumask_weight(&pmu->supported_cpus) == 0)
-               cpumask_setall(&pmu->supported_cpus);
+       /* If we didn't manage to parse anything, try the interrupt affinity */
+       if (cpumask_weight(&pmu->supported_cpus) == 0) {
+               if (!using_spi) {
+                       /* If using PPIs, check the affinity of the partition */
+                       int ret, irq;
+
+                       irq = platform_get_irq(pdev, 0);
+                       ret = irq_get_percpu_devid_partition(irq, &pmu->supported_cpus);
+                       if (ret) {
+                               kfree(irqs);
+                               return ret;
+                       }
+               } else {
+                       /* Otherwise default to all CPUs */
+                       cpumask_setall(&pmu->supported_cpus);
+               }
+       }
 
        /* If we matched up the IRQ affinities, use them to route the SPIs */
        if (using_spi && i == pdev->num_resources)