Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/hskinnemoen...
[linux-2.6] / 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 enum { PCCNT, PCNT0, PCNT1, NR_counter };
26
27 struct avr32_perf_counter {
28         unsigned long   enabled;
29         unsigned long   event;
30         unsigned long   count;
31         unsigned long   unit_mask;
32         unsigned long   kernel;
33         unsigned long   user;
34
35         u32             ie_mask;
36         u32             flag_mask;
37 };
38
39 static struct avr32_perf_counter counter[NR_counter] = {
40         {
41                 .ie_mask        = SYSREG_BIT(IEC),
42                 .flag_mask      = SYSREG_BIT(FC),
43         }, {
44                 .ie_mask        = SYSREG_BIT(IE0),
45                 .flag_mask      = SYSREG_BIT(F0),
46         }, {
47                 .ie_mask        = SYSREG_BIT(IE1),
48                 .flag_mask      = SYSREG_BIT(F1),
49         },
50 };
51
52 static void avr32_perf_counter_reset(void)
53 {
54         /* Reset all counter and disable/clear all interrupts */
55         sysreg_write(PCCR, (SYSREG_BIT(PCCR_R)
56                                 | SYSREG_BIT(PCCR_C)
57                                 | SYSREG_BIT(FC)
58                                 | SYSREG_BIT(F0)
59                                 | SYSREG_BIT(F1)));
60 }
61
62 static irqreturn_t avr32_perf_counter_interrupt(int irq, void *dev_id)
63 {
64         struct avr32_perf_counter *ctr = dev_id;
65         struct pt_regs *regs;
66         u32 pccr;
67
68         if (likely(!(intc_get_pending(AVR32_PERFCTR_IRQ_GROUP)
69                                         & (1 << AVR32_PERFCTR_IRQ_LINE))))
70                 return IRQ_NONE;
71
72         regs = get_irq_regs();
73         pccr = sysreg_read(PCCR);
74
75         /* Clear the interrupt flags we're about to handle */
76         sysreg_write(PCCR, pccr);
77
78         /* PCCNT */
79         if (ctr->enabled && (pccr & ctr->flag_mask)) {
80                 sysreg_write(PCCNT, -ctr->count);
81                 oprofile_add_sample(regs, PCCNT);
82         }
83         ctr++;
84         /* PCNT0 */
85         if (ctr->enabled && (pccr & ctr->flag_mask)) {
86                 sysreg_write(PCNT0, -ctr->count);
87                 oprofile_add_sample(regs, PCNT0);
88         }
89         ctr++;
90         /* PCNT1 */
91         if (ctr->enabled && (pccr & ctr->flag_mask)) {
92                 sysreg_write(PCNT1, -ctr->count);
93                 oprofile_add_sample(regs, PCNT1);
94         }
95
96         return IRQ_HANDLED;
97 }
98
99 static int avr32_perf_counter_create_files(struct super_block *sb,
100                 struct dentry *root)
101 {
102         struct dentry *dir;
103         unsigned int i;
104         char filename[4];
105
106         for (i = 0; i < NR_counter; i++) {
107                 snprintf(filename, sizeof(filename), "%u", i);
108                 dir = oprofilefs_mkdir(sb, root, filename);
109
110                 oprofilefs_create_ulong(sb, dir, "enabled",
111                                 &counter[i].enabled);
112                 oprofilefs_create_ulong(sb, dir, "event",
113                                 &counter[i].event);
114                 oprofilefs_create_ulong(sb, dir, "count",
115                                 &counter[i].count);
116
117                 /* Dummy entries */
118                 oprofilefs_create_ulong(sb, dir, "kernel",
119                                 &counter[i].kernel);
120                 oprofilefs_create_ulong(sb, dir, "user",
121                                 &counter[i].user);
122                 oprofilefs_create_ulong(sb, dir, "unit_mask",
123                                 &counter[i].unit_mask);
124         }
125
126         return 0;
127 }
128
129 static int avr32_perf_counter_setup(void)
130 {
131         struct avr32_perf_counter *ctr;
132         u32 pccr;
133         int ret;
134         int i;
135
136         pr_debug("avr32_perf_counter_setup\n");
137
138         if (sysreg_read(PCCR) & SYSREG_BIT(PCCR_E)) {
139                 printk(KERN_ERR
140                         "oprofile: setup: perf counter already enabled\n");
141                 return -EBUSY;
142         }
143
144         ret = request_irq(AVR32_PERFCTR_IRQ_GROUP,
145                         avr32_perf_counter_interrupt, IRQF_SHARED,
146                         "oprofile", counter);
147         if (ret)
148                 return ret;
149
150         avr32_perf_counter_reset();
151
152         pccr = 0;
153         for (i = PCCNT; i < NR_counter; i++) {
154                 ctr = &counter[i];
155                 if (!ctr->enabled)
156                         continue;
157
158                 pr_debug("enabling counter %d...\n", i);
159
160                 pccr |= ctr->ie_mask;
161
162                 switch (i) {
163                 case PCCNT:
164                         /* PCCNT always counts cycles, so no events */
165                         sysreg_write(PCCNT, -ctr->count);
166                         break;
167                 case PCNT0:
168                         pccr |= SYSREG_BF(CONF0, ctr->event);
169                         sysreg_write(PCNT0, -ctr->count);
170                         break;
171                 case PCNT1:
172                         pccr |= SYSREG_BF(CONF1, ctr->event);
173                         sysreg_write(PCNT1, -ctr->count);
174                         break;
175                 }
176         }
177
178         pr_debug("oprofile: writing 0x%x to PCCR...\n", pccr);
179
180         sysreg_write(PCCR, pccr);
181
182         return 0;
183 }
184
185 static void avr32_perf_counter_shutdown(void)
186 {
187         pr_debug("avr32_perf_counter_shutdown\n");
188
189         avr32_perf_counter_reset();
190         free_irq(AVR32_PERFCTR_IRQ_GROUP, counter);
191 }
192
193 static int avr32_perf_counter_start(void)
194 {
195         pr_debug("avr32_perf_counter_start\n");
196
197         sysreg_write(PCCR, sysreg_read(PCCR) | SYSREG_BIT(PCCR_E));
198
199         return 0;
200 }
201
202 static void avr32_perf_counter_stop(void)
203 {
204         pr_debug("avr32_perf_counter_stop\n");
205
206         sysreg_write(PCCR, sysreg_read(PCCR) & ~SYSREG_BIT(PCCR_E));
207 }
208
209 static struct oprofile_operations avr32_perf_counter_ops __initdata = {
210         .create_files   = avr32_perf_counter_create_files,
211         .setup          = avr32_perf_counter_setup,
212         .shutdown       = avr32_perf_counter_shutdown,
213         .start          = avr32_perf_counter_start,
214         .stop           = avr32_perf_counter_stop,
215         .cpu_type       = "avr32",
216 };
217
218 int __init oprofile_arch_init(struct oprofile_operations *ops)
219 {
220         if (!(current_cpu_data.features & AVR32_FEATURE_PCTR))
221                 return -ENODEV;
222
223         memcpy(ops, &avr32_perf_counter_ops,
224                         sizeof(struct oprofile_operations));
225
226         printk(KERN_INFO "oprofile: using AVR32 performance monitoring.\n");
227
228         return 0;
229 }
230
231 void oprofile_arch_exit(void)
232 {
233
234 }