1 /*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21
22 /*
23 * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
25 */
26
27 #pragma ident "%Z%%M% %I% %E% SMI"
28
29 #include <sys/param.h>
30 #include <sys/types.h>
31 #include <sys/errno.h>
32 #include <sys/kmem.h>
33 #include <sys/sunddi.h>
34 #include <sys/disp.h>
35 #include <fpc.h>
36 #include <fpc-impl.h>
37
38 #define BOUNDS_CHECK_FAILS(arg, max) ((arg < 0) && (arg >= max))
39
40 static int this_node = 0;
41 node_data_t node_data[NUM_LEAVES];
42
43 int fpc_debug = 0;
44
45 static int counters_per_type[MAX_REG_TYPES] = {
46 NUM_JBC_COUNTERS,
47 NUM_IMU_COUNTERS,
48 NUM_MMU_COUNTERS,
49 NUM_TLU_COUNTERS,
50 NUM_LPU_COUNTERS
51 };
52
53 static int first_reg_of_type[MAX_REG_TYPES];
54
55 static uint64_t event_field_mask[NUM_TOTAL_COUNTERS] = {
56 JBC_PIC0_EVT_MASK, /* JBC counter 0 */
57 JBC_PIC1_EVT_MASK, /* JBC counter 1 */
58 IMU_PIC0_EVT_MASK, /* IMU counter 0 */
59 IMU_PIC1_EVT_MASK, /* IMU counter 1 */
60 MMU_PIC0_EVT_MASK, /* MMU counter 0 */
61 MMU_PIC1_EVT_MASK, /* MMU counter 1 */
62 TLU_PIC0_EVT_MASK, /* TLU counter 0 */
63 TLU_PIC1_EVT_MASK, /* TLU counter 1 */
64 TLU_PIC2_EVT_MASK, /* TLU counter 2 */
65 LPU_PIC0_EVT_MASK, /* LPU counter 1 */
66 LPU_PIC1_EVT_MASK /* LPU counter 2 */
67 };
68
69 /* Offsets of the fields shown in event_field_masks. */
70 static int event_field_offset[NUM_TOTAL_COUNTERS] = {
71 PIC0_EVT_SEL_SHIFT, /* JBC counter 0 */
72 PIC1_EVT_SEL_SHIFT, /* JBC counter 1 */
73 PIC0_EVT_SEL_SHIFT, /* IMU counter 0 */
74 PIC1_EVT_SEL_SHIFT, /* IMU counter 1 */
75 PIC0_EVT_SEL_SHIFT, /* MMU counter 0 */
76 PIC1_EVT_SEL_SHIFT, /* MMU counter 1 */
77 PIC0_EVT_SEL_SHIFT, /* TLU counter 0 */
78 PIC1_EVT_SEL_SHIFT, /* TLU counter 1 */
79 PIC2_EVT_SEL_SHIFT, /* TLU counter 2 */
80 PIC0_EVT_SEL_SHIFT, /* LPU counter 1 */
81 PIC2_EVT_SEL_SHIFT /* LPU counter 2 */
82 };
83
84 /* For determining platform suitability at _init time. */
85 int
fpc_init_platform_check()86 fpc_init_platform_check()
87 {
88 return (fpc_platform_check());
89 }
90
91 /*ARGSUSED*/
92 void
fpc_common_node_setup(dev_info_t * dip,int * index_p)93 fpc_common_node_setup(dev_info_t *dip, int *index_p)
94 {
95 char pathname[MAXPATHLEN];
96
97 (void) ddi_pathname(dip, pathname);
98 node_data[this_node].name =
99 kmem_zalloc(strlen(pathname)+1, KM_SLEEP);
100 (void) strcpy(node_data[this_node].name, pathname);
101 mutex_init(&node_data[this_node].mutex, NULL, MUTEX_DRIVER, NULL);
102 *index_p = this_node++;
103 }
104
105 int
fpc_perfcnt_module_init(dev_info_t * fpc_dip,int * avail)106 fpc_perfcnt_module_init(dev_info_t *fpc_dip, int *avail)
107 {
108 int i;
109 dev_info_t *dip;
110
111 *avail = 0;
112
113 for (i = 1; i < MAX_REG_TYPES; i++) {
114 first_reg_of_type[i] =
115 first_reg_of_type[i-1] + counters_per_type[i-1];
116 }
117
118 /*
119 * Look thru first level of device tree only.
120 * Assume there can be no more than NUM_LEAVES nodes in the system.
121 */
122 dip = ddi_root_node();
123 for (dip = ddi_get_child(dip);
124 ((dip != NULL) && (this_node < NUM_LEAVES));
125 dip = ddi_get_next_sibling(dip)) {
126 if (fpc_platform_node_init(dip, avail) != SUCCESS)
127 return (DDI_FAILURE);
128 }
129
130 return ((*avail) ? fpc_platform_module_init(fpc_dip) : DDI_FAILURE);
131 }
132
133 int
fpc_perfcnt_module_fini(dev_info_t * dip)134 fpc_perfcnt_module_fini(dev_info_t *dip)
135 {
136 int i;
137
138 for (i = 0; i < NUM_LEAVES; i++) {
139 fpc_platform_node_fini(node_data[i].plat_data_p);
140 if (node_data[i].name != NULL) {
141 kmem_free(node_data[i].name,
142 strlen(node_data[i].name) + 1);
143 mutex_destroy(&node_data[i].mutex);
144 }
145 }
146
147 fpc_platform_module_fini(dip);
148 return (DDI_SUCCESS);
149 }
150
151 char
fpc_get_dev_name_by_number(int index)152 *fpc_get_dev_name_by_number(int index)
153 {
154 return (node_data[index].name);
155 }
156
157 void *
fpc_get_platform_data_by_number(int index)158 fpc_get_platform_data_by_number(int index)
159 {
160 return (node_data[index].plat_data_p);
161 }
162
163
164 int
fpc_set_platform_data_by_number(int index,void * data_p)165 fpc_set_platform_data_by_number(int index, void *data_p)
166 {
167 node_data[index].plat_data_p = data_p;
168 return (SUCCESS);
169 }
170
171
172 static int
fpc_get_mutex_by_number(int index,kmutex_t ** mutex_pp)173 fpc_get_mutex_by_number(int index, kmutex_t **mutex_pp)
174 {
175 *mutex_pp = &node_data[index].mutex;
176 return (SUCCESS);
177 }
178
179
180 static int
fpc_get_counter_reg_index(fire_perfcnt_t regtype,int counter)181 fpc_get_counter_reg_index(fire_perfcnt_t regtype, int counter)
182 {
183 FPC_DBG1(
184 "fpc_get_counter_reg_index: regtype:%d, counter:%d, bounds:%d\n",
185 regtype, counter, counters_per_type[regtype]);
186 if (BOUNDS_CHECK_FAILS(counter, counters_per_type[regtype]))
187 return (-1);
188 FPC_DBG1("returning: %d\n", first_reg_of_type[regtype] + counter);
189 return (first_reg_of_type[regtype] + counter);
190 }
191
192
193 /*
194 * Program a performance counter.
195 *
196 * reggroup is which type of counter.
197 * counter is the counter number.
198 * event is the event to program for that counter.
199 */
200 int
fpc_perfcnt_program(int devnum,fire_perfcnt_t reggroup,uint64_t new_events)201 fpc_perfcnt_program(int devnum, fire_perfcnt_t reggroup,
202 uint64_t new_events)
203 {
204 int counter_index;
205 fire_perfreg_handle_t firehdl;
206 kmutex_t *mutex_p;
207 uint64_t old_events;
208 int rval = SUCCESS;
209 uint64_t zero = 0ull;
210 int num_counters, counter;
211
212 FPC_DBG1("fpc_perfcnt_program enter:\n");
213 FPC_DBG1(" devnum:%d, reggroup:%d, new_events:0x%" PRIx64 "\n",
214 devnum, reggroup, new_events);
215
216 if ((firehdl = fpc_get_perfreg_handle(devnum)) ==
217 (fire_perfreg_handle_t)-1)
218 return (EIO);
219
220 num_counters = counters_per_type[reggroup];
221
222 if (fpc_get_mutex_by_number(devnum, &mutex_p) != DDI_SUCCESS) {
223 (void) fpc_free_counter_handle(firehdl);
224 return (EIO);
225 }
226
227 mutex_enter(mutex_p);
228
229 if ((rval = fpc_event_io(firehdl, reggroup, &old_events, IS_READ)) !=
230 SUCCESS) {
231 FPC_DBG1("Read of old event data failed, group:%d\n", reggroup);
232 goto done_pgm;
233 }
234
235 for (counter = 0; counter < num_counters; counter++) {
236
237 counter_index = fpc_get_counter_reg_index(reggroup, counter);
238
239 if ((old_events & event_field_mask[counter_index]) ==
240 (new_events & event_field_mask[counter_index]))
241 continue;
242
243 FPC_DBG1("Zeroing counter %d\n", counter_index);
244 if ((rval = fpc_counter_io(firehdl, reggroup, counter_index,
245 &zero, IS_WRITE)) != SUCCESS)
246 goto done_pgm;
247 }
248
249 if (old_events != new_events) {
250 if ((rval =
251 fpc_event_io(firehdl, reggroup, &new_events, IS_WRITE)) !=
252 SUCCESS) {
253 FPC_DBG1("Write of new event data failed, group:%d\n",
254 reggroup);
255 goto done_pgm;
256 }
257 }
258 done_pgm:
259 mutex_exit(mutex_p);
260 (void) fpc_free_counter_handle(firehdl);
261 return (rval);
262 }
263
264
265 /*
266 * Read a performance counter.
267 *
268 * reggroup is which type of counter.
269 * event_p returns the event programmed for that counter.
270 * values returns the counter values.
271 */
272 int
fpc_perfcnt_read(int devnum,fire_perfcnt_t reggroup,uint64_t * event_p,uint64_t values[NUM_MAX_COUNTERS])273 fpc_perfcnt_read(int devnum, fire_perfcnt_t reggroup,
274 uint64_t *event_p, uint64_t values[NUM_MAX_COUNTERS])
275 {
276 fire_perfreg_handle_t firehdl;
277 int counter_index;
278 kmutex_t *mutex_p;
279 int rval;
280 int num_counters, counter;
281
282 FPC_DBG1("fpc_perfcnt_read: devnum:%d\n", devnum);
283 num_counters = counters_per_type[reggroup];
284
285 if ((firehdl = fpc_get_perfreg_handle(devnum)) ==
286 (fire_perfreg_handle_t)-1)
287 return (EIO);
288
289 if (fpc_get_mutex_by_number(devnum, &mutex_p) != DDI_SUCCESS)
290 return (EIO);
291
292 mutex_enter(mutex_p);
293
294 if ((rval = fpc_event_io(firehdl, reggroup, event_p, IS_READ)) !=
295 SUCCESS)
296 goto done_read;
297
298 for (counter = 0; counter < num_counters; counter++) {
299 counter_index = fpc_get_counter_reg_index(reggroup, counter);
300
301 if ((rval = fpc_counter_io(firehdl, reggroup, counter_index,
302 &values[counter], IS_READ)) != SUCCESS)
303 goto done_read;
304
305 FPC_DBG1("Read_counter %d / %d, status:%d, value returned:0x%"
306 PRIx64 "\n", reggroup, counter, rval, values[counter]);
307 }
308
309 done_read:
310 mutex_exit(mutex_p);
311 (void) fpc_free_counter_handle(firehdl);
312 return (rval);
313 }
314