Commit d7ac4e28ccc63ed6b4d67bd9c4a67cb9533eeb45

Authored by Jean PIHET
Committed by Russell King
1 parent 46097c7dd8

[ARM] 5195/1: ARMv7 Oprofile support

Add Oprofile kernel support for ARMv7.
Tested on OMAP3430 and OMAP3530 chipsets (Cortex-A8).

Signed-off-by: Jean Pihet <jpihet@mvista.com>
Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>

Showing 7 changed files with 527 additions and 0 deletions Side-by-side Diff

... ... @@ -178,6 +178,11 @@
178 178 config OPROFILE_ARM11_CORE
179 179 bool
180 180  
  181 +config OPROFILE_ARMV7
  182 + def_bool y
  183 + depends on CPU_V7 && !SMP
  184 + bool
  185 +
181 186 endif
182 187  
183 188 config VECTORS_BASE
arch/arm/oprofile/Makefile
... ... @@ -11,4 +11,5 @@
11 11 oprofile-$(CONFIG_OPROFILE_ARM11_CORE) += op_model_arm11_core.o
12 12 oprofile-$(CONFIG_OPROFILE_ARMV6) += op_model_v6.o
13 13 oprofile-$(CONFIG_OPROFILE_MPCORE) += op_model_mpcore.o
  14 +oprofile-$(CONFIG_OPROFILE_ARMV7) += op_model_v7.o
arch/arm/oprofile/common.c
... ... @@ -145,6 +145,10 @@
145 145 spec = &op_mpcore_spec;
146 146 #endif
147 147  
  148 +#ifdef CONFIG_OPROFILE_ARMV7
  149 + spec = &op_armv7_spec;
  150 +#endif
  151 +
