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/err.h>
15 #include <linux/oprofile.h>
16 #include <linux/interrupt.h>
17 #include <linux/irq.h>
18 #include <linux/smp.h>
22 #include "op_counter.h"
23 #include "op_arm_model.h"
24 #include "op_model_v7.h"
33 static u32 cnt_en
[CNTMAX
];
35 static inline void armv7_pmnc_write(u32 val
)
38 asm volatile("mcr p15, 0, %0, c9, c12, 0" : : "r" (val
));
41 static inline u32
armv7_pmnc_read(void)
45 asm volatile("mrc p15, 0, %0, c9, c12, 0" : "=r" (val
));
49 static inline u32
armv7_pmnc_enable_counter(unsigned int cnt
)
54 printk(KERN_ERR
"oprofile: CPU%u enabling wrong PMNC counter"
55 " %d\n", smp_processor_id(), cnt
);
62 val
= (1 << (cnt
- CNT0
));
65 asm volatile("mcr p15, 0, %0, c9, c12, 1" : : "r" (val
));
70 static inline u32
armv7_pmnc_disable_counter(unsigned int cnt
)
75 printk(KERN_ERR
"oprofile: CPU%u disabling wrong PMNC counter"
76 " %d\n", smp_processor_id(), cnt
);
83 val
= (1 << (cnt
- CNT0
));
86 asm volatile("mcr p15, 0, %0, c9, c12, 2" : : "r" (val
));
91 static inline u32
armv7_pmnc_enable_intens(unsigned int cnt
)
96 printk(KERN_ERR
"oprofile: CPU%u enabling wrong PMNC counter"
97 " interrupt enable %d\n", smp_processor_id(), cnt
);
104 val
= (1 << (cnt
- CNT0
));
107 asm volatile("mcr p15, 0, %0, c9, c14, 1" : : "r" (val
));
112 static inline u32
armv7_pmnc_getreset_flags(void)
117 asm volatile("mrc p15, 0, %0, c9, c12, 3" : "=r" (val
));
119 /* Write to clear flags */
121 asm volatile("mcr p15, 0, %0, c9, c12, 3" : : "r" (val
));
126 static inline int armv7_pmnc_select_counter(unsigned int cnt
)
130 if ((cnt
== CCNT
) || (cnt
>= CNTMAX
)) {
131 printk(KERN_ERR
"oprofile: CPU%u selecting wrong PMNC counteri"
132 " %d\n", smp_processor_id(), cnt
);
136 val
= (cnt
- CNT0
) & SELECT_MASK
;
137 asm volatile("mcr p15, 0, %0, c9, c12, 5" : : "r" (val
));
142 static inline void armv7_pmnc_write_evtsel(unsigned int cnt
, u32 val
)
144 if (armv7_pmnc_select_counter(cnt
) == cnt
) {
146 asm volatile("mcr p15, 0, %0, c9, c13, 1" : : "r" (val
));
150 static void armv7_pmnc_reset_counter(unsigned int cnt
)
152 u32 cpu_cnt
= CPU_COUNTER(smp_processor_id(), cnt
);
153 u32 val
= -(u32
)counter_config
[cpu_cnt
].count
;
157 armv7_pmnc_disable_counter(cnt
);
159 asm volatile("mcr p15, 0, %0, c9, c13, 0" : : "r" (val
));
161 if (cnt_en
[cnt
] != 0)
162 armv7_pmnc_enable_counter(cnt
);
170 armv7_pmnc_disable_counter(cnt
);
172 if (armv7_pmnc_select_counter(cnt
) == cnt
)
173 asm volatile("mcr p15, 0, %0, c9, c13, 2" : : "r" (val
));
175 if (cnt_en
[cnt
] != 0)
176 armv7_pmnc_enable_counter(cnt
);
181 printk(KERN_ERR
"oprofile: CPU%u resetting wrong PMNC counter"
182 " %d\n", smp_processor_id(), cnt
);
187 int armv7_setup_pmnc(void)
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());
197 /* Initialize & Reset PMNC: C bit and P bit */
198 armv7_pmnc_write(PMNC_P
| PMNC_C
);
201 for (cnt
= CCNT
; cnt
< CNTMAX
; cnt
++) {
203 u32 cpu_cnt
= CPU_COUNTER(smp_processor_id(), cnt
);
208 armv7_pmnc_disable_counter(cnt
);
211 if (!counter_config
[cpu_cnt
].enabled
)
214 event
= counter_config
[cpu_cnt
].event
& 255;
217 * Set event (if destined for PMNx counters)
218 * We don't need to set the event if it's a cycle count
221 armv7_pmnc_write_evtsel(cnt
, event
);
224 * Enable interrupt for this counter
226 armv7_pmnc_enable_intens(cnt
);
231 armv7_pmnc_reset_counter(cnt
);
236 armv7_pmnc_enable_counter(cnt
);
243 static inline void armv7_start_pmnc(void)
245 armv7_pmnc_write(armv7_pmnc_read() | PMNC_E
);
248 static inline void armv7_stop_pmnc(void)
250 armv7_pmnc_write(armv7_pmnc_read() & ~PMNC_E
);
254 * CPU counters' IRQ handler (one IRQ per CPU)
256 static irqreturn_t
armv7_pmnc_interrupt(int irq
, void *arg
)
258 struct pt_regs
*regs
= get_irq_regs();
264 * Stop IRQ generation
269 * Get and reset overflow status flags
271 flags
= armv7_pmnc_getreset_flags();
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
);
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
);
294 * Allow IRQ generation
301 int armv7_request_interrupts(const int *irqs
, int nr
)
306 for (i
= 0; i
< nr
; i
++) {
307 ret
= request_irq(irqs
[i
], armv7_pmnc_interrupt
,
308 IRQF_DISABLED
, "CP15 PMNC", NULL
);
310 printk(KERN_ERR
"oprofile: unable to request IRQ%u"
319 free_irq(irqs
[i
], NULL
);
324 void armv7_release_interrupts(const int *irqs
, int nr
)
328 for (i
= 0; i
< nr
; i
++)
329 free_irq(irqs
[i
], NULL
);
333 static void armv7_pmnc_dump_regs(void)
338 printk(KERN_INFO
"PMNC registers dump:\n");
340 asm volatile("mrc p15, 0, %0, c9, c12, 0" : "=r" (val
));
341 printk(KERN_INFO
"PMNC =0x%08x\n", val
);
343 asm volatile("mrc p15, 0, %0, c9, c12, 1" : "=r" (val
));
344 printk(KERN_INFO
"CNTENS=0x%08x\n", val
);
346 asm volatile("mrc p15, 0, %0, c9, c14, 1" : "=r" (val
));
347 printk(KERN_INFO
"INTENS=0x%08x\n", val
);
349 asm volatile("mrc p15, 0, %0, c9, c12, 3" : "=r" (val
));
350 printk(KERN_INFO
"FLAGS =0x%08x\n", val
);
352 asm volatile("mrc p15, 0, %0, c9, c12, 5" : "=r" (val
));
353 printk(KERN_INFO
"SELECT=0x%08x\n", val
);
355 asm volatile("mrc p15, 0, %0, c9, c13, 0" : "=r" (val
));
356 printk(KERN_INFO
"CCNT =0x%08x\n", val
);
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
);
368 static const struct pmu_irqs
*pmu_irqs
;
370 static void armv7_pmnc_stop(void)
373 armv7_pmnc_dump_regs();
376 armv7_release_interrupts(pmu_irqs
->irqs
, pmu_irqs
->num_irqs
);
377 release_pmu(pmu_irqs
);
381 static int armv7_pmnc_start(void)
385 pmu_irqs
= reserve_pmu();
386 if (IS_ERR(pmu_irqs
))
387 return PTR_ERR(pmu_irqs
);
390 armv7_pmnc_dump_regs();
392 ret
= armv7_request_interrupts(pmu_irqs
->irqs
, pmu_irqs
->num_irqs
);
396 release_pmu(pmu_irqs
);
403 static int armv7_detect_pmnc(void)
408 struct op_arm_model_spec op_armv7_spec
= {
409 .init
= armv7_detect_pmnc
,
411 .setup_ctrs
= armv7_setup_pmnc
,
412 .start
= armv7_pmnc_start
,
413 .stop
= armv7_pmnc_stop
,