udf: BKL ioctl pushdown
[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/err.h>
15 #include <linux/oprofile.h>
16 #include <linux/interrupt.h>
17 #include <linux/irq.h>
18 #include <linux/smp.h>
19
20 #include <asm/pmu.h>
21
22 #include "op_counter.h"
23 #include "op_arm_model.h"
24 #include "op_model_v7.h"
25
26 /* #define DEBUG */
27
28
29 /*
30  * ARM V7 PMNC support
31  */
32
33 static u32 cnt_en[CNTMAX];
34
35 static inline void armv7_pmnc_write(u32 val)
36 {
37         val &= PMNC_MASK;
38         asm volatile("mcr p15, 0, %0, c9, c12, 0" : : "r" (val));
39 }
40
41 static inline u32 armv7_pmnc_read(void)
42 {
43         u32 val;
44
45         asm volatile("mrc p15, 0, %0, c9, c12, 0" : "=r" (val));
46         return val;
47 }
48
49 static inline u32 armv7_pmnc_enable_counter(unsigned int cnt)
50 {
51         u32 val;
52
53         if (cnt >= CNTMAX) {
54                 printk(KERN_ERR "oprofile: CPU%u enabling wrong PMNC counter"
55                         " %d\n", smp_processor_id(), cnt);
56                 return -1;
57         }
58
59         if (cnt == CCNT)
60                 val = CNTENS_C;
61         else
62                 val = (1 << (cnt - CNT0));
63
64         val &= CNTENS_MASK;
65         asm volatile("mcr p15, 0, %0, c9, c12, 1" : : "r" (val));
66
67         return cnt;
68 }
69
70 static inline u32 armv7_pmnc_disable_counter(unsigned int cnt)
71 {
72         u32 val;
73
74         if (cnt >= CNTMAX) {
75                 printk(KERN_ERR "oprofile: CPU%u disabling wrong PMNC counter"
76                         " %d\n", smp_processor_id(), cnt);
77                 return -1;
78         }
79
80         if (cnt == CCNT)
81                 val = CNTENC_C;
82         else
83                 val = (1 << (cnt - CNT0));
84
85         val &= CNTENC_MASK;
86         asm volatile("mcr p15, 0, %0, c9, c12, 2" : : "r" (val));
87
88         return cnt;
89 }
90
91 static inline u32 armv7_pmnc_enable_intens(unsigned int cnt)
92 {
93         u32 val;
94
95         if (cnt >= CNTMAX) {
96                 printk(KERN_ERR "oprofile: CPU%u enabling wrong PMNC counter"
97                         " interrupt enable %d\n", smp_processor_id(), cnt);
98                 return -1;
99         }
100
101         if (cnt == CCNT)
102                 val = INTENS_C;
103         else
104                 val = (1 << (cnt - CNT0));
105
106         val &= INTENS_MASK;
107         asm volatile("mcr p15, 0, %0, c9, c14, 1" : : "r" (val));
108
109         return cnt;
110 }
111
112 static inline u32 armv7_pmnc_getreset_flags(void)
113 {
114         u32 val;
115
116         /* Read */
117         asm volatile("mrc p15, 0, %0, c9, c12, 3" : "=r" (val));
118
119         /* Write to clear flags */
120         val &= FLAG_MASK;
121         asm volatile("mcr p15, 0, %0, c9, c12, 3" : : "r" (val));
122
123         return val;
124 }
125
126 static inline int armv7_pmnc_select_counter(unsigned int cnt)
127 {
128         u32 val;
129
130         if ((cnt == CCNT) || (cnt >= CNTMAX)) {
131                 printk(KERN_ERR "oprofile: CPU%u selecting wrong PMNC counteri"
132                         " %d\n", smp_processor_id(), cnt);
133                 return -1;
134         }
135
136         val = (cnt - CNT0) & SELECT_MASK;
137         asm volatile("mcr p15, 0, %0, c9, c12, 5" : : "r" (val));
138
139         return cnt;
140 }
141
142 static inline void armv7_pmnc_write_evtsel(unsigned int cnt, u32 val)
143 {
144         if (armv7_pmnc_select_counter(cnt) == cnt) {
145                 val &= EVTSEL_MASK;
146                 asm volatile("mcr p15, 0, %0, c9, c13, 1" : : "r" (val));
147         }
148 }
149
150 static void armv7_pmnc_reset_counter(unsigned int cnt)
151 {
152         u32 cpu_cnt = CPU_COUNTER(smp_processor_id(), cnt);
153         u32 val = -(u32)counter_config[cpu_cnt].count;
154
155         switch (cnt) {
156         case CCNT:
157                 armv7_pmnc_disable_counter(cnt);
158
159                 asm volatile("mcr p15, 0, %0, c9, c13, 0" : : "r" (val));
160
161                 if (cnt_en[cnt] != 0)
162                     armv7_pmnc_enable_counter(cnt);
163
164                 break;
165
166         case CNT0:
167         case CNT1:
168         case CNT2:
169         case CNT3:
170                 armv7_pmnc_disable_counter(cnt);
171
172                 if (armv7_pmnc_select_counter(cnt) == cnt)
173                     asm volatile("mcr p15, 0, %0, c9, c13, 2" : : "r" (val));
174
175                 if (cnt_en[cnt] != 0)
176                     armv7_pmnc_enable_counter(cnt);
177
178                 break;
179
180         default:
181                 printk(KERN_ERR "oprofile: CPU%u resetting wrong PMNC counter"
182                         " %d\n", smp_processor_id(), cnt);
183                 break;
184         }
185 }
186
187 int armv7_setup_pmnc(void)
188 {
189         unsigned int cnt;
190
191         if (armv7_pmnc_read() & PMNC_E) {
192                 printk(KERN_ERR "oprofile: CPU%u PMNC still enabled when setup"
193                         " new event counter.\n", smp_processor_id());
194                 return -EBUSY;
195         }
196
197         /* Initialize & Reset PMNC: C bit and P bit */
198         armv7_pmnc_write(PMNC_P | PMNC_C);
199
200
201         for (cnt = CCNT; cnt < CNTMAX; cnt++) {
202                 unsigned long event;
203                 u32 cpu_cnt = CPU_COUNTER(smp_processor_id(), cnt);
204
205                 /*
206                  * Disable counter
207                  */
208                 armv7_pmnc_disable_counter(cnt);
209                 cnt_en[cnt] = 0;
210
211                 if (!counter_config[cpu_cnt].enabled)
212                         continue;
213
214                 event = counter_config[cpu_cnt].event & 255;
215
216                 /*
217                  * Set event (if destined for PMNx counters)
218                  * We don't need to set the event if it's a cycle count
219                  */
220                 if (cnt != CCNT)
221                         armv7_pmnc_write_evtsel(cnt, event);
222
223                 /*
224                  * Enable interrupt for this counter
225                  */
226                 armv7_pmnc_enable_intens(cnt);
227
228                 /*
229                  * Reset counter
230                  */
231                 armv7_pmnc_reset_counter(cnt);
232
233                 /*
234                  * Enable counter
235                  */
236                 armv7_pmnc_enable_counter(cnt);
237                 cnt_en[cnt] = 1;
238         }
239
240         return 0;
241 }
242
243 static inline void armv7_start_pmnc(void)
244 {
245         armv7_pmnc_write(armv7_pmnc_read() | PMNC_E);
246 }
247
248 static inline void armv7_stop_pmnc(void)
249 {
250         armv7_pmnc_write(armv7_pmnc_read() & ~PMNC_E);
251 }
252
253 /*
254  * CPU counters' IRQ handler (one IRQ per CPU)
255  */
256 static irqreturn_t armv7_pmnc_interrupt(int irq, void *arg)
257 {
258         struct pt_regs *regs = get_irq_regs();
259         unsigned int cnt;
260         u32 flags;
261
262
263         /*
264          * Stop IRQ generation
265          */
266         armv7_stop_pmnc();
267
268         /*
269          * Get and reset overflow status flags
270          */
271         flags = armv7_pmnc_getreset_flags();
272
273         /*
274          * Cycle counter
275          */
276         if (flags & FLAG_C) {
277                 u32 cpu_cnt = CPU_COUNTER(smp_processor_id(), CCNT);
278                 armv7_pmnc_reset_counter(CCNT);
279                 oprofile_add_sample(regs, cpu_cnt);
280         }
281
282         /*
283          * PMNC counters 0:3
284          */
285         for (cnt = CNT0; cnt < CNTMAX; cnt++) {
286                 if (flags & (1 << (cnt - CNT0))) {
287                         u32 cpu_cnt = CPU_COUNTER(smp_processor_id(), cnt);
288                         armv7_pmnc_reset_counter(cnt);
289                         oprofile_add_sample(regs, cpu_cnt);
290                 }
291         }
292
293         /*
294          * Allow IRQ generation
295          */
296         armv7_start_pmnc();
297
298         return IRQ_HANDLED;
299 }
300
301 int armv7_request_interrupts(const int *irqs, int nr)
302 {
303         unsigned int i;
304         int ret = 0;
305
306         for (i = 0; i < nr; i++) {
307                 ret = request_irq(irqs[i], armv7_pmnc_interrupt,
308                                 IRQF_DISABLED, "CP15 PMNC", NULL);
309                 if (ret != 0) {
310                         printk(KERN_ERR "oprofile: unable to request IRQ%u"
311                                 " for ARMv7\n",
312                                irqs[i]);
313                         break;
314                 }
315         }
316
317         if (i != nr)
318                 while (i-- != 0)
319                         free_irq(irqs[i], NULL);
320
321         return ret;
322 }
323
324 void armv7_release_interrupts(const int *irqs, int nr)
325 {
326         unsigned int i;
327
328         for (i = 0; i < nr; i++)
329                 free_irq(irqs[i], NULL);
330 }
331
332 #ifdef DEBUG
333 static void armv7_pmnc_dump_regs(void)
334 {
335         u32 val;
336         unsigned int cnt;
337
338         printk(KERN_INFO "PMNC registers dump:\n");
339
340         asm volatile("mrc p15, 0, %0, c9, c12, 0" : "=r" (val));
341         printk(KERN_INFO "PMNC  =0x%08x\n", val);
342
343         asm volatile("mrc p15, 0, %0, c9, c12, 1" : "=r" (val));
344         printk(KERN_INFO "CNTENS=0x%08x\n", val);
345
346         asm volatile("mrc p15, 0, %0, c9, c14, 1" : "=r" (val));
347         printk(KERN_INFO "INTENS=0x%08x\n", val);
348
349         asm volatile("mrc p15, 0, %0, c9, c12, 3" : "=r" (val));
350         printk(KERN_INFO "FLAGS =0x%08x\n", val);
351
352         asm volatile("mrc p15, 0, %0, c9, c12, 5" : "=r" (val));
353         printk(KERN_INFO "SELECT=0x%08x\n", val);
354
355         asm volatile("mrc p15, 0, %0, c9, c13, 0" : "=r" (val));
356         printk(KERN_INFO "CCNT  =0x%08x\n", val);
357
358         for (cnt = CNT0; cnt < CNTMAX; cnt++) {
359                 armv7_pmnc_select_counter(cnt);
360                 asm volatile("mrc p15, 0, %0, c9, c13, 2" : "=r" (val));
361                 printk(KERN_INFO "CNT[%d] count =0x%08x\n", cnt-CNT0, val);
362                 asm volatile("mrc p15, 0, %0, c9, c13, 1" : "=r" (val));
363                 printk(KERN_INFO "CNT[%d] evtsel=0x%08x\n", cnt-CNT0, val);
364         }
365 }
366 #endif
367
368 static const struct pmu_irqs *pmu_irqs;
369
370 static void armv7_pmnc_stop(void)
371 {
372 #ifdef DEBUG
373         armv7_pmnc_dump_regs();
374 #endif
375         armv7_stop_pmnc();
376         armv7_release_interrupts(pmu_irqs->irqs, pmu_irqs->num_irqs);
377         release_pmu(pmu_irqs);
378         pmu_irqs = NULL;
379 }
380
381 static int armv7_pmnc_start(void)
382 {
383         int ret;
384
385         pmu_irqs = reserve_pmu();
386         if (IS_ERR(pmu_irqs))
387                 return PTR_ERR(pmu_irqs);
388
389 #ifdef DEBUG
390         armv7_pmnc_dump_regs();
391 #endif
392         ret = armv7_request_interrupts(pmu_irqs->irqs, pmu_irqs->num_irqs);
393         if (ret >= 0) {
394                 armv7_start_pmnc();
395         } else {
396                 release_pmu(pmu_irqs);
397                 pmu_irqs = NULL;
398         }
399
400         return ret;
401 }
402
403 static int armv7_detect_pmnc(void)
404 {
405         return 0;
406 }
407
408 struct op_arm_model_spec op_armv7_spec = {
409         .init           = armv7_detect_pmnc,
410         .num_counters   = 5,
411         .setup_ctrs     = armv7_setup_pmnc,
412         .start          = armv7_pmnc_start,
413         .stop           = armv7_pmnc_stop,
414         .name           = "arm/armv7",
415 };