linux/arch/arm/oprofile/op_model_v7.c
<<
>>
Prefs
   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
  30static u32 cnt_en[CNTMAX];
  31
  32static 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
  38static 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
  46static 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
  67static 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
  88static 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
 109static 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
 123static 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
 139static 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
 147static 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
 184int 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
 244static inline void armv7_start_pmnc(void)
 245{
 246        armv7_pmnc_write(armv7_pmnc_read() | PMNC_E);
 247}
 248
 249static 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 */
 257static 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
 302int 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
 325void 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
 334static 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
 370static int irqs[] = {
 371#ifdef CONFIG_ARCH_OMAP3
 372        INT_34XX_BENCH_MPU_EMUL,
 373#endif
 374};
 375
 376static 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
 385static 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
 399static int armv7_detect_pmnc(void)
 400{
 401        return 0;
 402}
 403
 404struct 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};
 412
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.