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