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> |
| 35 | #include <linux/sched.h> |
| 36 | #include <linux/oprofile.h> |
| 37 | #include <linux/interrupt.h> |
| 38 | #include <linux/smp.h> |
Russell King | fced80c | 2008-09-06 12:10:45 +0100 | [diff] [blame] | 39 | #include <linux/io.h> |
Russell King | 10c03f6 | 2006-12-19 14:17:46 +0000 | [diff] [blame] | 40 | |
Russell King | 10c03f6 | 2006-12-19 14:17:46 +0000 | [diff] [blame] | 41 | #include <asm/irq.h> |
| 42 | #include <asm/mach/irq.h> |
Russell King | a09e64f | 2008-08-05 16:14:15 +0100 | [diff] [blame] | 43 | #include <mach/hardware.h> |
Catalin Marinas | ee8c957 | 2009-05-30 14:00:17 +0100 | [diff] [blame] | 44 | #include <mach/board-eb.h> |
Russell King | 10c03f6 | 2006-12-19 14:17:46 +0000 | [diff] [blame] | 45 | #include <asm/system.h> |
| 46 | |
| 47 | #include "op_counter.h" |
| 48 | #include "op_arm_model.h" |
| 49 | #include "op_model_arm11_core.h" |
| 50 | #include "op_model_mpcore.h" |
| 51 | |
| 52 | /* |
| 53 | * MPCore SCU event monitor support |
| 54 | */ |
Catalin Marinas | fe6cfde | 2008-04-24 10:05:43 +0100 | [diff] [blame] | 55 | #define SCU_EVENTMONITORS_VA_BASE __io_address(REALVIEW_EB11MP_SCU_BASE + 0x10) |
Russell King | 10c03f6 | 2006-12-19 14:17:46 +0000 | [diff] [blame] | 56 | |
| 57 | /* |
| 58 | * Bitmask of used SCU counters |
| 59 | */ |
| 60 | static unsigned int scu_em_used; |
| 61 | |
| 62 | /* |
| 63 | * 2 helper fns take a counter number from 0-7 (not the userspace-visible counter number) |
| 64 | */ |
| 65 | static inline void scu_reset_counter(struct eventmonitor __iomem *emc, unsigned int n) |
| 66 | { |
| 67 | writel(-(u32)counter_config[SCU_COUNTER(n)].count, &emc->MC[n]); |
| 68 | } |
| 69 | |
| 70 | static inline void scu_set_event(struct eventmonitor __iomem *emc, unsigned int n, u32 event) |
| 71 | { |
| 72 | event &= 0xff; |
| 73 | writeb(event, &emc->MCEB[n]); |
| 74 | } |
| 75 | |
| 76 | /* |
| 77 | * SCU counters' IRQ handler (one IRQ per counter => 2 IRQs per CPU) |
| 78 | */ |
| 79 | static irqreturn_t scu_em_interrupt(int irq, void *arg) |
| 80 | { |
| 81 | struct eventmonitor __iomem *emc = SCU_EVENTMONITORS_VA_BASE; |
| 82 | unsigned int cnt; |
| 83 | |
Catalin Marinas | fe6cfde | 2008-04-24 10:05:43 +0100 | [diff] [blame] | 84 | cnt = irq - IRQ_EB11MP_PMU_SCU0; |
Russell King | 10c03f6 | 2006-12-19 14:17:46 +0000 | [diff] [blame] | 85 | oprofile_add_sample(get_irq_regs(), SCU_COUNTER(cnt)); |
| 86 | scu_reset_counter(emc, cnt); |
| 87 | |
| 88 | /* Clear overflow flag for this counter */ |
| 89 | writel(1 << (cnt + 16), &emc->PMCR); |
| 90 | |
| 91 | return IRQ_HANDLED; |
| 92 | } |
| 93 | |
| 94 | /* Configure just the SCU counters that the user has requested */ |
| 95 | static void scu_setup(void) |
| 96 | { |
| 97 | struct eventmonitor __iomem *emc = SCU_EVENTMONITORS_VA_BASE; |
| 98 | unsigned int i; |
| 99 | |
| 100 | scu_em_used = 0; |
| 101 | |
| 102 | for (i = 0; i < NUM_SCU_COUNTERS; i++) { |
| 103 | if (counter_config[SCU_COUNTER(i)].enabled && |
| 104 | counter_config[SCU_COUNTER(i)].event) { |
| 105 | scu_set_event(emc, i, 0); /* disable counter for now */ |
| 106 | scu_em_used |= 1 << i; |
| 107 | } |
| 108 | } |
| 109 | } |
| 110 | |
| 111 | static int scu_start(void) |
| 112 | { |
| 113 | struct eventmonitor __iomem *emc = SCU_EVENTMONITORS_VA_BASE; |
| 114 | unsigned int temp, i; |
| 115 | unsigned long event; |
| 116 | int ret = 0; |
| 117 | |
| 118 | /* |
| 119 | * request the SCU counter interrupts that we need |
| 120 | */ |
| 121 | for (i = 0; i < NUM_SCU_COUNTERS; i++) { |
| 122 | if (scu_em_used & (1 << i)) { |
Catalin Marinas | fe6cfde | 2008-04-24 10:05:43 +0100 | [diff] [blame] | 123 | 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] | 124 | if (ret) { |
| 125 | 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] | 126 | IRQ_EB11MP_PMU_SCU0 + i); |
Russell King | 10c03f6 | 2006-12-19 14:17:46 +0000 | [diff] [blame] | 127 | goto err_free_scu; |
| 128 | } |
| 129 | } |
| 130 | } |
| 131 | |
| 132 | /* |
| 133 | * clear overflow and enable interrupt for all used counters |
| 134 | */ |
| 135 | temp = readl(&emc->PMCR); |
| 136 | for (i = 0; i < NUM_SCU_COUNTERS; i++) { |
| 137 | if (scu_em_used & (1 << i)) { |
| 138 | scu_reset_counter(emc, i); |
| 139 | event = counter_config[SCU_COUNTER(i)].event; |
| 140 | scu_set_event(emc, i, event); |
| 141 | |
| 142 | /* clear overflow/interrupt */ |
| 143 | temp |= 1 << (i + 16); |
| 144 | /* enable interrupt*/ |
| 145 | temp |= 1 << (i + 8); |
| 146 | } |
| 147 | } |
| 148 | |
| 149 | /* Enable all 8 counters */ |
| 150 | temp |= PMCR_E; |
| 151 | writel(temp, &emc->PMCR); |
| 152 | |
| 153 | return 0; |
| 154 | |
| 155 | err_free_scu: |
| 156 | while (i--) |
Catalin Marinas | fe6cfde | 2008-04-24 10:05:43 +0100 | [diff] [blame] | 157 | free_irq(IRQ_EB11MP_PMU_SCU0 + i, NULL); |
Russell King | 10c03f6 | 2006-12-19 14:17:46 +0000 | [diff] [blame] | 158 | return ret; |
| 159 | } |
| 160 | |
| 161 | static void scu_stop(void) |
| 162 | { |
| 163 | struct eventmonitor __iomem *emc = SCU_EVENTMONITORS_VA_BASE; |
| 164 | unsigned int temp, i; |
| 165 | |
| 166 | /* Disable counter interrupts */ |
| 167 | /* Don't disable all 8 counters (with the E bit) as they may be in use */ |
| 168 | temp = readl(&emc->PMCR); |
| 169 | for (i = 0; i < NUM_SCU_COUNTERS; i++) { |
| 170 | if (scu_em_used & (1 << i)) |
| 171 | temp &= ~(1 << (i + 8)); |
| 172 | } |
| 173 | writel(temp, &emc->PMCR); |
| 174 | |
| 175 | /* Free counter interrupts and reset counters */ |
| 176 | for (i = 0; i < NUM_SCU_COUNTERS; i++) { |
| 177 | if (scu_em_used & (1 << i)) { |
| 178 | scu_reset_counter(emc, i); |
Catalin Marinas | fe6cfde | 2008-04-24 10:05:43 +0100 | [diff] [blame] | 179 | free_irq(IRQ_EB11MP_PMU_SCU0 + i, NULL); |
Russell King | 10c03f6 | 2006-12-19 14:17:46 +0000 | [diff] [blame] | 180 | } |
| 181 | } |
| 182 | } |
| 183 | |
| 184 | struct em_function_data { |
| 185 | int (*fn)(void); |
| 186 | int ret; |
| 187 | }; |
| 188 | |
| 189 | static void em_func(void *data) |
| 190 | { |
| 191 | struct em_function_data *d = data; |
| 192 | int ret = d->fn(); |
| 193 | if (ret) |
| 194 | d->ret = ret; |
| 195 | } |
| 196 | |
| 197 | static int em_call_function(int (*fn)(void)) |
| 198 | { |
| 199 | struct em_function_data data; |
| 200 | |
| 201 | data.fn = fn; |
| 202 | data.ret = 0; |
| 203 | |
Russell King | f72267c | 2007-06-02 15:36:37 +0100 | [diff] [blame] | 204 | preempt_disable(); |
Jens Axboe | 8691e5a | 2008-06-06 11:18:06 +0200 | [diff] [blame] | 205 | smp_call_function(em_func, &data, 1); |
Russell King | 10c03f6 | 2006-12-19 14:17:46 +0000 | [diff] [blame] | 206 | em_func(&data); |
Russell King | f72267c | 2007-06-02 15:36:37 +0100 | [diff] [blame] | 207 | preempt_enable(); |
Russell King | 10c03f6 | 2006-12-19 14:17:46 +0000 | [diff] [blame] | 208 | |
| 209 | return data.ret; |
| 210 | } |
| 211 | |
| 212 | /* |
| 213 | * Glue to stick the individual ARM11 PMUs and the SCU |
| 214 | * into the oprofile framework. |
| 215 | */ |
| 216 | static int em_setup_ctrs(void) |
| 217 | { |
| 218 | int ret; |
| 219 | |
| 220 | /* Configure CPU counters by cross-calling to the other CPUs */ |
| 221 | ret = em_call_function(arm11_setup_pmu); |
| 222 | if (ret == 0) |
| 223 | scu_setup(); |
| 224 | |
| 225 | return 0; |
| 226 | } |
| 227 | |
| 228 | static int arm11_irqs[] = { |
Catalin Marinas | fe6cfde | 2008-04-24 10:05:43 +0100 | [diff] [blame] | 229 | [0] = IRQ_EB11MP_PMU_CPU0, |
| 230 | [1] = IRQ_EB11MP_PMU_CPU1, |
| 231 | [2] = IRQ_EB11MP_PMU_CPU2, |
| 232 | [3] = IRQ_EB11MP_PMU_CPU3 |
Russell King | 10c03f6 | 2006-12-19 14:17:46 +0000 | [diff] [blame] | 233 | }; |
| 234 | |
| 235 | static int em_start(void) |
| 236 | { |
| 237 | int ret; |
| 238 | |
| 239 | ret = arm11_request_interrupts(arm11_irqs, ARRAY_SIZE(arm11_irqs)); |
| 240 | if (ret == 0) { |
| 241 | em_call_function(arm11_start_pmu); |
| 242 | |
| 243 | ret = scu_start(); |
| 244 | if (ret) |
| 245 | arm11_release_interrupts(arm11_irqs, ARRAY_SIZE(arm11_irqs)); |
| 246 | } |
| 247 | return ret; |
| 248 | } |
| 249 | |
| 250 | static void em_stop(void) |
| 251 | { |
| 252 | em_call_function(arm11_stop_pmu); |
| 253 | arm11_release_interrupts(arm11_irqs, ARRAY_SIZE(arm11_irqs)); |
| 254 | scu_stop(); |
| 255 | } |
| 256 | |
| 257 | /* |
| 258 | * Why isn't there a function to route an IRQ to a specific CPU in |
| 259 | * genirq? |
| 260 | */ |
| 261 | static void em_route_irq(int irq, unsigned int cpu) |
| 262 | { |
Russell King | 28c670c | 2007-05-26 12:08:29 +0100 | [diff] [blame] | 263 | struct irq_desc *desc = irq_desc + irq; |
Rusty Russell | 0de2652 | 2008-12-13 21:20:26 +1030 | [diff] [blame] | 264 | const struct cpumask *mask = cpumask_of(cpu); |
Russell King | 28c670c | 2007-05-26 12:08:29 +0100 | [diff] [blame] | 265 | |
| 266 | spin_lock_irq(&desc->lock); |
Mike Travis | e65e49d | 2009-01-12 15:27:13 -0800 | [diff] [blame] | 267 | cpumask_copy(desc->affinity, mask); |
Russell King | 28c670c | 2007-05-26 12:08:29 +0100 | [diff] [blame] | 268 | desc->chip->set_affinity(irq, mask); |
| 269 | spin_unlock_irq(&desc->lock); |
Russell King | 10c03f6 | 2006-12-19 14:17:46 +0000 | [diff] [blame] | 270 | } |
| 271 | |
| 272 | static int em_setup(void) |
| 273 | { |
| 274 | /* |
| 275 | * Send SCU PMU interrupts to the "owner" CPU. |
| 276 | */ |
Catalin Marinas | fe6cfde | 2008-04-24 10:05:43 +0100 | [diff] [blame] | 277 | em_route_irq(IRQ_EB11MP_PMU_SCU0, 0); |
| 278 | em_route_irq(IRQ_EB11MP_PMU_SCU1, 0); |
| 279 | em_route_irq(IRQ_EB11MP_PMU_SCU2, 1); |
| 280 | em_route_irq(IRQ_EB11MP_PMU_SCU3, 1); |
| 281 | em_route_irq(IRQ_EB11MP_PMU_SCU4, 2); |
| 282 | em_route_irq(IRQ_EB11MP_PMU_SCU5, 2); |
| 283 | em_route_irq(IRQ_EB11MP_PMU_SCU6, 3); |
| 284 | em_route_irq(IRQ_EB11MP_PMU_SCU7, 3); |
Russell King | 10c03f6 | 2006-12-19 14:17:46 +0000 | [diff] [blame] | 285 | |
| 286 | /* |
| 287 | * Send CP15 PMU interrupts to the owner CPU. |
| 288 | */ |
Catalin Marinas | fe6cfde | 2008-04-24 10:05:43 +0100 | [diff] [blame] | 289 | em_route_irq(IRQ_EB11MP_PMU_CPU0, 0); |
| 290 | em_route_irq(IRQ_EB11MP_PMU_CPU1, 1); |
| 291 | em_route_irq(IRQ_EB11MP_PMU_CPU2, 2); |
| 292 | em_route_irq(IRQ_EB11MP_PMU_CPU3, 3); |
Russell King | 10c03f6 | 2006-12-19 14:17:46 +0000 | [diff] [blame] | 293 | |
| 294 | return 0; |
| 295 | } |
| 296 | |
| 297 | struct op_arm_model_spec op_mpcore_spec = { |
| 298 | .init = em_setup, |
| 299 | .num_counters = MPCORE_NUM_COUNTERS, |
| 300 | .setup_ctrs = em_setup_ctrs, |
| 301 | .start = em_start, |
| 302 | .stop = em_stop, |
| 303 | .name = "arm/mpcore", |
| 304 | }; |