blob: 8642d0891ae15eac8f2de05e6b02a381ba7cf131 [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>
Jamie Iles1618fdd2010-02-02 20:24:07 +010014#include <linux/err.h>
Jean PIHETd7ac4e22008-08-12 19:07:39 +010015#include <linux/oprofile.h>
16#include <linux/interrupt.h>
17#include <linux/irq.h>
18#include <linux/smp.h>
19
Jamie Iles1618fdd2010-02-02 20:24:07 +010020#include <asm/pmu.h>
21
Jean PIHETd7ac4e22008-08-12 19:07:39 +010022#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
33static u32 cnt_en[CNTMAX];
34
35static 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
41static 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
49static 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
70static 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
91static 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
112static 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
126static 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
142static 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
150static 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
187int 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
Jean PIHET3321c2b2009-12-15 12:36:10 +0100197 /* Initialize & Reset PMNC: C bit and P bit */
198 armv7_pmnc_write(PMNC_P | PMNC_C);
Jean PIHETd7ac4e22008-08-12 19:07:39 +0100199
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
243static inline void armv7_start_pmnc(void)
244{
245 armv7_pmnc_write(armv7_pmnc_read() | PMNC_E);
246}
247
248static 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 */
256static 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
Jamie Iles1618fdd2010-02-02 20:24:07 +0100301int armv7_request_interrupts(const int *irqs, int nr)
Jean PIHETd7ac4e22008-08-12 19:07:39 +0100302{
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
Jamie Iles1618fdd2010-02-02 20:24:07 +0100324void armv7_release_interrupts(const int *irqs, int nr)
Jean PIHETd7ac4e22008-08-12 19:07:39 +0100325{
326 unsigned int i;
327
328 for (i = 0; i < nr; i++)
329 free_irq(irqs[i], NULL);
330}
331
332#ifdef DEBUG
333static 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
Jamie Iles1618fdd2010-02-02 20:24:07 +0100368static const struct pmu_irqs *pmu_irqs;
Jean PIHETd7ac4e22008-08-12 19:07:39 +0100369
370static void armv7_pmnc_stop(void)
371{
372#ifdef DEBUG
373 armv7_pmnc_dump_regs();
374#endif
375 armv7_stop_pmnc();
Jamie Iles1618fdd2010-02-02 20:24:07 +0100376 armv7_release_interrupts(pmu_irqs->irqs, pmu_irqs->num_irqs);
377 release_pmu(pmu_irqs);
378 pmu_irqs = NULL;
Jean PIHETd7ac4e22008-08-12 19:07:39 +0100379}
380
381static int armv7_pmnc_start(void)
382{
383 int ret;
384
Jamie Iles1618fdd2010-02-02 20:24:07 +0100385 pmu_irqs = reserve_pmu();
386 if (IS_ERR(pmu_irqs))
387 return PTR_ERR(pmu_irqs);
388
Jean PIHETd7ac4e22008-08-12 19:07:39 +0100389#ifdef DEBUG
390 armv7_pmnc_dump_regs();
391#endif
Jamie Iles1618fdd2010-02-02 20:24:07 +0100392 ret = armv7_request_interrupts(pmu_irqs->irqs, pmu_irqs->num_irqs);
393 if (ret >= 0) {
Jean PIHETd7ac4e22008-08-12 19:07:39 +0100394 armv7_start_pmnc();
Jamie Iles1618fdd2010-02-02 20:24:07 +0100395 } else {
396 release_pmu(pmu_irqs);
397 pmu_irqs = NULL;
398 }
Jean PIHETd7ac4e22008-08-12 19:07:39 +0100399
400 return ret;
401}
402
403static int armv7_detect_pmnc(void)
404{
405 return 0;
406}
407
408struct 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};