2 * AVR32 Performance Counter Driver
4 * Copyright (C) 2005-2007 Atmel Corporation
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.
10 * Author: Ronny Pedersen
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>
20 #include <asm/sysreg.h>
21 #include <asm/system.h>
23 #define AVR32_PERFCTR_IRQ_GROUP 0
24 #define AVR32_PERFCTR_IRQ_LINE 1
26 enum { PCCNT, PCNT0, PCNT1, NR_counter };
28 struct avr32_perf_counter {
29 unsigned long enabled;
32 unsigned long unit_mask;
40 static struct avr32_perf_counter counter[NR_counter] = {
42 .ie_mask = SYSREG_BIT(IEC),
43 .flag_mask = SYSREG_BIT(FC),
45 .ie_mask = SYSREG_BIT(IE0),
46 .flag_mask = SYSREG_BIT(F0),
48 .ie_mask = SYSREG_BIT(IE1),
49 .flag_mask = SYSREG_BIT(F1),
53 static void avr32_perf_counter_reset(void)
55 /* Reset all counter and disable/clear all interrupts */
56 sysreg_write(PCCR, (SYSREG_BIT(PCCR_R)
63 static irqreturn_t avr32_perf_counter_interrupt(int irq, void *dev_id)
65 struct avr32_perf_counter *ctr = dev_id;
69 if (likely(!(intc_get_pending(AVR32_PERFCTR_IRQ_GROUP)
70 & (1 << AVR32_PERFCTR_IRQ_LINE))))
73 regs = get_irq_regs();
74 pccr = sysreg_read(PCCR);
76 /* Clear the interrupt flags we're about to handle */
77 sysreg_write(PCCR, pccr);
80 if (ctr->enabled && (pccr & ctr->flag_mask)) {
81 sysreg_write(PCCNT, -ctr->count);
82 oprofile_add_sample(regs, PCCNT);
86 if (ctr->enabled && (pccr & ctr->flag_mask)) {
87 sysreg_write(PCNT0, -ctr->count);
88 oprofile_add_sample(regs, PCNT0);
92 if (ctr->enabled && (pccr & ctr->flag_mask)) {
93 sysreg_write(PCNT1, -ctr->count);
94 oprofile_add_sample(regs, PCNT1);
100 static int avr32_perf_counter_create_files(struct super_block *sb,
107 for (i = 0; i < NR_counter; i++) {
108 snprintf(filename, sizeof(filename), "%u", i);
109 dir = oprofilefs_mkdir(sb, root, filename);
111 oprofilefs_create_ulong(sb, dir, "enabled",
112 &counter[i].enabled);
113 oprofilefs_create_ulong(sb, dir, "event",
115 oprofilefs_create_ulong(sb, dir, "count",
119 oprofilefs_create_ulong(sb, dir, "kernel",
121 oprofilefs_create_ulong(sb, dir, "user",
123 oprofilefs_create_ulong(sb, dir, "unit_mask",
124 &counter[i].unit_mask);
130 static int avr32_perf_counter_setup(void)
132 struct avr32_perf_counter *ctr;
137 pr_debug("avr32_perf_counter_setup\n");
139 if (sysreg_read(PCCR) & SYSREG_BIT(PCCR_E)) {
141 "oprofile: setup: perf counter already enabled\n");
145 ret = request_irq(AVR32_PERFCTR_IRQ_GROUP,
146 avr32_perf_counter_interrupt, IRQF_SHARED,
147 "oprofile", counter);
151 avr32_perf_counter_reset();
154 for (i = PCCNT; i < NR_counter; i++) {
159 pr_debug("enabling counter %d...\n", i);
161 pccr |= ctr->ie_mask;
165 /* PCCNT always counts cycles, so no events */
166 sysreg_write(PCCNT, -ctr->count);
169 pccr |= SYSREG_BF(CONF0, ctr->event);
170 sysreg_write(PCNT0, -ctr->count);
173 pccr |= SYSREG_BF(CONF1, ctr->event);
174 sysreg_write(PCNT1, -ctr->count);
179 pr_debug("oprofile: writing 0x%x to PCCR...\n", pccr);
181 sysreg_write(PCCR, pccr);
186 static void avr32_perf_counter_shutdown(void)
188 pr_debug("avr32_perf_counter_shutdown\n");
190 avr32_perf_counter_reset();
191 free_irq(AVR32_PERFCTR_IRQ_GROUP, counter);
194 static int avr32_perf_counter_start(void)
196 pr_debug("avr32_perf_counter_start\n");
198 sysreg_write(PCCR, sysreg_read(PCCR) | SYSREG_BIT(PCCR_E));
203 static void avr32_perf_counter_stop(void)
205 pr_debug("avr32_perf_counter_stop\n");
207 sysreg_write(PCCR, sysreg_read(PCCR) & ~SYSREG_BIT(PCCR_E));
210 static 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,
219 int __init oprofile_arch_init(struct oprofile_operations *ops)
221 if (!(current_cpu_data.features & AVR32_FEATURE_PCTR))
224 memcpy(ops, &avr32_perf_counter_ops,
225 sizeof(struct oprofile_operations));
227 printk(KERN_INFO "oprofile: using AVR32 performance monitoring.\n");
232 void oprofile_arch_exit(void)