148 152 if (spec) {
149 153 ret = spec->init();
150 154 if (ret < 0)
arch/arm/oprofile/op_arm_model.h
... ... @@ -26,6 +26,7 @@
26 26  
27 27 extern struct op_arm_model_spec op_armv6_spec;
28 28 extern struct op_arm_model_spec op_mpcore_spec;
  29 +extern struct op_arm_model_spec op_armv7_spec;
29 30  
30 31 extern void arm_backtrace(struct pt_regs * const regs, unsigned int depth);
31 32  
arch/arm/oprofile/op_model_v7.c
  1 +/**
  2 + * op_model_v7.c
  3 + * ARM V7 (Cortex A8) Event Monitor Driver
  4 + *
  5 + * Copyright 2008 Jean Pihet <jpihet@mvista.com>
  6 + * Copyright 2004 ARM SMP Development Team
  7 + *
  8 + * This program is free software; you can redistribute it and/or modify
  9 + * it under the terms of the GNU General Public License version 2 as
  10 + * published by the Free Software Foundation.
  11 + */
  12 +#include <linux/types.h>
  13 +#include <linux/errno.h>
  14 +#include <linux/oprofile.h>
  15 +#include <linux/interrupt.h>
  16 +#include <linux/irq.h>
  17 +#include <linux/smp.h>
  18 +
  19 +#include "op_counter.h"
  20 +#include "op_arm_model.h"
  21 +#include "op_model_v7.h"
  22 +
  23 +/* #define DEBUG */
  24 +
  25 +
  26 +/*
  27 + * ARM V7 PMNC support
  28 + */
  29 +
  30 +static u32 cnt_en[CNTMAX];
  31 +
  32 +static inline void armv7_pmnc_write(u32 val)
  33 +{
  34 + val &= PMNC_MASK;
  35 + asm volatile("mcr p15, 0, %0, c9, c12, 0" : : "r" (val));
  36 +}
  37 +
  38 +static inline u32 armv7_pmnc_read(void)
  39 +{
  40 + u32 val;
  41 +
  42 + asm volatile("mrc p15, 0, %0, c9, c12, 0" : "=r" (val));
  43 + return val;
  44 +}
  45 +
  46 +static inline u32 armv7_pmnc_enable_counter(unsigned int cnt)
  47 +{
  48 + u32 val;
  49 +
  50 + if (cnt >= CNTMAX) {
  51 + printk(KERN_ERR "oprofile: CPU%u enabling wrong PMNC counter"
  52 + " %d\n", smp_processor_id(), cnt);
  53 + return -1;
  54 + }
  55 +
  56 + if (cnt == CCNT)
  57 + val = CNTENS_C;
  58 + else
  59 + val = (1 << (cnt - CNT0));
  60 +
  61 + val &= CNTENS_MASK;
  62 + asm volatile("mcr p15, 0, %0, c9, c12, 1" : : "r" (val));
  63 +
  64 + return cnt;
  65 +}
  66 +
  67 +static inline u32 armv7_pmnc_disable_counter(unsigned int cnt)
  68 +{
  69 + u32 val;
  70 +
  71 + if (cnt >= CNTMAX) {
  72 + printk(KERN_ERR "oprofile: CPU%u disabling wrong PMNC counter"
  73 + " %d\n", smp_processor_id(), cnt);
  74 + return -1;
  75 + }
  76 +
  77 + if (cnt == CCNT)
  78 + val = CNTENC_C;
  79 + else
  80 + val = (1 << (cnt - CNT0));
  81 +
  82 + val &= CNTENC_MASK;
  83 + asm volatile("mcr p15, 0, %0, c9, c12, 2" : : "r" (val));
  84 +
  85 + return cnt;
  86 +}
  87 +
  88 +static inline u32 armv7_pmnc_enable_intens(unsigned int cnt)
  89 +{
  90 + u32 val;
  91 +
  92 + if (cnt >= CNTMAX) {
  93 + printk(KERN_ERR "oprofile: CPU%u enabling wrong PMNC counter"
  94 + " interrupt enable %d\n", smp_processor_id(), cnt);
  95 + return -1;
  96 + }
  97 +
  98 + if (cnt == CCNT)
  99 + val = INTENS_C;
  100 + else
  101 + val = (1 << (cnt - CNT0));
  102 +
  103 + val &= INTENS_MASK;
  104 + asm volatile("mcr p15, 0, %0, c9, c14, 1" : : "r" (val));
  105 +
  106 + return cnt;
  107 +}
  108 +
  109 +static inline u32 armv7_pmnc_getreset_flags(void)
  110 +{
  111 + u32 val;
  112 +
  113 + /* Read */
  114 + asm volatile("mrc p15, 0, %0, c9, c12, 3" : "=r" (val));
  115 +
  116 + /* Write to clear flags */
  117 + val &= FLAG_MASK;
  118 + asm volatile("mcr p15, 0, %0, c9, c12, 3" : : "r" (val));
  119 +
  120 + return val;
  121 +}
  122 +
  123 +static inline int armv7_pmnc_select_counter(unsigned int cnt)
  124 +{
  125 + u32 val;
  126 +
  127 + if ((cnt == CCNT) || (cnt >= CNTMAX)) {
  128 + printk(KERN_ERR "oprofile: CPU%u selecting wrong PMNC counteri"
  129 + " %d\n", smp_processor_id(), cnt);
  130 + return -1;
  131 + }
  132 +
  133 + val = (cnt - CNT0) & SELECT_MASK;
  134 + asm volatile("mcr p15, 0, %0, c9, c12, 5" : : "r" (val));
  135 +
  136 + return cnt;
  137 +}
  138 +
  139 +static inline void armv7_pmnc_write_evtsel(unsigned int cnt, u32 val)
  140 +{
  141 + if (armv7_pmnc_select_counter(cnt) == cnt) {
  142 + val &= EVTSEL_MASK;
  143 + asm volatile("mcr p15, 0, %0, c9, c13, 1" : : "r" (val));
  144 + }
  145 +}
  146 +
  147 +static void armv7_pmnc_reset_counter(unsigned int cnt)
  148 +{
  149 + u32 cpu_cnt = CPU_COUNTER(smp_processor_id(), cnt);
  150 + u32 val = -(u32)counter_config[cpu_cnt].count;
  151 +
  152 + switch (cnt) {
  153 + case CCNT:
  154 + armv7_pmnc_disable_counter(cnt);
  155 +
  156 + asm volatile("mcr p15, 0, %0, c9, c13, 0" : : "r" (val));
  157 +
  158 + if (cnt_en[cnt] != 0)
  159 + armv7_pmnc_enable_counter(cnt);
  160 +
  161 + break;
  162 +
  163 + case CNT0:
  164 + case CNT1:
  165 + case CNT2:
  166 + case CNT3:
  167 + armv7_pmnc_disable_counter(cnt);
  168 +
  169 + if (armv7_pmnc_select_counter(cnt) == cnt)
  170 + asm volatile("mcr p15, 0, %0, c9, c13, 2" : : "r" (val));
  171 +
  172 + if (cnt_en[cnt] != 0)
  173 + armv7_pmnc_enable_counter(cnt);
  174 +
  175 + break;
  176 +
  177 + default:
  178 + printk(KERN_ERR "oprofile: CPU%u resetting wrong PMNC counter"
  179 + " %d\n", smp_processor_id(), cnt);
  180 + break;
  181 + }
  182 +}
  183 +
  184 +int armv7_setup_pmnc(void)
  185 +{
  186 + unsigned int cnt;
  187 +
  188 + if (armv7_pmnc_read() & PMNC_E) {
  189 + printk(KERN_ERR "oprofile: CPU%u PMNC still enabled when setup"
  190 + " new event counter.\n", smp_processor_id());
  191 + return -EBUSY;
  192 + }
  193 +
  194 + /*
  195 + * Initialize & Reset PMNC: C bit, D bit and P bit.
  196 + * Note: Using a slower count for CCNT (D bit: divide by 64) results
  197 + * in a more stable system
  198 + */
  199 + armv7_pmnc_write(PMNC_P | PMNC_C | PMNC_D);
  200 +
  201 +
  202 + for (cnt = CCNT; cnt < CNTMAX; cnt++) {
  203 + unsigned long event;
  204 + u32 cpu_cnt = CPU_COUNTER(smp_processor_id(), cnt);
  205 +
  206 + /*
  207 + * Disable counter
  208 + */
  209 + armv7_pmnc_disable_counter(cnt);
  210 + cnt_en[cnt] = 0;
  211 +
  212 + if (!counter_config[cpu_cnt].enabled)
  213 + continue;
  214 +
  215 + event = counter_config[cpu_cnt].event & 255;
  216 +
  217 + /*
  218 + * Set event (if destined for PMNx counters)
  219 + * We don't need to set the event if it's a cycle count
  220 + */
  221 + if (cnt != CCNT)
  222 + armv7_pmnc_write_evtsel(cnt, event);
  223 +
  224 + /*
  225 + * Enable interrupt for this counter
  226 + */
  227 + armv7_pmnc_enable_intens(cnt);
  228 +
  229 + /*
  230 + * Reset counter
  231 + */
  232 + armv7_pmnc_reset_counter(cnt);
  233 +
  234 + /*
  235 + * Enable counter
  236 + */
  237 + armv7_pmnc_enable_counter(cnt);
  238 + cnt_en[cnt] = 1;
  239 + }
  240 +
  241 + return 0;
  242 +}
  243 +
  244 +static inline void armv7_start_pmnc(void)
  245 +{
  246 + armv7_pmnc_write(armv7_pmnc_read() | PMNC_E);
  247 +}
  248 +
  249 +static inline void armv7_stop_pmnc(void)
  250 +{
  251 + armv7_pmnc_write(armv7_pmnc_read() & ~PMNC_E);
  252 +}
  253 +
  254 +/*
  255 + * CPU counters' IRQ handler (one IRQ per CPU)
  256 + */
  257 +static irqreturn_t armv7_pmnc_interrupt(int irq, void *arg)
  258 +{
  259 + struct pt_regs *regs = get_irq_regs();
  260 + unsigned int cnt;
  261 + u32 flags;
  262 +
  263 +
  264 + /*
  265 + * Stop IRQ generation
  266 + */
  267 + armv7_stop_pmnc();
  268 +
  269 + /*
  270 + * Get and reset overflow status flags
  271 + */
  272 + flags = armv7_pmnc_getreset_flags();
  273 +
  274 + /*
  275 + * Cycle counter
  276 + */
  277 + if (flags & FLAG_C) {
  278 + u32 cpu_cnt = CPU_COUNTER(smp_processor_id(), CCNT);
  279 + armv7_pmnc_reset_counter(CCNT);
  280 + oprofile_add_sample(regs, cpu_cnt);
  281 + }
  282 +
  283 + /*
  284 + * PMNC counters 0:3
  285 + */
  286 + for (cnt = CNT0; cnt < CNTMAX; cnt++) {
  287 + if (flags & (1 << (cnt - CNT0))) {
  288 + u32 cpu_cnt = CPU_COUNTER(smp_processor_id(), cnt);
  289 + armv7_pmnc_reset_counter(cnt);
  290 + oprofile_add_sample(regs, cpu_cnt);
  291 + }
  292 + }
  293 +
  294 + /*
  295 + * Allow IRQ generation
  296 + */
  297 + armv7_start_pmnc();
  298 +
  299 + return IRQ_HANDLED;
  300 +}
  301 +
  302 +int armv7_request_interrupts(int *irqs, int nr)
  303 +{
  304 + unsigned int i;
  305 + int ret = 0;
  306 +
  307 + for (i = 0; i < nr; i++) {
  308 + ret = request_irq(irqs[i], armv7_pmnc_interrupt,
  309 + IRQF_DISABLED, "CP15 PMNC", NULL);
  310 + if (ret != 0) {
  311 + printk(KERN_ERR "oprofile: unable to request IRQ%u"
  312 + " for ARMv7\n",
  313 + irqs[i]);
  314 + break;
  315 + }
  316 + }
  317 +
  318 + if (i != nr)
  319 + while (i-- != 0)
  320 + free_irq(irqs[i], NULL);
  321 +
  322 + return ret;
  323 +}
  324 +
  325 +void armv7_release_interrupts(int *irqs, int nr)
  326 +{
  327 + unsigned int i;
  328 +
  329 + for (i = 0; i < nr; i++)
  330 + free_irq(irqs[i], NULL);
  331 +}
  332 +
  333 +#ifdef DEBUG
  334 +static void armv7_pmnc_dump_regs(void)
  335 +{
  336 + u32 val;
  337 + unsigned int cnt;
  338 +
  339 + printk(KERN_INFO "PMNC registers dump:\n");
  340 +
  341 + asm volatile("mrc p15, 0, %0, c9, c12, 0" : "=r" (val));
  342 + printk(KERN_INFO "PMNC =0x%08x\n", val);
  343 +
  344 + asm volatile("mrc p15, 0, %0, c9, c12, 1" : "=r" (val));
  345 + printk(KERN_INFO "CNTENS=0x%08x\n", val);
  346 +
  347 + asm volatile("mrc p15, 0, %0, c9, c14, 1" : "=r" (val));
  348 + printk(KERN_INFO "INTENS=0x%08x\n", val);
  349 +
  350 + asm volatile("mrc p15, 0, %0, c9, c12, 3" : "=r" (val));
  351 + printk(KERN_INFO "FLAGS =0x%08x\n", val);
  352 +
  353 + asm volatile("mrc p15, 0, %0, c9, c12, 5" : "=r" (val));
  354 + printk(KERN_INFO "SELECT=0x%08x\n", val);
  355 +
  356 + asm volatile("mrc p15, 0, %0, c9, c13, 0" : "=r" (val));
  357 + printk(KERN_INFO "CCNT =0x%08x\n", val);
  358 +
  359 + for (cnt = CNT0; cnt < CNTMAX; cnt++) {
  360 + armv7_pmnc_select_counter(cnt);
  361 + asm volatile("mrc p15, 0, %0, c9, c13, 2" : "=r" (val));
  362 + printk(KERN_INFO "CNT[%d] count =0x%08x\n", cnt-CNT0, val);
  363 + asm volatile("mrc p15, 0, %0, c9, c13, 1" : "=r" (val));
  364 + printk(KERN_INFO "CNT[%d] evtsel=0x%08x\n", cnt-CNT0, val);
  365 + }
  366 +}
  367 +#endif
  368 +
  369 +
  370 +static int irqs[] = {
  371 +#ifdef CONFIG_ARCH_OMAP3
  372 + INT_34XX_BENCH_MPU_EMUL,
  373 +#endif
  374 +};
  375 +
  376 +static void armv7_pmnc_stop(void)
  377 +{
  378 +#ifdef DEBUG
  379 + armv7_pmnc_dump_regs();
  380 +#endif
  381 + armv7_stop_pmnc();
  382 + armv7_release_interrupts(irqs, ARRAY_SIZE(irqs));
  383 +}
  384 +
  385 +static int armv7_pmnc_start(void)
  386 +{
  387 + int ret;
  388 +
  389 +#ifdef DEBUG
  390 + armv7_pmnc_dump_regs();
  391 +#endif
  392 + ret = armv7_request_interrupts(irqs, ARRAY_SIZE(irqs));
  393 + if (ret >= 0)
  394 + armv7_start_pmnc();
  395 +
  396 + return ret;
  397 +}
  398 +
  399 +static int armv7_detect_pmnc(void)
  400 +{
  401 + return 0;
  402 +}
  403 +
  404 +struct op_arm_model_spec op_armv7_spec = {
  405 + .init = armv7_detect_pmnc,
  406 + .num_counters = 5,
  407 + .setup_ctrs = armv7_setup_pmnc,
  408 + .start = armv7_pmnc_start,
  409 + .stop = armv7_pmnc_stop,
  410 + .name = "arm/armv7",
  411 +};
arch/arm/oprofile/op_model_v7.h
  1 +/**
  2 + * op_model_v7.h
  3 + * ARM v7 (Cortex A8) Event Monitor Driver
  4 + *
  5 + * Copyright 2008 Jean Pihet <jpihet@mvista.com>
  6 + * Copyright 2004 ARM SMP Development Team
  7 + * Copyright 2000-2004 Deepak Saxena <dsaxena@mvista.com>
  8 + * Copyright 2000-2004 MontaVista Software Inc
  9 + * Copyright 2004 Dave Jiang <dave.jiang@intel.com>
  10 + * Copyright 2004 Intel Corporation
  11 + * Copyright 2004 Zwane Mwaikambo <zwane@arm.linux.org.uk>
  12 + * Copyright 2004 Oprofile Authors
  13 + *
  14 + * Read the file COPYING
  15 + *
  16 + * This program is free software; you can redistribute it and/or modify
  17 + * it under the terms of the GNU General Public License version 2 as
  18 + * published by the Free Software Foundation.
  19 + */
  20 +#ifndef OP_MODEL_V7_H
  21 +#define OP_MODEL_V7_H
  22 +
  23 +/*
  24 + * Per-CPU PMNC: config reg
  25 + */
  26 +#define PMNC_E (1 << 0) /* Enable all counters */
  27 +#define PMNC_P (1 << 1) /* Reset all counters */
  28 +#define PMNC_C (1 << 2) /* Cycle counter reset */
  29 +#define PMNC_D (1 << 3) /* CCNT counts every 64th cpu cycle */
  30 +#define PMNC_X (1 << 4) /* Export to ETM */
  31 +#define PMNC_DP (1 << 5) /* Disable CCNT if non-invasive debug*/
  32 +#define PMNC_MASK 0x3f /* Mask for writable bits */
  33 +
  34 +/*
  35 + * Available counters
  36 + */
  37 +#define CCNT 0
  38 +#define CNT0 1
  39 +#define CNT1 2
  40 +#define CNT2 3
  41 +#define CNT3 4
  42 +#define CNTMAX 5
  43 +
  44 +#define CPU_COUNTER(cpu, counter) ((cpu) * CNTMAX + (counter))
  45 +
  46 +/*
  47 + * CNTENS: counters enable reg
  48 + */
  49 +#define CNTENS_P0 (1 << 0)
  50 +#define CNTENS_P1 (1 << 1)
  51 +#define CNTENS_P2 (1 << 2)
  52 +#define CNTENS_P3 (1 << 3)
  53 +#define CNTENS_C (1 << 31)
  54 +#define CNTENS_MASK 0x8000000f /* Mask for writable bits */
  55 +
  56 +/*
  57 + * CNTENC: counters disable reg
  58 + */
  59 +#define CNTENC_P0 (1 << 0)
  60 +#define CNTENC_P1 (1 << 1)
  61 +#define CNTENC_P2 (1 << 2)
  62 +#define CNTENC_P3 (1 << 3)
  63 +#define CNTENC_C (1 << 31)
  64 +#define CNTENC_MASK 0x8000000f /* Mask for writable bits */
  65 +
  66 +/*
  67 + * INTENS: counters overflow interrupt enable reg
  68 + */
  69 +#define INTENS_P0 (1 << 0)
  70 +#define INTENS_P1 (1 << 1)
  71 +#define INTENS_P2 (1 << 2)
  72 +#define INTENS_P3 (1 << 3)
  73 +#define INTENS_C (1 << 31)
  74 +#define INTENS_MASK 0x8000000f /* Mask for writable bits */
  75 +
  76 +/*
  77 + * EVTSEL: Event selection reg
  78 + */
  79 +#define EVTSEL_MASK 0x7f /* Mask for writable bits */
  80 +
  81 +/*
  82 + * SELECT: Counter selection reg
  83 + */
  84 +#define SELECT_MASK 0x1f /* Mask for writable bits */
  85 +
  86 +/*
  87 + * FLAG: counters overflow flag status reg
  88 + */
  89 +#define FLAG_P0 (1 << 0)
  90 +#define FLAG_P1 (1 << 1)
  91 +#define FLAG_P2 (1 << 2)
  92 +#define FLAG_P3 (1 << 3)
  93 +#define FLAG_C (1 << 31)
  94 +#define FLAG_MASK 0x8000000f /* Mask for writable bits */
  95 +
  96 +
  97 +int armv7_setup_pmu(void);
  98 +int armv7_start_pmu(void);
  99 +int armv7_stop_pmu(void);
  100 +int armv7_request_interrupts(int *, int);
  101 +void armv7_release_interrupts(int *, int);
  102 +
  103 +#endif
arch/arm/plat-omap/include/mach/irqs.h
... ... @@ -280,6 +280,8 @@
280 280 #define INT_24XX_USB_IRQ_OTG 80
281 281 #define INT_24XX_MMC_IRQ 83
282 282  
  283 +#define INT_34XX_BENCH_MPU_EMUL 3
  284 +
283 285 /* Max. 128 level 2 IRQs (OMAP1610), 192 GPIOs (OMAP730) and
284 286 * 16 MPUIO lines */
285 287 #define OMAP_MAX_GPIO_LINES 192