MIPS: Oprofile: Add 5K, 20K and 25K support.
[linux-2.6.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 int 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         int handled = 0;
123
124         switch (counters) {
125 #define HANDLE_COUNTER(n)                                               \
126         case n + 1:                                                     \
127                 control = read_c0_perfctrl ## n();                      \
128                 counter = read_c0_perfcntr ## n();                      \
129                 if ((control & M_PERFCTL_INTERRUPT_ENABLE) &&           \
130                     (counter & M_COUNTER_OVERFLOW)) {                   \
131                         oprofile_add_sample(regs, n);                   \
132                         write_c0_perfcntr ## n(reg.counter[n]);         \
133                         handled = 1;                                    \
134                 }
135         HANDLE_COUNTER(3)
136         HANDLE_COUNTER(2)
137         HANDLE_COUNTER(1)
138         HANDLE_COUNTER(0)
139         }
140
141         return handled;
142 }
143
144 #define M_CONFIG1_PC    (1 << 4)
145
146 static inline int n_counters(void)
147 {
148         if (!(read_c0_config1() & M_CONFIG1_PC))
149                 return 0;
150         if (!(read_c0_perfctrl0() & M_PERFCTL_MORE))
151                 return 1;
152         if (!(read_c0_perfctrl1() & M_PERFCTL_MORE))
153                 return 2;
154         if (!(read_c0_perfctrl2() & M_PERFCTL_MORE))
155                 return 3;
156
157         return 4;
158 }
159
160 static inline void reset_counters(int counters)
161 {
162         switch (counters) {
163         case 4:
164                 write_c0_perfctrl3(0);
165                 write_c0_perfcntr3(0);
166         case 3:
167                 write_c0_perfctrl2(0);
168                 write_c0_perfcntr2(0);
169         case 2:
170                 write_c0_perfctrl1(0);
171                 write_c0_perfcntr1(0);
172         case 1:
173                 write_c0_perfctrl0(0);
174                 write_c0_perfcntr0(0);
175         }
176 }
177
178 static int __init mipsxx_init(void)
179 {
180         int counters;
181
182         counters = n_counters();
183         if (counters == 0) {
184                 printk(KERN_ERR "Oprofile: CPU has no performance counters\n");
185                 return -ENODEV;
186         }
187
188         reset_counters(counters);
189
190         op_model_mipsxx.num_counters = counters;
191         switch (current_cpu_data.cputype) {
192         case CPU_20KC:
193                 op_model_mipsxx.cpu_type = "mips/20K";
194                 break;
195
196         case CPU_24K:
197                 op_model_mipsxx.cpu_type = "mips/24K";
198                 break;
199
200         case CPU_25KF:
201                 op_model_mipsxx.cpu_type = "mips/25K";
202                 break;
203
204         case CPU_5KC:
205                 op_model_mipsxx.cpu_type = "mips/5K";
206                 break;
207
208         default:
209                 printk(KERN_ERR "Profiling unsupported for this CPU\n");
210
211                 return -ENODEV;
212         }
213
214         perf_irq = mipsxx_perfcount_handler;
215
216         return 0;
217 }
218
219 static void mipsxx_exit(void)
220 {
221         reset_counters(op_model_mipsxx.num_counters);
222
223         perf_irq = null_perf_irq;
224 }
225
226 struct op_mips_model op_model_mipsxx = {
227         .reg_setup      = mipsxx_reg_setup,
228         .cpu_setup      = mipsxx_cpu_setup,
229         .init           = mipsxx_init,
230         .exit           = mipsxx_exit,
231         .cpu_start      = mipsxx_cpu_start,
232         .cpu_stop       = mipsxx_cpu_stop,
233 };