xref: /illumos-gate/usr/src/uts/sun4v/io/fpc/fpc-impl-4v.c (revision 2caf0dcd2abc26b477e317999994020212790d38)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 
22 /*
23  * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #pragma ident	"%Z%%M%	%I%	%E% SMI"
28 
29 #include <sys/file.h>
30 #include <sys/hypervisor_api.h>
31 #include <sys/sunndi.h>
32 #include <fpc.h>
33 #include <fpc-impl.h>
34 #include <fpc-impl-4v.h>
35 
36 #define	PCIE_ROOTNEX_COMPATIBLE_NAME	"SUNW,sun4v-pci"
37 
38 /*
39  * The following typedef is used to represent a
40  * 1275 "reg" property of a PCI nexus.
41  */
42 typedef struct nexus_regspec {
43 	uint64_t phys_addr;
44 	uint64_t size;
45 } nexus_regspec_t;
46 
47 static uint64_t counter_select_index[] = {
48 	HVIO_FIRE_PERFREG_JBC_SEL,
49 	HVIO_FIRE_PERFREG_PCIE_IMU_SEL,
50 	HVIO_FIRE_PERFREG_PCIE_MMU_SEL,
51 	HVIO_FIRE_PERFREG_PCIE_TLU_SEL,
52 	HVIO_FIRE_PERFREG_PCIE_LNK_SEL
53 };
54 
55 /*
56  * The following event and offset arrays is organized by grouping in major
57  * order the fire_perfcnt_t register types, and in minor order the register
58  * numbers within that type.
59  */
60 
61 /*
62  * This table maps the above order into the hypervisor interface register
63  * indices.
64  */
65 static uint64_t counter_reg_index[] = {
66 	HVIO_FIRE_PERFREG_JBC_CNT0,
67 	HVIO_FIRE_PERFREG_JBC_CNT1,
68 	HVIO_FIRE_PERFREG_PCIE_IMU_CNT0,
69 	HVIO_FIRE_PERFREG_PCIE_IMU_CNT1,
70 	HVIO_FIRE_PERFREG_PCIE_MMU_CNT0,
71 	HVIO_FIRE_PERFREG_PCIE_MMU_CNT1,
72 	HVIO_FIRE_PERFREG_PCIE_TLU_CNT0,
73 	HVIO_FIRE_PERFREG_PCIE_TLU_CNT1,
74 	HVIO_FIRE_PERFREG_PCIE_TLU_CNT2,
75 	HVIO_FIRE_PERFREG_PCIE_LNK_CNT1,
76 	HVIO_FIRE_PERFREG_PCIE_LNK_CNT2
77 };
78 
79 /*ARGSUSED*/
80 int
81 fpc_platform_module_init(dev_info_t *dip)
82 {
83 	return (DDI_SUCCESS);
84 }
85 
86 int
87 fpc_platform_node_init(dev_info_t *dip, int *avail)
88 {
89 	nexus_regspec_t	*rp;
90 	uint_t reglen;
91 	devhandle_t dev_hdl;
92 	int index;
93 	boolean_t is_root_pcie_nexus;
94 	uint64_t dummy_data;
95 	char *name = NULL;
96 	boolean_t jbus_regs_avail;
97 	boolean_t pcie_regs_avail;
98 
99 	if (ddi_prop_lookup_string(DDI_DEV_T_ANY, dip,
100 	    DDI_PROP_DONTPASS, "compatible", &name) != DDI_PROP_SUCCESS)
101 		return (DDI_SUCCESS);
102 
103 	is_root_pcie_nexus = (strcmp(name, PCIE_ROOTNEX_COMPATIBLE_NAME) == 0);
104 	ddi_prop_free(name);
105 	if (!is_root_pcie_nexus)
106 		return (DDI_SUCCESS);
107 
108 	if (ddi_prop_lookup_byte_array(DDI_DEV_T_ANY, dip,
109 	    DDI_PROP_DONTPASS, "reg", (uchar_t **)&rp, &reglen) !=
110 	    DDI_PROP_SUCCESS)
111 		return (DDI_FAILURE);
112 
113 	/*
114 	 * Initilize device handle. The device handle uniquely
115 	 * identifies a SUN4V device. It consists of the lower 28-bits
116 	 * of the hi-cell of the first entry of the SUN4V device's
117 	 * "reg" property as defined by the SUN4V Bus Binding to Open
118 	 * Firmware.
119 	 */
120 	dev_hdl = (devhandle_t)((rp->phys_addr >> 32) & DEVHDLE_MASK);
121 
122 	ddi_prop_free(rp);
123 
124 	/* See which register sets are usable from this node. */
125 	jbus_regs_avail = (fpc_event_io(
126 	    (fire_perfreg_handle_t)dev_hdl, jbc, &dummy_data, IS_READ) ==
127 	    SUCCESS);
128 	pcie_regs_avail = (fpc_event_io(
129 	    (fire_perfreg_handle_t)dev_hdl, imu, &dummy_data, IS_READ) ==
130 	    SUCCESS);
131 
132 	/* Nothing usable at this node. */
133 	if ((!jbus_regs_avail) && (!pcie_regs_avail))
134 		return (DDI_SUCCESS);
135 
136 	fpc_common_node_setup(dip, &index);
137 	if (pcie_regs_avail)
138 		*avail |=
139 		    ((index == 0) ? PCIE_A_REGS_AVAIL : PCIE_B_REGS_AVAIL);
140 	if (jbus_regs_avail) {
141 		*avail |= JBUS_REGS_AVAIL;
142 		if (index != 0)
143 			cmn_err(CE_WARN,
144 			    "fpc: JBUS regs available on device idx %d!\n",
145 			    index);
146 	}
147 
148 	(void) fpc_set_platform_data_by_number(index, (void *)dev_hdl);
149 
150 	return (DDI_SUCCESS);
151 }
152 
153 /*ARGSUSED*/
154 void
155 fpc_platform_node_fini(void *arg)
156 {
157 }
158 
159 /*ARGSUSED*/
160 void
161 fpc_platform_module_fini(dev_info_t *dip)
162 {
163 }
164 
165 fire_perfreg_handle_t
166 fpc_get_perfreg_handle(int devnum)
167 {
168 	void *platform_specific_data;
169 
170 	if ((platform_specific_data =
171 	    fpc_get_platform_data_by_number(devnum)) == NULL)
172 		return ((fire_perfreg_handle_t)-1);
173 	else
174 		return ((fire_perfreg_handle_t)platform_specific_data);
175 }
176 
177 /*ARGSUSED*/
178 int
179 fpc_free_counter_handle(fire_perfreg_handle_t handle)
180 {
181 	return (SUCCESS);
182 }
183 
184 static int
185 fpc_hv_perfreg_io(fire_perfreg_handle_t handle, uint64_t hv_if_index,
186     uint64_t *reg_data, boolean_t is_write)
187 {
188 	int rval;
189 	devhandle_t dev_hdl = (devhandle_t)handle;
190 
191 	if (is_write)
192 		rval = fpc_set_fire_perfreg(dev_hdl, hv_if_index, *reg_data);
193 	else
194 		rval = fpc_get_fire_perfreg(dev_hdl, hv_if_index, reg_data);
195 
196 	return ((rval == H_EOK) ? SUCCESS : EIO);
197 }
198 
199 int
200 fpc_event_io(fire_perfreg_handle_t handle, fire_perfcnt_t group,
201     uint64_t *reg_data, boolean_t is_write)
202 {
203 	uint64_t hv_if_index = counter_select_index[group];
204 	return (fpc_hv_perfreg_io(handle, hv_if_index, reg_data, is_write));
205 }
206 
207 
208 /*ARGSUSED*/
209 int
210 fpc_counter_io(fire_perfreg_handle_t handle, fire_perfcnt_t group,
211     int counter_index, uint64_t *reg_data, boolean_t is_write)
212 {
213 	uint64_t hv_if_index = counter_reg_index[counter_index];
214 	return (fpc_hv_perfreg_io(handle, hv_if_index, reg_data, is_write));
215 }
216