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/oprofile.h>
10#include <linux/interrupt.h>
11#include <linux/smp.h>
12#include <asm/irq_regs.h>
13
14#include "op_impl.h"
15
16#define M_PERFCTL_EXL			(1UL      <<  0)
17#define M_PERFCTL_KERNEL		(1UL      <<  1)
18#define M_PERFCTL_SUPERVISOR		(1UL      <<  2)
19#define M_PERFCTL_USER			(1UL      <<  3)
20#define M_PERFCTL_INTERRUPT_ENABLE	(1UL      <<  4)
21#define M_PERFCTL_EVENT(event)		(((event) & 0x3f)  << 5)
22#define M_PERFCTL_VPEID(vpe)		((vpe)    << 16)
23#define M_PERFCTL_MT_EN(filter)		((filter) << 20)
24#define    M_TC_EN_ALL			M_PERFCTL_MT_EN(0)
25#define    M_TC_EN_VPE			M_PERFCTL_MT_EN(1)
26#define    M_TC_EN_TC			M_PERFCTL_MT_EN(2)
27#define M_PERFCTL_TCID(tcid)		((tcid)   << 22)
28#define M_PERFCTL_WIDE			(1UL      << 30)
29#define M_PERFCTL_MORE			(1UL      << 31)
30
31#define M_COUNTER_OVERFLOW		(1UL      << 31)
32
33#ifdef CONFIG_MIPS_MT_SMP
34#define WHAT		(M_TC_EN_VPE | M_PERFCTL_VPEID(smp_processor_id()))
35#define vpe_id()	smp_processor_id()
36#else
37#define WHAT		0
38#define vpe_id()	0
39#endif
40
41#define __define_perf_accessors(r, n, np)				\
42									\
43static inline unsigned int r_c0_ ## r ## n(void)			\
44{									\
45	unsigned int cpu = vpe_id();					\
46									\
47	switch (cpu) {							\
48	case 0:								\
49		return read_c0_ ## r ## n();				\
50	case 1:								\
51		return read_c0_ ## r ## np();				\
52	default:							\
53		BUG();							\
54	}								\
55	return 0;							\
56}									\
57									\
58static inline void w_c0_ ## r ## n(unsigned int value)			\
59{									\
60	unsigned int cpu = vpe_id();					\
61									\
62	switch (cpu) {							\
63	case 0:								\
64		write_c0_ ## r ## n(value);				\
65		return;							\
66	case 1:								\
67		write_c0_ ## r ## np(value);				\
68		return;							\
69	default:							\
70		BUG();							\
71	}								\
72	return;								\
73}									\
74
75__define_perf_accessors(perfcntr, 0, 2)
76__define_perf_accessors(perfcntr, 1, 3)
77__define_perf_accessors(perfcntr, 2, 0)
78__define_perf_accessors(perfcntr, 3, 1)
79
80__define_perf_accessors(perfctrl, 0, 2)
81__define_perf_accessors(perfctrl, 1, 3)
82__define_perf_accessors(perfctrl, 2, 0)
83__define_perf_accessors(perfctrl, 3, 1)
84
85struct op_mips_model op_model_mipsxx_ops;
86
87static struct mipsxx_register_config {
88	unsigned int control[4];
89	unsigned int counter[4];
90} reg;
91
92/* Compute all of the registers in preparation for enabling profiling.  */
93
94static void mipsxx_reg_setup(struct op_counter_config *ctr)
95{
96	unsigned int counters = op_model_mipsxx_ops.num_counters;
97	int i;
98
99	/* Compute the performance counter control word.  */
100	for (i = 0; i < counters; i++) {
101		reg.control[i] = 0;
102		reg.counter[i] = 0;
103
104		if (!ctr[i].enabled)
105			continue;
106
107		reg.control[i] = M_PERFCTL_EVENT(ctr[i].event) |
108		                 M_PERFCTL_INTERRUPT_ENABLE;
109		if (ctr[i].kernel)
110			reg.control[i] |= M_PERFCTL_KERNEL;
111		if (ctr[i].user)
112			reg.control[i] |= M_PERFCTL_USER;
113		if (ctr[i].exl)
114			reg.control[i] |= M_PERFCTL_EXL;
115		reg.counter[i] = 0x80000000 - ctr[i].count;
116	}
117}
118
119/* Program all of the registers in preparation for enabling profiling.  */
120
121static void mipsxx_cpu_setup (void *args)
122{
123	unsigned int counters = op_model_mipsxx_ops.num_counters;
124
125	switch (counters) {
126	case 4:
127		w_c0_perfctrl3(0);
128		w_c0_perfcntr3(reg.counter[3]);
129	case 3:
130		w_c0_perfctrl2(0);
131		w_c0_perfcntr2(reg.counter[2]);
132	case 2:
133		w_c0_perfctrl1(0);
134		w_c0_perfcntr1(reg.counter[1]);
135	case 1:
136		w_c0_perfctrl0(0);
137		w_c0_perfcntr0(reg.counter[0]);
138	}
139}
140
141/* Start all counters on current CPU */
142static void mipsxx_cpu_start(void *args)
143{
144	unsigned int counters = op_model_mipsxx_ops.num_counters;
145
146	switch (counters) {
147	case 4:
148		w_c0_perfctrl3(WHAT | reg.control[3]);
149	case 3:
150		w_c0_perfctrl2(WHAT | reg.control[2]);
151	case 2:
152		w_c0_perfctrl1(WHAT | reg.control[1]);
153	case 1:
154		w_c0_perfctrl0(WHAT | reg.control[0]);
155	}
156}
157
158/* Stop all counters on current CPU */
159static void mipsxx_cpu_stop(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	case 3:
167		w_c0_perfctrl2(0);
168	case 2:
169		w_c0_perfctrl1(0);
170	case 1:
171		w_c0_perfctrl0(0);
172	}
173}
174
175static int mipsxx_perfcount_handler(void)
176{
177	unsigned int counters = op_model_mipsxx_ops.num_counters;
178	unsigned int control;
179	unsigned int counter;
180	int handled = IRQ_NONE;
181
182	if (cpu_has_mips_r2 && !(read_c0_cause() & (1 << 26)))
183		return handled;
184
185	switch (counters) {
186#define HANDLE_COUNTER(n)						\
187	case n + 1:							\
188		control = r_c0_perfctrl ## n();				\
189		counter = r_c0_perfcntr ## n();				\
190		if ((control & M_PERFCTL_INTERRUPT_ENABLE) &&		\
191		    (counter & M_COUNTER_OVERFLOW)) {			\
192			oprofile_add_sample(get_irq_regs(), n);		\
193			w_c0_perfcntr ## n(reg.counter[n]);		\
194			handled = IRQ_HANDLED;				\
195		}
196	HANDLE_COUNTER(3)
197	HANDLE_COUNTER(2)
198	HANDLE_COUNTER(1)
199	HANDLE_COUNTER(0)
200	}
201
202	return handled;
203}
204
205#define M_CONFIG1_PC	(1 << 4)
206
207static inline int __n_counters(void)
208{
209	if (!(read_c0_config1() & M_CONFIG1_PC))
210		return 0;
211	if (!(r_c0_perfctrl0() & M_PERFCTL_MORE))
212		return 1;
213	if (!(r_c0_perfctrl1() & M_PERFCTL_MORE))
214		return 2;
215	if (!(r_c0_perfctrl2() & M_PERFCTL_MORE))
216		return 3;
217
218	return 4;
219}
220
221static inline int n_counters(void)
222{
223	int counters;
224
225	switch (current_cpu_data.cputype) {
226	case CPU_R10000:
227		counters = 2;
228		break;
229
230	case CPU_R12000:
231	case CPU_R14000:
232		counters = 4;
233		break;
234
235	default:
236		counters = __n_counters();
237	}
238
239	return counters;
240}
241
242static inline void reset_counters(int counters)
243{
244	switch (counters) {
245	case 4:
246		w_c0_perfctrl3(0);
247		w_c0_perfcntr3(0);
248	case 3:
249		w_c0_perfctrl2(0);
250		w_c0_perfcntr2(0);
251	case 2:
252		w_c0_perfctrl1(0);
253		w_c0_perfcntr1(0);
254	case 1:
255		w_c0_perfctrl0(0);
256		w_c0_perfcntr0(0);
257	}
258}
259
260static int __init mipsxx_init(void)
261{
262	int counters;
263
264	counters = n_counters();
265	if (counters == 0) {
266		printk(KERN_ERR "Oprofile: CPU has no performance counters\n");
267		return -ENODEV;
268	}
269
270	reset_counters(counters);
271
272#ifdef CONFIG_MIPS_MT_SMP
273	counters >>= 1;
274#endif
275
276	op_model_mipsxx_ops.num_counters = counters;
277	switch (current_cpu_data.cputype) {
278	case CPU_20KC:
279		op_model_mipsxx_ops.cpu_type = "mips/20K";
280		break;
281
282	case CPU_24K:
283		op_model_mipsxx_ops.cpu_type = "mips/24K";
284		break;
285
286	case CPU_25KF:
287		op_model_mipsxx_ops.cpu_type = "mips/25K";
288		break;
289
290	case CPU_34K:
291		op_model_mipsxx_ops.cpu_type = "mips/34K";
292		break;
293
294	case CPU_74K:
295		op_model_mipsxx_ops.cpu_type = "mips/74K";
296		break;
297
298	case CPU_5KC:
299		op_model_mipsxx_ops.cpu_type = "mips/5K";
300		break;
301
302	case CPU_R10000:
303		if ((current_cpu_data.processor_id & 0xff) == 0x20)
304			op_model_mipsxx_ops.cpu_type = "mips/r10000-v2.x";
305		else
306			op_model_mipsxx_ops.cpu_type = "mips/r10000";
307		break;
308
309	case CPU_R12000:
310	case CPU_R14000:
311		op_model_mipsxx_ops.cpu_type = "mips/r12000";
312		break;
313
314	case CPU_SB1:
315	case CPU_SB1A:
316		op_model_mipsxx_ops.cpu_type = "mips/sb1";
317		break;
318
319	default:
320		printk(KERN_ERR "Profiling unsupported for this CPU\n");
321
322		return -ENODEV;
323	}
324
325	perf_irq = mipsxx_perfcount_handler;
326
327	return 0;
328}
329
330static void mipsxx_exit(void)
331{
332	int counters = op_model_mipsxx_ops.num_counters;
333#ifdef CONFIG_MIPS_MT_SMP
334	counters <<= 1;
335#endif
336	reset_counters(counters);
337
338	perf_irq = null_perf_irq;
339}
340
341struct op_mips_model op_model_mipsxx_ops = {
342	.reg_setup	= mipsxx_reg_setup,
343	.cpu_setup	= mipsxx_cpu_setup,
344	.init		= mipsxx_init,
345	.exit		= mipsxx_exit,
346	.cpu_start	= mipsxx_cpu_start,
347	.cpu_stop	= mipsxx_cpu_stop,
348};
349