]> www.pilppa.org Git - linux-2.6-omap-h63xx.git/blob - arch/mips/oprofile/op_model_mipsxx.c
[MIPS] Add support for MIPS CMP platform.
[linux-2.6-omap-h63xx.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, 05, 06 by Ralf Baechle
7  * Copyright (C) 2005 by MIPS Technologies, Inc.
8  */
9 #include <linux/cpumask.h>
10 #include <linux/oprofile.h>
11 #include <linux/interrupt.h>
12 #include <linux/smp.h>
13 #include <asm/irq_regs.h>
14
15 #include "op_impl.h"
16
17 #define M_PERFCTL_EXL                   (1UL      <<  0)
18 #define M_PERFCTL_KERNEL                (1UL      <<  1)
19 #define M_PERFCTL_SUPERVISOR            (1UL      <<  2)
20 #define M_PERFCTL_USER                  (1UL      <<  3)
21 #define M_PERFCTL_INTERRUPT_ENABLE      (1UL      <<  4)
22 #define M_PERFCTL_EVENT(event)          (((event) & 0x3ff)  << 5)
23 #define M_PERFCTL_VPEID(vpe)            ((vpe)    << 16)
24 #define M_PERFCTL_MT_EN(filter)         ((filter) << 20)
25 #define    M_TC_EN_ALL                  M_PERFCTL_MT_EN(0)
26 #define    M_TC_EN_VPE                  M_PERFCTL_MT_EN(1)
27 #define    M_TC_EN_TC                   M_PERFCTL_MT_EN(2)
28 #define M_PERFCTL_TCID(tcid)            ((tcid)   << 22)
29 #define M_PERFCTL_WIDE                  (1UL      << 30)
30 #define M_PERFCTL_MORE                  (1UL      << 31)
31
32 #define M_COUNTER_OVERFLOW              (1UL      << 31)
33
34 #ifdef CONFIG_MIPS_MT_SMP
35 static int cpu_has_mipsmt_pertccounters;
36 #define WHAT            (M_TC_EN_VPE | \
37                          M_PERFCTL_VPEID(cpu_data[smp_processor_id()].vpe_id))
38 #define vpe_id()        (cpu_has_mipsmt_pertccounters ? \
39                         0 : cpu_data[smp_processor_id()].vpe_id)
40
41 /*
42  * The number of bits to shift to convert between counters per core and
43  * counters per VPE.  There is no reasonable interface atm to obtain the
44  * number of VPEs used by Linux and in the 34K this number is fixed to two
45  * anyways so we hardcore a few things here for the moment.  The way it's
46  * done here will ensure that oprofile VSMP kernel will run right on a lesser
47  * core like a 24K also or with maxcpus=1.
48  */
49 static inline unsigned int vpe_shift(void)
50 {
51         if (num_possible_cpus() > 1)
52                 return 1;
53
54         return 0;
55 }
56
57 #else
58
59 #define WHAT            0
60 #define vpe_id()        0
61
62 static inline unsigned int vpe_shift(void)
63 {
64         return 0;
65 }
66
67 #endif
68
69 static inline unsigned int counters_total_to_per_cpu(unsigned int counters)
70 {
71         return counters >> vpe_shift();
72 }
73
74 static inline unsigned int counters_per_cpu_to_total(unsigned int counters)
75 {
76         return counters << vpe_shift();
77 }
78
79 #define __define_perf_accessors(r, n, np)                               \
80                                                                         \
81 static inline unsigned int r_c0_ ## r ## n(void)                        \
82 {                                                                       \
83         unsigned int cpu = vpe_id();                                    \
84                                                                         \
85         switch (cpu) {                                                  \
86         case 0:                                                         \
87                 return read_c0_ ## r ## n();                            \
88         case 1:                                                         \
89                 return read_c0_ ## r ## np();                           \
90         default:                                                        \
91                 BUG();                                                  \
92         }                                                               \
93         return 0;                                                       \
94 }                                                                       \
95                                                                         \
96 static inline void w_c0_ ## r ## n(unsigned int value)                  \
97 {                                                                       \
98         unsigned int cpu = vpe_id();                                    \
99                                                                         \
100         switch (cpu) {                                                  \
101         case 0:                                                         \
102                 write_c0_ ## r ## n(value);                             \
103                 return;                                                 \
104         case 1:                                                         \
105                 write_c0_ ## r ## np(value);                            \
106                 return;                                                 \
107         default:                                                        \
108                 BUG();                                                  \
109         }                                                               \
110         return;                                                         \
111 }                                                                       \
112
113 __define_perf_accessors(perfcntr, 0, 2)
114 __define_perf_accessors(perfcntr, 1, 3)
115 __define_perf_accessors(perfcntr, 2, 0)
116 __define_perf_accessors(perfcntr, 3, 1)
117
118 __define_perf_accessors(perfctrl, 0, 2)
119 __define_perf_accessors(perfctrl, 1, 3)
120 __define_perf_accessors(perfctrl, 2, 0)
121 __define_perf_accessors(perfctrl, 3, 1)
122
123 struct op_mips_model op_model_mipsxx_ops;
124
125 static struct mipsxx_register_config {
126         unsigned int control[4];
127         unsigned int counter[4];
128 } reg;
129
130 /* Compute all of the registers in preparation for enabling profiling.  */
131
132 static void mipsxx_reg_setup(struct op_counter_config *ctr)
133 {
134         unsigned int counters = op_model_mipsxx_ops.num_counters;
135         int i;
136
137         /* Compute the performance counter control word.  */
138         for (i = 0; i < counters; i++) {
139                 reg.control[i] = 0;
140                 reg.counter[i] = 0;
141
142                 if (!ctr[i].enabled)
143                         continue;
144
145                 reg.control[i] = M_PERFCTL_EVENT(ctr[i].event) |
146                                  M_PERFCTL_INTERRUPT_ENABLE;
147                 if (ctr[i].kernel)
148                         reg.control[i] |= M_PERFCTL_KERNEL;
149                 if (ctr[i].user)
150                         reg.control[i] |= M_PERFCTL_USER;
151                 if (ctr[i].exl)
152                         reg.control[i] |= M_PERFCTL_EXL;
153                 reg.counter[i] = 0x80000000 - ctr[i].count;
154         }
155 }
156
157 /* Program all of the registers in preparation for enabling profiling.  */
158
159 static void mipsxx_cpu_setup(void *args)
160 {
161         unsigned int counters = op_model_mipsxx_ops.num_counters;
162
163         switch (counters) {
164         case 4:
165                 w_c0_perfctrl3(0);
166                 w_c0_perfcntr3(reg.counter[3]);
167         case 3:
168                 w_c0_perfctrl2(0);
169                 w_c0_perfcntr2(reg.counter[2]);
170         case 2:
171                 w_c0_perfctrl1(0);
172                 w_c0_perfcntr1(reg.counter[1]);
173         case 1:
174                 w_c0_perfctrl0(0);
175                 w_c0_perfcntr0(reg.counter[0]);
176         }
177 }
178
179 /* Start all counters on current CPU */
180 static void mipsxx_cpu_start(void *args)
181 {
182         unsigned int counters = op_model_mipsxx_ops.num_counters;
183
184         switch (counters) {
185         case 4:
186                 w_c0_perfctrl3(WHAT | reg.control[3]);
187         case 3:
188                 w_c0_perfctrl2(WHAT | reg.control[2]);
189         case 2:
190                 w_c0_perfctrl1(WHAT | reg.control[1]);
191         case 1:
192                 w_c0_perfctrl0(WHAT | reg.control[0]);
193         }
194 }
195
196 /* Stop all counters on current CPU */
197 static void mipsxx_cpu_stop(void *args)
198 {
199         unsigned int counters = op_model_mipsxx_ops.num_counters;
200
201         switch (counters) {
202         case 4:
203                 w_c0_perfctrl3(0);
204         case 3:
205                 w_c0_perfctrl2(0);
206         case 2:
207                 w_c0_perfctrl1(0);
208         case 1:
209                 w_c0_perfctrl0(0);
210         }
211 }
212
213 static int mipsxx_perfcount_handler(void)
214 {
215         unsigned int counters = op_model_mipsxx_ops.num_counters;
216         unsigned int control;
217         unsigned int counter;
218         int handled = IRQ_NONE;
219
220         if (cpu_has_mips_r2 && !(read_c0_cause() & (1 << 26)))
221                 return handled;
222
223         switch (counters) {
224 #define HANDLE_COUNTER(n)                                               \
225         case n + 1:                                                     \
226                 control = r_c0_perfctrl ## n();                         \
227                 counter = r_c0_perfcntr ## n();                         \
228                 if ((control & M_PERFCTL_INTERRUPT_ENABLE) &&           \
229                     (counter & M_COUNTER_OVERFLOW)) {                   \
230                         oprofile_add_sample(get_irq_regs(), n);         \
231                         w_c0_perfcntr ## n(reg.counter[n]);             \
232                         handled = IRQ_HANDLED;                          \
233                 }
234         HANDLE_COUNTER(3)
235         HANDLE_COUNTER(2)
236         HANDLE_COUNTER(1)
237         HANDLE_COUNTER(0)
238         }
239
240         return handled;
241 }
242
243 #define M_CONFIG1_PC    (1 << 4)
244
245 static inline int __n_counters(void)
246 {
247         if (!(read_c0_config1() & M_CONFIG1_PC))
248                 return 0;
249         if (!(read_c0_perfctrl0() & M_PERFCTL_MORE))
250                 return 1;
251         if (!(read_c0_perfctrl1() & M_PERFCTL_MORE))
252                 return 2;
253         if (!(read_c0_perfctrl2() & M_PERFCTL_MORE))
254                 return 3;
255
256         return 4;
257 }
258
259 static inline int n_counters(void)
260 {
261         int counters;
262
263         switch (current_cpu_type()) {
264         case CPU_R10000:
265                 counters = 2;
266                 break;
267
268         case CPU_R12000:
269         case CPU_R14000:
270                 counters = 4;
271                 break;
272
273         default:
274                 counters = __n_counters();
275         }
276
277         return counters;
278 }
279
280 static void reset_counters(void *arg)
281 {
282         int counters = (int)arg;
283         switch (counters) {
284         case 4:
285                 w_c0_perfctrl3(0);
286                 w_c0_perfcntr3(0);
287         case 3:
288                 w_c0_perfctrl2(0);
289                 w_c0_perfcntr2(0);
290         case 2:
291                 w_c0_perfctrl1(0);
292                 w_c0_perfcntr1(0);
293         case 1:
294                 w_c0_perfctrl0(0);
295                 w_c0_perfcntr0(0);
296         }
297 }
298
299 static int __init mipsxx_init(void)
300 {
301         int counters;
302
303         counters = n_counters();
304         if (counters == 0) {
305                 printk(KERN_ERR "Oprofile: CPU has no performance counters\n");
306                 return -ENODEV;
307         }
308
309 #ifdef CONFIG_MIPS_MT_SMP
310         cpu_has_mipsmt_pertccounters = read_c0_config7() & (1<<19);
311         if (!cpu_has_mipsmt_pertccounters)
312                 counters = counters_total_to_per_cpu(counters);
313 #endif
314         on_each_cpu(reset_counters, (void *)counters, 0, 1);
315
316         op_model_mipsxx_ops.num_counters = counters;
317         switch (current_cpu_type()) {
318         case CPU_20KC:
319                 op_model_mipsxx_ops.cpu_type = "mips/20K";
320                 break;
321
322         case CPU_24K:
323                 op_model_mipsxx_ops.cpu_type = "mips/24K";
324                 break;
325
326         case CPU_25KF:
327                 op_model_mipsxx_ops.cpu_type = "mips/25K";
328                 break;
329
330         case CPU_1004K:
331 #if 0
332                 /* FIXME: report as 34K for now */
333                 op_model_mipsxx_ops.cpu_type = "mips/1004K";
334                 break;
335 #endif
336
337         case CPU_34K:
338                 op_model_mipsxx_ops.cpu_type = "mips/34K";
339                 break;
340
341         case CPU_74K:
342                 op_model_mipsxx_ops.cpu_type = "mips/74K";
343                 break;
344
345         case CPU_5KC:
346                 op_model_mipsxx_ops.cpu_type = "mips/5K";
347                 break;
348
349         case CPU_R10000:
350                 if ((current_cpu_data.processor_id & 0xff) == 0x20)
351                         op_model_mipsxx_ops.cpu_type = "mips/r10000-v2.x";
352                 else
353                         op_model_mipsxx_ops.cpu_type = "mips/r10000";
354                 break;
355
356         case CPU_R12000:
357         case CPU_R14000:
358                 op_model_mipsxx_ops.cpu_type = "mips/r12000";
359                 break;
360
361         case CPU_SB1:
362         case CPU_SB1A:
363                 op_model_mipsxx_ops.cpu_type = "mips/sb1";
364                 break;
365
366         default:
367                 printk(KERN_ERR "Profiling unsupported for this CPU\n");
368
369                 return -ENODEV;
370         }
371
372         perf_irq = mipsxx_perfcount_handler;
373
374         return 0;
375 }
376
377 static void mipsxx_exit(void)
378 {
379         int counters = op_model_mipsxx_ops.num_counters;
380
381         counters = counters_per_cpu_to_total(counters);
382         on_each_cpu(reset_counters, (void *)counters, 0, 1);
383
384         perf_irq = null_perf_irq;
385 }
386
387 struct op_mips_model op_model_mipsxx_ops = {
388         .reg_setup      = mipsxx_reg_setup,
389         .cpu_setup      = mipsxx_cpu_setup,
390         .init           = mipsxx_init,
391         .exit           = mipsxx_exit,
392         .cpu_start      = mipsxx_cpu_start,
393         .cpu_stop       = mipsxx_cpu_stop,
394 };