xref: /illumos-gate/usr/src/uts/sun4/io/fpc/fpc-impl.c (revision a07094369b21309434206d9b3601d162693466fc)
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 /*ARGSUSED*/
85 void
86 fpc_common_node_setup(dev_info_t *dip, int *index_p)
87 {
88 	char pathname[MAXPATHLEN];
89 
90 	(void) ddi_pathname(dip, pathname);
91 	node_data[this_node].name =
92 	    kmem_zalloc(strlen(pathname)+1, KM_SLEEP);
93 	(void) strcpy(node_data[this_node].name, pathname);
94 	mutex_init(&node_data[this_node].mutex, NULL, MUTEX_DRIVER, NULL);
95 	*index_p = this_node++;
96 }
97 
98 int
99 fpc_perfcnt_module_init(dev_info_t *fpc_dip, int *avail)
100 {
101 	int i;
102 	dev_info_t *dip;
103 
104 	*avail = 0;
105 
106 	for (i = 1; i < MAX_REG_TYPES; i++) {
107 		first_reg_of_type[i] =
108 		    first_reg_of_type[i-1] + counters_per_type[i-1];
109 	}
110 
111 	/*
112 	 * Look thru first level of device tree only.
113 	 * Assume there can be no more than NUM_LEAVES nodes in the system.
114 	 */
115 	dip = ddi_root_node();
116 	for (dip = ddi_get_child(dip);
117 	    ((dip != NULL) && (this_node < NUM_LEAVES));
118 	    dip = ddi_get_next_sibling(dip)) {
119 		if (fpc_platform_node_init(dip, avail) != SUCCESS)
120 			return (DDI_FAILURE);
121 	}
122 
123 	return ((*avail) ? fpc_platform_module_init(fpc_dip) : DDI_FAILURE);
124 }
125 
126 int
127 fpc_perfcnt_module_fini(dev_info_t *dip)
128 {
129 	int i;
130 
131 	for (i = 0; i < NUM_LEAVES; i++) {
132 		fpc_platform_node_fini(node_data[i].plat_data_p);
133 		if (node_data[i].name != NULL)
134 			kmem_free(node_data[i].name,
135 			    strlen(node_data[i].name) + 1);
136 		mutex_destroy(&node_data[i].mutex);
137 	}
138 
139 	fpc_platform_module_fini(dip);
140 	return (DDI_SUCCESS);
141 }
142 
143 char
144 *fpc_get_dev_name_by_number(int index)
145 {
146 	return (node_data[index].name);
147 }
148 
149 void *
150 fpc_get_platform_data_by_number(int index)
151 {
152 	return (node_data[index].plat_data_p);
153 }
154 
155 
156 int
157 fpc_set_platform_data_by_number(int index, void *data_p)
158 {
159 	node_data[index].plat_data_p = data_p;
160 	return (SUCCESS);
161 }
162 
163 
164 static int
165 fpc_get_mutex_by_number(int index, kmutex_t **mutex_pp)
166 {
167 	*mutex_pp = &node_data[index].mutex;
168 	return (SUCCESS);
169 }
170 
171 
172 static int
173 fpc_get_counter_reg_index(fire_perfcnt_t regtype, int counter)
174 {
175 	FPC_DBG1(
176 	    "fpc_get_counter_reg_index: regtype:%d, counter:%d, bounds:%d\n",
177 	    regtype, counter, counters_per_type[regtype]);
178 	if (BOUNDS_CHECK_FAILS(counter, counters_per_type[regtype]))
179 		return (-1);
180 	FPC_DBG1("returning: %d\n", first_reg_of_type[regtype] + counter);
181 	return (first_reg_of_type[regtype] + counter);
182 }
183 
184 
185 /*
186  * Program a performance counter.
187  *
188  * reggroup is which type of counter.
189  * counter is the counter number.
190  * event is the event to program for that counter.
191  */
192 int
193 fpc_perfcnt_program(int devnum, fire_perfcnt_t reggroup,
194     uint64_t new_events)
195 {
196 	int counter_index;
197 	fire_perfreg_handle_t firehdl;
198 	kmutex_t *mutex_p;
199 	uint64_t old_events;
200 	int rval = SUCCESS;
201 	uint64_t zero = 0ull;
202 	int num_counters, counter;
203 
204 	FPC_DBG1("fpc_perfcnt_program enter:\n");
205 	FPC_DBG1("  devnum:%d, reggroup:%d, new_events:0x%" PRIx64 "\n",
206 	    devnum, reggroup, new_events);
207 
208 	if ((firehdl = fpc_get_perfreg_handle(devnum)) ==
209 	    (fire_perfreg_handle_t)-1)
210 		return (EIO);
211 
212 	num_counters = counters_per_type[reggroup];
213 
214 	if (fpc_get_mutex_by_number(devnum, &mutex_p) != DDI_SUCCESS) {
215 		(void) fpc_free_counter_handle(firehdl);
216 		return (EIO);
217 	}
218 
219 	mutex_enter(mutex_p);
220 
221 	if ((rval = fpc_event_io(firehdl, reggroup, &old_events, IS_READ)) !=
222 	    SUCCESS) {
223 		FPC_DBG1("Read of old event data failed, group:%d\n", reggroup);
224 		goto done_pgm;
225 	}
226 
227 	for (counter = 0; counter < num_counters; counter++) {
228 
229 		counter_index = fpc_get_counter_reg_index(reggroup, counter);
230 
231 		if ((old_events & event_field_mask[counter_index]) ==
232 		    (new_events & event_field_mask[counter_index]))
233 			continue;
234 
235 		FPC_DBG1("Zeroing counter %d\n", counter_index);
236 		if ((rval = fpc_counter_io(firehdl, reggroup, counter_index,
237 		    &zero, IS_WRITE)) != SUCCESS)
238 			goto done_pgm;
239 	}
240 
241 	if (old_events != new_events) {
242 		if ((rval =
243 		    fpc_event_io(firehdl, reggroup, &new_events, IS_WRITE)) !=
244 		    SUCCESS) {
245 			FPC_DBG1("Write of new event data failed, group:%d\n",
246 			    reggroup);
247 			goto done_pgm;
248 		}
249 	}
250 done_pgm:
251 	mutex_exit(mutex_p);
252 	(void) fpc_free_counter_handle(firehdl);
253 	return (rval);
254 }
255 
256 
257 /*
258  * Read a performance counter.
259  *
260  * reggroup is which type of counter.
261  * event_p returns the event programmed for that counter.
262  * values returns the counter values.
263  */
264 int
265 fpc_perfcnt_read(int devnum, fire_perfcnt_t reggroup,
266     uint64_t *event_p, uint64_t values[NUM_MAX_COUNTERS])
267 {
268 	fire_perfreg_handle_t firehdl;
269 	int counter_index;
270 	kmutex_t *mutex_p;
271 	int rval;
272 	int num_counters, counter;
273 
274 	FPC_DBG1("fpc_perfcnt_read: devnum:%d\n", devnum);
275 	num_counters = counters_per_type[reggroup];
276 
277 	if ((firehdl = fpc_get_perfreg_handle(devnum)) ==
278 	    (fire_perfreg_handle_t)-1)
279 		return (EIO);
280 
281 	if (fpc_get_mutex_by_number(devnum, &mutex_p) != DDI_SUCCESS)
282 		return (EIO);
283 
284 	mutex_enter(mutex_p);
285 
286 	if ((rval = fpc_event_io(firehdl, reggroup, event_p, IS_READ)) !=
287 	    SUCCESS)
288 		goto done_read;
289 
290 	for (counter = 0; counter < num_counters; counter++) {
291 		counter_index = fpc_get_counter_reg_index(reggroup, counter);
292 
293 		if ((rval = fpc_counter_io(firehdl, reggroup, counter_index,
294 		    &values[counter], IS_READ)) != SUCCESS)
295 			goto done_read;
296 
297 		FPC_DBG1("Read_counter %d / %d, status:%d, value returned:0x%"
298 		    PRIx64 "\n", reggroup, counter, rval, values[counter]);
299 	}
300 
301 done_read:
302 	mutex_exit(mutex_p);
303 	(void) fpc_free_counter_handle(firehdl);
304 	return (rval);
305 }
306