Linux 2.6.31.6
[linux/fpc-iii.git] / arch / arm / oprofile / op_model_v7.c
blobf20295f14adbdd49ec3751c693120fd15339dbc1
1 /**
2 * op_model_v7.c
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"
23 /* #define DEBUG */
27 * ARM V7 PMNC support
30 static u32 cnt_en[CNTMAX];
32 static inline void armv7_pmnc_write(u32 val)
34 val &= PMNC_MASK;
35 asm volatile("mcr p15, 0, %0, c9, c12, 0" : : "r" (val));
38 static inline u32 armv7_pmnc_read(void)
40 u32 val;
42 asm volatile("mrc p15, 0, %0, c9, c12, 0" : "=r" (val));
43 return val;
46 static inline u32 armv7_pmnc_enable_counter(unsigned int cnt)
48 u32 val;
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;
56 if (cnt == CCNT)
57 val = CNTENS_C;
58 else
59 val = (1 << (cnt - CNT0));
61 val &= CNTENS_MASK;
62 asm volatile("mcr p15, 0, %0, c9, c12, 1" : : "r" (val));
64 return cnt;
67 static inline u32 armv7_pmnc_disable_counter(unsigned int cnt)
69 u32 val;
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;
77 if (cnt == CCNT)
78 val = CNTENC_C;
79 else
80 val = (1 << (cnt - CNT0));
82 val &= CNTENC_MASK;
83 asm volatile("mcr p15, 0, %0, c9, c12, 2" : : "r" (val));
85 return cnt;
88 static inline u32 armv7_pmnc_enable_intens(unsigned int cnt)
90 u32 val;
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;
98 if (cnt == CCNT)
99 val = INTENS_C;
100 else
101 val = (1 << (cnt - CNT0));
103 val &= INTENS_MASK;
104 asm volatile("mcr p15, 0, %0, c9, c14, 1" : : "r" (val));
106 return cnt;
109 static inline u32 armv7_pmnc_getreset_flags(void)
111 u32 val;
113 /* Read */
114 asm volatile("mrc p15, 0, %0, c9, c12, 3" : "=r" (val));
116 /* Write to clear flags */
117 val &= FLAG_MASK;
118 asm volatile("mcr p15, 0, %0, c9, c12, 3" : : "r" (val));
120 return val;
123 static inline int armv7_pmnc_select_counter(unsigned int cnt)
125 u32 val;
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;
133 val = (cnt - CNT0) & SELECT_MASK;
134 asm volatile("mcr p15, 0, %0, c9, c12, 5" : : "r" (val));
136 return cnt;
139 static inline void armv7_pmnc_write_evtsel(unsigned int cnt, u32 val)
141 if (armv7_pmnc_select_counter(cnt) == cnt) {
142 val &= EVTSEL_MASK;
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;
152 switch (cnt) {
153 case CCNT:
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);
161 break;
163 case CNT0:
164 case CNT1:
165 case CNT2:
166 case CNT3:
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);
175 break;
177 default:
178 printk(KERN_ERR "oprofile: CPU%u resetting wrong PMNC counter"
179 " %d\n", smp_processor_id(), cnt);
180 break;
184 int armv7_setup_pmnc(void)
186 unsigned int cnt;
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;
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++) {
203 unsigned long event;
204 u32 cpu_cnt = CPU_COUNTER(smp_processor_id(), cnt);
207 * Disable counter
209 armv7_pmnc_disable_counter(cnt);
210 cnt_en[cnt] = 0;
212 if (!counter_config[cpu_cnt].enabled)
213 continue;
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
221 if (cnt != CCNT)
222 armv7_pmnc_write_evtsel(cnt, event);
225 * Enable interrupt for this counter
227 armv7_pmnc_enable_intens(cnt);
230 * Reset counter
232 armv7_pmnc_reset_counter(cnt);
235 * Enable counter
237 armv7_pmnc_enable_counter(cnt);
238 cnt_en[cnt] = 1;
241 return 0;
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();
260 unsigned int cnt;
261 u32 flags;
265 * Stop IRQ generation
267 armv7_stop_pmnc();
270 * Get and reset overflow status flags
272 flags = armv7_pmnc_getreset_flags();
275 * Cycle counter
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);
284 * PMNC counters 0:3
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
297 armv7_start_pmnc();
299 return IRQ_HANDLED;
302 int armv7_request_interrupts(int *irqs, int nr)
304 unsigned int i;
305 int ret = 0;
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;
318 if (i != nr)
319 while (i-- != 0)
320 free_irq(irqs[i], NULL);
322 return ret;
325 void armv7_release_interrupts(int *irqs, int nr)
327 unsigned int i;
329 for (i = 0; i < nr; i++)
330 free_irq(irqs[i], NULL);
333 #ifdef DEBUG
334 static void armv7_pmnc_dump_regs(void)
336 u32 val;
337 unsigned int cnt;
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);
367 #endif
370 static int irqs[] = {
371 #ifdef CONFIG_ARCH_OMAP3
372 INT_34XX_BENCH_MPU_EMUL,
373 #endif
376 static void armv7_pmnc_stop(void)
378 #ifdef DEBUG
379 armv7_pmnc_dump_regs();
380 #endif
381 armv7_stop_pmnc();
382 armv7_release_interrupts(irqs, ARRAY_SIZE(irqs));
385 static int armv7_pmnc_start(void)
387 int ret;
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();
396 return ret;
399 static int armv7_detect_pmnc(void)
401 return 0;
404 struct 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",