1/**
2 * @file arch/alpha/oprofile/op_model_ev5.c
3 *
4 * @remark Copyright 2002 OProfile authors
5 * @remark Read the file COPYING
6 *
7 * @author Richard Henderson <rth@twiddle.net>
8 */
9
10#include <linux/oprofile.h>
11#include <linux/init.h>
12#include <linux/smp.h>
13#include <asm/ptrace.h>
14#include <asm/system.h>
15
16#include "op_impl.h"
17
18
19/* Compute all of the registers in preparation for enabling profiling.
20
21   The 21164 (EV5) and 21164PC (PCA65) vary in the bit placement and
22   meaning of the "CBOX" events.  Given that we don't care about meaning
23   at this point, arrange for the difference in bit placement to be
24   handled by common code.  */
25
26static void
27common_reg_setup(struct op_register_config *reg,
28		 struct op_counter_config *ctr,
29		 struct op_system_config *sys,
30		 int cbox1_ofs, int cbox2_ofs)
31{
32	int i, ctl, reset, need_reset;
33
34	/* Select desired events.  The event numbers are selected such
35	   that they map directly into the event selection fields:
36
37		PCSEL0:	0, 1
38		PCSEL1:	24-39
39		 CBOX1: 40-47
40		PCSEL2: 48-63
41		 CBOX2: 64-71
42
43	   There are two special cases, in that CYCLES can be measured
44	   on PCSEL[02], and SCACHE_WRITE can be measured on CBOX[12].
45	   These event numbers are canonicalizes to their first appearance.  */
46
47	ctl = 0;
48	for (i = 0; i < 3; ++i) {
49		unsigned long event = ctr[i].event;
50		if (!ctr[i].enabled)
51			continue;
52
53		/* Remap the duplicate events, as described above.  */
54		if (i == 2) {
55			if (event == 0)
56				event = 12+48;
57			else if (event == 2+41)
58				event = 4+65;
59		}
60
61		/* Convert the event numbers onto mux_select bit mask.  */
62		if (event < 2)
63			ctl |= event << 31;
64		else if (event < 24)
65			/* error */;
66		else if (event < 40)
67			ctl |= (event - 24) << 4;
68		else if (event < 48)
69			ctl |= (event - 40) << cbox1_ofs | 15 << 4;
70		else if (event < 64)
71			ctl |= event - 48;
72		else if (event < 72)
73			ctl |= (event - 64) << cbox2_ofs | 15;
74	}
75	reg->mux_select = ctl;
76
77	/* Select processor mode.  */
78	/* ??? Need to come up with some mechanism to trace only selected
79	   processes.  For now select from pal, kernel and user mode.  */
80	ctl = 0;
81	ctl |= !sys->enable_pal << 9;
82	ctl |= !sys->enable_kernel << 8;
83	ctl |= !sys->enable_user << 30;
84	reg->proc_mode = ctl;
85
86	/* Select interrupt frequencies.  Take the interrupt count selected
87	   by the user, and map it onto one of the possible counter widths.
88	   If the user value is in between, compute a value to which the
89	   counter is reset at each interrupt.  */
90
91	ctl = reset = need_reset = 0;
92	for (i = 0; i < 3; ++i) {
93		unsigned long max, hilo, count = ctr[i].count;
94		if (!ctr[i].enabled)
95			continue;
96
97		if (count <= 256)
98			count = 256, hilo = 3, max = 256;
99		else {
100			max = (i == 2 ? 16384 : 65536);
101			hilo = 2;
102			if (count > max)
103				count = max;
104		}
105		ctr[i].count = count;
106
107		ctl |= hilo << (8 - i*2);
108		reset |= (max - count) << (48 - 16*i);
109		if (count != max)
110			need_reset |= 1 << i;
111	}
112	reg->freq = ctl;
113	reg->reset_values = reset;
114	reg->need_reset = need_reset;
115}
116
117static void
118ev5_reg_setup(struct op_register_config *reg,
119	      struct op_counter_config *ctr,
120	      struct op_system_config *sys)
121{
122	common_reg_setup(reg, ctr, sys, 19, 22);
123}
124
125static void
126pca56_reg_setup(struct op_register_config *reg,
127	        struct op_counter_config *ctr,
128	        struct op_system_config *sys)
129{
130	common_reg_setup(reg, ctr, sys, 8, 11);
131}
132
133/* Program all of the registers in preparation for enabling profiling.  */
134
135static void
136ev5_cpu_setup (void *x)
137{
138	struct op_register_config *reg = x;
139
140	wrperfmon(2, reg->mux_select);
141	wrperfmon(3, reg->proc_mode);
142	wrperfmon(4, reg->freq);
143	wrperfmon(6, reg->reset_values);
144}
145
146/* CTR is a counter for which the user has requested an interrupt count
147   in between one of the widths selectable in hardware.  Reset the count
148   for CTR to the value stored in REG->RESET_VALUES.
149
150   For EV5, this means disabling profiling, reading the current values,
151   masking in the value for the desired register, writing, then turning
152   profiling back on.
153
154   This can be streamlined if profiling is only enabled for user mode.
155   In that case we know that the counters are not currently incrementing
156   (due to being in kernel mode).  */
157
158static void
159ev5_reset_ctr(struct op_register_config *reg, unsigned long ctr)
160{
161	unsigned long values, mask, not_pk, reset_values;
162
163	mask = (ctr == 0 ? 0xfffful << 48
164	        : ctr == 1 ? 0xfffful << 32
165		: 0x3fff << 16);
166
167	not_pk = 1 << 9 | 1 << 8;
168
169	reset_values = reg->reset_values;
170
171	if ((reg->proc_mode & not_pk) == not_pk) {
172		values = wrperfmon(5, 0);
173		values = (reset_values & mask) | (values & ~mask & -2);
174		wrperfmon(6, values);
175	} else {
176		wrperfmon(0, -1);
177		values = wrperfmon(5, 0);
178		values = (reset_values & mask) | (values & ~mask & -2);
179		wrperfmon(6, values);
180		wrperfmon(1, reg->enable);
181	}
182}
183
184static void
185ev5_handle_interrupt(unsigned long which, struct pt_regs *regs,
186		     struct op_counter_config *ctr)
187{
188	/* Record the sample.  */
189	oprofile_add_sample(regs, which);
190}
191
192
193struct op_axp_model op_model_ev5 = {
194	.reg_setup		= ev5_reg_setup,
195	.cpu_setup		= ev5_cpu_setup,
196	.reset_ctr		= ev5_reset_ctr,
197	.handle_interrupt	= ev5_handle_interrupt,
198	.cpu_type		= "alpha/ev5",
199	.num_counters		= 3,
200	.can_set_proc_mode	= 1,
201};
202
203struct op_axp_model op_model_pca56 = {
204	.reg_setup		= pca56_reg_setup,
205	.cpu_setup		= ev5_cpu_setup,
206	.reset_ctr		= ev5_reset_ctr,
207	.handle_interrupt	= ev5_handle_interrupt,
208	.cpu_type		= "alpha/pca56",
209	.num_counters		= 3,
210	.can_set_proc_mode	= 1,
211};
212