Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
awilliam
GitHub Repository: awilliam/linux-vfio
Path: blob/master/arch/avr32/oprofile/op_model_avr32.c
10817 views
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
25
void avr32_backtrace(struct pt_regs * const regs, unsigned int depth);
26
27
enum { PCCNT, PCNT0, PCNT1, NR_counter };
28
29
struct 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
41
static 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
54
static 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
64
static 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
101
static 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
131
static 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
187
static 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
195
static 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
204
static 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
211
static 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
220
int __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
235
void oprofile_arch_exit(void)
236
{
237
238
}
239
240