summaryrefslogtreecommitdiff
path: root/arch/arm/kernel/perf_event.c
diff options
context:
space:
mode:
authorWill Deacon <will.deacon@arm.com>2012-07-28 17:42:22 +0100
committerWill Deacon <will.deacon@arm.com>2012-08-23 11:35:52 +0100
commit04236f9fe07462849215c67cae6147661368bfad (patch)
treefef6161a18a418db56cb3acb6cae263c3af1f436 /arch/arm/kernel/perf_event.c
parent9f44f9a234020947dd16500a203c9580a66ed67d (diff)
ARM: perf: probe devicetree in preference to current CPU
The CPU PMU is probed using the current cpuid information as part of the early_initcall initialising the architecture perf backend. For architectures without NMI (such as ARM), this does not need to be performed early and can be deferred to the driver probe callback. This also allows us to probe the devicetree in preference to parsing the current cpuid, which may be invalid on a big.LITTLE multi-cluster system. This patch defers the PMU probing and uses the devicetree information when available. Signed-off-by: Will Deacon <will.deacon@arm.com>
Diffstat (limited to 'arch/arm/kernel/perf_event.c')
-rw-r--r--arch/arm/kernel/perf_event.c175
1 files changed, 91 insertions, 84 deletions
diff --git a/arch/arm/kernel/perf_event.c b/arch/arm/kernel/perf_event.c
index 22ed5120a6ed..7c29914bdcc6 100644
--- a/arch/arm/kernel/perf_event.c
+++ b/arch/arm/kernel/perf_event.c
@@ -16,6 +16,7 @@
#include <linux/interrupt.h>
#include <linux/kernel.h>
#include <linux/export.h>
+#include <linux/of.h>
#include <linux/perf_event.h>
#include <linux/platform_device.h>
#include <linux/spinlock.h>
@@ -610,9 +611,11 @@ static void __init armpmu_init(struct arm_pmu *armpmu)
};
}
-int __init armpmu_register(struct arm_pmu *armpmu, char *name, int type)
+int armpmu_register(struct arm_pmu *armpmu, char *name, int type)
{
armpmu_init(armpmu);
+ pr_info("enabled with %s PMU driver, %d counters available\n",
+ armpmu->name, armpmu->num_events);
return perf_pmu_register(&armpmu->pmu, name, type);
}
@@ -621,74 +624,12 @@ int __init armpmu_register(struct arm_pmu *armpmu, char *name, int type)
#include "perf_event_v6.c"
#include "perf_event_v7.c"
-/*
- * Ensure the PMU has sane values out of reset.
- * This requires SMP to be available, so exists as a separate initcall.
- */
-static int __init
-cpu_pmu_reset(void)
-{
- if (cpu_pmu && cpu_pmu->reset)
- return on_each_cpu(cpu_pmu->reset, NULL, 1);
- return 0;
-}
-arch_initcall(cpu_pmu_reset);
-
-/*
- * PMU platform driver and devicetree bindings.
- */
-static struct of_device_id armpmu_of_device_ids[] = {
- {.compatible = "arm,cortex-a15-pmu"},
- {.compatible = "arm,cortex-a9-pmu"},
- {.compatible = "arm,cortex-a8-pmu"},
- {.compatible = "arm,cortex-a7-pmu"},
- {.compatible = "arm,cortex-a5-pmu"},
- {.compatible = "arm,arm11mpcore-pmu"},
- {.compatible = "arm,arm1176-pmu"},
- {.compatible = "arm,arm1136-pmu"},
- {},
-};
-
-static struct platform_device_id armpmu_plat_device_ids[] = {
- {.name = "arm-pmu"},
- {},
-};
-
-static int __devinit armpmu_device_probe(struct platform_device *pdev)
-{
- if (!cpu_pmu)
- return -ENODEV;
-
- cpu_pmu->plat_device = pdev;
- return 0;
-}
-
-static const struct dev_pm_ops armpmu_dev_pm_ops = {
- SET_RUNTIME_PM_OPS(armpmu_runtime_suspend, armpmu_runtime_resume, NULL)
-};
-
-static struct platform_driver armpmu_driver = {
- .driver = {
- .name = "arm-pmu",
- .pm = &armpmu_dev_pm_ops,
- .of_match_table = armpmu_of_device_ids,
- },
- .probe = armpmu_device_probe,
- .id_table = armpmu_plat_device_ids,
-};
-
-static int __init register_pmu_driver(void)
-{
- return platform_driver_register(&armpmu_driver);
-}
-device_initcall(register_pmu_driver);
-
static struct pmu_hw_events *armpmu_get_cpu_events(void)
{
return &__get_cpu_var(cpu_hw_events);
}
-static void __init cpu_pmu_init(struct arm_pmu *armpmu)
+static void __devinit cpu_pmu_init(struct arm_pmu *cpu_pmu)
{
int cpu;
for_each_possible_cpu(cpu) {
@@ -697,7 +638,11 @@ static void __init cpu_pmu_init(struct arm_pmu *armpmu)
events->used_mask = per_cpu(used_mask, cpu);
raw_spin_lock_init(&events->pmu_lock);
}
- armpmu->get_hw_events = armpmu_get_cpu_events;
+ cpu_pmu->get_hw_events = armpmu_get_cpu_events;
+
+ /* Ensure the PMU has sane values out of reset. */
+ if (cpu_pmu && cpu_pmu->reset)
+ on_each_cpu(cpu_pmu->reset, NULL, 1);
}
/*
@@ -722,41 +667,68 @@ static struct notifier_block __cpuinitdata pmu_cpu_notifier = {
.notifier_call = pmu_cpu_notify,
};
+static const struct dev_pm_ops armpmu_dev_pm_ops = {
+ SET_RUNTIME_PM_OPS(armpmu_runtime_suspend, armpmu_runtime_resume, NULL)
+};
+
+/*
+ * PMU platform driver and devicetree bindings.
+ */
+static struct of_device_id __devinitdata cpu_pmu_of_device_ids[] = {
+ {.compatible = "arm,cortex-a15-pmu", .data = armv7_a15_pmu_init},
+ {.compatible = "arm,cortex-a9-pmu", .data = armv7_a9_pmu_init},
+ {.compatible = "arm,cortex-a8-pmu", .data = armv7_a8_pmu_init},
+ {.compatible = "arm,cortex-a7-pmu", .data = armv7_a7_pmu_init},
+ {.compatible = "arm,cortex-a5-pmu", .data = armv7_a5_pmu_init},
+ {.compatible = "arm,arm11mpcore-pmu", .data = armv6mpcore_pmu_init},
+ {.compatible = "arm,arm1176-pmu", .data = armv6pmu_init},
+ {.compatible = "arm,arm1136-pmu", .data = armv6pmu_init},
+ {},
+};
+
+static struct platform_device_id __devinitdata cpu_pmu_plat_device_ids[] = {
+ {.name = "arm-pmu"},
+ {},
+};
+
/*
- * CPU PMU identification and registration.
+ * CPU PMU identification and probing.
*/
-static int __init
-init_hw_perf_events(void)
+static struct arm_pmu *__devinit probe_current_pmu(void)
{
+ struct arm_pmu *pmu = NULL;
+ int cpu = get_cpu();
unsigned long cpuid = read_cpuid_id();
unsigned long implementor = (cpuid & 0xFF000000) >> 24;
unsigned long part_number = (cpuid & 0xFFF0);
+ pr_info("probing PMU on CPU %d\n", cpu);
+
/* ARM Ltd CPUs. */
if (0x41 == implementor) {
switch (part_number) {
case 0xB360: /* ARM1136 */
case 0xB560: /* ARM1156 */
case 0xB760: /* ARM1176 */
- cpu_pmu = armv6pmu_init();
+ pmu = armv6pmu_init();
break;
case 0xB020: /* ARM11mpcore */
- cpu_pmu = armv6mpcore_pmu_init();
+ pmu = armv6mpcore_pmu_init();
break;
case 0xC080: /* Cortex-A8 */
- cpu_pmu = armv7_a8_pmu_init();
+ pmu = armv7_a8_pmu_init();
break;
case 0xC090: /* Cortex-A9 */
- cpu_pmu = armv7_a9_pmu_init();
+ pmu = armv7_a9_pmu_init();
break;
case 0xC050: /* Cortex-A5 */
- cpu_pmu = armv7_a5_pmu_init();
+ pmu = armv7_a5_pmu_init();
break;
case 0xC0F0: /* Cortex-A15 */
- cpu_pmu = armv7_a15_pmu_init();
+ pmu = armv7_a15_pmu_init();
break;
case 0xC070: /* Cortex-A7 */
- cpu_pmu = armv7_a7_pmu_init();
+ pmu = armv7_a7_pmu_init();
break;
}
/* Intel CPUs [xscale]. */
@@ -764,27 +736,62 @@ init_hw_perf_events(void)
part_number = (cpuid >> 13) & 0x7;
switch (part_number) {
case 1:
- cpu_pmu = xscale1pmu_init();
+ pmu = xscale1pmu_init();
break;
case 2:
- cpu_pmu = xscale2pmu_init();
+ pmu = xscale2pmu_init();
break;
}
}
+ put_cpu();
+ return pmu;
+}
+
+static int __devinit cpu_pmu_device_probe(struct platform_device *pdev)
+{
+ const struct of_device_id *of_id;
+ struct arm_pmu *(*init_fn)(void);
+ struct device_node *node = pdev->dev.of_node;
+
if (cpu_pmu) {
- pr_info("enabled with %s PMU driver, %d counters available\n",
- cpu_pmu->name, cpu_pmu->num_events);
- cpu_pmu_init(cpu_pmu);
- register_cpu_notifier(&pmu_cpu_notifier);
- armpmu_register(cpu_pmu, cpu_pmu->name, PERF_TYPE_RAW);
+ pr_info("attempt to register multiple PMU devices!");
+ return -ENOSPC;
+ }
+
+ if (node && (of_id = of_match_node(cpu_pmu_of_device_ids, pdev->dev.of_node))) {
+ init_fn = of_id->data;
+ cpu_pmu = init_fn();
} else {
- pr_info("no hardware support available\n");
+ cpu_pmu = probe_current_pmu();
}
+ if (!cpu_pmu)
+ return -ENODEV;
+
+ cpu_pmu->plat_device = pdev;
+ cpu_pmu_init(cpu_pmu);
+ register_cpu_notifier(&pmu_cpu_notifier);
+ armpmu_register(cpu_pmu, cpu_pmu->name, PERF_TYPE_RAW);
+
return 0;
}
-early_initcall(init_hw_perf_events);
+
+static struct platform_driver cpu_pmu_driver = {
+ .driver = {
+ .name = "arm-pmu",
+ .pm = &armpmu_dev_pm_ops,
+ .of_match_table = cpu_pmu_of_device_ids,
+ },
+ .probe = cpu_pmu_device_probe,
+ .id_table = cpu_pmu_plat_device_ids,
+};
+
+static int __init register_pmu_driver(void)
+{
+ return platform_driver_register(&cpu_pmu_driver);
+}
+device_initcall(register_pmu_driver);
/*
* Callchain handling code.