Merge branch 'reiserfs/kill-bkl' of git://git.kernel.org/pub/scm/linux/kernel/git...
[pandora-kernel.git] / arch / arm / oprofile / op_model_v7.c
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
30 static u32 cnt_en[CNTMAX];
31
32 static 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
38 static 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
46 static 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
67 static 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
88 static 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
109 static 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
123 static 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
139 static 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
147 static 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
184 int 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         /* Initialize & Reset PMNC: C bit and P bit */
195         armv7_pmnc_write(PMNC_P | PMNC_C);
196
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
240 static inline void armv7_start_pmnc(void)
241 {
242         armv7_pmnc_write(armv7_pmnc_read() | PMNC_E);
243 }
244
245 static 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  */
253 static 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
298 int 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
321 void 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
330 static 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
366 static int irqs[] = {
367 #ifdef CONFIG_ARCH_OMAP3
368         INT_34XX_BENCH_MPU_EMUL,
369 #endif
370 };
371
372 static 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
381 static 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
395 static int armv7_detect_pmnc(void)
396 {
397         return 0;
398 }
399
400 struct 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 };