xref: /onnv-gate/usr/src/uts/sun4v/io/fpc/fpc-impl-4v.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/file.h>
301370Sschwartz #include <sys/hypervisor_api.h>
31*1691Sschwartz #include <sys/hsvc.h>
321370Sschwartz #include <sys/sunndi.h>
331370Sschwartz #include <fpc.h>
341370Sschwartz #include <fpc-impl.h>
351370Sschwartz #include <fpc-impl-4v.h>
361370Sschwartz 
371370Sschwartz #define	PCIE_ROOTNEX_COMPATIBLE_NAME	"SUNW,sun4v-pci"
381370Sschwartz 
39*1691Sschwartz #define	FPC_MODULE_NAME			"fpc"
40*1691Sschwartz #define	FPC_REQ_MAJOR_VER		1
41*1691Sschwartz #define	FPC_REQ_MINOR_VER		0
42*1691Sschwartz 
43*1691Sschwartz static hsvc_info_t fpc_hsvc = {
44*1691Sschwartz 	HSVC_REV_1,
45*1691Sschwartz 	NULL,
46*1691Sschwartz 	HSVC_GROUP_FIRE_PERF,
47*1691Sschwartz 	FPC_REQ_MAJOR_VER,
48*1691Sschwartz 	FPC_REQ_MINOR_VER,
49*1691Sschwartz 	FPC_MODULE_NAME
50*1691Sschwartz };
51*1691Sschwartz 
52*1691Sschwartz static int hyp_regd_users = 0;
53*1691Sschwartz static uint64_t	fpc_sup_minor;
54*1691Sschwartz 
551370Sschwartz /*
561370Sschwartz  * The following typedef is used to represent a
571370Sschwartz  * 1275 "reg" property of a PCI nexus.
581370Sschwartz  */
591370Sschwartz typedef struct nexus_regspec {
601370Sschwartz 	uint64_t phys_addr;
611370Sschwartz 	uint64_t size;
621370Sschwartz } nexus_regspec_t;
631370Sschwartz 
641370Sschwartz static uint64_t counter_select_index[] = {
651370Sschwartz 	HVIO_FIRE_PERFREG_JBC_SEL,
661370Sschwartz 	HVIO_FIRE_PERFREG_PCIE_IMU_SEL,
671370Sschwartz 	HVIO_FIRE_PERFREG_PCIE_MMU_SEL,
681370Sschwartz 	HVIO_FIRE_PERFREG_PCIE_TLU_SEL,
691370Sschwartz 	HVIO_FIRE_PERFREG_PCIE_LNK_SEL
701370Sschwartz };
711370Sschwartz 
721370Sschwartz /*
731370Sschwartz  * The following event and offset arrays is organized by grouping in major
741370Sschwartz  * order the fire_perfcnt_t register types, and in minor order the register
751370Sschwartz  * numbers within that type.
761370Sschwartz  */
771370Sschwartz 
781370Sschwartz /*
791370Sschwartz  * This table maps the above order into the hypervisor interface register
801370Sschwartz  * indices.
811370Sschwartz  */
821370Sschwartz static uint64_t counter_reg_index[] = {
831370Sschwartz 	HVIO_FIRE_PERFREG_JBC_CNT0,
841370Sschwartz 	HVIO_FIRE_PERFREG_JBC_CNT1,
851370Sschwartz 	HVIO_FIRE_PERFREG_PCIE_IMU_CNT0,
861370Sschwartz 	HVIO_FIRE_PERFREG_PCIE_IMU_CNT1,
871370Sschwartz 	HVIO_FIRE_PERFREG_PCIE_MMU_CNT0,
881370Sschwartz 	HVIO_FIRE_PERFREG_PCIE_MMU_CNT1,
891370Sschwartz 	HVIO_FIRE_PERFREG_PCIE_TLU_CNT0,
901370Sschwartz 	HVIO_FIRE_PERFREG_PCIE_TLU_CNT1,
911370Sschwartz 	HVIO_FIRE_PERFREG_PCIE_TLU_CNT2,
921370Sschwartz 	HVIO_FIRE_PERFREG_PCIE_LNK_CNT1,
931370Sschwartz 	HVIO_FIRE_PERFREG_PCIE_LNK_CNT2
941370Sschwartz };
951370Sschwartz 
96*1691Sschwartz /* Called by _init to determine if it is OK to install driver. */
97*1691Sschwartz int
fpc_platform_check()98*1691Sschwartz fpc_platform_check()
99*1691Sschwartz {
100*1691Sschwartz 	int regstat;
101*1691Sschwartz 
102*1691Sschwartz 	if ((regstat = hsvc_register(&fpc_hsvc, &fpc_sup_minor)) == SUCCESS) {
103*1691Sschwartz 		(void) hsvc_unregister(&fpc_hsvc);
104*1691Sschwartz 	}
105*1691Sschwartz 	fpc_sup_minor = 0;
106*1691Sschwartz 	return (regstat);
107*1691Sschwartz }
108*1691Sschwartz 
109*1691Sschwartz /* Called during attach to do module-wide initialization. */
1101370Sschwartz /*ARGSUSED*/
1111370Sschwartz int
fpc_platform_module_init(dev_info_t * dip)1121370Sschwartz fpc_platform_module_init(dev_info_t *dip)
1131370Sschwartz {
1141370Sschwartz 	return (DDI_SUCCESS);
1151370Sschwartz }
1161370Sschwartz 
1171370Sschwartz int
fpc_platform_node_init(dev_info_t * dip,int * avail)1181370Sschwartz fpc_platform_node_init(dev_info_t *dip, int *avail)
1191370Sschwartz {
1201370Sschwartz 	nexus_regspec_t	*rp;
1211370Sschwartz 	uint_t reglen;
1221370Sschwartz 	devhandle_t dev_hdl;
123*1691Sschwartz 	int regstat;
1241370Sschwartz 	int index;
1251370Sschwartz 	boolean_t is_root_pcie_nexus;
1261370Sschwartz 	uint64_t dummy_data;
1271370Sschwartz 	char *name = NULL;
1281370Sschwartz 	boolean_t jbus_regs_avail;
1291370Sschwartz 	boolean_t pcie_regs_avail;
1301370Sschwartz 
1311370Sschwartz 	if (ddi_prop_lookup_string(DDI_DEV_T_ANY, dip,
1321370Sschwartz 	    DDI_PROP_DONTPASS, "compatible", &name) != DDI_PROP_SUCCESS)
1331370Sschwartz 		return (DDI_SUCCESS);
1341370Sschwartz 
1351370Sschwartz 	is_root_pcie_nexus = (strcmp(name, PCIE_ROOTNEX_COMPATIBLE_NAME) == 0);
1361370Sschwartz 	ddi_prop_free(name);
1371370Sschwartz 	if (!is_root_pcie_nexus)
1381370Sschwartz 		return (DDI_SUCCESS);
1391370Sschwartz 
1401370Sschwartz 	if (ddi_prop_lookup_byte_array(DDI_DEV_T_ANY, dip,
1411370Sschwartz 	    DDI_PROP_DONTPASS, "reg", (uchar_t **)&rp, &reglen) !=
1421370Sschwartz 	    DDI_PROP_SUCCESS)
1431370Sschwartz 		return (DDI_FAILURE);
1441370Sschwartz 
1451370Sschwartz 	/*
1461370Sschwartz 	 * Initilize device handle. The device handle uniquely
1471370Sschwartz 	 * identifies a SUN4V device. It consists of the lower 28-bits
1481370Sschwartz 	 * of the hi-cell of the first entry of the SUN4V device's
1491370Sschwartz 	 * "reg" property as defined by the SUN4V Bus Binding to Open
1501370Sschwartz 	 * Firmware.
1511370Sschwartz 	 */
1521370Sschwartz 	dev_hdl = (devhandle_t)((rp->phys_addr >> 32) & DEVHDLE_MASK);
1531370Sschwartz 
1541370Sschwartz 	ddi_prop_free(rp);
1551370Sschwartz 
156*1691Sschwartz 	/*
157*1691Sschwartz 	 * If this is the first time through here, negotiate with hypervisor
158*1691Sschwartz 	 * that it has the services needed to operate.  Don't do this in _init
159*1691Sschwartz 	 * since we may want to modload the driver without attaching,
160*1691Sschwartz 	 * for debugging purposes.
161*1691Sschwartz 	 *
162*1691Sschwartz 	 * Note that this is another way of weeding out unsupported platforms
163*1691Sschwartz 	 */
164*1691Sschwartz 	if (hyp_regd_users == 0) {
165*1691Sschwartz 		regstat = hsvc_register(&fpc_hsvc, &fpc_sup_minor);
166*1691Sschwartz 		if (regstat != SUCCESS) {
167*1691Sschwartz 			/*
168*1691Sschwartz 			 * Fail silently since we don't want to print an error
169*1691Sschwartz 			 * on future platforms which don't support this driver.
170*1691Sschwartz 			 */
171*1691Sschwartz 			return (DDI_FAILURE);
172*1691Sschwartz 		}
173*1691Sschwartz 	}
174*1691Sschwartz 	hyp_regd_users++;
175*1691Sschwartz 
1761370Sschwartz 	/* See which register sets are usable from this node. */
1771370Sschwartz 	jbus_regs_avail = (fpc_event_io(
1781370Sschwartz 	    (fire_perfreg_handle_t)dev_hdl, jbc, &dummy_data, IS_READ) ==
1791370Sschwartz 	    SUCCESS);
1801370Sschwartz 	pcie_regs_avail = (fpc_event_io(
1811370Sschwartz 	    (fire_perfreg_handle_t)dev_hdl, imu, &dummy_data, IS_READ) ==
1821370Sschwartz 	    SUCCESS);
1831370Sschwartz 
1841370Sschwartz 	/* Nothing usable at this node. */
1851370Sschwartz 	if ((!jbus_regs_avail) && (!pcie_regs_avail))
1861370Sschwartz 		return (DDI_SUCCESS);
1871370Sschwartz 
1881370Sschwartz 	fpc_common_node_setup(dip, &index);
1891370Sschwartz 	if (pcie_regs_avail)
1901370Sschwartz 		*avail |=
1911370Sschwartz 		    ((index == 0) ? PCIE_A_REGS_AVAIL : PCIE_B_REGS_AVAIL);
1921370Sschwartz 	if (jbus_regs_avail) {
1931370Sschwartz 		*avail |= JBUS_REGS_AVAIL;
1941370Sschwartz 		if (index != 0)
1951370Sschwartz 			cmn_err(CE_WARN,
1961370Sschwartz 			    "fpc: JBUS regs available on device idx %d!\n",
1971370Sschwartz 			    index);
1981370Sschwartz 	}
1991370Sschwartz 
2001370Sschwartz 	(void) fpc_set_platform_data_by_number(index, (void *)dev_hdl);
2011370Sschwartz 
2021370Sschwartz 	return (DDI_SUCCESS);
2031370Sschwartz }
2041370Sschwartz 
2051370Sschwartz /*ARGSUSED*/
2061370Sschwartz void
fpc_platform_node_fini(void * arg)2071370Sschwartz fpc_platform_node_fini(void *arg)
2081370Sschwartz {
209*1691Sschwartz 	if (--hyp_regd_users == 0)
210*1691Sschwartz 		(void) hsvc_unregister(&fpc_hsvc);
2111370Sschwartz }
2121370Sschwartz 
2131370Sschwartz /*ARGSUSED*/
2141370Sschwartz void
fpc_platform_module_fini(dev_info_t * dip)2151370Sschwartz fpc_platform_module_fini(dev_info_t *dip)
2161370Sschwartz {
2171370Sschwartz }
2181370Sschwartz 
2191370Sschwartz fire_perfreg_handle_t
fpc_get_perfreg_handle(int devnum)2201370Sschwartz fpc_get_perfreg_handle(int devnum)
2211370Sschwartz {
2221370Sschwartz 	void *platform_specific_data;
2231370Sschwartz 
2241370Sschwartz 	if ((platform_specific_data =
2251370Sschwartz 	    fpc_get_platform_data_by_number(devnum)) == NULL)
2261370Sschwartz 		return ((fire_perfreg_handle_t)-1);
2271370Sschwartz 	else
2281370Sschwartz 		return ((fire_perfreg_handle_t)platform_specific_data);
2291370Sschwartz }
2301370Sschwartz 
2311370Sschwartz /*ARGSUSED*/
2321370Sschwartz int
fpc_free_counter_handle(fire_perfreg_handle_t handle)2331370Sschwartz fpc_free_counter_handle(fire_perfreg_handle_t handle)
2341370Sschwartz {
2351370Sschwartz 	return (SUCCESS);
2361370Sschwartz }
2371370Sschwartz 
2381370Sschwartz static int
fpc_hv_perfreg_io(fire_perfreg_handle_t handle,uint64_t hv_if_index,uint64_t * reg_data,boolean_t is_write)2391370Sschwartz fpc_hv_perfreg_io(fire_perfreg_handle_t handle, uint64_t hv_if_index,
2401370Sschwartz     uint64_t *reg_data, boolean_t is_write)
2411370Sschwartz {
2421370Sschwartz 	int rval;
2431370Sschwartz 	devhandle_t dev_hdl = (devhandle_t)handle;
2441370Sschwartz 
2451370Sschwartz 	if (is_write)
2461370Sschwartz 		rval = fpc_set_fire_perfreg(dev_hdl, hv_if_index, *reg_data);
2471370Sschwartz 	else
2481370Sschwartz 		rval = fpc_get_fire_perfreg(dev_hdl, hv_if_index, reg_data);
2491370Sschwartz 
2501370Sschwartz 	return ((rval == H_EOK) ? SUCCESS : EIO);
2511370Sschwartz }
2521370Sschwartz 
2531370Sschwartz int
fpc_event_io(fire_perfreg_handle_t handle,fire_perfcnt_t group,uint64_t * reg_data,boolean_t is_write)2541370Sschwartz fpc_event_io(fire_perfreg_handle_t handle, fire_perfcnt_t group,
2551370Sschwartz     uint64_t *reg_data, boolean_t is_write)
2561370Sschwartz {
2571370Sschwartz 	uint64_t hv_if_index = counter_select_index[group];
2581370Sschwartz 	return (fpc_hv_perfreg_io(handle, hv_if_index, reg_data, is_write));
2591370Sschwartz }
2601370Sschwartz 
2611370Sschwartz 
2621370Sschwartz /*ARGSUSED*/
2631370Sschwartz int
fpc_counter_io(fire_perfreg_handle_t handle,fire_perfcnt_t group,int counter_index,uint64_t * reg_data,boolean_t is_write)2641370Sschwartz fpc_counter_io(fire_perfreg_handle_t handle, fire_perfcnt_t group,
2651370Sschwartz     int counter_index, uint64_t *reg_data, boolean_t is_write)
2661370Sschwartz {
2671370Sschwartz 	uint64_t hv_if_index = counter_reg_index[counter_index];
2681370Sschwartz 	return (fpc_hv_perfreg_io(handle, hv_if_index, reg_data, is_write));
2691370Sschwartz }
270