xref: /onnv-gate/usr/src/uts/sun4/io/fpc/fpc-impl.c (revision 1691:4e20a3756674)
11370Sschwartz /*
21370Sschwartz  * CDDL HEADER START
31370Sschwartz  *
41370Sschwartz  * The contents of this file are subject to the terms of the
51370Sschwartz  * Common Development and Distribution License (the "License").
61370Sschwartz  * You may not use this file except in compliance with the License.
71370Sschwartz  *
81370Sschwartz  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
91370Sschwartz  * or http://www.opensolaris.org/os/licensing.
101370Sschwartz  * See the License for the specific language governing permissions
111370Sschwartz  * and limitations under the License.
121370Sschwartz  *
131370Sschwartz  * When distributing Covered Code, include this CDDL HEADER in each
141370Sschwartz  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
151370Sschwartz  * If applicable, add the following below this CDDL HEADER, with the
161370Sschwartz  * fields enclosed by brackets "[]" replaced with your own identifying
171370Sschwartz  * information: Portions Copyright [yyyy] [name of copyright owner]
181370Sschwartz  *
191370Sschwartz  * CDDL HEADER END
201370Sschwartz  */
211370Sschwartz 
221370Sschwartz /*
231370Sschwartz  * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
241370Sschwartz  * Use is subject to license terms.
251370Sschwartz  */
261370Sschwartz 
271370Sschwartz #pragma ident	"%Z%%M%	%I%	%E% SMI"
281370Sschwartz 
291370Sschwartz #include <sys/param.h>
301370Sschwartz #include <sys/types.h>
311370Sschwartz #include <sys/errno.h>
321370Sschwartz #include <sys/kmem.h>
331370Sschwartz #include <sys/sunddi.h>
341370Sschwartz #include <sys/disp.h>
351370Sschwartz #include <fpc.h>
361370Sschwartz #include <fpc-impl.h>
371370Sschwartz 
381370Sschwartz #define	BOUNDS_CHECK_FAILS(arg, max)	((arg < 0) && (arg >= max))
391370Sschwartz 
401370Sschwartz static int this_node = 0;
411370Sschwartz node_data_t node_data[NUM_LEAVES];
421370Sschwartz 
431370Sschwartz int fpc_debug = 0;
441370Sschwartz 
451370Sschwartz static int counters_per_type[MAX_REG_TYPES] = {
461370Sschwartz 	NUM_JBC_COUNTERS,
471370Sschwartz 	NUM_IMU_COUNTERS,
481370Sschwartz 	NUM_MMU_COUNTERS,
491370Sschwartz 	NUM_TLU_COUNTERS,
501370Sschwartz 	NUM_LPU_COUNTERS
511370Sschwartz };
521370Sschwartz 
531370Sschwartz static int first_reg_of_type[MAX_REG_TYPES];
541370Sschwartz 
551370Sschwartz static uint64_t event_field_mask[NUM_TOTAL_COUNTERS] = {
561370Sschwartz 	JBC_PIC0_EVT_MASK,		/* JBC counter 0 */
571370Sschwartz 	JBC_PIC1_EVT_MASK,		/* JBC counter 1 */
581370Sschwartz 	IMU_PIC0_EVT_MASK,		/* IMU counter 0 */
591370Sschwartz 	IMU_PIC1_EVT_MASK,		/* IMU counter 1 */
601370Sschwartz 	MMU_PIC0_EVT_MASK,		/* MMU counter 0 */
611370Sschwartz 	MMU_PIC1_EVT_MASK,		/* MMU counter 1 */
621370Sschwartz 	TLU_PIC0_EVT_MASK,		/* TLU counter 0 */
631370Sschwartz 	TLU_PIC1_EVT_MASK,		/* TLU counter 1 */
641370Sschwartz 	TLU_PIC2_EVT_MASK,		/* TLU counter 2 */
651370Sschwartz 	LPU_PIC0_EVT_MASK,		/* LPU counter 1 */
661370Sschwartz 	LPU_PIC1_EVT_MASK		/* LPU counter 2 */
671370Sschwartz };
681370Sschwartz 
691370Sschwartz /* Offsets of the fields shown in event_field_masks. */
701370Sschwartz static int event_field_offset[NUM_TOTAL_COUNTERS] = {
711370Sschwartz 	PIC0_EVT_SEL_SHIFT,		/* JBC counter 0 */
721370Sschwartz 	PIC1_EVT_SEL_SHIFT,		/* JBC counter 1 */
731370Sschwartz 	PIC0_EVT_SEL_SHIFT,		/* IMU counter 0 */
741370Sschwartz 	PIC1_EVT_SEL_SHIFT,		/* IMU counter 1 */
751370Sschwartz 	PIC0_EVT_SEL_SHIFT,		/* MMU counter 0 */
761370Sschwartz 	PIC1_EVT_SEL_SHIFT,		/* MMU counter 1 */
771370Sschwartz 	PIC0_EVT_SEL_SHIFT,		/* TLU counter 0 */
781370Sschwartz 	PIC1_EVT_SEL_SHIFT,		/* TLU counter 1 */
791370Sschwartz 	PIC2_EVT_SEL_SHIFT,		/* TLU counter 2 */
801370Sschwartz 	PIC0_EVT_SEL_SHIFT,		/* LPU counter 1 */
811370Sschwartz 	PIC2_EVT_SEL_SHIFT		/* LPU counter 2 */
821370Sschwartz };
831370Sschwartz 
84*1691Sschwartz /* For determining platform suitability at _init time. */
85*1691Sschwartz int
fpc_init_platform_check()86*1691Sschwartz fpc_init_platform_check()
87*1691Sschwartz {
88*1691Sschwartz 	return (fpc_platform_check());
89*1691Sschwartz }
90*1691Sschwartz 
911370Sschwartz /*ARGSUSED*/
921370Sschwartz void
fpc_common_node_setup(dev_info_t * dip,int * index_p)931370Sschwartz fpc_common_node_setup(dev_info_t *dip, int *index_p)
941370Sschwartz {
951370Sschwartz 	char pathname[MAXPATHLEN];
961370Sschwartz 
971370Sschwartz 	(void) ddi_pathname(dip, pathname);
981370Sschwartz 	node_data[this_node].name =
991370Sschwartz 	    kmem_zalloc(strlen(pathname)+1, KM_SLEEP);
1001370Sschwartz 	(void) strcpy(node_data[this_node].name, pathname);
1011370Sschwartz 	mutex_init(&node_data[this_node].mutex, NULL, MUTEX_DRIVER, NULL);
1021370Sschwartz 	*index_p = this_node++;
1031370Sschwartz }
1041370Sschwartz 
1051370Sschwartz int
fpc_perfcnt_module_init(dev_info_t * fpc_dip,int * avail)1061370Sschwartz fpc_perfcnt_module_init(dev_info_t *fpc_dip, int *avail)
1071370Sschwartz {
1081370Sschwartz 	int i;
1091370Sschwartz 	dev_info_t *dip;
1101370Sschwartz 
1111370Sschwartz 	*avail = 0;
1121370Sschwartz 
1131370Sschwartz 	for (i = 1; i < MAX_REG_TYPES; i++) {
1141370Sschwartz 		first_reg_of_type[i] =
1151370Sschwartz 		    first_reg_of_type[i-1] + counters_per_type[i-1];
1161370Sschwartz 	}
1171370Sschwartz 
1181370Sschwartz 	/*
1191370Sschwartz 	 * Look thru first level of device tree only.
1201370Sschwartz 	 * Assume there can be no more than NUM_LEAVES nodes in the system.
1211370Sschwartz 	 */
1221370Sschwartz 	dip = ddi_root_node();
1231370Sschwartz 	for (dip = ddi_get_child(dip);
1241370Sschwartz 	    ((dip != NULL) && (this_node < NUM_LEAVES));
1251370Sschwartz 	    dip = ddi_get_next_sibling(dip)) {
1261370Sschwartz 		if (fpc_platform_node_init(dip, avail) != SUCCESS)
1271370Sschwartz 			return (DDI_FAILURE);
1281370Sschwartz 	}
1291370Sschwartz 
1301370Sschwartz 	return ((*avail) ? fpc_platform_module_init(fpc_dip) : DDI_FAILURE);
1311370Sschwartz }
1321370Sschwartz 
1331370Sschwartz int
fpc_perfcnt_module_fini(dev_info_t * dip)1341370Sschwartz fpc_perfcnt_module_fini(dev_info_t *dip)
1351370Sschwartz {
1361370Sschwartz 	int i;
1371370Sschwartz 
1381370Sschwartz 	for (i = 0; i < NUM_LEAVES; i++) {
1391370Sschwartz 		fpc_platform_node_fini(node_data[i].plat_data_p);
140*1691Sschwartz 		if (node_data[i].name != NULL) {
1411370Sschwartz 			kmem_free(node_data[i].name,
1421370Sschwartz 			    strlen(node_data[i].name) + 1);
143*1691Sschwartz 			mutex_destroy(&node_data[i].mutex);
144*1691Sschwartz 		}
1451370Sschwartz 	}
1461370Sschwartz 
1471370Sschwartz 	fpc_platform_module_fini(dip);
1481370Sschwartz 	return (DDI_SUCCESS);
1491370Sschwartz }
1501370Sschwartz 
1511370Sschwartz char
fpc_get_dev_name_by_number(int index)1521370Sschwartz *fpc_get_dev_name_by_number(int index)
1531370Sschwartz {
1541370Sschwartz 	return (node_data[index].name);
1551370Sschwartz }
1561370Sschwartz 
1571370Sschwartz void *
fpc_get_platform_data_by_number(int index)1581370Sschwartz fpc_get_platform_data_by_number(int index)
1591370Sschwartz {
1601370Sschwartz 	return (node_data[index].plat_data_p);
1611370Sschwartz }
1621370Sschwartz 
1631370Sschwartz 
1641370Sschwartz int
fpc_set_platform_data_by_number(int index,void * data_p)1651370Sschwartz fpc_set_platform_data_by_number(int index, void *data_p)
1661370Sschwartz {
1671370Sschwartz 	node_data[index].plat_data_p = data_p;
1681370Sschwartz 	return (SUCCESS);
1691370Sschwartz }
1701370Sschwartz 
1711370Sschwartz 
1721370Sschwartz static int
fpc_get_mutex_by_number(int index,kmutex_t ** mutex_pp)1731370Sschwartz fpc_get_mutex_by_number(int index, kmutex_t **mutex_pp)
1741370Sschwartz {
1751370Sschwartz 	*mutex_pp = &node_data[index].mutex;
1761370Sschwartz 	return (SUCCESS);
1771370Sschwartz }
1781370Sschwartz 
1791370Sschwartz 
1801370Sschwartz static int
fpc_get_counter_reg_index(fire_perfcnt_t regtype,int counter)1811370Sschwartz fpc_get_counter_reg_index(fire_perfcnt_t regtype, int counter)
1821370Sschwartz {
1831370Sschwartz 	FPC_DBG1(
1841370Sschwartz 	    "fpc_get_counter_reg_index: regtype:%d, counter:%d, bounds:%d\n",
1851370Sschwartz 	    regtype, counter, counters_per_type[regtype]);
1861370Sschwartz 	if (BOUNDS_CHECK_FAILS(counter, counters_per_type[regtype]))
1871370Sschwartz 		return (-1);
1881370Sschwartz 	FPC_DBG1("returning: %d\n", first_reg_of_type[regtype] + counter);
1891370Sschwartz 	return (first_reg_of_type[regtype] + counter);
1901370Sschwartz }
1911370Sschwartz 
1921370Sschwartz 
1931370Sschwartz /*
1941370Sschwartz  * Program a performance counter.
1951370Sschwartz  *
1961370Sschwartz  * reggroup is which type of counter.
1971370Sschwartz  * counter is the counter number.
1981370Sschwartz  * event is the event to program for that counter.
1991370Sschwartz  */
2001370Sschwartz int
fpc_perfcnt_program(int devnum,fire_perfcnt_t reggroup,uint64_t new_events)2011370Sschwartz fpc_perfcnt_program(int devnum, fire_perfcnt_t reggroup,
2021370Sschwartz     uint64_t new_events)
2031370Sschwartz {
2041370Sschwartz 	int counter_index;
2051370Sschwartz 	fire_perfreg_handle_t firehdl;
2061370Sschwartz 	kmutex_t *mutex_p;
2071370Sschwartz 	uint64_t old_events;
2081370Sschwartz 	int rval = SUCCESS;
2091370Sschwartz 	uint64_t zero = 0ull;
2101370Sschwartz 	int num_counters, counter;
2111370Sschwartz 
2121370Sschwartz 	FPC_DBG1("fpc_perfcnt_program enter:\n");
2131370Sschwartz 	FPC_DBG1("  devnum:%d, reggroup:%d, new_events:0x%" PRIx64 "\n",
2141370Sschwartz 	    devnum, reggroup, new_events);
2151370Sschwartz 
2161370Sschwartz 	if ((firehdl = fpc_get_perfreg_handle(devnum)) ==
2171370Sschwartz 	    (fire_perfreg_handle_t)-1)
2181370Sschwartz 		return (EIO);
2191370Sschwartz 
2201370Sschwartz 	num_counters = counters_per_type[reggroup];
2211370Sschwartz 
2221370Sschwartz 	if (fpc_get_mutex_by_number(devnum, &mutex_p) != DDI_SUCCESS) {
2231370Sschwartz 		(void) fpc_free_counter_handle(firehdl);
2241370Sschwartz 		return (EIO);
2251370Sschwartz 	}
2261370Sschwartz 
2271370Sschwartz 	mutex_enter(mutex_p);
2281370Sschwartz 
2291370Sschwartz 	if ((rval = fpc_event_io(firehdl, reggroup, &old_events, IS_READ)) !=
2301370Sschwartz 	    SUCCESS) {
2311370Sschwartz 		FPC_DBG1("Read of old event data failed, group:%d\n", reggroup);
2321370Sschwartz 		goto done_pgm;
2331370Sschwartz 	}
2341370Sschwartz 
2351370Sschwartz 	for (counter = 0; counter < num_counters; counter++) {
2361370Sschwartz 
2371370Sschwartz 		counter_index = fpc_get_counter_reg_index(reggroup, counter);
2381370Sschwartz 
2391370Sschwartz 		if ((old_events & event_field_mask[counter_index]) ==
2401370Sschwartz 		    (new_events & event_field_mask[counter_index]))
2411370Sschwartz 			continue;
2421370Sschwartz 
2431370Sschwartz 		FPC_DBG1("Zeroing counter %d\n", counter_index);
2441370Sschwartz 		if ((rval = fpc_counter_io(firehdl, reggroup, counter_index,
2451370Sschwartz 		    &zero, IS_WRITE)) != SUCCESS)
2461370Sschwartz 			goto done_pgm;
2471370Sschwartz 	}
2481370Sschwartz 
2491370Sschwartz 	if (old_events != new_events) {
2501370Sschwartz 		if ((rval =
2511370Sschwartz 		    fpc_event_io(firehdl, reggroup, &new_events, IS_WRITE)) !=
2521370Sschwartz 		    SUCCESS) {
2531370Sschwartz 			FPC_DBG1("Write of new event data failed, group:%d\n",
2541370Sschwartz 			    reggroup);
2551370Sschwartz 			goto done_pgm;
2561370Sschwartz 		}
2571370Sschwartz 	}
2581370Sschwartz done_pgm:
2591370Sschwartz 	mutex_exit(mutex_p);
2601370Sschwartz 	(void) fpc_free_counter_handle(firehdl);
2611370Sschwartz 	return (rval);
2621370Sschwartz }
2631370Sschwartz 
2641370Sschwartz 
2651370Sschwartz /*
2661370Sschwartz  * Read a performance counter.
2671370Sschwartz  *
2681370Sschwartz  * reggroup is which type of counter.
2691370Sschwartz  * event_p returns the event programmed for that counter.
2701370Sschwartz  * values returns the counter values.
2711370Sschwartz  */
2721370Sschwartz int
fpc_perfcnt_read(int devnum,fire_perfcnt_t reggroup,uint64_t * event_p,uint64_t values[NUM_MAX_COUNTERS])2731370Sschwartz fpc_perfcnt_read(int devnum, fire_perfcnt_t reggroup,
2741370Sschwartz     uint64_t *event_p, uint64_t values[NUM_MAX_COUNTERS])
2751370Sschwartz {
2761370Sschwartz 	fire_perfreg_handle_t firehdl;
2771370Sschwartz 	int counter_index;
2781370Sschwartz 	kmutex_t *mutex_p;
2791370Sschwartz 	int rval;
2801370Sschwartz 	int num_counters, counter;
2811370Sschwartz 
2821370Sschwartz 	FPC_DBG1("fpc_perfcnt_read: devnum:%d\n", devnum);
2831370Sschwartz 	num_counters = counters_per_type[reggroup];
2841370Sschwartz 
2851370Sschwartz 	if ((firehdl = fpc_get_perfreg_handle(devnum)) ==
2861370Sschwartz 	    (fire_perfreg_handle_t)-1)
2871370Sschwartz 		return (EIO);
2881370Sschwartz 
2891370Sschwartz 	if (fpc_get_mutex_by_number(devnum, &mutex_p) != DDI_SUCCESS)
2901370Sschwartz 		return (EIO);
2911370Sschwartz 
2921370Sschwartz 	mutex_enter(mutex_p);
2931370Sschwartz 
2941370Sschwartz 	if ((rval = fpc_event_io(firehdl, reggroup, event_p, IS_READ)) !=
2951370Sschwartz 	    SUCCESS)
2961370Sschwartz 		goto done_read;
2971370Sschwartz 
2981370Sschwartz 	for (counter = 0; counter < num_counters; counter++) {
2991370Sschwartz 		counter_index = fpc_get_counter_reg_index(reggroup, counter);
3001370Sschwartz 
3011370Sschwartz 		if ((rval = fpc_counter_io(firehdl, reggroup, counter_index,
3021370Sschwartz 		    &values[counter], IS_READ)) != SUCCESS)
3031370Sschwartz 			goto done_read;
3041370Sschwartz 
3051370Sschwartz 		FPC_DBG1("Read_counter %d / %d, status:%d, value returned:0x%"
3061370Sschwartz 		    PRIx64 "\n", reggroup, counter, rval, values[counter]);
3071370Sschwartz 	}
3081370Sschwartz 
3091370Sschwartz done_read:
3101370Sschwartz 	mutex_exit(mutex_p);
3111370Sschwartz 	(void) fpc_free_counter_handle(firehdl);
3121370Sschwartz 	return (rval);
3131370Sschwartz }
314