op_model_mipsxx.c 6.8 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14
/*
 * This file is subject to the terms and conditions of the GNU General Public
 * License.  See the file "COPYING" in the main directory of this archive
 * for more details.
 *
 * Copyright (C) 2004, 2005 by Ralf Baechle
 * Copyright (C) 2005 by MIPS Technologies, Inc.
 */
#include <linux/oprofile.h>
#include <linux/interrupt.h>
#include <linux/smp.h>

#include "op_impl.h"

15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
#define M_PERFCTL_EXL			(1UL      <<  0)
#define M_PERFCTL_KERNEL		(1UL      <<  1)
#define M_PERFCTL_SUPERVISOR		(1UL      <<  2)
#define M_PERFCTL_USER			(1UL      <<  3)
#define M_PERFCTL_INTERRUPT_ENABLE	(1UL      <<  4)
#define M_PERFCTL_EVENT(event)		((event)  << 5)
#define M_PERFCTL_VPEID(vpe)		((vpe)    << 16)
#define M_PERFCTL_MT_EN(filter)		((filter) << 20)
#define    M_TC_EN_ALL			M_PERFCTL_MT_EN(0)
#define    M_TC_EN_VPE			M_PERFCTL_MT_EN(1)
#define    M_TC_EN_TC			M_PERFCTL_MT_EN(2)
#define M_PERFCTL_TCID(tcid)		((tcid)   << 22)
#define M_PERFCTL_WIDE			(1UL      << 30)
#define M_PERFCTL_MORE			(1UL      << 31)

#define M_COUNTER_OVERFLOW		(1UL      << 31)

#ifdef CONFIG_MIPS_MT_SMP
#define WHAT	(M_TC_EN_VPE | M_PERFCTL_VPEID(smp_processor_id()))
#else
#define WHAT	0
#endif
37

38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78
#define __define_perf_accessors(r, n, np)				\
									\
static inline unsigned int r_c0_ ## r ## n(void)			\
{									\
	unsigned int cpu = smp_processor_id();				\
									\
	switch (cpu) {							\
	case 0:								\
		return read_c0_ ## r ## n();				\
	case 1:								\
		return read_c0_ ## r ## np();				\
	default:							\
		BUG();							\
	}								\
}									\
									\
static inline void w_c0_ ## r ## n(unsigned int value)			\
{									\
	unsigned int cpu = smp_processor_id();				\
									\
	switch (cpu) {							\
	case 0:								\
		write_c0_ ## r ## n(value);				\
		return;							\
	case 1:								\
		write_c0_ ## r ## np(value);				\
		return;							\
	default:							\
		BUG();							\
	}								\
}									\

__define_perf_accessors(perfcntr, 0, 2)
__define_perf_accessors(perfcntr, 1, 3)
__define_perf_accessors(perfcntr, 2, 2)
__define_perf_accessors(perfcntr, 3, 2)

__define_perf_accessors(perfctrl, 0, 2)
__define_perf_accessors(perfctrl, 1, 3)
__define_perf_accessors(perfctrl, 2, 2)
__define_perf_accessors(perfctrl, 3, 2)
79

80
struct op_mips_model op_model_mipsxx_ops;
81 82 83 84 85 86 87 88 89 90

static struct mipsxx_register_config {
	unsigned int control[4];
	unsigned int counter[4];
} reg;

/* Compute all of the registers in preparation for enabling profiling.  */

static void mipsxx_reg_setup(struct op_counter_config *ctr)
{
91
	unsigned int counters = op_model_mipsxx_ops.num_counters;
92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118
	int i;

	/* Compute the performance counter control word.  */
	/* For now count kernel and user mode */
	for (i = 0; i < counters; i++) {
		reg.control[i] = 0;
		reg.counter[i] = 0;

		if (!ctr[i].enabled)
			continue;

		reg.control[i] = M_PERFCTL_EVENT(ctr[i].event) |
		                 M_PERFCTL_INTERRUPT_ENABLE;
		if (ctr[i].kernel)
			reg.control[i] |= M_PERFCTL_KERNEL;
		if (ctr[i].user)
			reg.control[i] |= M_PERFCTL_USER;
		if (ctr[i].exl)
			reg.control[i] |= M_PERFCTL_EXL;
		reg.counter[i] = 0x80000000 - ctr[i].count;
	}
}

/* Program all of the registers in preparation for enabling profiling.  */

static void mipsxx_cpu_setup (void *args)
{
119
	unsigned int counters = op_model_mipsxx_ops.num_counters;
120 121 122

	switch (counters) {
	case 4:
123 124
		w_c0_perfctrl3(0);
		w_c0_perfcntr3(reg.counter[3]);
125
	case 3:
126 127
		w_c0_perfctrl2(0);
		w_c0_perfcntr2(reg.counter[2]);
128
	case 2:
129 130
		w_c0_perfctrl1(0);
		w_c0_perfcntr1(reg.counter[1]);
131
	case 1:
132 133
		w_c0_perfctrl0(0);
		w_c0_perfcntr0(reg.counter[0]);
134 135 136 137 138 139
	}
}

/* Start all counters on current CPU */
static void mipsxx_cpu_start(void *args)
{
140
	unsigned int counters = op_model_mipsxx_ops.num_counters;
141 142 143

	switch (counters) {
	case 4:
144
		w_c0_perfctrl3(WHAT | reg.control[3]);
145
	case 3:
146
		w_c0_perfctrl2(WHAT | reg.control[2]);
147
	case 2:
148
		w_c0_perfctrl1(WHAT | reg.control[1]);
149
	case 1:
150
		w_c0_perfctrl0(WHAT | reg.control[0]);
151 152 153 154 155 156
	}
}

