diff options
author | Will Deacon <will.deacon@arm.com> | 2011-03-25 13:13:34 +0100 |
---|---|---|
committer | Russell King <rmk+kernel@arm.linux.org.uk> | 2011-03-26 11:06:09 +0100 |
commit | 574b69cbb633037a9c305d2993aeb680f4a8badd (patch) | |
tree | 8fcef55167b8750eeaebffca51b937a993842136 | |
parent | ARM: 6833/1: perf: add required isbs() to ARMv7 backend (diff) | |
download | linux-574b69cbb633037a9c305d2993aeb680f4a8badd.tar.xz linux-574b69cbb633037a9c305d2993aeb680f4a8badd.zip |
ARM: 6834/1: perf: reset counters on all CPUs during initialisation
ARMv7 dictates that the interrupt-enable and count-enable registers for
each PMU counter are UNKNOWN following core reset.
This patch adds a new (optional) function pointer to struct arm_pmu for
resetting the PMU state during init. The reset function is called on
each CPU via an arch_initcall in the generic ARM perf_event code and
allows the PMU backend to write sane values to any UNKNOWN registers.
Acked-by: Jean Pihet <j-pihet@ti.com>
Signed-off-by: Will Deacon <will.deacon@arm.com>
Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
-rw-r--r-- | arch/arm/kernel/perf_event.c | 14 | ||||
-rw-r--r-- | arch/arm/kernel/perf_event_v7.c | 22 |
2 files changed, 30 insertions, 6 deletions
diff --git a/arch/arm/kernel/perf_event.c b/arch/arm/kernel/perf_event.c index 22e194eb8536..e422f4c269a0 100644 --- a/arch/arm/kernel/perf_event.c +++ b/arch/arm/kernel/perf_event.c @@ -79,6 +79,7 @@ struct arm_pmu { void (*write_counter)(int idx, u32 val); void (*start)(void); void (*stop)(void); + void (*reset)(void *); const unsigned (*cache_map)[PERF_COUNT_HW_CACHE_MAX] [PERF_COUNT_HW_CACHE_OP_MAX] [PERF_COUNT_HW_CACHE_RESULT_MAX]; @@ -624,6 +625,19 @@ static struct pmu pmu = { #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 +armpmu_reset(void) +{ + if (armpmu && armpmu->reset) + return on_each_cpu(armpmu->reset, NULL, 1); + return 0; +} +arch_initcall(armpmu_reset); + static int __init init_hw_perf_events(void) { diff --git a/arch/arm/kernel/perf_event_v7.c b/arch/arm/kernel/perf_event_v7.c index d6c9dcd1979f..c08d07a99fcc 100644 --- a/arch/arm/kernel/perf_event_v7.c +++ b/arch/arm/kernel/perf_event_v7.c @@ -849,6 +849,18 @@ static int armv7pmu_get_event_idx(struct cpu_hw_events *cpuc, } } +static void armv7pmu_reset(void *info) +{ + u32 idx, nb_cnt = armpmu->num_events; + + /* The counter and interrupt enable registers are unknown at reset. */ + for (idx = 1; idx < nb_cnt; ++idx) + armv7pmu_disable_event(NULL, idx); + + /* Initialize & Reset PMNC: C and P bits */ + armv7_pmnc_write(ARMV7_PMNC_P | ARMV7_PMNC_C); +} + static struct arm_pmu armv7pmu = { .handle_irq = armv7pmu_handle_irq, .enable = armv7pmu_enable_event, @@ -858,17 +870,15 @@ static struct arm_pmu armv7pmu = { .get_event_idx = armv7pmu_get_event_idx, .start = armv7pmu_start, .stop = armv7pmu_stop, + .reset = armv7pmu_reset, .raw_event_mask = 0xFF, .max_period = (1LLU << 32) - 1, }; -static u32 __init armv7_reset_read_pmnc(void) +static u32 __init armv7_read_num_pmnc_events(void) { u32 nb_cnt; - /* Initialize & Reset PMNC: C and P bits */ - armv7_pmnc_write(ARMV7_PMNC_P | ARMV7_PMNC_C); - /* Read the nb of CNTx counters supported from PMNC */ nb_cnt = (armv7_pmnc_read() >> ARMV7_PMNC_N_SHIFT) & ARMV7_PMNC_N_MASK; @@ -882,7 +892,7 @@ static const struct arm_pmu *__init armv7_a8_pmu_init(void) armv7pmu.name = "ARMv7 Cortex-A8"; armv7pmu.cache_map = &armv7_a8_perf_cache_map; armv7pmu.event_map = &armv7_a8_perf_map; - armv7pmu.num_events = armv7_reset_read_pmnc(); + armv7pmu.num_events = armv7_read_num_pmnc_events(); return &armv7pmu; } @@ -892,7 +902,7 @@ static const struct arm_pmu *__init armv7_a9_pmu_init(void) armv7pmu.name = "ARMv7 Cortex-A9"; armv7pmu.cache_map = &armv7_a9_perf_cache_map; armv7pmu.event_map = &armv7_a9_perf_map; - armv7pmu.num_events = armv7_reset_read_pmnc(); + armv7pmu.num_events = armv7_read_num_pmnc_events(); return &armv7pmu; } #else |