Russell King | 10c03f6 | 2006-12-19 14:17:46 +0000 | [diff] [blame] | 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> |
Jamie Iles | 1618fdd | 2010-02-02 20:24:07 +0100 | [diff] [blame] | 35 | #include <linux/err.h> |
Russell King | 10c03f6 | 2006-12-19 14:17:46 +0000 | [diff] [blame] | 36 | #include <linux/sched.h> |
| 37 | #include <linux/oprofile.h> |
| 38 | #include <linux/interrupt.h> |
| 39 | #include <linux/smp.h> |
Russell King | fced80c | 2008-09-06 12:10:45 +0100 | [diff] [blame] | 40 | #include <linux/io.h> |
Russell King | 10c03f6 | 2006-12-19 14:17:46 +0000 | [diff] [blame] | 41 | |
Russell King | 10c03f6 | 2006-12-19 14:17:46 +0000 | [diff] [blame] | 42 | #include <asm/irq.h> |
| 43 | #include <asm/mach/irq.h> |
Russell King | a09e64f | 2008-08-05 16:14:15 +0100 | [diff] [blame] | 44 | #include <mach/hardware.h> |
Catalin Marinas | ee8c957 | 2009-05-30 14:00:17 +0100 | [diff] [blame] | 45 | #include <mach/board-eb.h> |
Russell King | 10c03f6 | 2006-12-19 14:17:46 +0000 | [diff] [blame] | 46 | #include <asm/system.h> |
Jamie Iles | 1618fdd | 2010-02-02 20:24:07 +0100 | [diff] [blame] | 47 | #include <asm/pmu.h> |
Russell King | 10c03f6 | 2006-12-19 14:17:46 +0000 | [diff] [blame] | 48 | |
| 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 Marinas | fe6cfde | 2008-04-24 10:05:43 +0100 | [diff] [blame] | 57 | #define SCU_EVENTMONITORS_VA_BASE __io_address(REALVIEW_EB11MP_SCU_BASE + 0x10) |
Russell King | 10c03f6 | 2006-12-19 14:17:46 +0000 | [diff] [blame] | 58 | |
| 59 | /* |
| 60 | * Bitmask of used SCU counters |
| 61 | */ |
| 62 | static unsigned int scu_em_used; |
Jamie Iles | 1618fdd | 2010-02-02 20:24:07 +0100 | [diff] [blame] | 63 | static const struct pmu_irqs *pmu_irqs; |
Russell King | 10c03f6 | 2006-12-19 14:17:46 +0000 | [diff] [blame] | 64 | |
| 65 | /* |
| 66 | * 2 helper fns take a counter number from 0-7 (not the userspace-visible counter number) |
| 67 | */ |
| 68 | static 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 | |
| 73 | static 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 | */ |
| 82 | static 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 Marinas | fe6cfde | 2008-04-24 10:05:43 +0100 | [diff] [blame] | 87 | cnt = irq - IRQ_EB11MP_PMU_SCU0; |
Russell King | 10c03f6 | 2006-12-19 14:17:46 +0000 | [diff] [blame] | 88 | 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 */ |
| 98 | static 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 | |
| 114 | static 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 Marinas | fe6cfde | 2008-04-24 10:05:43 +0100 | [diff] [blame] | 126 | ret = request_irq(IRQ_EB11MP_PMU_SCU0 + i, scu_em_interrupt, IRQF_DISABLED, "SCU PMU", NULL); |
Russell King | 10c03f6 | 2006-12-19 14:17:46 +0000 | [diff] [blame] | 127 | if (ret) { |
| 128 | printk(KERN_ERR "oprofile: unable to request IRQ%u for SCU Event Monitor\n", |
Catalin Marinas | fe6cfde | 2008-04-24 10:05:43 +0100 | [diff] [blame] | 129 | IRQ_EB11MP_PMU_SCU0 + i); |
Russell King | 10c03f6 | 2006-12-19 14:17:46 +0000 | [diff] [blame] | 130 | 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 Marinas | fe6cfde | 2008-04-24 10:05:43 +0100 | [diff] [blame] | 160 | free_irq(IRQ_EB11MP_PMU_SCU0 + i, NULL); |
Russell King | 10c03f6 | 2006-12-19 14:17:46 +0000 | [diff] [blame] | 161 | return ret; |
| 162 | } |
| 163 | |
| 164 | static 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 Marinas | fe6cfde | 2008-04-24 10:05:43 +0100 | [diff] [blame] | 182 | free_irq(IRQ_EB11MP_PMU_SCU0 + i, NULL); |
Russell King | 10c03f6 | 2006-12-19 14:17:46 +0000 | [diff] [blame] | 183 | } |
| 184 | } |
| 185 | } |
| 186 | |
| 187 | struct em_function_data { |
| 188 | int (*fn)(void); |
| 189 | int ret; |
| 190 | }; |
| 191 | |
| 192 | static 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 | |
| 200 | static 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 King | f72267c | 2007-06-02 15:36:37 +0100 | [diff] [blame] | 207 | preempt_disable(); |
Jens Axboe | 8691e5a | 2008-06-06 11:18:06 +0200 | [diff] [blame] | 208 | smp_call_function(em_func, &data, 1); |
Russell King | 10c03f6 | 2006-12-19 14:17:46 +0000 | [diff] [blame] | 209 | em_func(&data); |
Russell King | f72267c | 2007-06-02 15:36:37 +0100 | [diff] [blame] | 210 | preempt_enable(); |
Russell King | 10c03f6 | 2006-12-19 14:17:46 +0000 | [diff] [blame] | 211 | |
| 212 | return data.ret; |
| 213 | } |
| 214 | |
| 215 | /* |
| 216 | * Glue to stick the individual ARM11 PMUs and the SCU |
| 217 | * into the oprofile framework. |
| 218 | */ |
| 219 | static 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 King | 10c03f6 | 2006-12-19 14:17:46 +0000 | [diff] [blame] | 231 | static int em_start(void) |
| 232 | { |
| 233 | int ret; |
| 234 | |
Jamie Iles | 1618fdd | 2010-02-02 20:24:07 +0100 | [diff] [blame] | 235 | 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 King | 10c03f6 | 2006-12-19 14:17:46 +0000 | [diff] [blame] | 242 | if (ret == 0) { |
| 243 | em_call_function(arm11_start_pmu); |
| 244 | |
| 245 | ret = scu_start(); |
Jamie Iles | 1618fdd | 2010-02-02 20:24:07 +0100 | [diff] [blame] | 246 | 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 King | 10c03f6 | 2006-12-19 14:17:46 +0000 | [diff] [blame] | 253 | } |
Jamie Iles | 1618fdd | 2010-02-02 20:24:07 +0100 | [diff] [blame] | 254 | |
| 255 | out: |
Russell King | 10c03f6 | 2006-12-19 14:17:46 +0000 | [diff] [blame] | 256 | return ret; |
| 257 | } |
| 258 | |
| 259 | static void em_stop(void) |
| 260 | { |
| 261 | em_call_function(arm11_stop_pmu); |
Jamie Iles | 1618fdd | 2010-02-02 20:24:07 +0100 | [diff] [blame] | 262 | arm11_release_interrupts(pmu_irqs->irqs, pmu_irqs->num_irqs); |
Russell King | 10c03f6 | 2006-12-19 14:17:46 +0000 | [diff] [blame] | 263 | scu_stop(); |
Jamie Iles | 1618fdd | 2010-02-02 20:24:07 +0100 | [diff] [blame] | 264 | release_pmu(pmu_irqs); |
Russell King | 10c03f6 | 2006-12-19 14:17:46 +0000 | [diff] [blame] | 265 | } |
| 266 | |
| 267 | /* |
| 268 | * Why isn't there a function to route an IRQ to a specific CPU in |
| 269 | * genirq? |
| 270 | */ |
| 271 | static void em_route_irq(int irq, unsigned int cpu) |
| 272 | { |
Russell King | 28c670c | 2007-05-26 12:08:29 +0100 | [diff] [blame] | 273 | struct irq_desc *desc = irq_desc + irq; |
Rusty Russell | 0de2652 | 2008-12-13 21:20:26 +1030 | [diff] [blame] | 274 | const struct cpumask *mask = cpumask_of(cpu); |
Russell King | 28c670c | 2007-05-26 12:08:29 +0100 | [diff] [blame] | 275 | |
| 276 | spin_lock_irq(&desc->lock); |
Mike Travis | e65e49d | 2009-01-12 15:27:13 -0800 | [diff] [blame] | 277 | cpumask_copy(desc->affinity, mask); |
Russell King | 28c670c | 2007-05-26 12:08:29 +0100 | [diff] [blame] | 278 | desc->chip->set_affinity(irq, mask); |
| 279 | spin_unlock_irq(&desc->lock); |
Russell King | 10c03f6 | 2006-12-19 14:17:46 +0000 | [diff] [blame] | 280 | } |
| 281 | |
| 282 | static int em_setup(void) |
| 283 | { |
| 284 | /* |
| 285 | * Send SCU PMU interrupts to the "owner" CPU. |
| 286 | */ |
Catalin Marinas | fe6cfde | 2008-04-24 10:05:43 +0100 | [diff] [blame] | 287 | 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 King | 10c03f6 | 2006-12-19 14:17:46 +0000 | [diff] [blame] | 295 | |
Jamie Iles | 1618fdd | 2010-02-02 20:24:07 +0100 | [diff] [blame] | 296 | return init_pmu(); |
Russell King | 10c03f6 | 2006-12-19 14:17:46 +0000 | [diff] [blame] | 297 | } |
| 298 | |
| 299 | struct 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 | }; |