linux/arch/avr32/oprofile/op_model_avr32.c
<<
>>
Prefs
   1/*
   2 * AVR32 Performance Counter Driver
   3 *
   4 * Copyright (C) 2005-2007 Atmel Corporation
   5 *
   6 * This program is free software; you can redistribute it and/or modify
   7 * it under the terms of the GNU General Public License version 2 as
   8 * published by the Free Software Foundation.
   9 *
  10 * Author: Ronny Pedersen
  11 */
  12#include <linux/errno.h>
  13#include <linux/interrupt.h>
  14#include <linux/irq.h>
  15#include <linux/oprofile.h>
  16#include <linux/sched.h>
  17#include <linux/types.h>
  18
  19#include <asm/sysreg.h>
  20#include <asm/system.h>
  21
  22#define AVR32_PERFCTR_IRQ_GROUP 0
  23#define AVR32_PERFCTR_IRQ_LINE  1
  24
  25void avr32_backtrace(struct pt_regs * const regs, unsigned int depth);
  26
  27enum { PCCNT, PCNT0, PCNT1, NR_counter };
  28
  29struct avr32_perf_counter {
  30        unsigned long   enabled;
  31        unsigned long   event;
  32        unsigned long   count;
  33        unsigned long   unit_mask;
  34        unsigned long   kernel;
  35        unsigned long   user;
  36
  37        u32             ie_mask;
  38        u32             flag_mask;
  39};
  40
  41static struct avr32_perf_counter counter[NR_counter] = {
  42        {
  43                .ie_mask        = SYSREG_BIT(IEC),
  44                .flag_mask      = SYSREG_BIT(FC),
  45        }, {
  46                .ie_mask        = SYSREG_BIT(IE0),
  47                .flag_mask      = SYSREG_BIT(F0),
  48        }, {
  49                .ie_mask        = SYSREG_BIT(IE1),
  50                .flag_mask      = SYSREG_BIT(F1),
  51        },
  52};
  53
  54static void avr32_perf_counter_reset(void)
  55{
  56        /* Reset all counter and disable/clear all interrupts */
  57        sysreg_write(PCCR, (SYSREG_BIT(PCCR_R)
  58                                | SYSREG_BIT(PCCR_C)
  59                                | SYSREG_BIT(FC)
  60                                | SYSREG_BIT(F0)
  61                                | SYSREG_BIT(F1)));
  62}
  63
  64static irqreturn_t avr32_perf_counter_interrupt(int irq, void *dev_id)
  65{
  66        struct avr32_perf_counter *ctr = dev_id;
  67        struct pt_regs *regs;
  68        u32 pccr;
  69
  70        if (likely(!(intc_get_pending(AVR32_PERFCTR_IRQ_GROUP)
  71                                        & (1 << AVR32_PERFCTR_IRQ_LINE))))
  72                return IRQ_NONE;
  73
  74        regs = get_irq_regs();
  75        pccr = sysreg_read(PCCR);
  76
  77        /* Clear the interrupt flags we're about to handle */
  78        sysreg_write(PCCR, pccr);
  79
  80        /* PCCNT */
  81        if (ctr->enabled && (pccr & ctr->flag_mask)) {
  82                sysreg_write(PCCNT, -ctr->count);
  83                oprofile_add_sample(regs, PCCNT);
  84        }
  85        ctr++;
  86        /* PCNT0 */
  87        if (ctr->enabled && (pccr & ctr->flag_mask)) {
  88                sysreg_write(PCNT0, -ctr->count);
  89                oprofile_add_sample(regs, PCNT0);
  90        }
  91        ctr++;
  92        /* PCNT1 */
  93        if (ctr->enabled && (pccr & ctr->flag_mask)) {
  94                sysreg_write(PCNT1, -ctr->count);
  95                oprofile_add_sample(regs, PCNT1);
  96        }
  97
  98        return IRQ_HANDLED;
  99}
 100
 101static int avr32_perf_counter_create_files(struct super_block *sb,
 102                struct dentry *root)
 103{
 104        struct dentry *dir;
 105        unsigned int i;
 106        char filename[4];
 107
 108        for (i = 0; i < NR_counter; i++) {
 109                snprintf(filename, sizeof(filename), "%u", i);
 110                dir = oprofilefs_mkdir(sb, root, filename);
 111
 112                oprofilefs_create_ulong(sb, dir, "enabled",
 113                                &counter[i].enabled);
 114                oprofilefs_create_ulong(sb, dir, "event",
 115                                &counter[i].event);
 116                oprofilefs_create_ulong(sb, dir, "count",
 117                                &counter[i].count);
 118
 119                /* Dummy entries */
 120                oprofilefs_create_ulong(sb, dir, "kernel",
 121                                &counter[i].kernel);
 122                oprofilefs_create_ulong(sb, dir, "user",
 123                                &counter[i].user);
 124                oprofilefs_create_ulong(sb, dir, "unit_mask",
 125                                &counter[i].unit_mask);
 126        }
 127
 128        return 0;
 129}
 130
 131static int avr32_perf_counter_setup(void)
 132{
 133        struct avr32_perf_counter *ctr;
 134        u32 pccr;
 135        int ret;
 136        int i;
 137
 138        pr_debug("avr32_perf_counter_setup\n");
 139
 140        if (sysreg_read(PCCR) & SYSREG_BIT(PCCR_E)) {
 141                printk(KERN_ERR
 142                        "oprofile: setup: perf counter already enabled\n");
 143                return -EBUSY;
 144        }
 145
 146        ret = request_irq(AVR32_PERFCTR_IRQ_GROUP,
 147                        avr32_perf_counter_interrupt, IRQF_SHARED,
 148                        "oprofile", counter);
 149        if (ret)
 150                return ret;
 151
 152        avr32_perf_counter_reset();
 153
 154        pccr = 0;
 155        for (i = PCCNT; i < NR_counter; i++) {
 156                ctr = &counter[i];
 157                if (!ctr->enabled)
 158                        continue;
 159
 160                pr_debug("enabling counter %d...\n", i);
 161
 162                pccr |= ctr->ie_mask;
 163
 164                switch (i) {
 165                case PCCNT:
 166                        /* PCCNT always counts cycles, so no events */
 167                        sysreg_write(PCCNT, -ctr->count);
 168                        break;
 169                case PCNT0:
 170                        pccr |= SYSREG_BF(CONF0, ctr->event);
 171                        sysreg_write(PCNT0, -ctr->count);
 172                        break;
 173                case PCNT1:
 174                        pccr |= SYSREG_BF(CONF1, ctr->event);
 175                        sysreg_write(PCNT1, -ctr->count);
 176                        break;
 177                }
 178        }
 179
 180        pr_debug("oprofile: writing 0x%x to PCCR...\n", pccr);
 181
 182        sysreg_write(PCCR, pccr);
 183
 184        return 0;
 185}
 186
 187static void avr32_perf_counter_shutdown(void)
 188{
 189        pr_debug("avr32_perf_counter_shutdown\n");
 190
 191        avr32_perf_counter_reset();
 192        free_irq(AVR32_PERFCTR_IRQ_GROUP, counter);
 193}
 194
 195static int avr32_perf_counter_start(void)
 196{
 197        pr_debug("avr32_perf_counter_start\n");
 198
 199        sysreg_write(PCCR, sysreg_read(PCCR) | SYSREG_BIT(PCCR_E));
 200
 201        return 0;
 202}
 203
 204static void avr32_perf_counter_stop(void)
 205{
 206        pr_debug("avr32_perf_counter_stop\n");
 207
 208        sysreg_write(PCCR, sysreg_read(PCCR) & ~SYSREG_BIT(PCCR_E));
 209}
 210
 211static struct oprofile_operations avr32_perf_counter_ops __initdata = {
 212        .create_files   = avr32_perf_counter_create_files,
 213        .setup          = avr32_perf_counter_setup,
 214        .shutdown       = avr32_perf_counter_shutdown,
 215        .start          = avr32_perf_counter_start,
 216        .stop           = avr32_perf_counter_stop,
 217        .cpu_type       = "avr32",
 218};
 219
 220int __init oprofile_arch_init(struct oprofile_operations *ops)
 221{
 222        if (!(current_cpu_data.features & AVR32_FEATURE_PCTR))
 223                return -ENODEV;
 224
 225        memcpy(ops, &avr32_perf_counter_ops,
 226                        sizeof(struct oprofile_operations));
 227
 228        ops->backtrace = avr32_backtrace;
 229
 230        printk(KERN_INFO "oprofile: using AVR32 performance monitoring.\n");
 231
 232        return 0;
 233}
 234
 235void oprofile_arch_exit(void)
 236{
 237
 238}
 239