xref: /onnv-gate/usr/src/uts/sun4u/chicago/io/fpc/fpc-impl-4u.c (revision 4397:8359c43a28c3)
14249Sschwartz /*
24249Sschwartz  * CDDL HEADER START
34249Sschwartz  *
44249Sschwartz  * The contents of this file are subject to the terms of the
54249Sschwartz  * Common Development and Distribution License (the "License").
64249Sschwartz  * You may not use this file except in compliance with the License.
74249Sschwartz  *
84249Sschwartz  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
94249Sschwartz  * or http://www.opensolaris.org/os/licensing.
104249Sschwartz  * See the License for the specific language governing permissions
114249Sschwartz  * and limitations under the License.
124249Sschwartz  *
134249Sschwartz  * When distributing Covered Code, include this CDDL HEADER in each
144249Sschwartz  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
154249Sschwartz  * If applicable, add the following below this CDDL HEADER, with the
164249Sschwartz  * fields enclosed by brackets "[]" replaced with your own identifying
174249Sschwartz  * information: Portions Copyright [yyyy] [name of copyright owner]
184249Sschwartz  *
194249Sschwartz  * CDDL HEADER END
204249Sschwartz  */
214249Sschwartz 
224249Sschwartz /*
23*4397Sschwartz  * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
244249Sschwartz  * Use is subject to license terms.
254249Sschwartz  */
264249Sschwartz 
274249Sschwartz #pragma ident	"%Z%%M%	%I%	%E% SMI"
284249Sschwartz 
294249Sschwartz #include <sys/file.h>
304249Sschwartz #include <sys/sunndi.h>
314249Sschwartz #include <sys/sunddi.h>
324249Sschwartz #include <sys/sunldi.h>
334249Sschwartz #include <io/px/px_regs.h>
344249Sschwartz #include <sys/pci_tools.h>
354249Sschwartz #include <fpc.h>
364249Sschwartz #include <fpc-impl.h>
374249Sschwartz 
384249Sschwartz #define	CHIP_COMPATIBLE_NAME	"pciex108e,80f0"
394249Sschwartz #define	BANK_ADDR_MASK		0x7FFFFF
404249Sschwartz 
414249Sschwartz #define	OPEN_FLAGS (FREAD | FWRITE)
424249Sschwartz 
434249Sschwartz #define	PCIE_BANK	0
444249Sschwartz #define	JBUS_BANK	1
454249Sschwartz 
464249Sschwartz typedef struct px_regs {
474249Sschwartz 	uint32_t addr_hi;
484249Sschwartz 	uint32_t addr_lo;
494249Sschwartz 	uint32_t size_hi;
504249Sschwartz 	uint32_t size_lo;
514249Sschwartz } px_regs_t;
524249Sschwartz 
534249Sschwartz /* There is one of these for every root nexus device found */
544249Sschwartz typedef struct fire4u_specific {
554249Sschwartz 	char *nodename;
564249Sschwartz 	uintptr_t jbus_bank_base;
574249Sschwartz } fire4u_specific_t;
584249Sschwartz 
594249Sschwartz typedef struct fire_counter_handle_impl {
604249Sschwartz 	ldi_handle_t devhandle;
614249Sschwartz 	fire4u_specific_t *devspec; /* Points to proper one for specific dev. */
624249Sschwartz } fire_counter_handle_impl_t;
634249Sschwartz 
644249Sschwartz static uint64_t counter_select_offsets[] = {
654249Sschwartz 	JBC_PERFORMANCE_COUNTER_SELECT,
664249Sschwartz 	IMU_PERFORMANCE_COUNTER_SELECT,
674249Sschwartz 	MMU_PERFORMANCE_COUNTER_SELECT,
684249Sschwartz 	TLU_PERFORMANCE_COUNTER_SELECT,
694249Sschwartz 	LPU_LINK_PERFORMANCE_COUNTER_SELECT
704249Sschwartz };
714249Sschwartz 
724249Sschwartz /*
734249Sschwartz  * The following event and offset arrays is organized by grouping in major
744249Sschwartz  * order the fire_perfcnt_t register types, and in minor order the register
754249Sschwartz  * numbers within that type.
764249Sschwartz  */
774249Sschwartz 
784249Sschwartz static uint64_t counter_reg_offsets[] = {
794249Sschwartz 	JBC_PERFORMANCE_COUNTER_ZERO,
804249Sschwartz 	JBC_PERFORMANCE_COUNTER_ONE,
814249Sschwartz 	IMU_PERFORMANCE_COUNTER_ZERO,
824249Sschwartz 	IMU_PERFORMANCE_COUNTER_ONE,
834249Sschwartz 	MMU_PERFORMANCE_COUNTER_ZERO,
844249Sschwartz 	MMU_PERFORMANCE_COUNTER_ONE,
854249Sschwartz 	TLU_PERFORMANCE_COUNTER_ZERO,
864249Sschwartz 	TLU_PERFORMANCE_COUNTER_ONE,
874249Sschwartz 	TLU_PERFORMANCE_COUNTER_TWO,
884249Sschwartz 	LPU_LINK_PERFORMANCE_COUNTER1,
894249Sschwartz 	LPU_LINK_PERFORMANCE_COUNTER2
904249Sschwartz };
914249Sschwartz 
924249Sschwartz /*
934249Sschwartz  * Add the following to one of the LPU_LINK_PERFORMANCE_COUNTERx offsets to
944249Sschwartz  * write a value to that counter.
954249Sschwartz  */
964249Sschwartz #define	LPU_LINK_PERFCTR_WRITE_OFFSET	0x8
974249Sschwartz 
984249Sschwartz /*
994249Sschwartz  * Note that LPU_LINK_PERFORMANCE_COUNTER_CONTROL register is hard-reset to
1004249Sschwartz  * zeros and this is the value we want.  This register isn't touched by this
1014249Sschwartz  * module, and as long as it remains untouched by other modules we're OK.
1024249Sschwartz  */
1034249Sschwartz 
1044249Sschwartz static ldi_ident_t ldi_identifier;
1054249Sschwartz static boolean_t ldi_identifier_valid = B_FALSE;
1064249Sschwartz static cred_t *credentials = NULL;
1074249Sschwartz 
1084249Sschwartz /* Called by _init to determine if it is OK to install driver. */
1094249Sschwartz int
fpc_platform_check()1104249Sschwartz fpc_platform_check()
1114249Sschwartz {
1124249Sschwartz 	return (SUCCESS);
1134249Sschwartz }
1144249Sschwartz 
1154249Sschwartz /* Called during attach to do module-wide initialization. */
1164249Sschwartz int
fpc_platform_module_init(dev_info_t * dip)1174249Sschwartz fpc_platform_module_init(dev_info_t *dip)
1184249Sschwartz {
1194249Sschwartz 	int status;
1204249Sschwartz 
1214249Sschwartz 	credentials = crget();
1224249Sschwartz 	status = ldi_ident_from_dip(dip, &ldi_identifier);
1234249Sschwartz 	if (status == 0)
1244249Sschwartz 		ldi_identifier_valid = B_TRUE;
1254249Sschwartz 	return ((status == 0) ? DDI_SUCCESS : DDI_FAILURE);
1264249Sschwartz }
1274249Sschwartz 
1284249Sschwartz int
fpc_platform_node_init(dev_info_t * dip,int * avail)1294249Sschwartz fpc_platform_node_init(dev_info_t *dip, int *avail)
1304249Sschwartz {
1314249Sschwartz 	int index;
1324249Sschwartz 	char *name;
1334249Sschwartz 	int nodename_size;
1344249Sschwartz 	char *nodename = NULL;
1354249Sschwartz 	fire4u_specific_t *platform_specific_data = NULL;
1364249Sschwartz 	char *compatible = NULL;
1374249Sschwartz 	px_regs_t *regs_p = NULL;
1384249Sschwartz 	int regs_length = 0;
1394249Sschwartz 
1404249Sschwartz 	if (ddi_prop_lookup_string(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
1414249Sschwartz 	    "compatible", &compatible) != DDI_PROP_SUCCESS)
1424249Sschwartz 		return (DDI_SUCCESS);
1434249Sschwartz 
1444249Sschwartz 	if (strcmp(compatible, CHIP_COMPATIBLE_NAME) != 0) {
1454249Sschwartz 		ddi_prop_free(compatible);
1464249Sschwartz 		return (DDI_SUCCESS);
1474249Sschwartz 	}
1484249Sschwartz 	ddi_prop_free(compatible);
1494249Sschwartz 
1504249Sschwartz 	fpc_common_node_setup(dip, &index);
1514249Sschwartz 
1524249Sschwartz 	name = fpc_get_dev_name_by_number(index);
1534249Sschwartz 	nodename_size = strlen(name) + strlen(PCI_MINOR_REG) + 2;
1544249Sschwartz 	nodename = kmem_zalloc(nodename_size, KM_SLEEP);
1554249Sschwartz 
1564249Sschwartz 	platform_specific_data =
157*4397Sschwartz 	    kmem_zalloc(sizeof (fire4u_specific_t), KM_SLEEP);
1584249Sschwartz 
1594249Sschwartz 	(void) strcpy(nodename, name);
1604249Sschwartz 	(void) strcat(nodename, ":");
1614249Sschwartz 	(void) strcat(nodename, PCI_MINOR_REG);
1624249Sschwartz 	platform_specific_data->nodename = nodename;
1634249Sschwartz 
1644249Sschwartz 	/* Get register banks. */
1654249Sschwartz 	if (ddi_getlongprop(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
166*4397Sschwartz 	    "reg", (caddr_t)&regs_p, &regs_length) != DDI_SUCCESS) {
1674249Sschwartz 		goto bad_regs_p;
1684249Sschwartz 	}
1694249Sschwartz 
1704249Sschwartz 	if ((regs_length / sizeof (px_regs_t)) < 2) {
1714249Sschwartz 		goto bad_regs_length;
1724249Sschwartz 	}
1734249Sschwartz 
1744249Sschwartz 	platform_specific_data->jbus_bank_base =
1754249Sschwartz 	    regs_p[JBUS_BANK].addr_lo & BANK_ADDR_MASK;
1764249Sschwartz 
1774249Sschwartz 	kmem_free(regs_p, regs_length);
1784249Sschwartz 
1794249Sschwartz 	if (index == 0)
1804249Sschwartz 		*avail |= (PCIE_A_REGS_AVAIL | JBUS_REGS_AVAIL);
1814249Sschwartz 	else
1824249Sschwartz 		*avail |= PCIE_B_REGS_AVAIL;
1834249Sschwartz 
1844249Sschwartz 	(void) fpc_set_platform_data_by_number(index, platform_specific_data);
1854249Sschwartz 
1864249Sschwartz 	return (DDI_SUCCESS);
1874249Sschwartz 
1884249Sschwartz bad_regs_length:
1894249Sschwartz 	if (regs_p)
1904249Sschwartz 		kmem_free(regs_p, regs_length);
1914249Sschwartz bad_regs_p:
1924249Sschwartz 	if (platform_specific_data)
1934249Sschwartz 		kmem_free(platform_specific_data, sizeof (fire4u_specific_t));
1944249Sschwartz 	if (nodename)
1954249Sschwartz 		kmem_free(nodename, nodename_size);
1964249Sschwartz 
1974249Sschwartz 	return (DDI_FAILURE);
1984249Sschwartz }
1994249Sschwartz 
2004249Sschwartz void
fpc_platform_node_fini(void * arg)2014249Sschwartz fpc_platform_node_fini(void *arg)
2024249Sschwartz {
2034249Sschwartz 	fire4u_specific_t *plat_arg = (fire4u_specific_t *)arg;
2044249Sschwartz 	if (plat_arg == NULL)
2054249Sschwartz 		return;
2064249Sschwartz 	if (plat_arg->nodename)
2074249Sschwartz 		kmem_free(plat_arg->nodename, strlen(plat_arg->nodename)+1);
2084249Sschwartz 	kmem_free(plat_arg, sizeof (fire4u_specific_t));
2094249Sschwartz }
2104249Sschwartz 
2114249Sschwartz /*ARGSUSED*/
2124249Sschwartz void
fpc_platform_module_fini(dev_info_t * dip)2134249Sschwartz fpc_platform_module_fini(dev_info_t *dip)
2144249Sschwartz {
2154249Sschwartz 	if (ldi_identifier_valid)
2164249Sschwartz 		ldi_ident_release(ldi_identifier);
2174249Sschwartz 	if (credentials)
2184249Sschwartz 		crfree(credentials);
2194249Sschwartz }
2204249Sschwartz 
2214249Sschwartz fire_perfreg_handle_t
fpc_get_perfreg_handle(int devnum)2224249Sschwartz fpc_get_perfreg_handle(int devnum)
2234249Sschwartz {
2244249Sschwartz 	int rval = EINVAL;
2254249Sschwartz 
2264249Sschwartz 	fire_counter_handle_impl_t *handle_impl =
2274249Sschwartz 	    kmem_zalloc(sizeof (fire_counter_handle_impl_t), KM_SLEEP);
2284249Sschwartz 
2294249Sschwartz 	if ((handle_impl->devspec =
2304249Sschwartz 	    fpc_get_platform_data_by_number(devnum)) != NULL) {
2314249Sschwartz 		rval = ldi_open_by_name(handle_impl->devspec->nodename,
2324249Sschwartz 		    OPEN_FLAGS, credentials, &handle_impl->devhandle,
2334249Sschwartz 		    ldi_identifier);
2344249Sschwartz 	}
2354249Sschwartz 
2364249Sschwartz 	if (rval != SUCCESS) {
2374249Sschwartz 		kmem_free(handle_impl, sizeof (fire_counter_handle_impl_t));
2384249Sschwartz 		return ((fire_perfreg_handle_t)-1);
2394249Sschwartz 	} else {
2404249Sschwartz 		return ((fire_perfreg_handle_t)handle_impl);
2414249Sschwartz 	}
2424249Sschwartz }
2434249Sschwartz 
2444249Sschwartz int
fpc_free_counter_handle(fire_perfreg_handle_t handle)2454249Sschwartz fpc_free_counter_handle(fire_perfreg_handle_t handle)
2464249Sschwartz {
2474249Sschwartz 	fire_counter_handle_impl_t *handle_impl =
2484249Sschwartz 	    (fire_counter_handle_impl_t *)handle;
2494249Sschwartz 	(void) ldi_close(handle_impl->devhandle, OPEN_FLAGS, credentials);
2504249Sschwartz 	kmem_free(handle_impl, sizeof (fire_counter_handle_impl_t));
2514249Sschwartz 	return (SUCCESS);
2524249Sschwartz }
2534249Sschwartz 
2544249Sschwartz int
fpc_event_io(fire_perfreg_handle_t handle,fire_perfcnt_t group,uint64_t * reg_data,boolean_t is_write)2554249Sschwartz fpc_event_io(fire_perfreg_handle_t handle, fire_perfcnt_t group,
2564249Sschwartz     uint64_t *reg_data, boolean_t is_write)
2574249Sschwartz {
2584249Sschwartz 	int rval;
2594249Sschwartz 	int ioctl_rval;
2604249Sschwartz 	pcitool_reg_t prg;
2614249Sschwartz 	fire_counter_handle_impl_t *handle_impl =
2624249Sschwartz 	    (fire_counter_handle_impl_t *)handle;
2634249Sschwartz 	int cmd = is_write ? PCITOOL_NEXUS_SET_REG : PCITOOL_NEXUS_GET_REG;
2644249Sschwartz 
265*4397Sschwartz 	prg.user_version = PCITOOL_VERSION;
2664249Sschwartz 
2674249Sschwartz 	if (group == jbc) {
2684249Sschwartz 		prg.barnum = JBUS_BANK;
2694249Sschwartz 		prg.offset = counter_select_offsets[group] -
2704249Sschwartz 		    handle_impl->devspec->jbus_bank_base;
2714249Sschwartz 	} else {
2724249Sschwartz 		prg.barnum = PCIE_BANK;
2734249Sschwartz 
2744249Sschwartz 		/*
2754249Sschwartz 		 * Note that a pcie_bank_base isn't needed.  Pcie register
2764249Sschwartz 		 * offsets are already relative to the start of their bank.  No
2774249Sschwartz 		 * base needs to be subtracted to get the relative offset that
2784249Sschwartz 		 * pcitool ioctls want.
2794249Sschwartz 		 */
2804249Sschwartz 		prg.offset = counter_select_offsets[group];
2814249Sschwartz 	}
2824249Sschwartz 	prg.acc_attr = PCITOOL_ACC_ATTR_SIZE_8 | PCITOOL_ACC_ATTR_ENDN_BIG;
2834249Sschwartz 	prg.data = *reg_data;
2844249Sschwartz 
2854249Sschwartz 	/* Read original value. */
2864249Sschwartz 	if (((rval = ldi_ioctl(handle_impl->devhandle, cmd, (intptr_t)&prg,
2874249Sschwartz 	    FKIOCTL, credentials, &ioctl_rval)) == SUCCESS) && (!is_write)) {
2884249Sschwartz 		*reg_data = prg.data;
2894249Sschwartz 	}
2904249Sschwartz 
2914249Sschwartz 	return (rval);
2924249Sschwartz }
2934249Sschwartz 
2944249Sschwartz int
fpc_counter_io(fire_perfreg_handle_t handle,fire_perfcnt_t group,int counter_index,uint64_t * value,boolean_t is_write)2954249Sschwartz fpc_counter_io(fire_perfreg_handle_t handle, fire_perfcnt_t group,
2964249Sschwartz     int counter_index, uint64_t *value, boolean_t is_write)
2974249Sschwartz {
2984249Sschwartz 	int rval;
2994249Sschwartz 	int ioctl_rval;
3004249Sschwartz 	pcitool_reg_t prg;
3014249Sschwartz 	fire_counter_handle_impl_t *handle_impl =
3024249Sschwartz 	    (fire_counter_handle_impl_t *)handle;
3034249Sschwartz 	int command =
3044249Sschwartz 	    (is_write) ? PCITOOL_NEXUS_SET_REG : PCITOOL_NEXUS_GET_REG;
3054249Sschwartz 
306*4397Sschwartz 	prg.user_version = PCITOOL_VERSION;
3074249Sschwartz 	/*
3084249Sschwartz 	 * Note that stated PCIE offsets are relative to the beginning of their
3094249Sschwartz 	 * register bank, while JBUS offsets are absolute.
3104249Sschwartz 	 */
3114249Sschwartz 	if (group == jbc) {
3124249Sschwartz 		prg.barnum = JBUS_BANK;
3134249Sschwartz 		prg.offset = counter_reg_offsets[counter_index] -
3144249Sschwartz 		    handle_impl->devspec->jbus_bank_base;
3154249Sschwartz 	} else {
3164249Sschwartz 		prg.barnum = PCIE_BANK;
3174249Sschwartz 		prg.offset = counter_reg_offsets[counter_index];
3184249Sschwartz 	}
3194249Sschwartz 
3204249Sschwartz 	if ((group == lpu) && (is_write)) {
3214249Sschwartz 		prg.offset += LPU_LINK_PERFCTR_WRITE_OFFSET;
3224249Sschwartz 	}
3234249Sschwartz 
3244249Sschwartz 	prg.acc_attr = PCITOOL_ACC_ATTR_SIZE_8 | PCITOOL_ACC_ATTR_ENDN_BIG;
3254249Sschwartz 	prg.data = *value;
3264249Sschwartz 
3274249Sschwartz 	if (((rval = ldi_ioctl(handle_impl->devhandle, command, (intptr_t)&prg,
3284249Sschwartz 	    FKIOCTL, credentials, &ioctl_rval)) == SUCCESS) && (!is_write)) {
3294249Sschwartz 		*value = prg.data;
3304249Sschwartz 	}
3314249Sschwartz 
3324249Sschwartz 	return (rval);
3334249Sschwartz }
334