aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--arch/avr32/Kconfig3
-rw-r--r--arch/avr32/Makefile1
-rw-r--r--arch/avr32/oprofile/Makefile8
-rw-r--r--arch/avr32/oprofile/op_model_avr32.c235
4 files changed, 247 insertions, 0 deletions
diff --git a/arch/avr32/Kconfig b/arch/avr32/Kconfig
index bc08c7f0883f..767a19cfa423 100644
--- a/arch/avr32/Kconfig
+++ b/arch/avr32/Kconfig
@@ -54,6 +54,9 @@ config ARCH_HAS_ILOG2_U32
54config ARCH_HAS_ILOG2_U64 54config ARCH_HAS_ILOG2_U64
55 def_bool n 55 def_bool n
56 56
57config ARCH_SUPPORTS_OPROFILE
58 def_bool y
59
57config GENERIC_HWEIGHT 60config GENERIC_HWEIGHT
58 def_bool y 61 def_bool y
59 62
diff --git a/arch/avr32/Makefile b/arch/avr32/Makefile
index 87918647be6d..f75d52cd2a4c 100644
--- a/arch/avr32/Makefile
+++ b/arch/avr32/Makefile
@@ -31,6 +31,7 @@ core-$(CONFIG_BOARD_ATNGW100) += arch/avr32/boards/atngw100/
31core-$(CONFIG_LOADER_U_BOOT) += arch/avr32/boot/u-boot/ 31core-$(CONFIG_LOADER_U_BOOT) += arch/avr32/boot/u-boot/
32core-y += arch/avr32/kernel/ 32core-y += arch/avr32/kernel/
33core-y += arch/avr32/mm/ 33core-y += arch/avr32/mm/
34drivers-$(CONFIG_OPROFILE) += arch/avr32/oprofile/
34libs-y += arch/avr32/lib/ 35libs-y += arch/avr32/lib/
35 36
36archincdir-$(CONFIG_PLATFORM_AT32AP) := arch-at32ap 37archincdir-$(CONFIG_PLATFORM_AT32AP) := arch-at32ap
diff --git a/arch/avr32/oprofile/Makefile b/arch/avr32/oprofile/Makefile
new file mode 100644
index 000000000000..1fe81c3c1e86
--- /dev/null
+++ b/arch/avr32/oprofile/Makefile
@@ -0,0 +1,8 @@
1obj-$(CONFIG_OPROFILE) += oprofile.o
2
3oprofile-y := $(addprefix ../../../drivers/oprofile/, \
4 oprof.o cpu_buffer.o buffer_sync.o \
5 event_buffer.o oprofile_files.o \
6 oprofilefs.o oprofile_stats.o \
7 timer_int.o)
8oprofile-y += op_model_avr32.o
diff --git a/arch/avr32/oprofile/op_model_avr32.c b/arch/avr32/oprofile/op_model_avr32.c
new file mode 100644
index 000000000000..e2f876bfc86b
--- /dev/null
+++ b/arch/avr32/oprofile/op_model_avr32.c
@@ -0,0 +1,235 @@
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/intc.h>
20#include <asm/sysreg.h>
21#include <asm/system.h>
22
23#define AVR32_PERFCTR_IRQ_GROUP 0
24#define AVR32_PERFCTR_IRQ_LINE 1
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 super_block *sb,
101 struct dentry *root)
102{
103 struct dentry *dir;
104 unsigned int i;
105 char filename[4];
106
107 for (i = 0; i < NR_counter; i++) {
108 snprintf(filename, sizeof(filename), "%u", i);
109 dir = oprofilefs_mkdir(sb, root, filename);
110
111 oprofilefs_create_ulong(sb, dir, "enabled",
112 &counter[i].enabled);
113 oprofilefs_create_ulong(sb, dir, "event",
114 &counter[i].event);
115 oprofilefs_create_ulong(sb, dir, "count",
116 &counter[i].count);
117
118 /* Dummy entries */
119 oprofilefs_create_ulong(sb, dir, "kernel",
120 &counter[i].kernel);
121 oprofilefs_create_ulong(sb, dir, "user",
122 &counter[i].user);
123 oprofilefs_create_ulong(sb, dir, "unit_mask",
124 &counter[i].unit_mask);
125 }
126
127 return 0;
128}
129
130static int avr32_perf_counter_setup(void)
131{
132 struct avr32_perf_counter *ctr;
133 u32 pccr;
134 int ret;
135 int i;
136
137 pr_debug("avr32_perf_counter_setup\n");
138
139 if (sysreg_read(PCCR) & SYSREG_BIT(PCCR_E)) {
140 printk(KERN_ERR
141 "oprofile: setup: perf counter already enabled\n");
142 return -EBUSY;
143 }
144
145 ret = request_irq(AVR32_PERFCTR_IRQ_GROUP,
146 avr32_perf_counter_interrupt, IRQF_SHARED,
147 "oprofile", counter);
148 if (ret)
149 return ret;
150
151 avr32_perf_counter_reset();
152
153 pccr = 0;
154 for (i = PCCNT; i < NR_counter; i++) {
155 ctr = &counter[i];
156 if (!ctr->enabled)
157 continue;
158
159 pr_debug("enabling counter %d...\n", i);
160
161 pccr |= ctr->ie_mask;
162
163 switch (i) {
164 case PCCNT:
165 /* PCCNT always counts cycles, so no events */
166 sysreg_write(PCCNT, -ctr->count);
167 break;
168 case PCNT0:
169 pccr |= SYSREG_BF(CONF0, ctr->event);
170 sysreg_write(PCNT0, -ctr->count);
171 break;
172 case PCNT1:
173 pccr |= SYSREG_BF(CONF1, ctr->event);
174 sysreg_write(PCNT1, -ctr->count);
175 break;
176 }
177 }
178
179 pr_debug("oprofile: writing 0x%x to PCCR...\n", pccr);
180
181 sysreg_write(PCCR, pccr);
182
183 return 0;
184}
185
186static void avr32_perf_counter_shutdown(void)
187{
188 pr_debug("avr32_perf_counter_shutdown\n");
189
190 avr32_perf_counter_reset();
191 free_irq(AVR32_PERFCTR_IRQ_GROUP, counter);
192}
193
194static int avr32_perf_counter_start(void)
195{
196 pr_debug("avr32_perf_counter_start\n");
197
198 sysreg_write(PCCR, sysreg_read(PCCR) | SYSREG_BIT(PCCR_E));
199
200 return 0;
201}
202
203static void avr32_perf_counter_stop(void)
204{
205 pr_debug("avr32_perf_counter_stop\n");
206
207 sysreg_write(PCCR, sysreg_read(PCCR) & ~SYSREG_BIT(PCCR_E));
208}
209
210static 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};
218
219int __init oprofile_arch_init(struct oprofile_operations *ops)
220{
221 if (!(current_cpu_data.features & AVR32_FEATURE_PCTR))
222 return -ENODEV;
223
224 memcpy(ops, &avr32_perf_counter_ops,
225 sizeof(struct oprofile_operations));
226
227 printk(KERN_INFO "oprofile: using AVR32 performance monitoring.\n");
228
229 return 0;
230}
231
232void oprofile_arch_exit(void)
233{
234
235}