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/oprofile.h>
15 #include <linux/interrupt.h>
16 #include <linux/irq.h>
17 #include <linux/smp.h>
19 #include "op_counter.h"
20 #include "op_arm_model.h"
21 #include "op_model_v7.h"
30 static u32 cnt_en[CNTMAX];
32 static inline void armv7_pmnc_write(u32 val)
35 asm volatile("mcr p15, 0, %0, c9, c12, 0" : : "r" (val));
38 static inline u32 armv7_pmnc_read(void)
42 asm volatile("mrc p15, 0, %0, c9, c12, 0" : "=r" (val));
46 static inline u32 armv7_pmnc_enable_counter(unsigned int cnt)
51 printk(KERN_ERR "oprofile: CPU%u enabling wrong PMNC counter"
52 " %d\n", smp_processor_id(), cnt);
59 val = (1 << (cnt - CNT0));
62 asm volatile("mcr p15, 0, %0, c9, c12, 1" : : "r" (val));
67 static inline u32 armv7_pmnc_disable_counter(unsigned int cnt)
72 printk(KERN_ERR "oprofile: CPU%u disabling wrong PMNC counter"
73 " %d\n", smp_processor_id(), cnt);
80 val = (1 << (cnt - CNT0));
83 asm volatile("mcr p15, 0, %0, c9, c12, 2" : : "r" (val));
88 static inline u32 armv7_pmnc_enable_intens(unsigned int cnt)
93 printk(KERN_ERR "oprofile: CPU%u enabling wrong PMNC counter"
94 " interrupt enable %d\n", smp_processor_id(), cnt);
101 val = (1 << (cnt - CNT0));
104 asm volatile("mcr p15, 0, %0, c9, c14, 1" : : "r" (val));
109 static inline u32 armv7_pmnc_getreset_flags(void)
114 asm volatile("mrc p15, 0, %0, c9, c12, 3" : "=r" (val));
116 /* Write to clear flags */
118 asm volatile("mcr p15, 0, %0, c9, c12, 3" : : "r" (val));
123 static inline int armv7_pmnc_select_counter(unsigned int cnt)
127 if ((cnt == CCNT) || (cnt >= CNTMAX)) {
128 printk(KERN_ERR "oprofile: CPU%u selecting wrong PMNC counteri"
129 " %d\n", smp_processor_id(), cnt);
133 val = (cnt - CNT0) & SELECT_MASK;
134 asm volatile("mcr p15, 0, %0, c9, c12, 5" : : "r" (val));
139 static inline void armv7_pmnc_write_evtsel(unsigned int cnt, u32 val)
141 if (armv7_pmnc_select_counter(cnt) == cnt) {
143 asm volatile("mcr p15, 0, %0, c9, c13, 1" : : "r" (val));
147 static void armv7_pmnc_reset_counter(unsigned int cnt)
149 u32 cpu_cnt = CPU_COUNTER(smp_processor_id(), cnt);
150 u32 val = -(u32)counter_config[cpu_cnt].count;
154 armv7_pmnc_disable_counter(cnt);
156 asm volatile("mcr p15, 0, %0, c9, c13, 0" : : "r" (val));
158 if (cnt_en[cnt] != 0)
159 armv7_pmnc_enable_counter(cnt);
167 armv7_pmnc_disable_counter(cnt);
169 if (armv7_pmnc_select_counter(cnt) == cnt)
170 asm volatile("mcr p15, 0, %0, c9, c13, 2" : : "r" (val));
172 if (cnt_en[cnt] != 0)
173 armv7_pmnc_enable_counter(cnt);
178 printk(KERN_ERR "oprofile: CPU%u resetting wrong PMNC counter"
179 " %d\n", smp_processor_id(), cnt);
184 int armv7_setup_pmnc(void)
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());
194 /* Initialize & Reset PMNC: C bit and P bit */
195 armv7_pmnc_write(PMNC_P | PMNC_C);
198 for (cnt = CCNT; cnt < CNTMAX; cnt++) {
200 u32 cpu_cnt = CPU_COUNTER(smp_processor_id(), cnt);
205 armv7_pmnc_disable_counter(cnt);
208 if (!counter_config[cpu_cnt].enabled)
211 event = counter_config[cpu_cnt].event & 255;
214 * Set event (if destined for PMNx counters)
215 * We don't need to set the event if it's a cycle count
218 armv7_pmnc_write_evtsel(cnt, event);
221 * Enable interrupt for this counter
223 armv7_pmnc_enable_intens(cnt);
228 armv7_pmnc_reset_counter(cnt);
233 armv7_pmnc_enable_counter(cnt);
240 static inline void armv7_start_pmnc(void)
242 armv7_pmnc_write(armv7_pmnc_read() | PMNC_E);
245 static inline void armv7_stop_pmnc(void)
247 armv7_pmnc_write(armv7_pmnc_read() & ~PMNC_E);
251 * CPU counters' IRQ handler (one IRQ per CPU)
253 static irqreturn_t armv7_pmnc_interrupt(int irq, void *arg)
255 struct pt_regs *regs = get_irq_regs();
261 * Stop IRQ generation
266 * Get and reset overflow status flags
268 flags = armv7_pmnc_getreset_flags();
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);
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);
291 * Allow IRQ generation
298 int armv7_request_interrupts(int *irqs, int nr)
303 for (i = 0; i < nr; i++) {
304 ret = request_irq(irqs[i], armv7_pmnc_interrupt,
305 IRQF_DISABLED, "CP15 PMNC", NULL);
307 printk(KERN_ERR "oprofile: unable to request IRQ%u"
316 free_irq(irqs[i], NULL);
321 void armv7_release_interrupts(int *irqs, int nr)
325 for (i = 0; i < nr; i++)
326 free_irq(irqs[i], NULL);
330 static void armv7_pmnc_dump_regs(void)
335 printk(KERN_INFO "PMNC registers dump:\n");
337 asm volatile("mrc p15, 0, %0, c9, c12, 0" : "=r" (val));
338 printk(KERN_INFO "PMNC =0x%08x\n", val);
340 asm volatile("mrc p15, 0, %0, c9, c12, 1" : "=r" (val));
341 printk(KERN_INFO "CNTENS=0x%08x\n", val);
343 asm volatile("mrc p15, 0, %0, c9, c14, 1" : "=r" (val));
344 printk(KERN_INFO "INTENS=0x%08x\n", val);
346 asm volatile("mrc p15, 0, %0, c9, c12, 3" : "=r" (val));
347 printk(KERN_INFO "FLAGS =0x%08x\n", val);
349 asm volatile("mrc p15, 0, %0, c9, c12, 5" : "=r" (val));
350 printk(KERN_INFO "SELECT=0x%08x\n", val);
352 asm volatile("mrc p15, 0, %0, c9, c13, 0" : "=r" (val));
353 printk(KERN_INFO "CCNT =0x%08x\n", val);
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);
366 static int irqs[] = {
367 #ifdef CONFIG_ARCH_OMAP3
368 INT_34XX_BENCH_MPU_EMUL,
372 static void armv7_pmnc_stop(void)
375 armv7_pmnc_dump_regs();
378 armv7_release_interrupts(irqs, ARRAY_SIZE(irqs));
381 static int armv7_pmnc_start(void)
386 armv7_pmnc_dump_regs();
388 ret = armv7_request_interrupts(irqs, ARRAY_SIZE(irqs));
395 static int armv7_detect_pmnc(void)
400 struct op_arm_model_spec op_armv7_spec = {
401 .init = armv7_detect_pmnc,
403 .setup_ctrs = armv7_setup_pmnc,
404 .start = armv7_pmnc_start,
405 .stop = armv7_pmnc_stop,