Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

[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>

authored by

Russell King and committed by
Russell King
10c03f69 2d9e1ae0

+381
+6
arch/arm/oprofile/Kconfig
··· 27 27 default y 28 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 36 config OPROFILE_ARM11_CORE 31 37 bool 32 38
+1
arch/arm/oprofile/Makefile
··· 10 10 oprofile-$(CONFIG_CPU_XSCALE) += op_model_xscale.o 11 11 oprofile-$(CONFIG_OPROFILE_ARM11_CORE) += op_model_arm11_core.o 12 12 oprofile-$(CONFIG_OPROFILE_ARMV6) += op_model_v6.o 13 + oprofile-$(CONFIG_OPROFILE_MPCORE) += op_model_mpcore.o
+4
arch/arm/oprofile/common.c
··· 139 139 spec = &op_armv6_spec; 140 140 #endif 141 141 142 + #ifdef CONFIG_OPROFILE_MPCORE 143 + spec = &op_mpcore_spec; 144 + #endif 145 + 142 146 if (spec) { 143 147 ret = spec->init(); 144 148 if (ret < 0)
+1
arch/arm/oprofile/op_arm_model.h
··· 25 25 #endif 26 26 27 27 extern struct op_arm_model_spec op_armv6_spec; 28 + extern struct op_arm_model_spec op_mpcore_spec; 28 29 29 30 extern void arm_backtrace(struct pt_regs * const regs, unsigned int depth); 30 31
+296
arch/arm/oprofile/op_model_mpcore.c
··· 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 + };
+61
arch/arm/oprofile/op_model_mpcore.h
··· 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
+12
include/asm-arm/arch-realview/irqs.h
··· 65 65 #define IRQ_AACI (IRQ_GIC_START + INT_AACI) 66 66 #define IRQ_ETH (IRQ_GIC_START + INT_ETH) 67 67 #define IRQ_USB (IRQ_GIC_START + INT_USB) 68 + #define IRQ_PMU_CPU0 (IRQ_GIC_START + INT_PMU_CPU0) 69 + #define IRQ_PMU_CPU1 (IRQ_GIC_START + INT_PMU_CPU1) 70 + #define IRQ_PMU_CPU2 (IRQ_GIC_START + INT_PMU_CPU2) 71 + #define IRQ_PMU_CPU3 (IRQ_GIC_START + INT_PMU_CPU3) 72 + #define IRQ_PMU_SCU0 (IRQ_GIC_START + INT_PMU_SCU0) 73 + #define IRQ_PMU_SCU1 (IRQ_GIC_START + INT_PMU_SCU1) 74 + #define IRQ_PMU_SCU2 (IRQ_GIC_START + INT_PMU_SCU2) 75 + #define IRQ_PMU_SCU3 (IRQ_GIC_START + INT_PMU_SCU3) 76 + #define IRQ_PMU_SCU4 (IRQ_GIC_START + INT_PMU_SCU4) 77 + #define IRQ_PMU_SCU5 (IRQ_GIC_START + INT_PMU_SCU5) 78 + #define IRQ_PMU_SCU6 (IRQ_GIC_START + INT_PMU_SCU6) 79 + #define IRQ_PMU_SCU7 (IRQ_GIC_START + INT_PMU_SCU7) 68 80 69 81 #define IRQMASK_WDOGINT INTMASK_WDOGINT 70 82 #define IRQMASK_SOFTINT INTMASK_SOFTINT