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