Merge branch 'master' of git://git.kernel.org/pub/scm/linux/kernel/git/paulus/powerpc
[linux-2.6] / arch / arm / oprofile / op_model_arm11_core.c
1 /**
2  * @file op_model_arm11_core.c
3  * ARM11 Event Monitor Driver
4  * @remark Copyright 2004 ARM SMP Development Team
5  */
6 #include <linux/types.h>
7 #include <linux/errno.h>
8 #include <linux/oprofile.h>
9 #include <linux/interrupt.h>
10 #include <linux/irq.h>
11 #include <linux/smp.h>
12
13 #include "op_counter.h"
14 #include "op_arm_model.h"
15 #include "op_model_arm11_core.h"
16
17 /*
18  * ARM11 PMU support
19  */
20 static inline void arm11_write_pmnc(u32 val)
21 {
22         /* upper 4bits and 7, 11 are write-as-0 */
23         val &= 0x0ffff77f;
24         asm volatile("mcr p15, 0, %0, c15, c12, 0" : : "r" (val));
25 }
26
27 static inline u32 arm11_read_pmnc(void)
28 {
29         u32 val;
30         asm volatile("mrc p15, 0, %0, c15, c12, 0" : "=r" (val));
31         return val;
32 }
33
34 static void arm11_reset_counter(unsigned int cnt)
35 {
36         u32 val = -(u32)counter_config[CPU_COUNTER(smp_processor_id(), cnt)].count;
37         switch (cnt) {
38         case CCNT:
39                 asm volatile("mcr p15, 0, %0, c15, c12, 1" : : "r" (val));
40                 break;
41
42         case PMN0:
43                 asm volatile("mcr p15, 0, %0, c15, c12, 2" : : "r" (val));
44                 break;
45
46         case PMN1:
47                 asm volatile("mcr p15, 0, %0, c15, c12, 3" : : "r" (val));
48                 break;
49         }
50 }
51
52 int arm11_setup_pmu(void)
53 {
54         unsigned int cnt;
55         u32 pmnc;
56
57         if (arm11_read_pmnc() & PMCR_E) {
58                 printk(KERN_ERR "oprofile: CPU%u PMU still enabled when setup new event counter.\n", smp_processor_id());
59                 return -EBUSY;
60         }
61
62         /* initialize PMNC, reset overflow, D bit, C bit and P bit. */
63         arm11_write_pmnc(PMCR_OFL_PMN0 | PMCR_OFL_PMN1 | PMCR_OFL_CCNT |
64                          PMCR_C | PMCR_P);
65
66         for (pmnc = 0, cnt = PMN0; cnt <= CCNT; cnt++) {
67                 unsigned long event;
68
69                 if (!counter_config[CPU_COUNTER(smp_processor_id(), cnt)].enabled)
70                         continue;
71
72                 event = counter_config[CPU_COUNTER(smp_processor_id(), cnt)].event & 255;
73
74                 /*
75                  * Set event (if destined for PMNx counters)
76                  */
77                 if (cnt == PMN0) {
78                         pmnc |= event << 20;
79                 } else if (cnt == PMN1) {
80                         pmnc |= event << 12;
81                 }
82
83                 /*
84                  * We don't need to set the event if it's a cycle count
85                  * Enable interrupt for this counter
86                  */
87                 pmnc |= PMCR_IEN_PMN0 << cnt;
88                 arm11_reset_counter(cnt);
89         }
90         arm11_write_pmnc(pmnc);
91
92         return 0;
93 }
94
95 int arm11_start_pmu(void)
96 {
97         arm11_write_pmnc(arm11_read_pmnc() | PMCR_E);
98         return 0;
99 }
100
101 int arm11_stop_pmu(void)
102 {
103         unsigned int cnt;
104
105         arm11_write_pmnc(arm11_read_pmnc() & ~PMCR_E);
106
107         for (cnt = PMN0; cnt <= CCNT; cnt++)
108                 arm11_reset_counter(cnt);
109
110         return 0;
111 }
112
113 /*
114  * CPU counters' IRQ handler (one IRQ per CPU)
115  */
116 static irqreturn_t arm11_pmu_interrupt(int irq, void *arg)
117 {
118         struct pt_regs *regs = get_irq_regs();
119         unsigned int cnt;
120         u32 pmnc;
121
122         pmnc = arm11_read_pmnc();
123
124         for (cnt = PMN0; cnt <= CCNT; cnt++) {
125                 if ((pmnc & (PMCR_OFL_PMN0 << cnt)) && (pmnc & (PMCR_IEN_PMN0 << cnt))) {
126                         arm11_reset_counter(cnt);
127                         oprofile_add_sample(regs, CPU_COUNTER(smp_processor_id(), cnt));
128                 }
129         }
130         /* Clear counter flag(s) */
131         arm11_write_pmnc(pmnc);
132         return IRQ_HANDLED;
133 }
134
135 int arm11_request_interrupts(int *irqs, int nr)
136 {
137         unsigned int i;
138         int ret = 0;
139
140         for(i = 0; i < nr; i++) {
141                 ret = request_irq(irqs[i], arm11_pmu_interrupt, IRQF_DISABLED, "CP15 PMU", NULL);
142                 if (ret != 0) {
143                         printk(KERN_ERR "oprofile: unable to request IRQ%u for MPCORE-EM\n",
144                                irqs[i]);
145                         break;
146                 }
147         }
148
149         if (i != nr)
150                 while (i-- != 0)
151                         free_irq(irqs[i], NULL);
152
153         return ret;
154 }
155
156 void arm11_release_interrupts(int *irqs, int nr)
157 {
158         unsigned int i;
159
160         for (i = 0; i < nr; i++)
161                 free_irq(irqs[i], NULL);
162 }