Merge branch 'for-3.1' of git://git.kernel.org/pub/scm/linux/kernel/git/tj/wq
[deliverable/linux.git] / arch / avr32 / oprofile / op_model_avr32.c
CommitLineData
2853ce5e
HS
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
2853ce5e
HS
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
3d256151
NV
25void avr32_backtrace(struct pt_regs * const regs, unsigned int depth);
26
2853ce5e
HS
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
3d256151
NV
228 ops->backtrace = avr32_backtrace;
229
2853ce5e
HS
230 printk(KERN_INFO "oprofile: using AVR32 performance monitoring.\n");
231
232 return 0;
233}
234
235void oprofile_arch_exit(void)
236{
237
238}
This page took 0.419991 seconds and 5 git commands to generate.