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