Merge branches 'sh/serial-rework' and 'sh/oprofile'
[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         /*
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
244 static inline void armv7_start_pmnc(void)
245 {
246         armv7_pmnc_write(armv7_pmnc_read() | PMNC_E);
247 }
248
249 static 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  */
257 static 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
302 int 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
325 void 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
334 static 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
370 static int irqs[] = {
371 #ifdef CONFIG_ARCH_OMAP3
372         INT_34XX_BENCH_MPU_EMUL,
373 #endif
374 };
375
376 static 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
385 static 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
399 static int armv7_detect_pmnc(void)
400 {
401         return 0;
402 }
403
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",
411 };