xref: /illumos-gate/usr/src/uts/sun4/io/fpc/fpc-impl.c (revision 0ad689d6)
1110e73f9Sschwartz /*
2110e73f9Sschwartz  * CDDL HEADER START
3110e73f9Sschwartz  *
4110e73f9Sschwartz  * The contents of this file are subject to the terms of the
5110e73f9Sschwartz  * Common Development and Distribution License (the "License").
6110e73f9Sschwartz  * You may not use this file except in compliance with the License.
7110e73f9Sschwartz  *
8110e73f9Sschwartz  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9110e73f9Sschwartz  * or http://www.opensolaris.org/os/licensing.
10110e73f9Sschwartz  * See the License for the specific language governing permissions
11110e73f9Sschwartz  * and limitations under the License.
12110e73f9Sschwartz  *
13110e73f9Sschwartz  * When distributing Covered Code, include this CDDL HEADER in each
14110e73f9Sschwartz  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15110e73f9Sschwartz  * If applicable, add the following below this CDDL HEADER, with the
16110e73f9Sschwartz  * fields enclosed by brackets "[]" replaced with your own identifying
17110e73f9Sschwartz  * information: Portions Copyright [yyyy] [name of copyright owner]
18110e73f9Sschwartz  *
19110e73f9Sschwartz  * CDDL HEADER END
20110e73f9Sschwartz  */
21110e73f9Sschwartz 
22110e73f9Sschwartz /*
23110e73f9Sschwartz  * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
24110e73f9Sschwartz  * Use is subject to license terms.
25110e73f9Sschwartz  */
26110e73f9Sschwartz 
27110e73f9Sschwartz #pragma ident	"%Z%%M%	%I%	%E% SMI"
28110e73f9Sschwartz 
29110e73f9Sschwartz #include <sys/param.h>
30110e73f9Sschwartz #include <sys/types.h>
31110e73f9Sschwartz #include <sys/errno.h>
32110e73f9Sschwartz #include <sys/kmem.h>
33110e73f9Sschwartz #include <sys/sunddi.h>
34110e73f9Sschwartz #include <sys/disp.h>
35110e73f9Sschwartz #include <fpc.h>
36110e73f9Sschwartz #include <fpc-impl.h>
37110e73f9Sschwartz 
38110e73f9Sschwartz #define	BOUNDS_CHECK_FAILS(arg, max)	((arg < 0) && (arg >= max))
39110e73f9Sschwartz 
40110e73f9Sschwartz static int this_node = 0;
41110e73f9Sschwartz node_data_t node_data[NUM_LEAVES];
42110e73f9Sschwartz 
43110e73f9Sschwartz int fpc_debug = 0;
44110e73f9Sschwartz 
45110e73f9Sschwartz static int counters_per_type[MAX_REG_TYPES] = {
46110e73f9Sschwartz 	NUM_JBC_COUNTERS,
47110e73f9Sschwartz 	NUM_IMU_COUNTERS,
48110e73f9Sschwartz 	NUM_MMU_COUNTERS,
49110e73f9Sschwartz 	NUM_TLU_COUNTERS,
50110e73f9Sschwartz 	NUM_LPU_COUNTERS
51110e73f9Sschwartz };
52110e73f9Sschwartz 
53110e73f9Sschwartz static int first_reg_of_type[MAX_REG_TYPES];
54110e73f9Sschwartz 
55110e73f9Sschwartz static uint64_t event_field_mask[NUM_TOTAL_COUNTERS] = {
56110e73f9Sschwartz 	JBC_PIC0_EVT_MASK,		/* JBC counter 0 */
57110e73f9Sschwartz 	JBC_PIC1_EVT_MASK,		/* JBC counter 1 */
58110e73f9Sschwartz 	IMU_PIC0_EVT_MASK,		/* IMU counter 0 */
59110e73f9Sschwartz 	IMU_PIC1_EVT_MASK,		/* IMU counter 1 */
60110e73f9Sschwartz 	MMU_PIC0_EVT_MASK,		/* MMU counter 0 */
61110e73f9Sschwartz 	MMU_PIC1_EVT_MASK,		/* MMU counter 1 */
62110e73f9Sschwartz 	TLU_PIC0_EVT_MASK,		/* TLU counter 0 */
63110e73f9Sschwartz 	TLU_PIC1_EVT_MASK,		/* TLU counter 1 */
64110e73f9Sschwartz 	TLU_PIC2_EVT_MASK,		/* TLU counter 2 */
65110e73f9Sschwartz 	LPU_PIC0_EVT_MASK,		/* LPU counter 1 */
66110e73f9Sschwartz 	LPU_PIC1_EVT_MASK		/* LPU counter 2 */
67110e73f9Sschwartz };
68110e73f9Sschwartz 
69110e73f9Sschwartz /* Offsets of the fields shown in event_field_masks. */
70110e73f9Sschwartz static int event_field_offset[NUM_TOTAL_COUNTERS] = {
71110e73f9Sschwartz 	PIC0_EVT_SEL_SHIFT,		/* JBC counter 0 */
72110e73f9Sschwartz 	PIC1_EVT_SEL_SHIFT,		/* JBC counter 1 */
73110e73f9Sschwartz 	PIC0_EVT_SEL_SHIFT,		/* IMU counter 0 */
74110e73f9Sschwartz 	PIC1_EVT_SEL_SHIFT,		/* IMU counter 1 */
75110e73f9Sschwartz 	PIC0_EVT_SEL_SHIFT,		/* MMU counter 0 */
76110e73f9Sschwartz 	PIC1_EVT_SEL_SHIFT,		/* MMU counter 1 */
77110e73f9Sschwartz 	PIC0_EVT_SEL_SHIFT,		/* TLU counter 0 */
78110e73f9Sschwartz 	PIC1_EVT_SEL_SHIFT,		/* TLU counter 1 */
79110e73f9Sschwartz 	PIC2_EVT_SEL_SHIFT,		/* TLU counter 2 */
80110e73f9Sschwartz 	PIC0_EVT_SEL_SHIFT,		/* LPU counter 1 */
81110e73f9Sschwartz 	PIC2_EVT_SEL_SHIFT		/* LPU counter 2 */
82110e73f9Sschwartz };
83110e73f9Sschwartz 
84*0ad689d6Sschwartz /* For determining platform suitability at _init time. */
85*0ad689d6Sschwartz int
fpc_init_platform_check()86*0ad689d6Sschwartz fpc_init_platform_check()
87*0ad689d6Sschwartz {
88*0ad689d6Sschwartz 	return (fpc_platform_check());
89*0ad689d6Sschwartz }
90*0ad689d6Sschwartz 
91110e73f9Sschwartz /*ARGSUSED*/
92110e73f9Sschwartz void
fpc_common_node_setup(dev_info_t * dip,int * index_p)93110e73f9Sschwartz fpc_common_node_setup(dev_info_t *dip, int *index_p)
94110e73f9Sschwartz {
95110e73f9Sschwartz 	char pathname[MAXPATHLEN];
96110e73f9Sschwartz 
97110e73f9Sschwartz 	(void) ddi_pathname(dip, pathname);
98110e73f9Sschwartz 	node_data[this_node].name =
99110e73f9Sschwartz 	    kmem_zalloc(strlen(pathname)+1, KM_SLEEP);
100110e73f9Sschwartz 	(void) strcpy(node_data[this_node].name, pathname);
101110e73f9Sschwartz 	mutex_init(&node_data[this_node].mutex, NULL, MUTEX_DRIVER, NULL);
102110e73f9Sschwartz 	*index_p = this_node++;
103110e73f9Sschwartz }
104110e73f9Sschwartz 
105110e73f9Sschwartz int
fpc_perfcnt_module_init(dev_info_t * fpc_dip,int * avail)106110e73f9Sschwartz fpc_perfcnt_module_init(dev_info_t *fpc_dip, int *avail)
107110e73f9Sschwartz {
108110e73f9Sschwartz 	int i;
109110e73f9Sschwartz 	dev_info_t *dip;
110110e73f9Sschwartz 
111110e73f9Sschwartz 	*avail = 0;
112110e73f9Sschwartz 
113110e73f9Sschwartz 	for (i = 1; i < MAX_REG_TYPES; i++) {
114110e73f9Sschwartz 		first_reg_of_type[i] =
115110e73f9Sschwartz 		    first_reg_of_type[i-1] + counters_per_type[i-1];
116110e73f9Sschwartz 	}
117110e73f9Sschwartz 
118110e73f9Sschwartz 	/*
119110e73f9Sschwartz 	 * Look thru first level of device tree only.
120110e73f9Sschwartz 	 * Assume there can be no more than NUM_LEAVES nodes in the system.
121110e73f9Sschwartz 	 */
122110e73f9Sschwartz 	dip = ddi_root_node();
123110e73f9Sschwartz 	for (dip = ddi_get_child(dip);
124110e73f9Sschwartz 	    ((dip != NULL) && (this_node < NUM_LEAVES));
125110e73f9Sschwartz 	    dip = ddi_get_next_sibling(dip)) {
126110e73f9Sschwartz 		if (fpc_platform_node_init(dip, avail) != SUCCESS)
127110e73f9Sschwartz 			return (DDI_FAILURE);
128110e73f9Sschwartz 	}
129110e73f9Sschwartz 
130110e73f9Sschwartz 	return ((*avail) ? fpc_platform_module_init(fpc_dip) : DDI_FAILURE);
131110e73f9Sschwartz }
132110e73f9Sschwartz 
133110e73f9Sschwartz int
fpc_perfcnt_module_fini(dev_info_t * dip)134110e73f9Sschwartz fpc_perfcnt_module_fini(dev_info_t *dip)
135110e73f9Sschwartz {
136110e73f9Sschwartz 	int i;
137110e73f9Sschwartz 
138110e73f9Sschwartz 	for (i = 0; i < NUM_LEAVES; i++) {
139110e73f9Sschwartz 		fpc_platform_node_fini(node_data[i].plat_data_p);
140*0ad689d6Sschwartz 		if (node_data[i].name != NULL) {
141110e73f9Sschwartz 			kmem_free(node_data[i].name,
142110e73f9Sschwartz 			    strlen(node_data[i].name) + 1);
143*0ad689d6Sschwartz 			mutex_destroy(&node_data[i].mutex);
144*0ad689d6Sschwartz 		}
145110e73f9Sschwartz 	}
146110e73f9Sschwartz 
147110e73f9Sschwartz 	fpc_platform_module_fini(dip);
148110e73f9Sschwartz 	return (DDI_SUCCESS);
149110e73f9Sschwartz }
150110e73f9Sschwartz 
151110e73f9Sschwartz char
fpc_get_dev_name_by_number(int index)152110e73f9Sschwartz *fpc_get_dev_name_by_number(int index)
153110e73f9Sschwartz {
154110e73f9Sschwartz 	return (node_data[index].name);
155110e73f9Sschwartz }
156110e73f9Sschwartz 
157110e73f9Sschwartz void *
fpc_get_platform_data_by_number(int index)158110e73f9Sschwartz fpc_get_platform_data_by_number(int index)
159110e73f9Sschwartz {
160110e73f9Sschwartz 	return (node_data[index].plat_data_p);
161110e73f9Sschwartz }
162110e73f9Sschwartz 
163110e73f9Sschwartz 
164110e73f9Sschwartz int
fpc_set_platform_data_by_number(int index,void * data_p)165110e73f9Sschwartz fpc_set_platform_data_by_number(int index, void *data_p)
166110e73f9Sschwartz {
167110e73f9Sschwartz 	node_data[index].plat_data_p = data_p;
168110e73f9Sschwartz 	return (SUCCESS);
169110e73f9Sschwartz }
170110e73f9Sschwartz 
171110e73f9Sschwartz 
172110e73f9Sschwartz static int
fpc_get_mutex_by_number(int index,kmutex_t ** mutex_pp)173110e73f9Sschwartz fpc_get_mutex_by_number(int index, kmutex_t **mutex_pp)
174110e73f9Sschwartz {
175110e73f9Sschwartz 	*mutex_pp = &node_data[index].mutex;
176110e73f9Sschwartz 	return (SUCCESS);
177110e73f9Sschwartz }
178110e73f9Sschwartz 
179110e73f9Sschwartz 
180110e73f9Sschwartz static int
fpc_get_counter_reg_index(fire_perfcnt_t regtype,int counter)181110e73f9Sschwartz fpc_get_counter_reg_index(fire_perfcnt_t regtype, int counter)
182110e73f9Sschwartz {
183110e73f9Sschwartz 	FPC_DBG1(
184110e73f9Sschwartz 	    "fpc_get_counter_reg_index: regtype:%d, counter:%d, bounds:%d\n",
185110e73f9Sschwartz 	    regtype, counter, counters_per_type[regtype]);
186110e73f9Sschwartz 	if (BOUNDS_CHECK_FAILS(counter, counters_per_type[regtype]))
187110e73f9Sschwartz 		return (-1);
188110e73f9Sschwartz 	FPC_DBG1("returning: %d\n", first_reg_of_type[regtype] + counter);
189110e73f9Sschwartz 	return (first_reg_of_type[regtype] + counter);
190110e73f9Sschwartz }
191110e73f9Sschwartz 
192110e73f9Sschwartz 
193110e73f9Sschwartz /*
194110e73f9Sschwartz  * Program a performance counter.
195110e73f9Sschwartz  *
196110e73f9Sschwartz  * reggroup is which type of counter.
197110e73f9Sschwartz  * counter is the counter number.
198110e73f9Sschwartz  * event is the event to program for that counter.
199110e73f9Sschwartz  */
200110e73f9Sschwartz int
fpc_perfcnt_program(int devnum,fire_perfcnt_t reggroup,uint64_t new_events)201110e73f9Sschwartz fpc_perfcnt_program(int devnum, fire_perfcnt_t reggroup,
202110e73f9Sschwartz     uint64_t new_events)
203110e73f9Sschwartz {
204110e73f9Sschwartz 	int counter_index;
205110e73f9Sschwartz 	fire_perfreg_handle_t firehdl;
206110e73f9Sschwartz 	kmutex_t *mutex_p;
207110e73f9Sschwartz 	uint64_t old_events;
208110e73f9Sschwartz 	int rval = SUCCESS;
209110e73f9Sschwartz 	uint64_t zero = 0ull;
210110e73f9Sschwartz 	int num_counters, counter;
211110e73f9Sschwartz 
212110e73f9Sschwartz 	FPC_DBG1("fpc_perfcnt_program enter:\n");
213110e73f9Sschwartz 	FPC_DBG1("  devnum:%d, reggroup:%d, new_events:0x%" PRIx64 "\n",
214110e73f9Sschwartz 	    devnum, reggroup, new_events);
215110e73f9Sschwartz 
216110e73f9Sschwartz 	if ((firehdl = fpc_get_perfreg_handle(devnum)) ==
217110e73f9Sschwartz 	    (fire_perfreg_handle_t)-1)
218110e73f9Sschwartz 		return (EIO);
219110e73f9Sschwartz 
220110e73f9Sschwartz 	num_counters = counters_per_type[reggroup];
221110e73f9Sschwartz 
222110e73f9Sschwartz 	if (fpc_get_mutex_by_number(devnum, &mutex_p) != DDI_SUCCESS) {
223110e73f9Sschwartz 		(void) fpc_free_counter_handle(firehdl);
224110e73f9Sschwartz 		return (EIO);
225110e73f9Sschwartz 	}
226110e73f9Sschwartz 
227110e73f9Sschwartz 	mutex_enter(mutex_p);
228110e73f9Sschwartz 
229110e73f9Sschwartz 	if ((rval = fpc_event_io(firehdl, reggroup, &old_events, IS_READ)) !=
230110e73f9Sschwartz 	    SUCCESS) {
231110e73f9Sschwartz 		FPC_DBG1("Read of old event data failed, group:%d\n", reggroup);
232110e73f9Sschwartz 		goto done_pgm;
233110e73f9Sschwartz 	}
234110e73f9Sschwartz 
235110e73f9Sschwartz 	for (counter = 0; counter < num_counters; counter++) {
236110e73f9Sschwartz 
237110e73f9Sschwartz 		counter_index = fpc_get_counter_reg_index(reggroup, counter);
238110e73f9Sschwartz 
239110e73f9Sschwartz 		if ((old_events & event_field_mask[counter_index]) ==
240110e73f9Sschwartz 		    (new_events & event_field_mask[counter_index]))
241110e73f9Sschwartz 			continue;
242110e73f9Sschwartz 
243110e73f9Sschwartz 		FPC_DBG1("Zeroing counter %d\n", counter_index);
244110e73f9Sschwartz 		if ((rval = fpc_counter_io(firehdl, reggroup, counter_index,
245110e73f9Sschwartz 		    &zero, IS_WRITE)) != SUCCESS)
246110e73f9Sschwartz 			goto done_pgm;
247110e73f9Sschwartz 	}
248110e73f9Sschwartz 
249110e73f9Sschwartz 	if (old_events != new_events) {
250110e73f9Sschwartz 		if ((rval =
251110e73f9Sschwartz 		    fpc_event_io(firehdl, reggroup, &new_events, IS_WRITE)) !=
252110e73f9Sschwartz 		    SUCCESS) {
253110e73f9Sschwartz 			FPC_DBG1("Write of new event data failed, group:%d\n",
254110e73f9Sschwartz 			    reggroup);
255110e73f9Sschwartz 			goto done_pgm;
256110e73f9Sschwartz 		}
257110e73f9Sschwartz 	}
258110e73f9Sschwartz done_pgm:
259110e73f9Sschwartz 	mutex_exit(mutex_p);
260110e73f9Sschwartz 	(void) fpc_free_counter_handle(firehdl);
261110e73f9Sschwartz 	return (rval);
262110e73f9Sschwartz }
263110e73f9Sschwartz 
264110e73f9Sschwartz 
265110e73f9Sschwartz /*
266110e73f9Sschwartz  * Read a performance counter.
267110e73f9Sschwartz  *
268110e73f9Sschwartz  * reggroup is which type of counter.
269110e73f9Sschwartz  * event_p returns the event programmed for that counter.
270110e73f9Sschwartz  * values returns the counter values.
271110e73f9Sschwartz  */
272110e73f9Sschwartz int
fpc_perfcnt_read(int devnum,fire_perfcnt_t reggroup,uint64_t * event_p,uint64_t values[NUM_MAX_COUNTERS])273110e73f9Sschwartz fpc_perfcnt_read(int devnum, fire_perfcnt_t reggroup,
274110e73f9Sschwartz     uint64_t *event_p, uint64_t values[NUM_MAX_COUNTERS])
275110e73f9Sschwartz {
276110e73f9Sschwartz 	fire_perfreg_handle_t firehdl;
277110e73f9Sschwartz 	int counter_index;
278110e73f9Sschwartz 	kmutex_t *mutex_p;
279110e73f9Sschwartz 	int rval;
280110e73f9Sschwartz 	int num_counters, counter;
281110e73f9Sschwartz 
282110e73f9Sschwartz 	FPC_DBG1("fpc_perfcnt_read: devnum:%d\n", devnum);
283110e73f9Sschwartz 	num_counters = counters_per_type[reggroup];
284110e73f9Sschwartz 
285110e73f9Sschwartz 	if ((firehdl = fpc_get_perfreg_handle(devnum)) ==
286110e73f9Sschwartz 	    (fire_perfreg_handle_t)-1)
287110e73f9Sschwartz 		return (EIO);
288110e73f9Sschwartz 
289110e73f9Sschwartz 	if (fpc_get_mutex_by_number(devnum, &mutex_p) != DDI_SUCCESS)
290110e73f9Sschwartz 		return (EIO);
291110e73f9Sschwartz 
292110e73f9Sschwartz 	mutex_enter(mutex_p);
293110e73f9Sschwartz 
294110e73f9Sschwartz 	if ((rval = fpc_event_io(firehdl, reggroup, event_p, IS_READ)) !=
295110e73f9Sschwartz 	    SUCCESS)
296110e73f9Sschwartz 		goto done_read;
297110e73f9Sschwartz 
298110e73f9Sschwartz 	for (counter = 0; counter < num_counters; counter++) {
299110e73f9Sschwartz 		counter_index = fpc_get_counter_reg_index(reggroup, counter);
300110e73f9Sschwartz 
301110e73f9Sschwartz 		if ((rval = fpc_counter_io(firehdl, reggroup, counter_index,
302110e73f9Sschwartz 		    &values[counter], IS_READ)) != SUCCESS)
303110e73f9Sschwartz 			goto done_read;
304110e73f9Sschwartz 
305110e73f9Sschwartz 		FPC_DBG1("Read_counter %d / %d, status:%d, value returned:0x%"
306110e73f9Sschwartz 		    PRIx64 "\n", reggroup, counter, rval, values[counter]);
307110e73f9Sschwartz 	}
308110e73f9Sschwartz 
309110e73f9Sschwartz done_read:
310110e73f9Sschwartz 	mutex_exit(mutex_p);
311110e73f9Sschwartz 	(void) fpc_free_counter_handle(firehdl);
312110e73f9Sschwartz 	return (rval);
313110e73f9Sschwartz }
314