diff options
author | Russell King <rmk@dyn-67.arm.linux.org.uk> | 2006-12-19 09:17:46 -0500 |
---|---|---|
committer | Russell King <rmk+kernel@arm.linux.org.uk> | 2007-02-06 11:46:45 -0500 |
commit | 10c03f69680e9e2acd8a9409a230aef37295ac49 (patch) | |
tree | 7ac927b0e93b20584f25de300ec2a4b2803c8529 /arch | |
parent | 2d9e1ae06d8f0bb187ea083fabab2dfb6f589270 (diff) |
[ARM] oprofile: add ARM11 SMP support
Add the glue for ARM11 SMP oprofile support, which also supports the
performance monitor in the coherency unit.
Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
Diffstat (limited to 'arch')
-rw-r--r-- | arch/arm/oprofile/Kconfig | 6 | ||||
-rw-r--r-- | arch/arm/oprofile/Makefile | 1 | ||||
-rw-r--r-- | arch/arm/oprofile/common.c | 4 | ||||
-rw-r--r-- | arch/arm/oprofile/op_arm_model.h | 1 | ||||
-rw-r--r-- | arch/arm/oprofile/op_model_mpcore.c | 296 | ||||
-rw-r--r-- | arch/arm/oprofile/op_model_mpcore.h | 61 |
6 files changed, 369 insertions, 0 deletions
diff --git a/arch/arm/oprofile/Kconfig b/arch/arm/oprofile/Kconfig index 40cc1894b517..afd93ad02feb 100644 --- a/arch/arm/oprofile/Kconfig +++ b/arch/arm/oprofile/Kconfig | |||
@@ -27,6 +27,12 @@ config OPROFILE_ARMV6 | |||
27 | default y | 27 | default y |
28 | select OPROFILE_ARM11_CORE | 28 | select OPROFILE_ARM11_CORE |
29 | 29 | ||
30 | config OPROFILE_MPCORE | ||
31 | bool | ||
32 | depends on CPU_V6 && SMP | ||
33 | default y | ||
34 | select OPROFILE_ARM11_CORE | ||
35 | |||
30 | config OPROFILE_ARM11_CORE | 36 | config OPROFILE_ARM11_CORE |
31 | bool | 37 | bool |
32 | 38 | ||
diff --git a/arch/arm/oprofile/Makefile b/arch/arm/oprofile/Makefile index 3d5ff306db78..e61d0cc520b7 100644 --- a/arch/arm/oprofile/Makefile +++ b/arch/arm/oprofile/Makefile | |||
@@ -10,3 +10,4 @@ oprofile-y := $(DRIVER_OBJS) common.o backtrace.o | |||
10 | oprofile-$(CONFIG_CPU_XSCALE) += op_model_xscale.o | 10 | oprofile-$(CONFIG_CPU_XSCALE) += op_model_xscale.o |
11 | oprofile-$(CONFIG_OPROFILE_ARM11_CORE) += op_model_arm11_core.o | 11 | oprofile-$(CONFIG_OPROFILE_ARM11_CORE) += op_model_arm11_core.o |
12 | oprofile-$(CONFIG_OPROFILE_ARMV6) += op_model_v6.o | 12 | oprofile-$(CONFIG_OPROFILE_ARMV6) += op_model_v6.o |
13 | oprofile-$(CONFIG_OPROFILE_MPCORE) += op_model_mpcore.o | ||
diff --git a/arch/arm/oprofile/common.c b/arch/arm/oprofile/common.c index f1b24fbd8e67..0a007b931f63 100644 --- a/arch/arm/oprofile/common.c +++ b/arch/arm/oprofile/common.c | |||
@@ -139,6 +139,10 @@ int __init oprofile_arch_init(struct oprofile_operations *ops) | |||
139 | spec = &op_armv6_spec; | 139 | spec = &op_armv6_spec; |
140 | #endif | 140 | #endif |
141 | 141 | ||
142 | #ifdef CONFIG_OPROFILE_MPCORE | ||
143 | spec = &op_mpcore_spec; | ||
144 | #endif | ||
145 | |||
142 | if (spec) { | 146 | if (spec) { |
143 | ret = spec->init(); | 147 | ret = spec->init(); |
144 | if (ret < 0) | 148 | if (ret < 0) |
diff --git a/arch/arm/oprofile/op_arm_model.h b/arch/arm/oprofile/op_arm_model.h index ad1c962ed17b..4899c629aa03 100644 --- a/arch/arm/oprofile/op_arm_model.h +++ b/arch/arm/oprofile/op_arm_model.h | |||
@@ -25,6 +25,7 @@ extern struct op_arm_model_spec op_xscale_spec; | |||
25 | #endif | 25 | #endif |
26 | 26 | ||
27 | extern struct op_arm_model_spec op_armv6_spec; | 27 | extern struct op_arm_model_spec op_armv6_spec; |
28 | extern struct op_arm_model_spec op_mpcore_spec; | ||
28 | 29 | ||
29 | extern void arm_backtrace(struct pt_regs * const regs, unsigned int depth); | 30 | extern void arm_backtrace(struct pt_regs * const regs, unsigned int depth); |
30 | 31 | ||
diff --git a/arch/arm/oprofile/op_model_mpcore.c b/arch/arm/oprofile/op_model_mpcore.c new file mode 100644 index 000000000000..898500718249 --- /dev/null +++ b/arch/arm/oprofile/op_model_mpcore.c | |||
@@ -0,0 +1,296 @@ | |||
1 | /** | ||
2 | * @file op_model_mpcore.c | ||
3 | * MPCORE Event Monitor Driver | ||
4 | * @remark Copyright 2004 ARM SMP Development Team | ||
5 | * @remark Copyright 2000-2004 Deepak Saxena <dsaxena@mvista.com> | ||
6 | * @remark Copyright 2000-2004 MontaVista Software Inc | ||
7 | * @remark Copyright 2004 Dave Jiang <dave.jiang@intel.com> | ||
8 | * @remark Copyright 2004 Intel Corporation | ||
9 | * @remark Copyright 2004 Zwane Mwaikambo <zwane@arm.linux.org.uk> | ||
10 | * @remark Copyright 2004 Oprofile Authors | ||
11 | * | ||
12 | * @remark Read the file COPYING | ||
13 | * | ||
14 | * @author Zwane Mwaikambo | ||
15 | * | ||
16 | * Counters: | ||
17 | * 0: PMN0 on CPU0, per-cpu configurable event counter | ||
18 | * 1: PMN1 on CPU0, per-cpu configurable event counter | ||
19 | * 2: CCNT on CPU0 | ||
20 | * 3: PMN0 on CPU1 | ||
21 | * 4: PMN1 on CPU1 | ||
22 | * 5: CCNT on CPU1 | ||
23 | * 6: PMN0 on CPU1 | ||
24 | * 7: PMN1 on CPU1 | ||
25 | * 8: CCNT on CPU1 | ||
26 | * 9: PMN0 on CPU1 | ||
27 | * 10: PMN1 on CPU1 | ||
28 | * 11: CCNT on CPU1 | ||
29 | * 12-19: configurable SCU event counters | ||
30 | */ | ||
31 | |||
32 | /* #define DEBUG */ | ||
33 | #include <linux/types.h> | ||
34 | #include <linux/errno.h> | ||
35 | #include <linux/sched.h> | ||
36 | #include <linux/oprofile.h> | ||
37 | #include <linux/interrupt.h> | ||
38 | #include <linux/smp.h> | ||
39 | |||
40 | #include <asm/io.h> | ||
41 | #include <asm/irq.h> | ||
42 | #include <asm/mach/irq.h> | ||
43 | #include <asm/hardware.h> | ||
44 | #include <asm/system.h> | ||
45 | |||
46 | #include "op_counter.h" | ||
47 | #include "op_arm_model.h" | ||
48 | #include "op_model_arm11_core.h" | ||
49 | #include "op_model_mpcore.h" | ||
50 | |||
51 | /* | ||
52 | * MPCore SCU event monitor support | ||
53 | */ | ||
54 | #define SCU_EVENTMONITORS_VA_BASE __io_address(REALVIEW_MPCORE_SCU_BASE + 0x10) | ||
55 | |||
56 | /* | ||
57 | * Bitmask of used SCU counters | ||
58 | */ | ||
59 | static unsigned int scu_em_used; | ||
60 | |||
61 | /* | ||
62 | * 2 helper fns take a counter number from 0-7 (not the userspace-visible counter number) | ||
63 | */ | ||
64 | static inline void scu_reset_counter(struct eventmonitor __iomem *emc, unsigned int n) | ||
65 | { | ||
66 | writel(-(u32)counter_config[SCU_COUNTER(n)].count, &emc->MC[n]); | ||
67 | } | ||
68 | |||
69 | static inline void scu_set_event(struct eventmonitor __iomem *emc, unsigned int n, u32 event) | ||
70 | { | ||
71 | event &= 0xff; | ||
72 | writeb(event, &emc->MCEB[n]); | ||
73 | } | ||
74 | |||
75 | /* | ||
76 | * SCU counters' IRQ handler (one IRQ per counter => 2 IRQs per CPU) | ||
77 | */ | ||
78 | static irqreturn_t scu_em_interrupt(int irq, void *arg) | ||
79 | { | ||
80 | struct eventmonitor __iomem *emc = SCU_EVENTMONITORS_VA_BASE; | ||
81 | unsigned int cnt; | ||
82 | |||
83 | cnt = irq - IRQ_PMU_SCU0; | ||
84 | oprofile_add_sample(get_irq_regs(), SCU_COUNTER(cnt)); | ||
85 | scu_reset_counter(emc, cnt); | ||
86 | |||
87 | /* Clear overflow flag for this counter */ | ||
88 | writel(1 << (cnt + 16), &emc->PMCR); | ||
89 | |||
90 | return IRQ_HANDLED; | ||
91 | } | ||
92 | |||
93 | /* Configure just the SCU counters that the user has requested */ | ||
94 | static void scu_setup(void) | ||
95 | { | ||
96 | struct eventmonitor __iomem *emc = SCU_EVENTMONITORS_VA_BASE; | ||
97 | unsigned int i; | ||
98 | |||
99 | scu_em_used = 0; | ||
100 | |||
101 | for (i = 0; i < NUM_SCU_COUNTERS; i++) { | ||
102 | if (counter_config[SCU_COUNTER(i)].enabled && | ||
103 | counter_config[SCU_COUNTER(i)].event) { | ||
104 | scu_set_event(emc, i, 0); /* disable counter for now */ | ||
105 | scu_em_used |= 1 << i; | ||
106 | } | ||
107 | } | ||
108 | } | ||
109 | |||
110 | static int scu_start(void) | ||
111 | { | ||
112 | struct eventmonitor __iomem *emc = SCU_EVENTMONITORS_VA_BASE; | ||
113 | unsigned int temp, i; | ||
114 | unsigned long event; | ||
115 | int ret = 0; | ||
116 | |||
117 | /* | ||
118 | * request the SCU counter interrupts that we need | ||
119 | */ | ||
120 | for (i = 0; i < NUM_SCU_COUNTERS; i++) { | ||
121 | if (scu_em_used & (1 << i)) { | ||
122 | ret = request_irq(IRQ_PMU_SCU0 + i, scu_em_interrupt, IRQF_DISABLED, "SCU PMU", NULL); | ||
123 | if (ret) { | ||
124 | printk(KERN_ERR "oprofile: unable to request IRQ%u for SCU Event Monitor\n", | ||
125 | IRQ_PMU_SCU0 + i); | ||
126 | goto err_free_scu; | ||
127 | } | ||
128 | } | ||
129 | } | ||
130 | |||
131 | /* | ||
132 | * clear overflow and enable interrupt for all used counters | ||
133 | */ | ||
134 | temp = readl(&emc->PMCR); | ||
135 | for (i = 0; i < NUM_SCU_COUNTERS; i++) { | ||
136 | if (scu_em_used & (1 << i)) { | ||
137 | scu_reset_counter(emc, i); | ||
138 | event = counter_config[SCU_COUNTER(i)].event; | ||
139 | scu_set_event(emc, i, event); | ||
140 | |||
141 | /* clear overflow/interrupt */ | ||
142 | temp |= 1 << (i + 16); | ||
143 | /* enable interrupt*/ | ||
144 | temp |= 1 << (i + 8); | ||
145 | } | ||
146 | } | ||
147 | |||
148 | /* Enable all 8 counters */ | ||
149 | temp |= PMCR_E; | ||
150 | writel(temp, &emc->PMCR); | ||
151 | |||
152 | return 0; | ||
153 | |||
154 | err_free_scu: | ||
155 | while (i--) | ||
156 | free_irq(IRQ_PMU_SCU0 + i, NULL); | ||
157 | return ret; | ||
158 | } | ||
159 | |||
160 | static void scu_stop(void) | ||
161 | { | ||
162 | struct eventmonitor __iomem *emc = SCU_EVENTMONITORS_VA_BASE; | ||
163 | unsigned int temp, i; | ||
164 | |||
165 | /* Disable counter interrupts */ | ||
166 | /* Don't disable all 8 counters (with the E bit) as they may be in use */ | ||
167 | temp = readl(&emc->PMCR); | ||
168 | for (i = 0; i < NUM_SCU_COUNTERS; i++) { | ||
169 | if (scu_em_used & (1 << i)) | ||
170 | temp &= ~(1 << (i + 8)); | ||
171 | } | ||
172 | writel(temp, &emc->PMCR); | ||
173 | |||
174 | /* Free counter interrupts and reset counters */ | ||
175 | for (i = 0; i < NUM_SCU_COUNTERS; i++) { | ||
176 | if (scu_em_used & (1 << i)) { | ||
177 | scu_reset_counter(emc, i); | ||
178 | free_irq(IRQ_PMU_SCU0 + i, NULL); | ||
179 | } | ||
180 | } | ||
181 | } | ||
182 | |||
183 | struct em_function_data { | ||
184 | int (*fn)(void); | ||
185 | int ret; | ||
186 | }; | ||
187 | |||
188 | static void em_func(void *data) | ||
189 | { | ||
190 | struct em_function_data *d = data; | ||
191 | int ret = d->fn(); | ||
192 | if (ret) | ||
193 | d->ret = ret; | ||
194 | } | ||
195 | |||
196 | static int em_call_function(int (*fn)(void)) | ||
197 | { | ||
198 | struct em_function_data data; | ||
199 | |||
200 | data.fn = fn; | ||
201 | data.ret = 0; | ||
202 | |||
203 | smp_call_function(em_func, &data, 1, 1); | ||
204 | em_func(&data); | ||
205 | |||
206 | return data.ret; | ||
207 | } | ||
208 | |||
209 | /* | ||
210 | * Glue to stick the individual ARM11 PMUs and the SCU | ||
211 | * into the oprofile framework. | ||
212 | */ | ||
213 | static int em_setup_ctrs(void) | ||
214 | { | ||
215 | int ret; | ||
216 | |||
217 | /* Configure CPU counters by cross-calling to the other CPUs */ | ||
218 | ret = em_call_function(arm11_setup_pmu); | ||
219 | if (ret == 0) | ||
220 | scu_setup(); | ||
221 | |||
222 | return 0; | ||
223 | } | ||
224 | |||
225 | static int arm11_irqs[] = { | ||
226 | [0] = IRQ_PMU_CPU0, | ||
227 | [1] = IRQ_PMU_CPU1, | ||
228 | [2] = IRQ_PMU_CPU2, | ||
229 | [3] = IRQ_PMU_CPU3 | ||
230 | }; | ||
231 | |||
232 | static int em_start(void) | ||
233 | { | ||
234 | int ret; | ||
235 | |||
236 | ret = arm11_request_interrupts(arm11_irqs, ARRAY_SIZE(arm11_irqs)); | ||
237 | if (ret == 0) { | ||
238 | em_call_function(arm11_start_pmu); | ||
239 | |||
240 | ret = scu_start(); | ||
241 | if (ret) | ||
242 | arm11_release_interrupts(arm11_irqs, ARRAY_SIZE(arm11_irqs)); | ||
243 | } | ||
244 | return ret; | ||
245 | } | ||
246 | |||
247 | static void em_stop(void) | ||
248 | { | ||
249 | em_call_function(arm11_stop_pmu); | ||
250 | arm11_release_interrupts(arm11_irqs, ARRAY_SIZE(arm11_irqs)); | ||
251 | scu_stop(); | ||
252 | } | ||
253 | |||
254 | /* | ||
255 | * Why isn't there a function to route an IRQ to a specific CPU in | ||
256 | * genirq? | ||
257 | */ | ||
258 | static void em_route_irq(int irq, unsigned int cpu) | ||
259 | { | ||
260 | irq_desc[irq].affinity = cpumask_of_cpu(cpu); | ||
261 | irq_desc[irq].chip->set_affinity(irq, cpumask_of_cpu(cpu)); | ||
262 | } | ||
263 | |||
264 | static int em_setup(void) | ||
265 | { | ||
266 | /* | ||
267 | * Send SCU PMU interrupts to the "owner" CPU. | ||
268 | */ | ||
269 | em_route_irq(IRQ_PMU_SCU0, 0); | ||
270 | em_route_irq(IRQ_PMU_SCU1, 0); | ||
271 | em_route_irq(IRQ_PMU_SCU2, 1); | ||
272 | em_route_irq(IRQ_PMU_SCU3, 1); | ||
273 | em_route_irq(IRQ_PMU_SCU4, 2); | ||
274 | em_route_irq(IRQ_PMU_SCU5, 2); | ||
275 | em_route_irq(IRQ_PMU_SCU6, 3); | ||
276 | em_route_irq(IRQ_PMU_SCU7, 3); | ||
277 | |||
278 | /* | ||
279 | * Send CP15 PMU interrupts to the owner CPU. | ||
280 | */ | ||
281 | em_route_irq(IRQ_PMU_CPU0, 0); | ||
282 | em_route_irq(IRQ_PMU_CPU1, 1); | ||
283 | em_route_irq(IRQ_PMU_CPU2, 2); | ||
284 | em_route_irq(IRQ_PMU_CPU3, 3); | ||
285 | |||
286 | return 0; | ||
287 | } | ||
288 | |||
289 | struct op_arm_model_spec op_mpcore_spec = { | ||
290 | .init = em_setup, | ||
291 | .num_counters = MPCORE_NUM_COUNTERS, | ||
292 | .setup_ctrs = em_setup_ctrs, | ||
293 | .start = em_start, | ||
294 | .stop = em_stop, | ||
295 | .name = "arm/mpcore", | ||
296 | }; | ||
diff --git a/arch/arm/oprofile/op_model_mpcore.h b/arch/arm/oprofile/op_model_mpcore.h new file mode 100644 index 000000000000..73d811023688 --- /dev/null +++ b/arch/arm/oprofile/op_model_mpcore.h | |||
@@ -0,0 +1,61 @@ | |||
1 | /** | ||
2 | * @file op_model_mpcore.c | ||
3 | * MPCORE Event Monitor Driver | ||
4 | * @remark Copyright 2004 ARM SMP Development Team | ||
5 | * @remark Copyright 2000-2004 Deepak Saxena <dsaxena@mvista.com> | ||
6 | * @remark Copyright 2000-2004 MontaVista Software Inc | ||
7 | * @remark Copyright 2004 Dave Jiang <dave.jiang@intel.com> | ||
8 | * @remark Copyright 2004 Intel Corporation | ||
9 | * @remark Copyright 2004 Zwane Mwaikambo <zwane@arm.linux.org.uk> | ||
10 | * @remark Copyright 2004 Oprofile Authors | ||
11 | * | ||
12 | * @remark Read the file COPYING | ||
13 | * | ||
14 | * @author Zwane Mwaikambo | ||
15 | */ | ||
16 | #ifndef OP_MODEL_MPCORE_H | ||
17 | #define OP_MODEL_MPCORE_H | ||
18 | |||
19 | struct eventmonitor { | ||
20 | unsigned long PMCR; | ||
21 | unsigned char MCEB[8]; | ||
22 | unsigned long MC[8]; | ||
23 | }; | ||
24 | |||
25 | /* | ||
26 | * List of userspace counter numbers: note that the structure is important. | ||
27 | * The code relies on CPUn's counters being CPU0's counters + 3n | ||
28 | * and on CPU0's counters starting at 0 | ||
29 | */ | ||
30 | |||
31 | #define COUNTER_CPU0_PMN0 0 | ||
32 | #define COUNTER_CPU0_PMN1 1 | ||
33 | #define COUNTER_CPU0_CCNT 2 | ||
34 | |||
35 | #define COUNTER_CPU1_PMN0 3 | ||
36 | #define COUNTER_CPU1_PMN1 4 | ||
37 | #define COUNTER_CPU1_CCNT 5 | ||
38 | |||
39 | #define COUNTER_CPU2_PMN0 6 | ||
40 | #define COUNTER_CPU2_PMN1 7 | ||
41 | #define COUNTER_CPU2_CCNT 8 | ||
42 | |||
43 | #define COUNTER_CPU3_PMN0 9 | ||
44 | #define COUNTER_CPU3_PMN1 10 | ||
45 | #define COUNTER_CPU3_CCNT 11 | ||
46 | |||
47 | #define COUNTER_SCU_MN0 12 | ||
48 | #define COUNTER_SCU_MN1 13 | ||
49 | #define COUNTER_SCU_MN2 14 | ||
50 | #define COUNTER_SCU_MN3 15 | ||
51 | #define COUNTER_SCU_MN4 16 | ||
52 | #define COUNTER_SCU_MN5 17 | ||
53 | #define COUNTER_SCU_MN6 18 | ||
54 | #define COUNTER_SCU_MN7 19 | ||
55 | #define NUM_SCU_COUNTERS 8 | ||
56 | |||
57 | #define SCU_COUNTER(number) ((number) + COUNTER_SCU_MN0) | ||
58 | |||
59 | #define MPCORE_NUM_COUNTERS SCU_COUNTER(NUM_SCU_COUNTERS) | ||
60 | |||
61 | #endif | ||