3 This patch adds Oprofile support on ARMv7, using the PMNC unit.
4 Tested on OMAP3430 SDP.
6 Feedback and comments are welcome.
8 The patch to user space components is attached for reference. It i applies
9 against version 0.9.3 of oprofile source
10 (http://prdownloads.sourceforge.net/oprofile/oprofile-0.9.3.tar.gz).
17 From: Jean Pihet <jpihet@mvista.com>
18 Date: Tue, 6 May 2008 17:21:44 +0200
19 Subject: [PATCH] ARM: Add ARMv7 oprofile support
21 Add ARMv7 Oprofile support to kernel
23 Signed-off-by: Jean Pihet <jpihet@mvista.com>
26 diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig
27 index c60a27d..60b50a0 100644
28 --- a/arch/arm/Kconfig
29 +++ b/arch/arm/Kconfig
30 @@ -161,6 +161,11 @@ config OPROFILE_MPCORE
31 config OPROFILE_ARM11_CORE
34 +config OPROFILE_ARMV7
36 + depends on CPU_V7 && !SMP
42 diff --git a/arch/arm/oprofile/Makefile b/arch/arm/oprofile/Makefile
43 index e61d0cc..88e31f5 100644
44 --- a/arch/arm/oprofile/Makefile
45 +++ b/arch/arm/oprofile/Makefile
46 @@ -11,3 +11,4 @@ oprofile-$(CONFIG_CPU_XSCALE) += op_model_xscale.o
47 oprofile-$(CONFIG_OPROFILE_ARM11_CORE) += op_model_arm11_core.o
48 oprofile-$(CONFIG_OPROFILE_ARMV6) += op_model_v6.o
49 oprofile-$(CONFIG_OPROFILE_MPCORE) += op_model_mpcore.o
50 +oprofile-$(CONFIG_OPROFILE_ARMV7) += op_model_v7.o
51 diff --git a/arch/arm/oprofile/common.c b/arch/arm/oprofile/common.c
52 index 0a5cf3a..3fcd752 100644
53 --- a/arch/arm/oprofile/common.c
54 +++ b/arch/arm/oprofile/common.c
55 @@ -145,6 +145,10 @@ int __init oprofile_arch_init(struct oprofile_operations *ops)
56 spec = &op_mpcore_spec;
59 +#ifdef CONFIG_OPROFILE_ARMV7
60 + spec = &op_armv7_spec;
66 diff --git a/arch/arm/oprofile/op_arm_model.h
67 b/arch/arm/oprofile/op_arm_model.h
68 index 4899c62..8c4e4f6 100644
69 --- a/arch/arm/oprofile/op_arm_model.h
70 +++ b/arch/arm/oprofile/op_arm_model.h
71 @@ -26,6 +26,7 @@ extern struct op_arm_model_spec op_xscale_spec;
73 extern struct op_arm_model_spec op_armv6_spec;
74 extern struct op_arm_model_spec op_mpcore_spec;
75 +extern struct op_arm_model_spec op_armv7_spec;
77 extern void arm_backtrace(struct pt_regs * const regs, unsigned int depth);
79 diff --git a/arch/arm/oprofile/op_model_v7.c b/arch/arm/oprofile/op_model_v7.c
81 index 0000000..a159bc1
83 +++ b/arch/arm/oprofile/op_model_v7.c
86 + * @file op_model_v7.c
87 + * ARM V7 (Cortex A8) Event Monitor Driver
89 + * @remark Copyright 2008 Jean Pihet <jpihet@mvista.com>
90 + * @remark Copyright 2004 ARM SMP Development Team
92 +#include <linux/types.h>
93 +#include <linux/errno.h>
94 +#include <linux/oprofile.h>
95 +#include <linux/interrupt.h>
96 +#include <linux/irq.h>
97 +#include <linux/smp.h>
99 +#include "op_counter.h"
100 +#include "op_arm_model.h"
101 +#include "op_model_v7.h"
107 + * ARM V7 PMNC support
110 +static u32 cnt_en[CNTMAX];
112 +static inline void armv7_pmnc_write(u32 val)
115 + asm volatile("mcr p15, 0, %0, c9, c12, 0" : : "r" (val));
118 +static inline u32 armv7_pmnc_read(void)
122 + asm volatile("mrc p15, 0, %0, c9, c12, 0" : "=r" (val));
126 +static inline u32 armv7_pmnc_enable_counter(unsigned int cnt)
130 + if (cnt >= CNTMAX) {
131 + printk(KERN_ERR "oprofile: CPU%u enabling wrong PMNC counter"
132 + " %d\n", smp_processor_id(), cnt);
139 + val = (1 << (cnt - CNT0));
141 + val &= CNTENS_MASK;
142 + asm volatile("mcr p15, 0, %0, c9, c12, 1" : : "r" (val));
147 +static inline u32 armv7_pmnc_disable_counter(unsigned int cnt)
151 + if (cnt >= CNTMAX) {
152 + printk(KERN_ERR "oprofile: CPU%u disabling wrong PMNC counter"
153 + " %d\n", smp_processor_id(), cnt);
160 + val = (1 << (cnt - CNT0));
162 + val &= CNTENC_MASK;
163 + asm volatile("mcr p15, 0, %0, c9, c12, 2" : : "r" (val));
168 +static inline u32 armv7_pmnc_enable_intens(unsigned int cnt)
172 + if (cnt >= CNTMAX) {
173 + printk(KERN_ERR "oprofile: CPU%u enabling wrong PMNC counter"
174 + " interrupt enable %d\n", smp_processor_id(), cnt);
181 + val = (1 << (cnt - CNT0));
183 + val &= INTENS_MASK;
184 + asm volatile("mcr p15, 0, %0, c9, c14, 1" : : "r" (val));
189 +static inline u32 armv7_pmnc_getreset_flags(void)
194 + asm volatile("mrc p15, 0, %0, c9, c12, 3" : "=r" (val));
196 + /* Write to clear flags */
198 + asm volatile("mcr p15, 0, %0, c9, c12, 3" : : "r" (val));
203 +static inline int armv7_pmnc_select_counter(unsigned int cnt)
207 + if ((cnt == CCNT) || (cnt >= CNTMAX)) {
208 + printk(KERN_ERR "oprofile: CPU%u selecting wrong PMNC counteri"
209 + " %d\n", smp_processor_id(), cnt);
213 + val = (cnt - CNT0) & SELECT_MASK;
214 + asm volatile("mcr p15, 0, %0, c9, c12, 5" : : "r" (val));
219 +static inline void armv7_pmnc_write_evtsel(unsigned int cnt, u32 val)
221 + if (armv7_pmnc_select_counter(cnt) == cnt) {
222 + val &= EVTSEL_MASK;
223 + asm volatile("mcr p15, 0, %0, c9, c13, 1" : : "r" (val));
227 +static void armv7_pmnc_reset_counter(unsigned int cnt)
229 + u32 cpu_cnt = CPU_COUNTER(smp_processor_id(), cnt);
230 + u32 val = -(u32)counter_config[cpu_cnt].count;
234 + armv7_pmnc_disable_counter(cnt);
236 + asm volatile("mcr p15, 0, %0, c9, c13, 0" : : "r" (val));
238 + if (cnt_en[cnt] != 0)
239 + armv7_pmnc_enable_counter(cnt);
247 + armv7_pmnc_disable_counter(cnt);
249 + if (armv7_pmnc_select_counter(cnt) == cnt)
250 + asm volatile("mcr p15, 0, %0, c9, c13, 2" : : "r" (val));
252 + if (cnt_en[cnt] != 0)
253 + armv7_pmnc_enable_counter(cnt);
258 + printk(KERN_ERR "oprofile: CPU%u resetting wrong PMNC counter"
259 + " %d\n", smp_processor_id(), cnt);
264 +int armv7_setup_pmnc(void)
268 + if (armv7_pmnc_read() & PMNC_E) {
269 + printk(KERN_ERR "oprofile: CPU%u PMNC still enabled when setup"
270 + " new event counter.\n", smp_processor_id());
275 + * Initialize & Reset PMNC: C bit, D bit and P bit.
276 + * Note: Using a slower count for CCNT (D bit: divide by 64) results
277 + * in a more stable system
279 + armv7_pmnc_write(PMNC_P | PMNC_C | PMNC_D);
282 + for (cnt = CCNT; cnt < CNTMAX; cnt++) {
283 + unsigned long event;
284 + u32 cpu_cnt = CPU_COUNTER(smp_processor_id(), cnt);
289 + armv7_pmnc_disable_counter(cnt);
292 + if (!counter_config[cpu_cnt].enabled)
295 + event = counter_config[cpu_cnt].event & 255;
298 + * Set event (if destined for PMNx counters)
299 + * We don't need to set the event if it's a cycle count
302 + armv7_pmnc_write_evtsel(cnt, event);
305 + * Enable interrupt for this counter
307 + armv7_pmnc_enable_intens(cnt);
312 + armv7_pmnc_reset_counter(cnt);
317 + armv7_pmnc_enable_counter(cnt);
324 +static inline void armv7_start_pmnc(void)
326 + armv7_pmnc_write(armv7_pmnc_read() | PMNC_E);
329 +static inline void armv7_stop_pmnc(void)
331 + armv7_pmnc_write(armv7_pmnc_read() & ~PMNC_E);
335 + * CPU counters' IRQ handler (one IRQ per CPU)
337 +static irqreturn_t armv7_pmnc_interrupt(int irq, void *arg)
339 + struct pt_regs *regs = get_irq_regs();
345 + * Stop IRQ generation
350 + * Get and reset overflow status flags
352 + flags = armv7_pmnc_getreset_flags();
357 + if (flags & FLAG_C) {
358 + u32 cpu_cnt = CPU_COUNTER(smp_processor_id(), CCNT);
359 + armv7_pmnc_reset_counter(CCNT);
360 + oprofile_add_sample(regs, cpu_cnt);
364 + * PMNC counters 0:3
366 + for (cnt = CNT0; cnt < CNTMAX; cnt++) {
367 + if (flags & (1 << (cnt - CNT0))) {
368 + u32 cpu_cnt = CPU_COUNTER(smp_processor_id(), cnt);
369 + armv7_pmnc_reset_counter(cnt);
370 + oprofile_add_sample(regs, cpu_cnt);
375 + * Allow IRQ generation
377 + armv7_start_pmnc();
379 + return IRQ_HANDLED;
382 +int armv7_request_interrupts(int *irqs, int nr)
387 + for (i = 0; i < nr; i++) {
388 + ret = request_irq(irqs[i], armv7_pmnc_interrupt,
389 + IRQF_DISABLED, "CP15 PMNC", NULL);
391 + printk(KERN_ERR "oprofile: unable to request IRQ%u"
400 + free_irq(irqs[i], NULL);
405 +void armv7_release_interrupts(int *irqs, int nr)
409 + for (i = 0; i < nr; i++)
410 + free_irq(irqs[i], NULL);
414 +static void armv7_pmnc_dump_regs(void)
419 + printk(KERN_INFO "PMNC registers dump:\n");
421 + asm volatile("mrc p15, 0, %0, c9, c12, 0" : "=r" (val));
422 + printk(KERN_INFO "PMNC =0x%08x\n", val);
424 + asm volatile("mrc p15, 0, %0, c9, c12, 1" : "=r" (val));
425 + printk(KERN_INFO "CNTENS=0x%08x\n", val);
427 + asm volatile("mrc p15, 0, %0, c9, c14, 1" : "=r" (val));
428 + printk(KERN_INFO "INTENS=0x%08x\n", val);
430 + asm volatile("mrc p15, 0, %0, c9, c12, 3" : "=r" (val));
431 + printk(KERN_INFO "FLAGS =0x%08x\n", val);
433 + asm volatile("mrc p15, 0, %0, c9, c12, 5" : "=r" (val));
434 + printk(KERN_INFO "SELECT=0x%08x\n", val);
436 + asm volatile("mrc p15, 0, %0, c9, c13, 0" : "=r" (val));
437 + printk(KERN_INFO "CCNT =0x%08x\n", val);
439 + for (cnt = CNT0; cnt < CNTMAX; cnt++) {
440 + armv7_pmnc_select_counter(cnt);
441 + asm volatile("mrc p15, 0, %0, c9, c13, 2" : "=r" (val));
442 + printk(KERN_INFO "CNT[%d] count =0x%08x\n", cnt-CNT0, val);
443 + asm volatile("mrc p15, 0, %0, c9, c13, 1" : "=r" (val));
444 + printk(KERN_INFO "CNT[%d] evtsel=0x%08x\n", cnt-CNT0, val);
450 +static int irqs[] = {
451 +#ifdef CONFIG_ARCH_OMAP3
452 + INT_34XX_BENCH_MPU_EMUL,
456 +static void armv7_pmnc_stop(void)
459 + armv7_pmnc_dump_regs();
462 + armv7_release_interrupts(irqs, ARRAY_SIZE(irqs));
465 +static int armv7_pmnc_start(void)
470 + armv7_pmnc_dump_regs();
472 + ret = armv7_request_interrupts(irqs, ARRAY_SIZE(irqs));
474 + armv7_start_pmnc();
479 +static int armv7_detect_pmnc(void)
484 +struct op_arm_model_spec op_armv7_spec = {
485 + .init = armv7_detect_pmnc,
487 + .setup_ctrs = armv7_setup_pmnc,
488 + .start = armv7_pmnc_start,
489 + .stop = armv7_pmnc_stop,
490 + .name = "arm/armv7",
492 diff --git a/arch/arm/oprofile/op_model_v7.h b/arch/arm/oprofile/op_model_v7.h
494 index 0000000..08f40ea
496 +++ b/arch/arm/oprofile/op_model_v7.h
499 + * @file op_model_v7.h
500 + * ARM v7 (Cortex A8) Event Monitor Driver
502 + * @remark Copyright 2008 Jean Pihet <jpihet@mvista.com>
503 + * @remark Copyright 2004 ARM SMP Development Team
504 + * @remark Copyright 2000-2004 Deepak Saxena <dsaxena@mvista.com>
505 + * @remark Copyright 2000-2004 MontaVista Software Inc
506 + * @remark Copyright 2004 Dave Jiang <dave.jiang@intel.com>
507 + * @remark Copyright 2004 Intel Corporation
508 + * @remark Copyright 2004 Zwane Mwaikambo <zwane@arm.linux.org.uk>
509 + * @remark Copyright 2004 Oprofile Authors
511 + * @remark Read the file COPYING
513 + * @author Zwane Mwaikambo
515 +#ifndef OP_MODEL_V7_H
516 +#define OP_MODEL_V7_H
519 + * Per-CPU PMNC: config reg
521 +#define PMNC_E (1 << 0) /* Enable all counters */
522 +#define PMNC_P (1 << 1) /* Reset all counters */
523 +#define PMNC_C (1 << 2) /* Cycle counter reset */
524 +#define PMNC_D (1 << 3) /* CCNT counts every 64th cpu cycle */
525 +#define PMNC_X (1 << 4) /* Export to ETM */
526 +#define PMNC_DP (1 << 5) /* Disable CCNT if non-invasive debug*/
527 +#define PMNC_MASK 0x3f /* Mask for writable bits */
530 + * Available counters
539 +#define CPU_COUNTER(cpu, counter) ((cpu) * CNTMAX + (counter))
542 + * CNTENS: counters enable reg
544 +#define CNTENS_P0 (1 << 0)
545 +#define CNTENS_P1 (1 << 1)
546 +#define CNTENS_P2 (1 << 2)
547 +#define CNTENS_P3 (1 << 3)
548 +#define CNTENS_C (1 << 31)
549 +#define CNTENS_MASK 0x8000000f /* Mask for writable bits */
552 + * CNTENC: counters disable reg
554 +#define CNTENC_P0 (1 << 0)
555 +#define CNTENC_P1 (1 << 1)
556 +#define CNTENC_P2 (1 << 2)
557 +#define CNTENC_P3 (1 << 3)
558 +#define CNTENC_C (1 << 31)
559 +#define CNTENC_MASK 0x8000000f /* Mask for writable bits */
562 + * INTENS: counters overflow interrupt enable reg
564 +#define INTENS_P0 (1 << 0)
565 +#define INTENS_P1 (1 << 1)
566 +#define INTENS_P2 (1 << 2)
567 +#define INTENS_P3 (1 << 3)
568 +#define INTENS_C (1 << 31)
569 +#define INTENS_MASK 0x8000000f /* Mask for writable bits */
572 + * EVTSEL: Event selection reg
574 +#define EVTSEL_MASK 0x7f /* Mask for writable bits */
577 + * SELECT: Counter selection reg
579 +#define SELECT_MASK 0x1f /* Mask for writable bits */
582 + * FLAG: counters overflow flag status reg
584 +#define FLAG_P0 (1 << 0)
585 +#define FLAG_P1 (1 << 1)
586 +#define FLAG_P2 (1 << 2)
587 +#define FLAG_P3 (1 << 3)
588 +#define FLAG_C (1 << 31)
589 +#define FLAG_MASK 0x8000000f /* Mask for writable bits */
592 +int armv7_setup_pmu(void);
593 +int armv7_start_pmu(void);
594 +int armv7_stop_pmu(void);
595 +int armv7_request_interrupts(int *, int);
596 +void armv7_release_interrupts(int *, int);