http://downloads.netgear.com/files/GPL/GPL_Source_V361j_DM111PSP_series_consumer_rele...
[bcm963xx.git] / kernel / linux / arch / alpha / oprofile / op_model_ev5.c
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
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
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
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
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
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
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->pc, !user_mode(regs),
190                             which, smp_processor_id());
191 }
192
193
194 struct op_axp_model op_model_ev5 = {
195         .reg_setup              = ev5_reg_setup,
196         .cpu_setup              = ev5_cpu_setup,
197         .reset_ctr              = ev5_reset_ctr,
198         .handle_interrupt       = ev5_handle_interrupt,
199         .cpu_type               = "alpha/ev5",
200         .num_counters           = 3,
201         .can_set_proc_mode      = 1,
202 };
203
204 struct op_axp_model op_model_pca56 = {
205         .reg_setup              = pca56_reg_setup,
206         .cpu_setup              = ev5_cpu_setup,
207         .reset_ctr              = ev5_reset_ctr,
208         .handle_interrupt       = ev5_handle_interrupt,
209         .cpu_type               = "alpha/pca56",
210         .num_counters           = 3,
211         .can_set_proc_mode      = 1,
212 };