Merge branch 'linus' into core/futexes
[sfrench/cifs-2.6.git] / arch / avr32 / oprofile / op_model_avr32.c
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 }