Path: blob/master/arch/avr32/oprofile/op_model_avr32.c
10817 views
/*1* AVR32 Performance Counter Driver2*3* Copyright (C) 2005-2007 Atmel Corporation4*5* This program is free software; you can redistribute it and/or modify6* it under the terms of the GNU General Public License version 2 as7* published by the Free Software Foundation.8*9* Author: Ronny Pedersen10*/11#include <linux/errno.h>12#include <linux/interrupt.h>13#include <linux/irq.h>14#include <linux/oprofile.h>15#include <linux/sched.h>16#include <linux/types.h>1718#include <asm/sysreg.h>19#include <asm/system.h>2021#define AVR32_PERFCTR_IRQ_GROUP 022#define AVR32_PERFCTR_IRQ_LINE 12324void avr32_backtrace(struct pt_regs * const regs, unsigned int depth);2526enum { PCCNT, PCNT0, PCNT1, NR_counter };2728struct avr32_perf_counter {29unsigned long enabled;30unsigned long event;31unsigned long count;32unsigned long unit_mask;33unsigned long kernel;34unsigned long user;3536u32 ie_mask;37u32 flag_mask;38};3940static 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};5253static void avr32_perf_counter_reset(void)54{55/* Reset all counter and disable/clear all interrupts */56sysreg_write(PCCR, (SYSREG_BIT(PCCR_R)57| SYSREG_BIT(PCCR_C)58| SYSREG_BIT(FC)59| SYSREG_BIT(F0)60| SYSREG_BIT(F1)));61}6263static irqreturn_t avr32_perf_counter_interrupt(int irq, void *dev_id)64{65struct avr32_perf_counter *ctr = dev_id;66struct pt_regs *regs;67u32 pccr;6869if (likely(!(intc_get_pending(AVR32_PERFCTR_IRQ_GROUP)70& (1 << AVR32_PERFCTR_IRQ_LINE))))71return IRQ_NONE;7273regs = get_irq_regs();74pccr = sysreg_read(PCCR);7576/* Clear the interrupt flags we're about to handle */77sysreg_write(PCCR, pccr);7879/* PCCNT */80if (ctr->enabled && (pccr & ctr->flag_mask)) {81sysreg_write(PCCNT, -ctr->count);82oprofile_add_sample(regs, PCCNT);83}84ctr++;85/* PCNT0 */86if (ctr->enabled && (pccr & ctr->flag_mask)) {87sysreg_write(PCNT0, -ctr->count);88oprofile_add_sample(regs, PCNT0);89}90ctr++;91/* PCNT1 */92if (ctr->enabled && (pccr & ctr->flag_mask)) {93sysreg_write(PCNT1, -ctr->count);94oprofile_add_sample(regs, PCNT1);95}9697return IRQ_HANDLED;98}99100static int avr32_perf_counter_create_files(struct super_block *sb,101struct dentry *root)102{103struct dentry *dir;104unsigned int i;105char filename[4];106107for (i = 0; i < NR_counter; i++) {108snprintf(filename, sizeof(filename), "%u", i);109dir = oprofilefs_mkdir(sb, root, filename);110111oprofilefs_create_ulong(sb, dir, "enabled",112&counter[i].enabled);113oprofilefs_create_ulong(sb, dir, "event",114&counter[i].event);115oprofilefs_create_ulong(sb, dir, "count",116&counter[i].count);117118/* Dummy entries */119oprofilefs_create_ulong(sb, dir, "kernel",120&counter[i].kernel);121oprofilefs_create_ulong(sb, dir, "user",122&counter[i].user);123oprofilefs_create_ulong(sb, dir, "unit_mask",124&counter[i].unit_mask);125}126127return 0;128}129130static int avr32_perf_counter_setup(void)131{132struct avr32_perf_counter *ctr;133u32 pccr;134int ret;135int i;136137pr_debug("avr32_perf_counter_setup\n");138139if (sysreg_read(PCCR) & SYSREG_BIT(PCCR_E)) {140printk(KERN_ERR141"oprofile: setup: perf counter already enabled\n");142return -EBUSY;143}144145ret = request_irq(AVR32_PERFCTR_IRQ_GROUP,146avr32_perf_counter_interrupt, IRQF_SHARED,147"oprofile", counter);148if (ret)149return ret;150151avr32_perf_counter_reset();152153pccr = 0;154for (i = PCCNT; i < NR_counter; i++) {155ctr = &counter[i];156if (!ctr->enabled)157continue;158159pr_debug("enabling counter %d...\n", i);160161pccr |= ctr->ie_mask;162163switch (i) {164case PCCNT:165/* PCCNT always counts cycles, so no events */166sysreg_write(PCCNT, -ctr->count);167break;168case PCNT0:169pccr |= SYSREG_BF(CONF0, ctr->event);170sysreg_write(PCNT0, -ctr->count);171break;172case PCNT1:173pccr |= SYSREG_BF(CONF1, ctr->event);174sysreg_write(PCNT1, -ctr->count);175break;176}177}178179pr_debug("oprofile: writing 0x%x to PCCR...\n", pccr);180181sysreg_write(PCCR, pccr);182183return 0;184}185186static void avr32_perf_counter_shutdown(void)187{188pr_debug("avr32_perf_counter_shutdown\n");189190avr32_perf_counter_reset();191free_irq(AVR32_PERFCTR_IRQ_GROUP, counter);192}193194static int avr32_perf_counter_start(void)195{196pr_debug("avr32_perf_counter_start\n");197198sysreg_write(PCCR, sysreg_read(PCCR) | SYSREG_BIT(PCCR_E));199200return 0;201}202203static void avr32_perf_counter_stop(void)204{205pr_debug("avr32_perf_counter_stop\n");206207sysreg_write(PCCR, sysreg_read(PCCR) & ~SYSREG_BIT(PCCR_E));208}209210static struct oprofile_operations avr32_perf_counter_ops __initdata = {211.create_files = avr32_perf_counter_create_files,212.setup = avr32_perf_counter_setup,213.shutdown = avr32_perf_counter_shutdown,214.start = avr32_perf_counter_start,215.stop = avr32_perf_counter_stop,216.cpu_type = "avr32",217};218219int __init oprofile_arch_init(struct oprofile_operations *ops)220{221if (!(current_cpu_data.features & AVR32_FEATURE_PCTR))222return -ENODEV;223224memcpy(ops, &avr32_perf_counter_ops,225sizeof(struct oprofile_operations));226227ops->backtrace = avr32_backtrace;228229printk(KERN_INFO "oprofile: using AVR32 performance monitoring.\n");230231return 0;232}233234void oprofile_arch_exit(void)235{236237}238239240