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 2007 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 /*
28  * Copyright 2020 Nexenta by DDN, Inc. All rights reserved.
29  */
30 
31 #include <sys/file.h>
32 #include <sys/sunndi.h>
33 #include <sys/sunddi.h>
34 #include <sys/sunldi.h>
35 #include <io/px/px_regs.h>
36 #include <sys/pci_tools.h>
37 #include <fpc.h>
38 #include <fpc-impl.h>
39 
40 #define	CHIP_COMPATIBLE_NAME	"pciex108e,80f0"
41 #define	BANK_ADDR_MASK		0x7FFFFF
42 
43 #define	OPEN_FLAGS (FREAD | FWRITE)
44 
45 #define	PCIE_BANK	0
46 #define	JBUS_BANK	1
47 
48 typedef struct px_regs {
49 	uint32_t addr_hi;
50 	uint32_t addr_lo;
51 	uint32_t size_hi;
52 	uint32_t size_lo;
53 } px_regs_t;
54 
55 /* There is one of these for every root nexus device found */
56 typedef struct fire4u_specific {
57 	char *nodename;
58 	uintptr_t jbus_bank_base;
59 } fire4u_specific_t;
60 
61 typedef struct fire_counter_handle_impl {
62 	ldi_handle_t devhandle;
63 	fire4u_specific_t *devspec; /* Points to proper one for specific dev. */
64 } fire_counter_handle_impl_t;
65 
66 static uint64_t counter_select_offsets[] = {
67 	JBC_PERFORMANCE_COUNTER_SELECT,
68 	IMU_PERFORMANCE_COUNTER_SELECT,
69 	MMU_PERFORMANCE_COUNTER_SELECT,
70 	TLU_PERFORMANCE_COUNTER_SELECT,
71 	LPU_LINK_PERFORMANCE_COUNTER_SELECT
72 };
73 
74 /*
75  * The following event and offset arrays is organized by grouping in major
76  * order the fire_perfcnt_t register types, and in minor order the register
77  * numbers within that type.
78  */
79 
80 static uint64_t counter_reg_offsets[] = {
81 	JBC_PERFORMANCE_COUNTER_ZERO,
82 	JBC_PERFORMANCE_COUNTER_ONE,
83 	IMU_PERFORMANCE_COUNTER_ZERO,
84 	IMU_PERFORMANCE_COUNTER_ONE,
85 	MMU_PERFORMANCE_COUNTER_ZERO,
86 	MMU_PERFORMANCE_COUNTER_ONE,
87 	TLU_PERFORMANCE_COUNTER_ZERO,
88 	TLU_PERFORMANCE_COUNTER_ONE,
89 	TLU_PERFORMANCE_COUNTER_TWO,
90 	LPU_LINK_PERFORMANCE_COUNTER1,
91 	LPU_LINK_PERFORMANCE_COUNTER2
92 };
93 
94 /*
95  * Add the following to one of the LPU_LINK_PERFORMANCE_COUNTERx offsets to
96  * write a value to that counter.
97  */
98 #define	LPU_LINK_PERFCTR_WRITE_OFFSET	0x8
99 
100 /*
101  * Note that LPU_LINK_PERFORMANCE_COUNTER_CONTROL register is hard-reset to
102  * zeros and this is the value we want.  This register isn't touched by this
103  * module, and as long as it remains untouched by other modules we're OK.
104  */
105 
106 static ldi_ident_t ldi_identifier;
107 static boolean_t ldi_identifier_valid = B_FALSE;
108 
109 /* Called by _init to determine if it is OK to install driver. */
110 int
fpc_platform_check()111 fpc_platform_check()
112 {
113 	return (SUCCESS);
114 }
115 
116 /* Called during attach to do module-wide initialization. */
117 int
fpc_platform_module_init(dev_info_t * dip)118 fpc_platform_module_init(dev_info_t *dip)
119 {
120 	int status;
121 
122 	status = ldi_ident_from_dip(dip, &ldi_identifier);
123 	if (status == 0)
124 		ldi_identifier_valid = B_TRUE;
125 	return ((status == 0) ? DDI_SUCCESS : DDI_FAILURE);
126 }
127 
128 int
fpc_platform_node_init(dev_info_t * dip,int * avail)129 fpc_platform_node_init(dev_info_t *dip, int *avail)
130 {
131 	int index;
132 	char *name;
133 	int nodename_size;
134 	char *nodename = NULL;
135 	fire4u_specific_t *platform_specific_data = NULL;
136 	char *compatible = NULL;
137 	px_regs_t *regs_p = NULL;
138 	int regs_length = 0;
139 
140 	if (ddi_prop_lookup_string(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
141 	    "compatible", &compatible) != DDI_PROP_SUCCESS)
142 		return (DDI_SUCCESS);
143 
144 	if (strcmp(compatible, CHIP_COMPATIBLE_NAME) != 0) {
145 		ddi_prop_free(compatible);
146 		return (DDI_SUCCESS);
147 	}
148 	ddi_prop_free(compatible);
149 
150 	fpc_common_node_setup(dip, &index);
151 
152 	name = fpc_get_dev_name_by_number(index);
153 	nodename_size = strlen(name) + strlen(PCI_MINOR_REG) + 2;
154 	nodename = kmem_zalloc(nodename_size, KM_SLEEP);
155 
156 	platform_specific_data =
157 	    kmem_zalloc(sizeof (fire4u_specific_t), KM_SLEEP);
158 
159 	(void) strcpy(nodename, name);
160 	(void) strcat(nodename, ":");
161 	(void) strcat(nodename, PCI_MINOR_REG);
162 	platform_specific_data->nodename = nodename;
163 
164 	/* Get register banks. */
165 	if (ddi_getlongprop(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
166 	    "reg", (caddr_t)&regs_p, &regs_length) != DDI_SUCCESS) {
167 		goto bad_regs_p;
168 	}
169 
170 	if ((regs_length / sizeof (px_regs_t)) < 2) {
171 		goto bad_regs_length;
172 	}
173 
174 	platform_specific_data->jbus_bank_base =
175 	    regs_p[JBUS_BANK].addr_lo & BANK_ADDR_MASK;
176 
177 	kmem_free(regs_p, regs_length);
178 
179 	if (index == 0)
180 		*avail |= (PCIE_A_REGS_AVAIL | JBUS_REGS_AVAIL);
181 	else
182 		*avail |= PCIE_B_REGS_AVAIL;
183 
184 	(void) fpc_set_platform_data_by_number(index, platform_specific_data);
185 
186 	return (DDI_SUCCESS);
187 
188 bad_regs_length:
189 	if (regs_p)
190 		kmem_free(regs_p, regs_length);
191 bad_regs_p:
192 	kmem_free(platform_specific_data, sizeof (fire4u_specific_t));
193 	if (nodename)
194 		kmem_free(nodename, nodename_size);
195 
196 	return (DDI_FAILURE);
197 }
198 
199 void
fpc_platform_node_fini(void * arg)200 fpc_platform_node_fini(void *arg)
201 {
202 	fire4u_specific_t *plat_arg = (fire4u_specific_t *)arg;
203 	if (plat_arg == NULL)
204 		return;
205 	if (plat_arg->nodename)
206 		kmem_free(plat_arg->nodename, strlen(plat_arg->nodename)+1);
207 	kmem_free(plat_arg, sizeof (fire4u_specific_t));
208 }
209 
210 /*ARGSUSED*/
211 void
fpc_platform_module_fini(dev_info_t * dip)212 fpc_platform_module_fini(dev_info_t *dip)
213 {
214 	if (ldi_identifier_valid)
215 		ldi_ident_release(ldi_identifier);
216 }
217 
218 fire_perfreg_handle_t
fpc_get_perfreg_handle(int devnum)219 fpc_get_perfreg_handle(int devnum)
220 {
221 	int rval = EINVAL;
222 
223 	fire_counter_handle_impl_t *handle_impl =
224 	    kmem_zalloc(sizeof (fire_counter_handle_impl_t), KM_SLEEP);
225 
226 	if ((handle_impl->devspec =
227 	    fpc_get_platform_data_by_number(devnum)) != NULL) {
228 		rval = ldi_open_by_name(handle_impl->devspec->nodename,
229 		    OPEN_FLAGS, kcred, &handle_impl->devhandle,
230 		    ldi_identifier);
231 	}
232 
233 	if (rval != SUCCESS) {
234 		kmem_free(handle_impl, sizeof (fire_counter_handle_impl_t));
235 		return ((fire_perfreg_handle_t)-1);
236 	} else {
237 		return ((fire_perfreg_handle_t)handle_impl);
238 	}
239 }
240 
241 int
fpc_free_counter_handle(fire_perfreg_handle_t handle)242 fpc_free_counter_handle(fire_perfreg_handle_t handle)
243 {
244 	fire_counter_handle_impl_t *handle_impl =
245 	    (fire_counter_handle_impl_t *)handle;
246 	(void) ldi_close(handle_impl->devhandle, OPEN_FLAGS, kcred);
247 	kmem_free(handle_impl, sizeof (fire_counter_handle_impl_t));
248 	return (SUCCESS);
249 }
250 
251 int
fpc_event_io(fire_perfreg_handle_t handle,fire_perfcnt_t group,uint64_t * reg_data,boolean_t is_write)252 fpc_event_io(fire_perfreg_handle_t handle, fire_perfcnt_t group,
253     uint64_t *reg_data, boolean_t is_write)
254 {
255 	int rval;
256 	int ioctl_rval;
257 	pcitool_reg_t prg;
258 	fire_counter_handle_impl_t *handle_impl =
259 	    (fire_counter_handle_impl_t *)handle;
260 	int cmd = is_write ? PCITOOL_NEXUS_SET_REG : PCITOOL_NEXUS_GET_REG;
261 
262 	prg.user_version = PCITOOL_VERSION;
263 
264 	if (group == jbc) {
265 		prg.barnum = JBUS_BANK;
266 		prg.offset = counter_select_offsets[group] -
267 		    handle_impl->devspec->jbus_bank_base;
268 	} else {
269 		prg.barnum = PCIE_BANK;
270 
271 		/*
272 		 * Note that a pcie_bank_base isn't needed.  Pcie register
273 		 * offsets are already relative to the start of their bank.  No
274 		 * base needs to be subtracted to get the relative offset that
275 		 * pcitool ioctls want.
276 		 */
277 		prg.offset = counter_select_offsets[group];
278 	}
279 	prg.acc_attr = PCITOOL_ACC_ATTR_SIZE_8 | PCITOOL_ACC_ATTR_ENDN_BIG;
280 	prg.data = *reg_data;
281 
282 	/* Read original value. */
283 	if (((rval = ldi_ioctl(handle_impl->devhandle, cmd, (intptr_t)&prg,
284 	    FKIOCTL, kcred, &ioctl_rval)) == SUCCESS) && (!is_write)) {
285 		*reg_data = prg.data;
286 	}
287 
288 	return (rval);
289 }
290 
291 int
fpc_counter_io(fire_perfreg_handle_t handle,fire_perfcnt_t group,int counter_index,uint64_t * value,boolean_t is_write)292 fpc_counter_io(fire_perfreg_handle_t handle, fire_perfcnt_t group,
293     int counter_index, uint64_t *value, boolean_t is_write)
294 {
295 	int rval;
296 	int ioctl_rval;
297 	pcitool_reg_t prg;
298 	fire_counter_handle_impl_t *handle_impl =
299 	    (fire_counter_handle_impl_t *)handle;
300 	int command =
301 	    (is_write) ? PCITOOL_NEXUS_SET_REG : PCITOOL_NEXUS_GET_REG;
302 
303 	prg.user_version = PCITOOL_VERSION;
304 	/*
305 	 * Note that stated PCIE offsets are relative to the beginning of their
306 	 * register bank, while JBUS offsets are absolute.
307 	 */
308 	if (group == jbc) {
309 		prg.barnum = JBUS_BANK;
310 		prg.offset = counter_reg_offsets[counter_index] -
311 		    handle_impl->devspec->jbus_bank_base;
312 	} else {
313 		prg.barnum = PCIE_BANK;
314 		prg.offset = counter_reg_offsets[counter_index];
315 	}
316 
317 	if ((group == lpu) && (is_write)) {
318 		prg.offset += LPU_LINK_PERFCTR_WRITE_OFFSET;
319 	}
320 
321 	prg.acc_attr = PCITOOL_ACC_ATTR_SIZE_8 | PCITOOL_ACC_ATTR_ENDN_BIG;
322 	prg.data = *value;
323 
324 	if (((rval = ldi_ioctl(handle_impl->devhandle, command, (intptr_t)&prg,
325 	    FKIOCTL, kcred, &ioctl_rval)) == SUCCESS) && (!is_write)) {
326 		*value = prg.data;
327 	}
328 
329 	return (rval);
330 }
331