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 
26 static void
common_reg_setup(struct op_register_config * reg,struct op_counter_config * ctr,struct op_system_config * sys,int cbox1_ofs,int cbox2_ofs)27 common_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 
117 static void
ev5_reg_setup(struct op_register_config * reg,struct op_counter_config * ctr,struct op_system_config * sys)118 ev5_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 
125 static void
pca56_reg_setup(struct op_register_config * reg,struct op_counter_config * ctr,struct op_system_config * sys)126 pca56_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 
135 static void
ev5_cpu_setup(void * x)136 ev5_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 
158 static void
ev5_reset_ctr(struct op_register_config * reg,unsigned long ctr)159 ev5_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 
184 static void
ev5_handle_interrupt(unsigned long which,struct pt_regs * regs,struct op_counter_config * ctr)185 ev5_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 
193 struct 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 
203 struct 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