aboutsummaryrefslogtreecommitdiffstats
path: root/arch/arm/oprofile
diff options
context:
space:
mode:
Diffstat (limited to 'arch/arm/oprofile')
-rw-r--r--arch/arm/oprofile/Makefile1
-rw-r--r--arch/arm/oprofile/common.c4
-rw-r--r--arch/arm/oprofile/op_arm_model.h1
-rw-r--r--arch/arm/oprofile/op_model_v7.c411
-rw-r--r--arch/arm/oprofile/op_model_v7.h103
5 files changed, 520 insertions, 0 deletions
diff --git a/arch/arm/oprofile/Makefile b/arch/arm/oprofile/Makefile
index e61d0cc520b7..88e31f549f50 100644
--- a/arch/arm/oprofile/Makefile
+++ b/arch/arm/oprofile/Makefile
@@ -11,3 +11,4 @@ oprofile-$(CONFIG_CPU_XSCALE) += op_model_xscale.o
11oprofile-$(CONFIG_OPROFILE_ARM11_CORE) += op_model_arm11_core.o 11oprofile-$(CONFIG_OPROFILE_ARM11_CORE) += op_model_arm11_core.o
12oprofile-$(CONFIG_OPROFILE_ARMV6) += op_model_v6.o 12oprofile-$(CONFIG_OPROFILE_ARMV6) += op_model_v6.o
13oprofile-$(CONFIG_OPROFILE_MPCORE) += op_model_mpcore.o 13oprofile-$(CONFIG_OPROFILE_MPCORE) += op_model_mpcore.o
14oprofile-$(CONFIG_OPROFILE_ARMV7) += op_model_v7.o
diff --git a/arch/arm/oprofile/common.c b/arch/arm/oprofile/common.c
index 0a5cf3a6438b..3fcd752d6146 100644
--- a/arch/arm/oprofile/common.c
+++ b/arch/arm/oprofile/common.c
@@ -145,6 +145,10 @@ int __init oprofile_arch_init(struct oprofile_operations *ops)
145 spec = &op_mpcore_spec; 145 spec = &op_mpcore_spec;
146#endif 146#endif
147 147
148#ifdef CONFIG_OPROFILE_ARMV7
149 spec = &op_armv7_spec;
150#endif
151
148 if (spec) { 152 if (spec) {
149 ret = spec->init(); 153 ret = spec->init();
150 if (ret < 0) 154 if (ret < 0)
diff --git a/arch/arm/oprofile/op_arm_model.h b/arch/arm/oprofile/op_arm_model.h
index 4899c629aa03..8c4e4f6a1de3 100644
--- a/arch/arm/oprofile/op_arm_model.h
+++ b/arch/arm/oprofile/op_arm_model.h
@@ -26,6 +26,7 @@ extern struct op_arm_model_spec op_xscale_spec;
26 26
27extern struct op_arm_model_spec op_armv6_spec; 27extern struct op_arm_model_spec op_armv6_spec;
28extern struct op_arm_model_spec op_mpcore_spec; 28extern struct op_arm_model_spec op_mpcore_spec;
29extern struct op_arm_model_spec op_armv7_spec;
29 30
30extern void arm_backtrace(struct pt_regs * const regs, unsigned int depth); 31extern void arm_backtrace(struct pt_regs * const regs, unsigned int depth);
31 32
diff --git a/arch/arm/oprofile/op_model_v7.c b/arch/arm/oprofile/op_model_v7.c
new file mode 100644
index 000000000000..f20295f14adb
--- /dev/null
+++ b/arch/arm/oprofile/op_model_v7.c
@@ -0,0 +1,411 @@
1/**
2 * op_model_v7.c
3 * ARM V7 (Cortex A8) Event Monitor Driver
4 *
5 * Copyright 2008 Jean Pihet <jpihet@mvista.com>
6 * Copyright 2004 ARM SMP Development Team
7 *
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.
11 */
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>
18
19#include "op_counter.h"
20#include "op_arm_model.h"
21#include "op_model_v7.h"
22
23/* #define DEBUG */
24
25
26/*
27 * ARM V7 PMNC support
28 */
29
30static u32 cnt_en[CNTMAX];
31
32static inline void armv7_pmnc_write(u32 val)
33{
34 val &= PMNC_MASK;
35 asm volatile("mcr p15, 0, %0, c9, c12, 0" : : "r" (val));
36}
37
38static inline u32 armv7_pmnc_read(void)
39{
40 u32 val;
41
42 asm volatile("mrc p15, 0, %0, c9, c12, 0" : "=r" (val));
43 return val;
44}
45
46static inline u32 armv7_pmnc_enable_counter(unsigned int cnt)
47{
48 u32 val;
49
50 if (cnt >= CNTMAX) {
51 printk(KERN_ERR "oprofile: CPU%u enabling wrong PMNC counter"
52 " %d\n", smp_processor_id(), cnt);
53 return -1;
54 }
55
56 if (cnt == CCNT)
57 val = CNTENS_C;
58 else
59 val = (1 << (cnt - CNT0));
60
61 val &= CNTENS_MASK;
62 asm volatile("mcr p15, 0, %0, c9, c12, 1" : : "r" (val));
63
64 return cnt;
65}
66
67static inline u32 armv7_pmnc_disable_counter(unsigned int cnt)
68{
69 u32 val;
70
71 if (cnt >= CNTMAX) {
72 printk(KERN_ERR "oprofile: CPU%u disabling wrong PMNC counter"
73 " %d\n", smp_processor_id(), cnt);
74 return -1;
75 }
76
77 if (cnt == CCNT)
78 val = CNTENC_C;
79 else
80 val = (1 << (cnt - CNT0));
81
82 val &= CNTENC_MASK;
83 asm volatile("mcr p15, 0, %0, c9, c12, 2" : : "r" (val));
84
85 return cnt;
86}
87
88static inline u32 armv7_pmnc_enable_intens(unsigned int cnt)
89{
90 u32 val;
91
92 if (cnt >= CNTMAX) {
93 printk(KERN_ERR "oprofile: CPU%u enabling wrong PMNC counter"
94 " interrupt enable %d\n", smp_processor_id(), cnt);
95 return -1;
96 }
97
98 if (cnt == CCNT)
99 val = INTENS_C;
100 else
101 val = (1 << (cnt - CNT0));
102
103 val &= INTENS_MASK;
104 asm volatile("mcr p15, 0, %0, c9, c14, 1" : : "r" (val));
105
106 return cnt;
107}
108
109static inline u32 armv7_pmnc_getreset_flags(void)
110{
111 u32 val;
112
113 /* Read */
114 asm volatile("mrc p15, 0, %0, c9, c12, 3" : "=r" (val));
115
116 /* Write to clear flags */
117 val &= FLAG_MASK;
118 asm volatile("mcr p15, 0, %0, c9, c12, 3" : : "r" (val));
119
120 return val;
121}
122
123static inline int armv7_pmnc_select_counter(unsigned int cnt)
124{
125 u32 val;
126
127 if ((cnt == CCNT) || (cnt >= CNTMAX)) {
128 printk(KERN_ERR "oprofile: CPU%u selecting wrong PMNC counteri"
129 " %d\n", smp_processor_id(), cnt);
130 return -1;
131 }
132
133 val = (cnt - CNT0) & SELECT_MASK;
134 asm volatile("mcr p15, 0, %0, c9, c12, 5" : : "r" (val));
135
136 return cnt;
137}
138
139static inline void armv7_pmnc_write_evtsel(unsigned int cnt, u32 val)
140{
141 if (armv7_pmnc_select_counter(cnt) == cnt) {
142 val &= EVTSEL_MASK;
143 asm volatile("mcr p15, 0, %0, c9, c13, 1" : : "r" (val));
144 }
145}
146
147static void armv7_pmnc_reset_counter(unsigned int cnt)
148{
149 u32 cpu_cnt = CPU_COUNTER(smp_processor_id(), cnt);
150 u32 val = -(u32)counter_config[cpu_cnt].count;
151
152 switch (cnt) {
153 case CCNT:
154 armv7_pmnc_disable_counter(cnt);
155
156 asm volatile("mcr p15, 0, %0, c9, c13, 0" : : "r" (val));
157
158 if (cnt_en[cnt] != 0)
159 armv7_pmnc_enable_counter(cnt);
160
161 break;
162
163 case CNT0:
164 case CNT1:
165 case CNT2:
166 case CNT3:
167 armv7_pmnc_disable_counter(cnt);
168
169 if (armv7_pmnc_select_counter(cnt) == cnt)
170 asm volatile("mcr p15, 0, %0, c9, c13, 2" : : "r" (val));
171
172 if (cnt_en[cnt] != 0)
173 armv7_pmnc_enable_counter(cnt);
174
175 break;
176
177 default:
178 printk(KERN_ERR "oprofile: CPU%u resetting wrong PMNC counter"
179 " %d\n", smp_processor_id(), cnt);
180 break;
181 }
182}
183
184int armv7_setup_pmnc(void)
185{
186 unsigned int cnt;
187
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());
191 return -EBUSY;
192 }
193
194 /*
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
198 */
199 armv7_pmnc_write(PMNC_P | PMNC_C | PMNC_D);
200
201
202 for (cnt = CCNT; cnt < CNTMAX; cnt++) {
203 unsigned long event;
204 u32 cpu_cnt = CPU_COUNTER(smp_processor_id(), cnt);
205
206 /*
207 * Disable counter
208 */
209 armv7_pmnc_disable_counter(cnt);
210 cnt_en[cnt] = 0;
211
212 if (!counter_config[cpu_cnt].enabled)
213 continue;
214
215 event = counter_config[cpu_cnt].event & 255;
216
217 /*
218 * Set event (if destined for PMNx counters)
219 * We don't need to set the event if it's a cycle count
220 */
221 if (cnt != CCNT)
222 armv7_pmnc_write_evtsel(cnt, event);
223
224 /*
225 * Enable interrupt for this counter
226 */
227 armv7_pmnc_enable_intens(cnt);
228
229 /*
230 * Reset counter
231 */
232 armv7_pmnc_reset_counter(cnt);
233
234 /*
235 * Enable counter
236 */
237 armv7_pmnc_enable_counter(cnt);
238 cnt_en[cnt] = 1;
239 }
240
241 return 0;
242}
243
244static inline void armv7_start_pmnc(void)
245{
246 armv7_pmnc_write(armv7_pmnc_read() | PMNC_E);
247}
248
249static inline void armv7_stop_pmnc(void)
250{
251 armv7_pmnc_write(armv7_pmnc_read() & ~PMNC_E);
252}
253
254/*
255 * CPU counters' IRQ handler (one IRQ per CPU)
256 */
257static irqreturn_t armv7_pmnc_interrupt(int irq, void *arg)
258{
259 struct pt_regs *regs = get_irq_regs();
260 unsigned int cnt;
261 u32 flags;
262
263
264 /*
265 * Stop IRQ generation
266 */
267 armv7_stop_pmnc();
268
269 /*
270 * Get and reset overflow status flags
271 */
272 flags = armv7_pmnc_getreset_flags();
273
274 /*
275 * Cycle counter
276 */
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);
281 }
282
283 /*
284 * PMNC counters 0:3
285 */
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);
291 }
292 }
293
294 /*
295 * Allow IRQ generation
296 */
297 armv7_start_pmnc();
298
299 return IRQ_HANDLED;
300}
301
302int armv7_request_interrupts(int *irqs, int nr)
303{
304 unsigned int i;
305 int ret = 0;
306
307 for (i = 0; i < nr; i++) {
308 ret = request_irq(irqs[i], armv7_pmnc_interrupt,
309 IRQF_DISABLED, "CP15 PMNC", NULL);
310 if (ret != 0) {
311 printk(KERN_ERR "oprofile: unable to request IRQ%u"
312 " for ARMv7\n",
313 irqs[i]);
314 break;
315 }
316 }
317
318 if (i != nr)
319 while (i-- != 0)
320 free_irq(irqs[i], NULL);
321
322 return ret;
323}
324
325void armv7_release_interrupts(int *irqs, int nr)
326{
327 unsigned int i;
328
329 for (i = 0; i < nr; i++)
330 free_irq(irqs[i], NULL);
331}
332
333#ifdef DEBUG
334static void armv7_pmnc_dump_regs(void)
335{
336 u32 val;
337 unsigned int cnt;
338
339 printk(KERN_INFO "PMNC registers dump:\n");
340
341 asm volatile("mrc p15, 0, %0, c9, c12, 0" : "=r" (val));
342 printk(KERN_INFO "PMNC =0x%08x\n", val);
343
344 asm volatile("mrc p15, 0, %0, c9, c12, 1" : "=r" (val));
345 printk(KERN_INFO "CNTENS=0x%08x\n", val);
346
347 asm volatile("mrc p15, 0, %0, c9, c14, 1" : "=r" (val));
348 printk(KERN_INFO "INTENS=0x%08x\n", val);
349
350 asm volatile("mrc p15, 0, %0, c9, c12, 3" : "=r" (val));
351 printk(KERN_INFO "FLAGS =0x%08x\n", val);
352
353 asm volatile("mrc p15, 0, %0, c9, c12, 5" : "=r" (val));
354 printk(KERN_INFO "SELECT=0x%08x\n", val);
355
356 asm volatile("mrc p15, 0, %0, c9, c13, 0" : "=r" (val));
357 printk(KERN_INFO "CCNT =0x%08x\n", val);
358
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);
365 }
366}
367#endif
368
369
370static int irqs[] = {
371#ifdef CONFIG_ARCH_OMAP3
372 INT_34XX_BENCH_MPU_EMUL,
373#endif
374};
375
376static void armv7_pmnc_stop(void)
377{
378#ifdef DEBUG
379 armv7_pmnc_dump_regs();
380#endif
381 armv7_stop_pmnc();
382 armv7_release_interrupts(irqs, ARRAY_SIZE(irqs));
383}
384
385static int armv7_pmnc_start(void)
386{
387 int ret;
388
389#ifdef DEBUG
390 armv7_pmnc_dump_regs();
391#endif
392 ret = armv7_request_interrupts(irqs, ARRAY_SIZE(irqs));
393 if (ret >= 0)
394 armv7_start_pmnc();
395
396 return ret;
397}
398
399static int armv7_detect_pmnc(void)
400{
401 return 0;
402}
403
404struct op_arm_model_spec op_armv7_spec = {
405 .init = armv7_detect_pmnc,
406 .num_counters = 5,
407 .setup_ctrs = armv7_setup_pmnc,
408 .start = armv7_pmnc_start,
409 .stop = armv7_pmnc_stop,
410 .name = "arm/armv7",
411};
diff --git a/arch/arm/oprofile/op_model_v7.h b/arch/arm/oprofile/op_model_v7.h
new file mode 100644
index 000000000000..0e19bcc2e100
--- /dev/null
+++ b/arch/arm/oprofile/op_model_v7.h
@@ -0,0 +1,103 @@
1/**
2 * op_model_v7.h
3 * ARM v7 (Cortex A8) Event Monitor Driver
4 *
5 * Copyright 2008 Jean Pihet <jpihet@mvista.com>
6 * Copyright 2004 ARM SMP Development Team
7 * Copyright 2000-2004 Deepak Saxena <dsaxena@mvista.com>
8 * Copyright 2000-2004 MontaVista Software Inc
9 * Copyright 2004 Dave Jiang <dave.jiang@intel.com>
10 * Copyright 2004 Intel Corporation
11 * Copyright 2004 Zwane Mwaikambo <zwane@arm.linux.org.uk>
12 * Copyright 2004 Oprofile Authors
13 *
14 * Read the file COPYING
15 *
16 * This program is free software; you can redistribute it and/or modify
17 * it under the terms of the GNU General Public License version 2 as
18 * published by the Free Software Foundation.
19 */
20#ifndef OP_MODEL_V7_H
21#define OP_MODEL_V7_H
22
23/*
24 * Per-CPU PMNC: config reg
25 */
26#define PMNC_E (1 << 0) /* Enable all counters */
27#define PMNC_P (1 << 1) /* Reset all counters */
28#define PMNC_C (1 << 2) /* Cycle counter reset */
29#define PMNC_D (1 << 3) /* CCNT counts every 64th cpu cycle */
30#define PMNC_X (1 << 4) /* Export to ETM */
31#define PMNC_DP (1 << 5) /* Disable CCNT if non-invasive debug*/
32#define PMNC_MASK 0x3f /* Mask for writable bits */
33
34/*
35 * Available counters
36 */
37#define CCNT 0
38#define CNT0 1
39#define CNT1 2
40#define CNT2 3
41#define CNT3 4
42#define CNTMAX 5
43
44#define CPU_COUNTER(cpu, counter) ((cpu) * CNTMAX + (counter))
45
46/*
47 * CNTENS: counters enable reg
48 */
49#define CNTENS_P0 (1 << 0)
50#define CNTENS_P1 (1 << 1)
51#define CNTENS_P2 (1 << 2)
52#define CNTENS_P3 (1 << 3)
53#define CNTENS_C (1 << 31)
54#define CNTENS_MASK 0x8000000f /* Mask for writable bits */
55
56/*
57 * CNTENC: counters disable reg
58 */
59#define CNTENC_P0 (1 << 0)
60#define CNTENC_P1 (1 << 1)
61#define CNTENC_P2 (1 << 2)
62#define CNTENC_P3 (1 << 3)
63#define CNTENC_C (1 << 31)
64#define CNTENC_MASK 0x8000000f /* Mask for writable bits */
65
66/*
67 * INTENS: counters overflow interrupt enable reg
68 */
69#define INTENS_P0 (1 << 0)
70#define INTENS_P1 (1 << 1)
71#define INTENS_P2 (1 << 2)
72#define INTENS_P3 (1 << 3)
73#define INTENS_C (1 << 31)
74#define INTENS_MASK 0x8000000f /* Mask for writable bits */
75
76/*
77 * EVTSEL: Event selection reg
78 */
79#define EVTSEL_MASK 0x7f /* Mask for writable bits */
80
81/*
82 * SELECT: Counter selection reg
83 */
84#define SELECT_MASK 0x1f /* Mask for writable bits */
85
86/*
87 * FLAG: counters overflow flag status reg
88 */
89#define FLAG_P0 (1 << 0)
90#define FLAG_P1 (1 << 1)
91#define FLAG_P2 (1 << 2)
92#define FLAG_P3 (1 << 3)
93#define FLAG_C (1 << 31)
94#define FLAG_MASK 0x8000000f /* Mask for writable bits */
95
96
97int armv7_setup_pmu(void);
98int armv7_start_pmu(void);
99int armv7_stop_pmu(void);
100int armv7_request_interrupts(int *, int);
101void armv7_release_interrupts(int *, int);
102
103#endif