3 * ARM V7 (Cortex A8) Event Monitor Driver
5 * Copyright 2008 Jean Pihet <jpihet@mvista.com>
6 * Copyright 2004 ARM SMP Development Team
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License version 2 as
10 * published by the Free Software Foundation.
12 #include <linux/types.h>
13 #include <linux/errno.h>
14 #include <linux/oprofile.h>
15 #include <linux/interrupt.h>
16 #include <linux/irq.h>
17 #include <linux/smp.h>
19 #include "op_counter.h"
20 #include "op_arm_model.h"
21 #include "op_model_v7.h"
30 static u32 cnt_en
[CNTMAX
];
32 static inline void armv7_pmnc_write(u32 val
)
35 asm volatile("mcr p15, 0, %0, c9, c12, 0" : : "r" (val
));
38 static inline u32
armv7_pmnc_read(void)
42 asm volatile("mrc p15, 0, %0, c9, c12, 0" : "=r" (val
));
46 static inline u32
armv7_pmnc_enable_counter(unsigned int cnt
)
51 printk(KERN_ERR
"oprofile: CPU%u enabling wrong PMNC counter"
52 " %d\n", smp_processor_id(), cnt
);
59 val
= (1 << (cnt
- CNT0
));
62 asm volatile("mcr p15, 0, %0, c9, c12, 1" : : "r" (val
));
67 static inline u32
armv7_pmnc_disable_counter(unsigned int cnt
)
72 printk(KERN_ERR
"oprofile: CPU%u disabling wrong PMNC counter"
73 " %d\n", smp_processor_id(), cnt
);
80 val
= (1 << (cnt
- CNT0
));
83 asm volatile("mcr p15, 0, %0, c9, c12, 2" : : "r" (val
));
88 static inline u32
armv7_pmnc_enable_intens(unsigned int cnt
)
93 printk(KERN_ERR
"oprofile: CPU%u enabling wrong PMNC counter"
94 " interrupt enable %d\n", smp_processor_id(), cnt
);
101 val
= (1 << (cnt
- CNT0
));
104 asm volatile("mcr p15, 0, %0, c9, c14, 1" : : "r" (val
));
109 static inline u32
armv7_pmnc_getreset_flags(void)
114 asm volatile("mrc p15, 0, %0, c9, c12, 3" : "=r" (val
));
116 /* Write to clear flags */
118 asm volatile("mcr p15, 0, %0, c9, c12, 3" : : "r" (val
));
123 static inline int armv7_pmnc_select_counter(unsigned int cnt
)
127 if ((cnt
== CCNT
) || (cnt
>= CNTMAX
)) {
128 printk(KERN_ERR
"oprofile: CPU%u selecting wrong PMNC counteri"
129 " %d\n", smp_processor_id(), cnt
);
133 val
= (cnt
- CNT0
) & SELECT_MASK
;
134 asm volatile("mcr p15, 0, %0, c9, c12, 5" : : "r" (val
));
139 static inline void armv7_pmnc_write_evtsel(unsigned int cnt
, u32 val
)
141 if (armv7_pmnc_select_counter(cnt
) == cnt
) {
143 asm volatile("mcr p15, 0, %0, c9, c13, 1" : : "r" (val
));
147 static void armv7_pmnc_reset_counter(unsigned int cnt
)
149 u32 cpu_cnt
= CPU_COUNTER(smp_processor_id(), cnt
);
150 u32 val
= -(u32
)counter_config
[cpu_cnt
].count
;
154 armv7_pmnc_disable_counter(cnt
);
156 asm volatile("mcr p15, 0, %0, c9, c13, 0" : : "r" (val
));
158 if (cnt_en
[cnt
] != 0)
159 armv7_pmnc_enable_counter(cnt
);
167 armv7_pmnc_disable_counter(cnt
);
169 if (armv7_pmnc_select_counter(cnt
) == cnt
)
170 asm volatile("mcr p15, 0, %0, c9, c13, 2" : : "r" (val
));
172 if (cnt_en
[cnt
] != 0)
173 armv7_pmnc_enable_counter(cnt
);
178 printk(KERN_ERR
"oprofile: CPU%u resetting wrong PMNC counter"
179 " %d\n", smp_processor_id(), cnt
);
184 int armv7_setup_pmnc(void)
188 if (armv7_pmnc_read() & PMNC_E
) {
189 printk(KERN_ERR
"oprofile: CPU%u PMNC still enabled when setup"
190 " new event counter.\n", smp_processor_id());
195 * Initialize & Reset PMNC: C bit, D bit and P bit.
196 * Note: Using a slower count for CCNT (D bit: divide by 64) results
197 * in a more stable system
199 armv7_pmnc_write(PMNC_P
| PMNC_C
| PMNC_D
);
202 for (cnt
= CCNT
; cnt
< CNTMAX
; cnt
++) {
204 u32 cpu_cnt
= CPU_COUNTER(smp_processor_id(), cnt
);
209 armv7_pmnc_disable_counter(cnt
);
212 if (!counter_config
[cpu_cnt
].enabled
)
215 event
= counter_config
[cpu_cnt
].event
& 255;
218 * Set event (if destined for PMNx counters)
219 * We don't need to set the event if it's a cycle count
222 armv7_pmnc_write_evtsel(cnt
, event
);
225 * Enable interrupt for this counter
227 armv7_pmnc_enable_intens(cnt
);
232 armv7_pmnc_reset_counter(cnt
);
237 armv7_pmnc_enable_counter(cnt
);
244 static inline void armv7_start_pmnc(void)
246 armv7_pmnc_write(armv7_pmnc_read() | PMNC_E
);
249 static inline void armv7_stop_pmnc(void)
251 armv7_pmnc_write(armv7_pmnc_read() & ~PMNC_E
);
255 * CPU counters' IRQ handler (one IRQ per CPU)
257 static irqreturn_t
armv7_pmnc_interrupt(int irq
, void *arg
)
259 struct pt_regs
*regs
= get_irq_regs();
265 * Stop IRQ generation
270 * Get and reset overflow status flags
272 flags
= armv7_pmnc_getreset_flags();
277 if (flags
& FLAG_C
) {
278 u32 cpu_cnt
= CPU_COUNTER(smp_processor_id(), CCNT
);
279 armv7_pmnc_reset_counter(CCNT
);
280 oprofile_add_sample(regs
, cpu_cnt
);
286 for (cnt
= CNT0
; cnt
< CNTMAX
; cnt
++) {
287 if (flags
& (1 << (cnt
- CNT0
))) {
288 u32 cpu_cnt
= CPU_COUNTER(smp_processor_id(), cnt
);
289 armv7_pmnc_reset_counter(cnt
);
290 oprofile_add_sample(regs
, cpu_cnt
);
295 * Allow IRQ generation
302 int armv7_request_interrupts(int *irqs
, int nr
)
307 for (i
= 0; i
< nr
; i
++) {
308 ret
= request_irq(irqs
[i
], armv7_pmnc_interrupt
,
309 IRQF_DISABLED
, "CP15 PMNC", NULL
);
311 printk(KERN_ERR
"oprofile: unable to request IRQ%u"
320 free_irq(irqs
[i
], NULL
);
325 void armv7_release_interrupts(int *irqs
, int nr
)
329 for (i
= 0; i
< nr
; i
++)
330 free_irq(irqs
[i
], NULL
);
334 static void armv7_pmnc_dump_regs(void)
339 printk(KERN_INFO
"PMNC registers dump:\n");
341 asm volatile("mrc p15, 0, %0, c9, c12, 0" : "=r" (val
));
342 printk(KERN_INFO
"PMNC =0x%08x\n", val
);
344 asm volatile("mrc p15, 0, %0, c9, c12, 1" : "=r" (val
));
345 printk(KERN_INFO
"CNTENS=0x%08x\n", val
);
347 asm volatile("mrc p15, 0, %0, c9, c14, 1" : "=r" (val
));
348 printk(KERN_INFO
"INTENS=0x%08x\n", val
);
350 asm volatile("mrc p15, 0, %0, c9, c12, 3" : "=r" (val
));
351 printk(KERN_INFO
"FLAGS =0x%08x\n", val
);
353 asm volatile("mrc p15, 0, %0, c9, c12, 5" : "=r" (val
));
354 printk(KERN_INFO
"SELECT=0x%08x\n", val
);
356 asm volatile("mrc p15, 0, %0, c9, c13, 0" : "=r" (val
));
357 printk(KERN_INFO
"CCNT =0x%08x\n", val
);
359 for (cnt
= CNT0
; cnt
< CNTMAX
; cnt
++) {
360 armv7_pmnc_select_counter(cnt
);
361 asm volatile("mrc p15, 0, %0, c9, c13, 2" : "=r" (val
));
362 printk(KERN_INFO
"CNT[%d] count =0x%08x\n", cnt
-CNT0
, val
);
363 asm volatile("mrc p15, 0, %0, c9, c13, 1" : "=r" (val
));
364 printk(KERN_INFO
"CNT[%d] evtsel=0x%08x\n", cnt
-CNT0
, val
);
370 static int irqs
[] = {
371 #ifdef CONFIG_ARCH_OMAP3
372 INT_34XX_BENCH_MPU_EMUL
,
376 static void armv7_pmnc_stop(void)
379 armv7_pmnc_dump_regs();
382 armv7_release_interrupts(irqs
, ARRAY_SIZE(irqs
));
385 static int armv7_pmnc_start(void)
390 armv7_pmnc_dump_regs();
392 ret
= armv7_request_interrupts(irqs
, ARRAY_SIZE(irqs
));
399 static int armv7_detect_pmnc(void)
404 struct op_arm_model_spec op_armv7_spec
= {
405 .init
= armv7_detect_pmnc
,
407 .setup_ctrs
= armv7_setup_pmnc
,
408 .start
= armv7_pmnc_start
,
409 .stop
= armv7_pmnc_stop
,