/* Stop all counters on current CPU */
static void mipsxx_cpu_stop(void *args)
{
157
	unsigned int counters = op_model_mipsxx_ops.num_counters;
158 159 160

	switch (counters) {
	case 4:
161
		w_c0_perfctrl3(0);
162
	case 3:
163
		w_c0_perfctrl2(0);
164
	case 2:
165
		w_c0_perfctrl1(0);
166
	case 1:
167
		w_c0_perfctrl0(0);
168 169 170
	}
}

171
static int mipsxx_perfcount_handler(struct pt_regs *regs)
172
{
173
	unsigned int counters = op_model_mipsxx_ops.num_counters;
174 175
	unsigned int control;
	unsigned int counter;
176
	int handled = 0;
177 178 179 180

	switch (counters) {
#define HANDLE_COUNTER(n)						\
	case n + 1:							\
181 182
		control = r_c0_perfctrl ## n();				\
		counter = r_c0_perfcntr ## n();				\
183 184 185
		if ((control & M_PERFCTL_INTERRUPT_ENABLE) &&		\
		    (counter & M_COUNTER_OVERFLOW)) {			\
			oprofile_add_sample(regs, n);			\
186
			w_c0_perfcntr ## n(reg.counter[n]);		\
187
			handled = 1;					\
188 189 190 191 192 193
		}
	HANDLE_COUNTER(3)
	HANDLE_COUNTER(2)
	HANDLE_COUNTER(1)
	HANDLE_COUNTER(0)
	}
194 195

	return handled;
196 197 198 199
}

#define M_CONFIG1_PC	(1 << 4)

200
static inline int __n_counters(void)
201 202 203
{
	if (!(read_c0_config1() & M_CONFIG1_PC))
		return 0;
204
	if (!(r_c0_perfctrl0() & M_PERFCTL_MORE))
205
		return 1;
206
	if (!(r_c0_perfctrl1() & M_PERFCTL_MORE))
207
		return 2;
208
	if (!(r_c0_perfctrl2() & M_PERFCTL_MORE))
209 210 211 212 213
		return 3;

	return 4;
}

214 215 216 217 218 219 220 221 222 223 224 225
static inline int n_counters(void)
{
	int counters = __n_counters();

#ifndef CONFIG_SMP
	if (current_cpu_data.cputype == CPU_34K)
		return counters >> 1;
#endif

	return counters;
}

226 227 228 229
static inline void reset_counters(int counters)
{
	switch (counters) {
	case 4:
230 231
		w_c0_perfctrl3(0);
		w_c0_perfcntr3(0);
232
	case 3:
233 234
		w_c0_perfctrl2(0);
		w_c0_perfcntr2(0);
235
	case 2:
236 237
		w_c0_perfctrl1(0);
		w_c0_perfcntr1(0);
238
	case 1:
239 240
		w_c0_perfctrl0(0);
		w_c0_perfcntr0(0);
241 242 243 244 245 246 247 248
	}
}

static int __init mipsxx_init(void)
{
	int counters;

	counters = n_counters();
249 250
	if (counters == 0) {
		printk(KERN_ERR "Oprofile: CPU has no performance counters\n");
251
		return -ENODEV;
252
	}
253 254 255

	reset_counters(counters);

256
	op_model_mipsxx_ops.num_counters = counters;
257
	switch (current_cpu_data.cputype) {
258
	case CPU_20KC:
259
		op_model_mipsxx_ops.cpu_type = "mips/20K";
260 261
		break;

262
	case CPU_24K:
263
		op_model_mipsxx_ops.cpu_type = "mips/24K";
264 265
		break;

266
	case CPU_25KF:
267
		op_model_mipsxx_ops.cpu_type = "mips/25K";
268 269
		break;

270
	case CPU_34K:
271
		op_model_mipsxx_ops.cpu_type = "mips/34K";
272
		break;
273 274

	case CPU_74K:
275
		op_model_mipsxx_ops.cpu_type = "mips/74K";
276
		break;
277

278
	case CPU_5KC:
279
		op_model_mipsxx_ops.cpu_type = "mips/5K";
280 281
		break;

M
Mark Mason 已提交
282 283
	case CPU_SB1:
	case CPU_SB1A:
284
		op_model_mipsxx_ops.cpu_type = "mips/sb1";
M
Mark Mason 已提交
285 286
		break;

287 288 289 290 291 292 293 294 295 296 297 298 299
	default:
		printk(KERN_ERR "Profiling unsupported for this CPU\n");

		return -ENODEV;
	}

	perf_irq = mipsxx_perfcount_handler;

	return 0;
}

static void mipsxx_exit(void)
{
300
	reset_counters(op_model_mipsxx_ops.num_counters);
301 302 303 304

	perf_irq = null_perf_irq;
}

305
struct op_mips_model op_model_mipsxx_ops = {
306 307 308 309 310 311 312
	.reg_setup	= mipsxx_reg_setup,
	.cpu_setup	= mipsxx_cpu_setup,
	.init		= mipsxx_init,
	.exit		= mipsxx_exit,
	.cpu_start	= mipsxx_cpu_start,
	.cpu_stop	= mipsxx_cpu_stop,
};