Merge branch 'master' of git://git.kernel.org/pub/scm/linux/kernel/git/paulus/powerpc
[linux-2.6] / arch / blackfin / oprofile / op_model_bf533.c
1 /*
2  * File:         arch/blackfin/oprofile/op_model_bf533.c
3  * Based on:
4  * Author:       Anton Blanchard <anton@au.ibm.com>
5  *
6  * Created:
7  * Description:
8  *
9  * Modified:
10  *               Copyright (C) 2004 Anton Blanchard <anton@au.ibm.com>, IBM
11  *               Copyright 2004-2006 Analog Devices Inc.
12  *
13  * Bugs:         Enter bugs at http://blackfin.uclinux.org/
14  *
15  * This program is free software; you can redistribute it and/or modify
16  * it under the terms of the GNU General Public License as published by
17  * the Free Software Foundation; either version 2 of the License, or
18  * (at your option) any later version.
19  *
20  * This program is distributed in the hope that it will be useful,
21  * but WITHOUT ANY WARRANTY; without even the implied warranty of
22  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
23  * GNU General Public License for more details.
24  *
25  * You should have received a copy of the GNU General Public License
26  * along with this program; if not, see the file COPYING, or write
27  * to the Free Software Foundation, Inc.,
28  * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
29  */
30
31 #include <linux/oprofile.h>
32 #include <linux/init.h>
33 #include <linux/smp.h>
34 #include <linux/interrupt.h>
35 #include <linux/ptrace.h>
36 #include <linux/irq.h>
37 #include <linux/io.h>
38 #include <asm/system.h>
39 #include <asm/processor.h>
40 #include <asm/blackfin.h>
41
42 #include "op_blackfin.h"
43
44 #define PM_ENABLE 0x01;
45 #define PM_CTL1_ENABLE  0x18
46 #define PM_CTL0_ENABLE  0xC000
47 #define COUNT_EDGE_ONLY 0x3000000
48
49 static int oprofile_running;
50
51 static unsigned curr_pfctl, curr_count[2];
52
53 static int bfin533_reg_setup(struct op_counter_config *ctr)
54 {
55         unsigned int pfctl = ctr_read();
56         unsigned int count[2];
57
58         /* set Blackfin perf monitor regs with ctr */
59         if (ctr[0].enabled) {
60                 pfctl |= (PM_CTL0_ENABLE | ((char)ctr[0].event << 5));
61                 count[0] = 0xFFFFFFFF - ctr[0].count;
62                 curr_count[0] = count[0];
63         }
64         if (ctr[1].enabled) {
65                 pfctl |= (PM_CTL1_ENABLE | ((char)ctr[1].event << 16));
66                 count[1] = 0xFFFFFFFF - ctr[1].count;
67                 curr_count[1] = count[1];
68         }
69
70         pr_debug("ctr[0].enabled=%d,ctr[1].enabled=%d,ctr[0].event<<5=0x%x,ctr[1].event<<16=0x%x\n", ctr[0].enabled, ctr[1].enabled, ctr[0].event << 5, ctr[1].event << 16);
71         pfctl |= COUNT_EDGE_ONLY;
72         curr_pfctl = pfctl;
73
74         pr_debug("write 0x%x to pfctl\n", pfctl);
75         ctr_write(pfctl);
76         count_write(count);
77
78         return 0;
79 }
80
81 static int bfin533_start(struct op_counter_config *ctr)
82 {
83         unsigned int pfctl = ctr_read();
84
85         pfctl |= PM_ENABLE;
86         curr_pfctl = pfctl;
87
88         ctr_write(pfctl);
89
90         oprofile_running = 1;
91         pr_debug("start oprofile counter \n");
92
93         return 0;
94 }
95
96 static void bfin533_stop(void)
97 {
98         int pfctl;
99
100         pfctl = ctr_read();
101         pfctl &= ~PM_ENABLE;
102         /* freeze counters */
103         ctr_write(pfctl);
104
105         oprofile_running = 0;
106         pr_debug("stop oprofile counter \n");
107 }
108
109 static int get_kernel(void)
110 {
111         int ipend, is_kernel;
112
113         ipend = bfin_read_IPEND();
114
115         /* test bit 15 */
116         is_kernel = ((ipend & 0x8000) != 0);
117
118         return is_kernel;
119 }
120
121 int pm_overflow_handler(int irq, struct pt_regs *regs)
122 {
123         int is_kernel;
124         int i, cpu;
125         unsigned int pc, pfctl;
126         unsigned int count[2];
127
128         pr_debug("get interrupt in %s\n", __func__);
129         if (oprofile_running == 0) {
130                 pr_debug("error: entering interrupt when oprofile is stopped.\n\r");
131                 return -1;
132         }
133
134         is_kernel = get_kernel();
135         cpu = smp_processor_id();
136         pc = regs->pc;
137         pfctl = ctr_read();
138
139         /* read the two event counter regs */
140         count_read(count);
141
142         /* if the counter overflows, add sample to oprofile buffer */
143         for (i = 0; i < 2; ++i) {
144                 if (oprofile_running) {
145                         oprofile_add_sample(regs, i);
146                 }
147         }
148
149         /* reset the perfmon counter */
150         ctr_write(curr_pfctl);
151         count_write(curr_count);
152         return 0;
153 }
154
155 struct op_bfin533_model op_model_bfin533 = {
156         .reg_setup = bfin533_reg_setup,
157         .start = bfin533_start,
158         .stop = bfin533_stop,
159         .num_counters = 2,
160         .name = "blackfin/bf533"
161 };