Merge master.kernel.org:/home/rmk/linux-2.6-arm
[pandora-kernel.git] / arch / mips / oprofile / op_model_mipsxx.c
1 /*
2  * This file is subject to the terms and conditions of the GNU General Public
3  * License.  See the file "COPYING" in the main directory of this archive
4  * for more details.
5  *
6  * Copyright (C) 2004, 2005 by Ralf Baechle
7  * Copyright (C) 2005 by MIPS Technologies, Inc.
8  */
9 #include <linux/oprofile.h>
10 #include <linux/interrupt.h>
11 #include <linux/smp.h>
12
13 #include "op_impl.h"
14
15 #define M_PERFCTL_EXL                   (1UL    <<  0)
16 #define M_PERFCTL_KERNEL                (1UL    <<  1)
17 #define M_PERFCTL_SUPERVISOR            (1UL    <<  2)
18 #define M_PERFCTL_USER                  (1UL    <<  3)
19 #define M_PERFCTL_INTERRUPT_ENABLE      (1UL    <<  4)
20 #define M_PERFCTL_EVENT(event)          ((event) << 5)
21 #define M_PERFCTL_WIDE                  (1UL    << 30)
22 #define M_PERFCTL_MORE                  (1UL    << 31)
23
24 #define M_COUNTER_OVERFLOW              (1UL    << 31)
25
26 struct op_mips_model op_model_mipsxx;
27
28 static struct mipsxx_register_config {
29         unsigned int control[4];
30         unsigned int counter[4];
31 } reg;
32
33 /* Compute all of the registers in preparation for enabling profiling.  */
34
35 static void mipsxx_reg_setup(struct op_counter_config *ctr)
36 {
37         unsigned int counters = op_model_mipsxx.num_counters;
38         int i;
39
40         /* Compute the performance counter control word.  */
41         /* For now count kernel and user mode */
42         for (i = 0; i < counters; i++) {
43                 reg.control[i] = 0;
44                 reg.counter[i] = 0;
45
46                 if (!ctr[i].enabled)
47                         continue;
48
49                 reg.control[i] = M_PERFCTL_EVENT(ctr[i].event) |
50                                  M_PERFCTL_INTERRUPT_ENABLE;
51                 if (ctr[i].kernel)
52                         reg.control[i] |= M_PERFCTL_KERNEL;
53                 if (ctr[i].user)
54                         reg.control[i] |= M_PERFCTL_USER;
55                 if (ctr[i].exl)
56                         reg.control[i] |= M_PERFCTL_EXL;
57                 reg.counter[i] = 0x80000000 - ctr[i].count;
58         }
59 }
60
61 /* Program all of the registers in preparation for enabling profiling.  */
62
63 static void mipsxx_cpu_setup (void *args)
64 {
65         unsigned int counters = op_model_mipsxx.num_counters;
66
67         switch (counters) {
68         case 4:
69                 write_c0_perfctrl3(0);
70                 write_c0_perfcntr3(reg.counter[3]);
71         case 3:
72                 write_c0_perfctrl2(0);
73                 write_c0_perfcntr2(reg.counter[2]);
74         case 2:
75                 write_c0_perfctrl1(0);
76                 write_c0_perfcntr1(reg.counter[1]);
77         case 1:
78                 write_c0_perfctrl0(0);
79                 write_c0_perfcntr0(reg.counter[0]);
80         }
81 }
82
83 /* Start all counters on current CPU */
84 static void mipsxx_cpu_start(void *args)
85 {
86         unsigned int counters = op_model_mipsxx.num_counters;
87
88         switch (counters) {
89         case 4:
90                 write_c0_perfctrl3(reg.control[3]);
91         case 3:
92                 write_c0_perfctrl2(reg.control[2]);
93         case 2:
94                 write_c0_perfctrl1(reg.control[1]);
95         case 1:
96                 write_c0_perfctrl0(reg.control[0]);
97         }
98 }
99
100 /* Stop all counters on current CPU */
101 static void mipsxx_cpu_stop(void *args)
102 {
103         unsigned int counters = op_model_mipsxx.num_counters;
104
105         switch (counters) {
106         case 4:
107                 write_c0_perfctrl3(0);
108         case 3:
109                 write_c0_perfctrl2(0);
110         case 2:
111                 write_c0_perfctrl1(0);
112         case 1:
113                 write_c0_perfctrl0(0);
114         }
115 }
116
117 static void mipsxx_perfcount_handler(struct pt_regs *regs)
118 {
119         unsigned int counters = op_model_mipsxx.num_counters;
120         unsigned int control;
121         unsigned int counter;
122
123         switch (counters) {
124 #define HANDLE_COUNTER(n)                                               \
125         case n + 1:                                                     \
126                 control = read_c0_perfctrl ## n();                      \
127                 counter = read_c0_perfcntr ## n();                      \
128                 if ((control & M_PERFCTL_INTERRUPT_ENABLE) &&           \
129                     (counter & M_COUNTER_OVERFLOW)) {                   \
130                         oprofile_add_sample(regs, n);                   \
131                         write_c0_perfcntr ## n(reg.counter[n]);         \
132                 }
133         HANDLE_COUNTER(3)
134         HANDLE_COUNTER(2)
135         HANDLE_COUNTER(1)
136         HANDLE_COUNTER(0)
137         }
138 }
139
140 #define M_CONFIG1_PC    (1 << 4)
141
142 static inline int n_counters(void)
143 {
144         if (!(read_c0_config1() & M_CONFIG1_PC))
145                 return 0;
146         if (!(read_c0_perfctrl0() & M_PERFCTL_MORE))
147                 return 1;
148         if (!(read_c0_perfctrl1() & M_PERFCTL_MORE))
149                 return 2;
150         if (!(read_c0_perfctrl2() & M_PERFCTL_MORE))
151                 return 3;
152
153         return 4;
154 }
155
156 static inline void reset_counters(int counters)
157 {
158         switch (counters) {
159         case 4:
160                 write_c0_perfctrl3(0);
161                 write_c0_perfcntr3(0);
162         case 3:
163                 write_c0_perfctrl2(0);
164                 write_c0_perfcntr2(0);
165         case 2:
166                 write_c0_perfctrl1(0);
167                 write_c0_perfcntr1(0);
168         case 1:
169                 write_c0_perfctrl0(0);
170                 write_c0_perfcntr0(0);
171         }
172 }
173
174 static int __init mipsxx_init(void)
175 {
176         int counters;
177
178         counters = n_counters();
179         if (counters == 0)
180                 return -ENODEV;
181
182         reset_counters(counters);
183
184         op_model_mipsxx.num_counters = counters;
185         switch (current_cpu_data.cputype) {
186         case CPU_24K:
187                 op_model_mipsxx.cpu_type = "mips/24K";
188                 break;
189
190         default:
191                 printk(KERN_ERR "Profiling unsupported for this CPU\n");
192
193                 return -ENODEV;
194         }
195
196         perf_irq = mipsxx_perfcount_handler;
197
198         return 0;
199 }
200
201 static void mipsxx_exit(void)
202 {
203         reset_counters(op_model_mipsxx.num_counters);
204
205         perf_irq = null_perf_irq;
206 }
207
208 struct op_mips_model op_model_mipsxx = {
209         .reg_setup      = mipsxx_reg_setup,
210         .cpu_setup      = mipsxx_cpu_setup,
211         .init           = mipsxx_init,
212         .exit           = mipsxx_exit,
213         .cpu_start      = mipsxx_cpu_start,
214         .cpu_stop       = mipsxx_cpu_stop,
215 };