blob: f73ce875a395012c7f7b9500a5af1abbecafc438 [file] [log] [blame]
Russell King10c03f62006-12-19 14:17:46 +00001/**
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>
Jamie Iles1618fdd2010-02-02 20:24:07 +010035#include <linux/err.h>
Russell King10c03f62006-12-19 14:17:46 +000036#include <linux/sched.h>
37#include <linux/oprofile.h>
38#include <linux/interrupt.h>
39#include <linux/smp.h>
Russell Kingfced80c2008-09-06 12:10:45 +010040#include <linux/io.h>
Russell King10c03f62006-12-19 14:17:46 +000041
Russell King10c03f62006-12-19 14:17:46 +000042#include <asm/irq.h>
43#include <asm/mach/irq.h>
Russell Kinga09e64f2008-08-05 16:14:15 +010044#include <mach/hardware.h>
Catalin Marinasee8c9572009-05-30 14:00:17 +010045#include <mach/board-eb.h>
Russell King10c03f62006-12-19 14:17:46 +000046#include <asm/system.h>
Jamie Iles1618fdd2010-02-02 20:24:07 +010047#include <asm/pmu.h>
Russell King10c03f62006-12-19 14:17:46 +000048
49#include "op_counter.h"
50#include "op_arm_model.h"
51#include "op_model_arm11_core.h"
52#include "op_model_mpcore.h"
53
54/*
55 * MPCore SCU event monitor support
56 */
Catalin Marinasfe6cfde2008-04-24 10:05:43 +010057#define SCU_EVENTMONITORS_VA_BASE __io_address(REALVIEW_EB11MP_SCU_BASE + 0x10)
Russell King10c03f62006-12-19 14:17:46 +000058
59/*
60 * Bitmask of used SCU counters
61 */
62static unsigned int scu_em_used;
Jamie Iles1618fdd2010-02-02 20:24:07 +010063static const struct pmu_irqs *pmu_irqs;
Russell King10c03f62006-12-19 14:17:46 +000064
65/*
66 * 2 helper fns take a counter number from 0-7 (not the userspace-visible counter number)
67 */
68static inline void scu_reset_counter(struct eventmonitor __iomem *emc, unsigned int n)
69{
70 writel(-(u32)counter_config[SCU_COUNTER(n)].count, &emc->MC[n]);
71}
72
73static inline void scu_set_event(struct eventmonitor __iomem *emc, unsigned int n, u32 event)
74{
75 event &= 0xff;
76 writeb(event, &emc->MCEB[n]);
77}
78
79/*
80 * SCU counters' IRQ handler (one IRQ per counter => 2 IRQs per CPU)
81 */
82static irqreturn_t scu_em_interrupt(int irq, void *arg)
83{
84 struct eventmonitor __iomem *emc = SCU_EVENTMONITORS_VA_BASE;
85 unsigned int cnt;
86
Catalin Marinasfe6cfde2008-04-24 10:05:43 +010087 cnt = irq - IRQ_EB11MP_PMU_SCU0;
Russell King10c03f62006-12-19 14:17:46 +000088 oprofile_add_sample(get_irq_regs(), SCU_COUNTER(cnt));
89 scu_reset_counter(emc, cnt);
90
91 /* Clear overflow flag for this counter */
92 writel(1 << (cnt + 16), &emc->PMCR);
93
94 return IRQ_HANDLED;
95}
96
97/* Configure just the SCU counters that the user has requested */
98static void scu_setup(void)
99{
100 struct eventmonitor __iomem *emc = SCU_EVENTMONITORS_VA_BASE;
101 unsigned int i;
102
103 scu_em_used = 0;
104
105 for (i = 0; i < NUM_SCU_COUNTERS; i++) {
106 if (counter_config[SCU_COUNTER(i)].enabled &&
107 counter_config[SCU_COUNTER(i)].event) {
108 scu_set_event(emc, i, 0); /* disable counter for now */
109 scu_em_used |= 1 << i;
110 }
111 }
112}
113
114static int scu_start(void)
115{
116 struct eventmonitor __iomem *emc = SCU_EVENTMONITORS_VA_BASE;
117 unsigned int temp, i;
118 unsigned long event;
119 int ret = 0;
120
121 /*
122 * request the SCU counter interrupts that we need
123 */
124 for (i = 0; i < NUM_SCU_COUNTERS; i++) {
125 if (scu_em_used & (1 << i)) {
Catalin Marinasfe6cfde2008-04-24 10:05:43 +0100126 ret = request_irq(IRQ_EB11MP_PMU_SCU0 + i, scu_em_interrupt, IRQF_DISABLED, "SCU PMU", NULL);
Russell King10c03f62006-12-19 14:17:46 +0000127 if (ret) {
128 printk(KERN_ERR "oprofile: unable to request IRQ%u for SCU Event Monitor\n",
Catalin Marinasfe6cfde2008-04-24 10:05:43 +0100129 IRQ_EB11MP_PMU_SCU0 + i);
Russell King10c03f62006-12-19 14:17:46 +0000130 goto err_free_scu;
131 }
132 }
133 }
134
135 /*
136 * clear overflow and enable interrupt for all used counters
137 */
138 temp = readl(&emc->PMCR);
139 for (i = 0; i < NUM_SCU_COUNTERS; i++) {
140 if (scu_em_used & (1 << i)) {
141 scu_reset_counter(emc, i);
142 event = counter_config[SCU_COUNTER(i)].event;
143 scu_set_event(emc, i, event);
144
145 /* clear overflow/interrupt */
146 temp |= 1 << (i + 16);
147 /* enable interrupt*/
148 temp |= 1 << (i + 8);
149 }
150 }
151
152 /* Enable all 8 counters */
153 temp |= PMCR_E;
154 writel(temp, &emc->PMCR);
155
156 return 0;
157
158 err_free_scu:
159 while (i--)
Catalin Marinasfe6cfde2008-04-24 10:05:43 +0100160 free_irq(IRQ_EB11MP_PMU_SCU0 + i, NULL);
Russell King10c03f62006-12-19 14:17:46 +0000161 return ret;
162}
163
164static void scu_stop(void)
165{
166 struct eventmonitor __iomem *emc = SCU_EVENTMONITORS_VA_BASE;
167 unsigned int temp, i;
168
169 /* Disable counter interrupts */
170 /* Don't disable all 8 counters (with the E bit) as they may be in use */
171 temp = readl(&emc->PMCR);
172 for (i = 0; i < NUM_SCU_COUNTERS; i++) {
173 if (scu_em_used & (1 << i))
174 temp &= ~(1 << (i + 8));
175 }
176 writel(temp, &emc->PMCR);
177
178 /* Free counter interrupts and reset counters */
179 for (i = 0; i < NUM_SCU_COUNTERS; i++) {
180 if (scu_em_used & (1 << i)) {
181 scu_reset_counter(emc, i);
Catalin Marinasfe6cfde2008-04-24 10:05:43 +0100182 free_irq(IRQ_EB11MP_PMU_SCU0 + i, NULL);
Russell King10c03f62006-12-19 14:17:46 +0000183 }
184 }
185}
186
187struct em_function_data {
188 int (*fn)(void);
189 int ret;
190};
191
192static void em_func(void *data)
193{
194 struct em_function_data *d = data;
195 int ret = d->fn();
196 if (ret)
197 d->ret = ret;
198}
199
200static int em_call_function(int (*fn)(void))
201{
202 struct em_function_data data;
203
204 data.fn = fn;
205 data.ret = 0;
206
Russell Kingf72267c2007-06-02 15:36:37 +0100207 preempt_disable();
Jens Axboe8691e5a2008-06-06 11:18:06 +0200208 smp_call_function(em_func, &data, 1);
Russell King10c03f62006-12-19 14:17:46 +0000209 em_func(&data);
Russell Kingf72267c2007-06-02 15:36:37 +0100210 preempt_enable();
Russell King10c03f62006-12-19 14:17:46 +0000211
212 return data.ret;
213}
214
215/*
216 * Glue to stick the individual ARM11 PMUs and the SCU
217 * into the oprofile framework.
218 */
219static int em_setup_ctrs(void)
220{
221 int ret;
222
223 /* Configure CPU counters by cross-calling to the other CPUs */
224 ret = em_call_function(arm11_setup_pmu);
225 if (ret == 0)
226 scu_setup();
227
228 return 0;
229}
230
Russell King10c03f62006-12-19 14:17:46 +0000231static int em_start(void)
232{
233 int ret;
234
Jamie Iles1618fdd2010-02-02 20:24:07 +0100235 pmu_irqs = reserve_pmu();
236 if (IS_ERR(pmu_irqs)) {
237 ret = PTR_ERR(pmu_irqs);
238 goto out;
239 }
240
241 ret = arm11_request_interrupts(pmu_irqs->irqs, pmu_irqs->num_irqs);
Russell King10c03f62006-12-19 14:17:46 +0000242 if (ret == 0) {
243 em_call_function(arm11_start_pmu);
244
245 ret = scu_start();
Jamie Iles1618fdd2010-02-02 20:24:07 +0100246 if (ret) {
247 arm11_release_interrupts(pmu_irqs->irqs,
248 pmu_irqs->num_irqs);
249 } else {
250 release_pmu(pmu_irqs);
251 pmu_irqs = NULL;
252 }
Russell King10c03f62006-12-19 14:17:46 +0000253 }
Jamie Iles1618fdd2010-02-02 20:24:07 +0100254
255out:
Russell King10c03f62006-12-19 14:17:46 +0000256 return ret;
257}
258
259static void em_stop(void)
260{
261 em_call_function(arm11_stop_pmu);
Jamie Iles1618fdd2010-02-02 20:24:07 +0100262 arm11_release_interrupts(pmu_irqs->irqs, pmu_irqs->num_irqs);
Russell King10c03f62006-12-19 14:17:46 +0000263 scu_stop();
Jamie Iles1618fdd2010-02-02 20:24:07 +0100264 release_pmu(pmu_irqs);
Russell King10c03f62006-12-19 14:17:46 +0000265}
266
267/*
268 * Why isn't there a function to route an IRQ to a specific CPU in
269 * genirq?
270 */
271static void em_route_irq(int irq, unsigned int cpu)
272{
Russell King28c670c2007-05-26 12:08:29 +0100273 struct irq_desc *desc = irq_desc + irq;
Rusty Russell0de26522008-12-13 21:20:26 +1030274 const struct cpumask *mask = cpumask_of(cpu);
Russell King28c670c2007-05-26 12:08:29 +0100275
276 spin_lock_irq(&desc->lock);
Mike Travise65e49d2009-01-12 15:27:13 -0800277 cpumask_copy(desc->affinity, mask);
Russell King28c670c2007-05-26 12:08:29 +0100278 desc->chip->set_affinity(irq, mask);
279 spin_unlock_irq(&desc->lock);
Russell King10c03f62006-12-19 14:17:46 +0000280}
281
282static int em_setup(void)
283{
284 /*
285 * Send SCU PMU interrupts to the "owner" CPU.
286 */
Catalin Marinasfe6cfde2008-04-24 10:05:43 +0100287 em_route_irq(IRQ_EB11MP_PMU_SCU0, 0);
288 em_route_irq(IRQ_EB11MP_PMU_SCU1, 0);
289 em_route_irq(IRQ_EB11MP_PMU_SCU2, 1);
290 em_route_irq(IRQ_EB11MP_PMU_SCU3, 1);
291 em_route_irq(IRQ_EB11MP_PMU_SCU4, 2);
292 em_route_irq(IRQ_EB11MP_PMU_SCU5, 2);
293 em_route_irq(IRQ_EB11MP_PMU_SCU6, 3);
294 em_route_irq(IRQ_EB11MP_PMU_SCU7, 3);
Russell King10c03f62006-12-19 14:17:46 +0000295
Jamie Iles1618fdd2010-02-02 20:24:07 +0100296 return init_pmu();
Russell King10c03f62006-12-19 14:17:46 +0000297}
298
299struct op_arm_model_spec op_mpcore_spec = {
300 .init = em_setup,
301 .num_counters = MPCORE_NUM_COUNTERS,
302 .setup_ctrs = em_setup_ctrs,
303 .start = em_start,
304 .stop = em_stop,
305 .name = "arm/mpcore",
306};