From f999487712defce5a96ec39105de94de41fe053a Mon Sep 17 00:00:00 2001 From: Ashwin Chaugule Date: Thu, 7 Jun 2012 13:41:37 -0400 Subject: [PATCH] Perf: Update power collapse support for perf Update the CPU PM notifier functions in perf to use the new perf data structures. Change-Id: I0f183072b8de65057f56d92301c22d1e9f93218b Signed-off-by: Ashwin Chaugule --- arch/arm/kernel/perf_event.c | 87 ++++++++++++++++++++++++++++-------- 1 file changed, 69 insertions(+), 18 deletions(-) diff --git a/arch/arm/kernel/perf_event.c b/arch/arm/kernel/perf_event.c index ae439efad7f..7316589499a 100644 --- a/arch/arm/kernel/perf_event.c +++ b/arch/arm/kernel/perf_event.c @@ -28,6 +28,8 @@ #include #include +#include + /* * ARMv6 supports a maximum of 3 events, starting from index 0. If we add * another platform that supports more, we need to increase this to be the @@ -383,8 +385,6 @@ armpmu_release_hardware(struct arm_pmu *armpmu) { int i, irq, irqs; struct platform_device *pmu_device = armpmu->plat_device; - struct arm_pmu_platdata *plat = - dev_get_platdata(&pmu_device->dev); irqs = min(pmu_device->num_resources, num_possible_cpus()); @@ -393,11 +393,6 @@ armpmu_release_hardware(struct arm_pmu *armpmu) continue; irq = platform_get_irq(pmu_device, i); armpmu->free_pmu_irq(irq); - if (irq >= 0) { - if (plat && plat->disable_irq) - plat->disable_irq(irq); - free_irq(irq, armpmu); - } } release_pmu(armpmu->type); @@ -459,17 +454,6 @@ armpmu_reserve_hardware(struct arm_pmu *armpmu) return err; } - err = request_irq(irq, handle_irq, - IRQF_DISABLED | IRQF_NOBALANCING, - "arm-pmu", armpmu); - if (err) { - pr_err("unable to request IRQ%d for ARM PMU counters\n", - irq); - armpmu_release_hardware(armpmu); - return err; - } else if (plat && plat->enable_irq) - plat->enable_irq(irq); - cpumask_set_cpu(i, &armpmu->active_irqs); } @@ -729,10 +713,76 @@ static int __cpuinit pmu_cpu_notify(struct notifier_block *b, return NOTIFY_OK; } +static void armpmu_update_counters(void) +{ + struct pmu_hw_events *hw_events; + int idx; + + if (!cpu_pmu) + return; + + hw_events = cpu_pmu->get_hw_events(); + + for (idx = 0; idx <= cpu_pmu->num_events; ++idx) { + struct perf_event *event = hw_events->events[idx]; + + if (!event) + continue; + + armpmu_read(event); + } +} + +static int cpu_has_active_perf(void) +{ + struct pmu_hw_events *hw_events; + int enabled; + + if (!cpu_pmu) + return 0; + + hw_events = cpu_pmu->get_hw_events(); + enabled = bitmap_weight(hw_events->used_mask, cpu_pmu->num_events); + + if (enabled) + /*Even one event's existence is good enough.*/ + return 1; + + return 0; +} + static struct notifier_block __cpuinitdata pmu_cpu_notifier = { .notifier_call = pmu_cpu_notify, }; +/*TODO: Unify with pending patch from ARM */ +static int perf_cpu_pm_notifier(struct notifier_block *self, unsigned long cmd, + void *v) +{ + switch (cmd) { + case CPU_PM_ENTER: + if (cpu_has_active_perf()) { + armpmu_update_counters(); + perf_pmu_disable(&cpu_pmu->pmu); + } + break; + + case CPU_PM_ENTER_FAILED: + case CPU_PM_EXIT: + if (cpu_has_active_perf() && cpu_pmu->reset) { + cpu_pmu->reset(NULL); + perf_pmu_enable(&cpu_pmu->pmu); + } + break; + } + + return NOTIFY_OK; +} + +static struct notifier_block perf_cpu_pm_notifier_block = { + .notifier_call = perf_cpu_pm_notifier, +}; + /* * CPU PMU identification and registration. */ @@ -809,6 +859,7 @@ init_hw_perf_events(void) cpu_pmu_init(cpu_pmu); register_cpu_notifier(&pmu_cpu_notifier); armpmu_register(cpu_pmu, "cpu", PERF_TYPE_RAW); + cpu_pm_register_notifier(&perf_cpu_pm_notifier_block); } else { pr_info("no hardware support available\n"); }