xref: /illumos-gate/usr/src/uts/sun4v/io/fpc/fpc-impl-4v.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/file.h>
30110e73f9Sschwartz #include <sys/hypervisor_api.h>
31*0ad689d6Sschwartz #include <sys/hsvc.h>
32110e73f9Sschwartz #include <sys/sunndi.h>
33110e73f9Sschwartz #include <fpc.h>
34110e73f9Sschwartz #include <fpc-impl.h>
35110e73f9Sschwartz #include <fpc-impl-4v.h>
36110e73f9Sschwartz 
37110e73f9Sschwartz #define	PCIE_ROOTNEX_COMPATIBLE_NAME	"SUNW,sun4v-pci"
38110e73f9Sschwartz 
39*0ad689d6Sschwartz #define	FPC_MODULE_NAME			"fpc"
40*0ad689d6Sschwartz #define	FPC_REQ_MAJOR_VER		1
41*0ad689d6Sschwartz #define	FPC_REQ_MINOR_VER		0
42*0ad689d6Sschwartz 
43*0ad689d6Sschwartz static hsvc_info_t fpc_hsvc = {
44*0ad689d6Sschwartz 	HSVC_REV_1,
45*0ad689d6Sschwartz 	NULL,
46*0ad689d6Sschwartz 	HSVC_GROUP_FIRE_PERF,
47*0ad689d6Sschwartz 	FPC_REQ_MAJOR_VER,
48*0ad689d6Sschwartz 	FPC_REQ_MINOR_VER,
49*0ad689d6Sschwartz 	FPC_MODULE_NAME
50*0ad689d6Sschwartz };
51*0ad689d6Sschwartz 
52*0ad689d6Sschwartz static int hyp_regd_users = 0;
53*0ad689d6Sschwartz static uint64_t	fpc_sup_minor;
54*0ad689d6Sschwartz 
55110e73f9Sschwartz /*
56110e73f9Sschwartz  * The following typedef is used to represent a
57110e73f9Sschwartz  * 1275 "reg" property of a PCI nexus.
58110e73f9Sschwartz  */
59110e73f9Sschwartz typedef struct nexus_regspec {
60110e73f9Sschwartz 	uint64_t phys_addr;
61110e73f9Sschwartz 	uint64_t size;
62110e73f9Sschwartz } nexus_regspec_t;
63110e73f9Sschwartz 
64110e73f9Sschwartz static uint64_t counter_select_index[] = {
65110e73f9Sschwartz 	HVIO_FIRE_PERFREG_JBC_SEL,
66110e73f9Sschwartz 	HVIO_FIRE_PERFREG_PCIE_IMU_SEL,
67110e73f9Sschwartz 	HVIO_FIRE_PERFREG_PCIE_MMU_SEL,
68110e73f9Sschwartz 	HVIO_FIRE_PERFREG_PCIE_TLU_SEL,
69110e73f9Sschwartz 	HVIO_FIRE_PERFREG_PCIE_LNK_SEL
70110e73f9Sschwartz };
71110e73f9Sschwartz 
72110e73f9Sschwartz /*
73110e73f9Sschwartz  * The following event and offset arrays is organized by grouping in major
74110e73f9Sschwartz  * order the fire_perfcnt_t register types, and in minor order the register
75110e73f9Sschwartz  * numbers within that type.
76110e73f9Sschwartz  */
77110e73f9Sschwartz 
78110e73f9Sschwartz /*
79110e73f9Sschwartz  * This table maps the above order into the hypervisor interface register
80110e73f9Sschwartz  * indices.
81110e73f9Sschwartz  */
82110e73f9Sschwartz static uint64_t counter_reg_index[] = {
83110e73f9Sschwartz 	HVIO_FIRE_PERFREG_JBC_CNT0,
84110e73f9Sschwartz 	HVIO_FIRE_PERFREG_JBC_CNT1,
85110e73f9Sschwartz 	HVIO_FIRE_PERFREG_PCIE_IMU_CNT0,
86110e73f9Sschwartz 	HVIO_FIRE_PERFREG_PCIE_IMU_CNT1,
87110e73f9Sschwartz 	HVIO_FIRE_PERFREG_PCIE_MMU_CNT0,
88110e73f9Sschwartz 	HVIO_FIRE_PERFREG_PCIE_MMU_CNT1,
89110e73f9Sschwartz 	HVIO_FIRE_PERFREG_PCIE_TLU_CNT0,
90110e73f9Sschwartz 	HVIO_FIRE_PERFREG_PCIE_TLU_CNT1,
91110e73f9Sschwartz 	HVIO_FIRE_PERFREG_PCIE_TLU_CNT2,
92110e73f9Sschwartz 	HVIO_FIRE_PERFREG_PCIE_LNK_CNT1,
93110e73f9Sschwartz 	HVIO_FIRE_PERFREG_PCIE_LNK_CNT2
94110e73f9Sschwartz };
95110e73f9Sschwartz 
96*0ad689d6Sschwartz /* Called by _init to determine if it is OK to install driver. */
97*0ad689d6Sschwartz int
fpc_platform_check()98*0ad689d6Sschwartz fpc_platform_check()
99*0ad689d6Sschwartz {
100*0ad689d6Sschwartz 	int regstat;
101*0ad689d6Sschwartz 
102*0ad689d6Sschwartz 	if ((regstat = hsvc_register(&fpc_hsvc, &fpc_sup_minor)) == SUCCESS) {
103*0ad689d6Sschwartz 		(void) hsvc_unregister(&fpc_hsvc);
104*0ad689d6Sschwartz 	}
105*0ad689d6Sschwartz 	fpc_sup_minor = 0;
106*0ad689d6Sschwartz 	return (regstat);
107*0ad689d6Sschwartz }
108*0ad689d6Sschwartz 
109*0ad689d6Sschwartz /* Called during attach to do module-wide initialization. */
110110e73f9Sschwartz /*ARGSUSED*/
111110e73f9Sschwartz int
fpc_platform_module_init(dev_info_t * dip)112110e73f9Sschwartz fpc_platform_module_init(dev_info_t *dip)
113110e73f9Sschwartz {
114110e73f9Sschwartz 	return (DDI_SUCCESS);
115110e73f9Sschwartz }
116110e73f9Sschwartz 
117110e73f9Sschwartz int
fpc_platform_node_init(dev_info_t * dip,int * avail)118110e73f9Sschwartz fpc_platform_node_init(dev_info_t *dip, int *avail)
119110e73f9Sschwartz {
120110e73f9Sschwartz 	nexus_regspec_t	*rp;
121110e73f9Sschwartz 	uint_t reglen;
122110e73f9Sschwartz 	devhandle_t dev_hdl;
123*0ad689d6Sschwartz 	int regstat;
124110e73f9Sschwartz 	int index;
125110e73f9Sschwartz 	boolean_t is_root_pcie_nexus;
126110e73f9Sschwartz 	uint64_t dummy_data;
127110e73f9Sschwartz 	char *name = NULL;
128110e73f9Sschwartz 	boolean_t jbus_regs_avail;
129110e73f9Sschwartz 	boolean_t pcie_regs_avail;
130110e73f9Sschwartz 
131110e73f9Sschwartz 	if (ddi_prop_lookup_string(DDI_DEV_T_ANY, dip,
132110e73f9Sschwartz 	    DDI_PROP_DONTPASS, "compatible", &name) != DDI_PROP_SUCCESS)
133110e73f9Sschwartz 		return (DDI_SUCCESS);
134110e73f9Sschwartz 
135110e73f9Sschwartz 	is_root_pcie_nexus = (strcmp(name, PCIE_ROOTNEX_COMPATIBLE_NAME) == 0);
136110e73f9Sschwartz 	ddi_prop_free(name);
137110e73f9Sschwartz 	if (!is_root_pcie_nexus)
138110e73f9Sschwartz 		return (DDI_SUCCESS);
139110e73f9Sschwartz 
140110e73f9Sschwartz 	if (ddi_prop_lookup_byte_array(DDI_DEV_T_ANY, dip,
141110e73f9Sschwartz 	    DDI_PROP_DONTPASS, "reg", (uchar_t **)&rp, &reglen) !=
142110e73f9Sschwartz 	    DDI_PROP_SUCCESS)
143110e73f9Sschwartz 		return (DDI_FAILURE);
144110e73f9Sschwartz 
145110e73f9Sschwartz 	/*
146110e73f9Sschwartz 	 * Initilize device handle. The device handle uniquely
147110e73f9Sschwartz 	 * identifies a SUN4V device. It consists of the lower 28-bits
148110e73f9Sschwartz 	 * of the hi-cell of the first entry of the SUN4V device's
149110e73f9Sschwartz 	 * "reg" property as defined by the SUN4V Bus Binding to Open
150110e73f9Sschwartz 	 * Firmware.
151110e73f9Sschwartz 	 */
152110e73f9Sschwartz 	dev_hdl = (devhandle_t)((rp->phys_addr >> 32) & DEVHDLE_MASK);
153110e73f9Sschwartz 
154110e73f9Sschwartz 	ddi_prop_free(rp);
155110e73f9Sschwartz 
156*0ad689d6Sschwartz 	/*
157*0ad689d6Sschwartz 	 * If this is the first time through here, negotiate with hypervisor
158*0ad689d6Sschwartz 	 * that it has the services needed to operate.  Don't do this in _init
159*0ad689d6Sschwartz 	 * since we may want to modload the driver without attaching,
160*0ad689d6Sschwartz 	 * for debugging purposes.
161*0ad689d6Sschwartz 	 *
162*0ad689d6Sschwartz 	 * Note that this is another way of weeding out unsupported platforms
163*0ad689d6Sschwartz 	 */
164*0ad689d6Sschwartz 	if (hyp_regd_users == 0) {
165*0ad689d6Sschwartz 		regstat = hsvc_register(&fpc_hsvc, &fpc_sup_minor);
166*0ad689d6Sschwartz 		if (regstat != SUCCESS) {
167*0ad689d6Sschwartz 			/*
168*0ad689d6Sschwartz 			 * Fail silently since we don't want to print an error
169*0ad689d6Sschwartz 			 * on future platforms which don't support this driver.
170*0ad689d6Sschwartz 			 */
171*0ad689d6Sschwartz 			return (DDI_FAILURE);
172*0ad689d6Sschwartz 		}
173*0ad689d6Sschwartz 	}
174*0ad689d6Sschwartz 	hyp_regd_users++;
175*0ad689d6Sschwartz 
176110e73f9Sschwartz 	/* See which register sets are usable from this node. */
177110e73f9Sschwartz 	jbus_regs_avail = (fpc_event_io(
178110e73f9Sschwartz 	    (fire_perfreg_handle_t)dev_hdl, jbc, &dummy_data, IS_READ) ==
179110e73f9Sschwartz 	    SUCCESS);
180110e73f9Sschwartz 	pcie_regs_avail = (fpc_event_io(
181110e73f9Sschwartz 	    (fire_perfreg_handle_t)dev_hdl, imu, &dummy_data, IS_READ) ==
182110e73f9Sschwartz 	    SUCCESS);
183110e73f9Sschwartz 
184110e73f9Sschwartz 	/* Nothing usable at this node. */
185110e73f9Sschwartz 	if ((!jbus_regs_avail) && (!pcie_regs_avail))
186110e73f9Sschwartz 		return (DDI_SUCCESS);
187110e73f9Sschwartz 
188110e73f9Sschwartz 	fpc_common_node_setup(dip, &index);
189110e73f9Sschwartz 	if (pcie_regs_avail)
190110e73f9Sschwartz 		*avail |=
191110e73f9Sschwartz 		    ((index == 0) ? PCIE_A_REGS_AVAIL : PCIE_B_REGS_AVAIL);
192110e73f9Sschwartz 	if (jbus_regs_avail) {
193110e73f9Sschwartz 		*avail |= JBUS_REGS_AVAIL;
194110e73f9Sschwartz 		if (index != 0)
195110e73f9Sschwartz 			cmn_err(CE_WARN,
196110e73f9Sschwartz 			    "fpc: JBUS regs available on device idx %d!\n",
197110e73f9Sschwartz 			    index);
198110e73f9Sschwartz 	}
199110e73f9Sschwartz 
200110e73f9Sschwartz 	(void) fpc_set_platform_data_by_number(index, (void *)dev_hdl);
201110e73f9Sschwartz 
202110e73f9Sschwartz 	return (DDI_SUCCESS);
203110e73f9Sschwartz }
204110e73f9Sschwartz 
205110e73f9Sschwartz /*ARGSUSED*/
206110e73f9Sschwartz void
fpc_platform_node_fini(void * arg)207110e73f9Sschwartz fpc_platform_node_fini(void *arg)
208110e73f9Sschwartz {
209*0ad689d6Sschwartz 	if (--hyp_regd_users == 0)
210*0ad689d6Sschwartz 		(void) hsvc_unregister(&fpc_hsvc);
211110e73f9Sschwartz }
212110e73f9Sschwartz 
213110e73f9Sschwartz /*ARGSUSED*/
214110e73f9Sschwartz void
fpc_platform_module_fini(dev_info_t * dip)215110e73f9Sschwartz fpc_platform_module_fini(dev_info_t *dip)
216110e73f9Sschwartz {
217110e73f9Sschwartz }
218110e73f9Sschwartz 
219110e73f9Sschwartz fire_perfreg_handle_t
fpc_get_perfreg_handle(int devnum)220110e73f9Sschwartz fpc_get_perfreg_handle(int devnum)
221110e73f9Sschwartz {
222110e73f9Sschwartz 	void *platform_specific_data;
223110e73f9Sschwartz 
224110e73f9Sschwartz 	if ((platform_specific_data =
225110e73f9Sschwartz 	    fpc_get_platform_data_by_number(devnum)) == NULL)
226110e73f9Sschwartz 		return ((fire_perfreg_handle_t)-1);
227110e73f9Sschwartz 	else
228110e73f9Sschwartz 		return ((fire_perfreg_handle_t)platform_specific_data);
229110e73f9Sschwartz }
230110e73f9Sschwartz 
231110e73f9Sschwartz /*ARGSUSED*/
232110e73f9Sschwartz int
fpc_free_counter_handle(fire_perfreg_handle_t handle)233110e73f9Sschwartz fpc_free_counter_handle(fire_perfreg_handle_t handle)
234110e73f9Sschwartz {
235110e73f9Sschwartz 	return (SUCCESS);
236110e73f9Sschwartz }
237110e73f9Sschwartz 
238110e73f9Sschwartz static int
fpc_hv_perfreg_io(fire_perfreg_handle_t handle,uint64_t hv_if_index,uint64_t * reg_data,boolean_t is_write)239110e73f9Sschwartz fpc_hv_perfreg_io(fire_perfreg_handle_t handle, uint64_t hv_if_index,
240110e73f9Sschwartz     uint64_t *reg_data, boolean_t is_write)
241110e73f9Sschwartz {
242110e73f9Sschwartz 	int rval;
243110e73f9Sschwartz 	devhandle_t dev_hdl = (devhandle_t)handle;
244110e73f9Sschwartz 
245110e73f9Sschwartz 	if (is_write)
246110e73f9Sschwartz 		rval = fpc_set_fire_perfreg(dev_hdl, hv_if_index, *reg_data);
247110e73f9Sschwartz 	else
248110e73f9Sschwartz 		rval = fpc_get_fire_perfreg(dev_hdl, hv_if_index, reg_data);
249110e73f9Sschwartz 
250110e73f9Sschwartz 	return ((rval == H_EOK) ? SUCCESS : EIO);
251110e73f9Sschwartz }
252110e73f9Sschwartz 
253110e73f9Sschwartz int
fpc_event_io(fire_perfreg_handle_t handle,fire_perfcnt_t group,uint64_t * reg_data,boolean_t is_write)254110e73f9Sschwartz fpc_event_io(fire_perfreg_handle_t handle, fire_perfcnt_t group,
255110e73f9Sschwartz     uint64_t *reg_data, boolean_t is_write)
256110e73f9Sschwartz {
257110e73f9Sschwartz 	uint64_t hv_if_index = counter_select_index[group];
258110e73f9Sschwartz 	return (fpc_hv_perfreg_io(handle, hv_if_index, reg_data, is_write));
259110e73f9Sschwartz }
260110e73f9Sschwartz 
261110e73f9Sschwartz 
262110e73f9Sschwartz /*ARGSUSED*/
263110e73f9Sschwartz int
fpc_counter_io(fire_perfreg_handle_t handle,fire_perfcnt_t group,int counter_index,uint64_t * reg_data,boolean_t is_write)264110e73f9Sschwartz fpc_counter_io(fire_perfreg_handle_t handle, fire_perfcnt_t group,
265110e73f9Sschwartz     int counter_index, uint64_t *reg_data, boolean_t is_write)
266110e73f9Sschwartz {
267110e73f9Sschwartz 	uint64_t hv_if_index = counter_reg_index[counter_index];
268110e73f9Sschwartz 	return (fpc_hv_perfreg_io(handle, hv_if_index, reg_data, is_write));
269110e73f9Sschwartz }
270