blob: 2088a6c0cc0e36f3e8ba23239f1a62fbb80e4858 [file] [log] [blame]
Jean PIHETd7ac4e22008-08-12 19:07:39 +01001/**
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
Jean PIHET3321c2b2009-12-15 12:36:10 +0100194 /* Initialize & Reset PMNC: C bit and P bit */
195 armv7_pmnc_write(PMNC_P | PMNC_C);
Jean PIHETd7ac4e22008-08-12 19:07:39 +0100196
197
198 for (cnt = CCNT; cnt < CNTMAX; cnt++) {
199 unsigned long event;
200 u32 cpu_cnt = CPU_COUNTER(smp_processor_id(), cnt);
201
202 /*
203 * Disable counter
204 */
205 armv7_pmnc_disable_counter(cnt);
206 cnt_en[cnt] = 0;
207
208 if (!counter_config[cpu_cnt].enabled)
209 continue;
210
211 event = counter_config[cpu_cnt].event & 255;
212
213 /*
214 * Set event (if destined for PMNx counters)
215 * We don't need to set the event if it's a cycle count
216 */
217 if (cnt != CCNT)
218 armv7_pmnc_write_evtsel(cnt, event);
219
220 /*
221 * Enable interrupt for this counter
222 */
223 armv7_pmnc_enable_intens(cnt);
224
225 /*
226 * Reset counter
227 */
228 armv7_pmnc_reset_counter(cnt);
229
230 /*
231 * Enable counter
232 */
233 armv7_pmnc_enable_counter(cnt);
234 cnt_en[cnt] = 1;
235 }
236
237 return 0;
238}
239
240static inline void armv7_start_pmnc(void)
241{
242 armv7_pmnc_write(armv7_pmnc_read() | PMNC_E);
243}
244
245static inline void armv7_stop_pmnc(void)
246{
247 armv7_pmnc_write(armv7_pmnc_read() & ~PMNC_E);
248}
249
250/*
251 * CPU counters' IRQ handler (one IRQ per CPU)
252 */
253static irqreturn_t armv7_pmnc_interrupt(int irq, void *arg)
254{
255 struct pt_regs *regs = get_irq_regs();
256 unsigned int cnt;
257 u32 flags;
258
259
260 /*
261 * Stop IRQ generation
262 */
263 armv7_stop_pmnc();
264
265 /*
266 * Get and reset overflow status flags
267 */
268 flags = armv7_pmnc_getreset_flags();
269
270 /*
271 * Cycle counter
272 */
273 if (flags & FLAG_C) {
274 u32 cpu_cnt = CPU_COUNTER(smp_processor_id(), CCNT);
275 armv7_pmnc_reset_counter(CCNT);
276 oprofile_add_sample(regs, cpu_cnt);
277 }
278
279 /*
280 * PMNC counters 0:3
281 */
282 for (cnt = CNT0; cnt < CNTMAX; cnt++) {
283 if (flags & (1 << (cnt - CNT0))) {
284 u32 cpu_cnt = CPU_COUNTER(smp_processor_id(), cnt);
285 armv7_pmnc_reset_counter(cnt);
286 oprofile_add_sample(regs, cpu_cnt);
287 }
288 }
289
290 /*
291 * Allow IRQ generation
292 */
293 armv7_start_pmnc();
294
295 return IRQ_HANDLED;
296}
297
298int armv7_request_interrupts(int *irqs, int nr)
299{
300 unsigned int i;
301 int ret = 0;
302
303 for (i = 0; i < nr; i++) {
304 ret = request_irq(irqs[i], armv7_pmnc_interrupt,
305 IRQF_DISABLED, "CP15 PMNC", NULL);
306 if (ret != 0) {
307 printk(KERN_ERR "oprofile: unable to request IRQ%u"
308 " for ARMv7\n",
309 irqs[i]);
310 break;
311 }
312 }
313
314 if (i != nr)
315 while (i-- != 0)
316 free_irq(irqs[i], NULL);
317
318 return ret;
319}
320
321void armv7_release_interrupts(int *irqs, int nr)
322{
323 unsigned int i;
324
325 for (i = 0; i < nr; i++)
326 free_irq(irqs[i], NULL);
327}
328
329#ifdef DEBUG
330static void armv7_pmnc_dump_regs(void)
331{
332 u32 val;
333 unsigned int cnt;
334
335 printk(KERN_INFO "PMNC registers dump:\n");
336
337 asm volatile("mrc p15, 0, %0, c9, c12, 0" : "=r" (val));
338 printk(KERN_INFO "PMNC =0x%08x\n", val);
339
340 asm volatile("mrc p15, 0, %0, c9, c12, 1" : "=r" (val));
341 printk(KERN_INFO "CNTENS=0x%08x\n", val);
342
343 asm volatile("mrc p15, 0, %0, c9, c14, 1" : "=r" (val));
344 printk(KERN_INFO "INTENS=0x%08x\n", val);
345
346 asm volatile("mrc p15, 0, %0, c9, c12, 3" : "=r" (val));
347 printk(KERN_INFO "FLAGS =0x%08x\n", val);
348
349 asm volatile("mrc p15, 0, %0, c9, c12, 5" : "=r" (val));
350 printk(KERN_INFO "SELECT=0x%08x\n", val);
351
352 asm volatile("mrc p15, 0, %0, c9, c13, 0" : "=r" (val));
353 printk(KERN_INFO "CCNT =0x%08x\n", val);
354
355 for (cnt = CNT0; cnt < CNTMAX; cnt++) {
356 armv7_pmnc_select_counter(cnt);
357 asm volatile("mrc p15, 0, %0, c9, c13, 2" : "=r" (val));
358 printk(KERN_INFO "CNT[%d] count =0x%08x\n", cnt-CNT0, val);
359 asm volatile("mrc p15, 0, %0, c9, c13, 1" : "=r" (val));
360 printk(KERN_INFO "CNT[%d] evtsel=0x%08x\n", cnt-CNT0, val);
361 }
362}
363#endif
364
365
366static int irqs[] = {
367#ifdef CONFIG_ARCH_OMAP3
368 INT_34XX_BENCH_MPU_EMUL,
369#endif
370};
371
372static void armv7_pmnc_stop(void)
373{
374#ifdef DEBUG
375 armv7_pmnc_dump_regs();
376#endif
377 armv7_stop_pmnc();
378 armv7_release_interrupts(irqs, ARRAY_SIZE(irqs));
379}
380
381static int armv7_pmnc_start(void)
382{
383 int ret;
384
385#ifdef DEBUG
386 armv7_pmnc_dump_regs();
387#endif
388 ret = armv7_request_interrupts(irqs, ARRAY_SIZE(irqs));
389 if (ret >= 0)
390 armv7_start_pmnc();
391
392 return ret;
393}
394
395static int armv7_detect_pmnc(void)
396{
397 return 0;
398}
399
400struct op_arm_model_spec op_armv7_spec = {
401 .init = armv7_detect_pmnc,
402 .num_counters = 5,
403 .setup_ctrs = armv7_setup_pmnc,
404 .start = armv7_pmnc_start,
405 .stop = armv7_pmnc_stop,
406 .name = "arm/armv7",
407};