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 /*
232917a9c9Sschwartz  * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
24110e73f9Sschwartz  * Use is subject to license terms.
25110e73f9Sschwartz  */
26110e73f9Sschwartz 
27*526073d8SMatt Barden /*
28*526073d8SMatt Barden  * Copyright 2020 Nexenta by DDN, Inc. All rights reserved.
29*526073d8SMatt Barden  */
30*526073d8SMatt Barden 
31110e73f9Sschwartz #include <sys/file.h>
32110e73f9Sschwartz #include <sys/sunndi.h>
33110e73f9Sschwartz #include <sys/sunddi.h>
34110e73f9Sschwartz #include <sys/sunldi.h>
35110e73f9Sschwartz #include <io/px/px_regs.h>
36110e73f9Sschwartz #include <sys/pci_tools.h>
37110e73f9Sschwartz #include <fpc.h>
38110e73f9Sschwartz #include <fpc-impl.h>
39110e73f9Sschwartz 
40110e73f9Sschwartz #define	CHIP_COMPATIBLE_NAME	"pciex108e,80f0"
41110e73f9Sschwartz #define	BANK_ADDR_MASK		0x7FFFFF
42110e73f9Sschwartz 
43110e73f9Sschwartz #define	OPEN_FLAGS (FREAD | FWRITE)
44110e73f9Sschwartz 
45110e73f9Sschwartz #define	PCIE_BANK	0
46110e73f9Sschwartz #define	JBUS_BANK	1
47110e73f9Sschwartz 
48110e73f9Sschwartz typedef struct px_regs {
49110e73f9Sschwartz 	uint32_t addr_hi;
50110e73f9Sschwartz 	uint32_t addr_lo;
51110e73f9Sschwartz 	uint32_t size_hi;
52110e73f9Sschwartz 	uint32_t size_lo;
53110e73f9Sschwartz } px_regs_t;
54110e73f9Sschwartz 
55110e73f9Sschwartz /* There is one of these for every root nexus device found */
56110e73f9Sschwartz typedef struct fire4u_specific {
57110e73f9Sschwartz 	char *nodename;
58110e73f9Sschwartz 	uintptr_t jbus_bank_base;
59110e73f9Sschwartz } fire4u_specific_t;
60110e73f9Sschwartz 
61110e73f9Sschwartz typedef struct fire_counter_handle_impl {
62110e73f9Sschwartz 	ldi_handle_t devhandle;
63110e73f9Sschwartz 	fire4u_specific_t *devspec; /* Points to proper one for specific dev. */
64110e73f9Sschwartz } fire_counter_handle_impl_t;
65110e73f9Sschwartz 
66110e73f9Sschwartz static uint64_t counter_select_offsets[] = {
67110e73f9Sschwartz 	JBC_PERFORMANCE_COUNTER_SELECT,
68110e73f9Sschwartz 	IMU_PERFORMANCE_COUNTER_SELECT,
69110e73f9Sschwartz 	MMU_PERFORMANCE_COUNTER_SELECT,
70110e73f9Sschwartz 	TLU_PERFORMANCE_COUNTER_SELECT,
71110e73f9Sschwartz 	LPU_LINK_PERFORMANCE_COUNTER_SELECT
72110e73f9Sschwartz };
73110e73f9Sschwartz 
74110e73f9Sschwartz /*
75110e73f9Sschwartz  * The following event and offset arrays is organized by grouping in major
76110e73f9Sschwartz  * order the fire_perfcnt_t register types, and in minor order the register
77110e73f9Sschwartz  * numbers within that type.
78110e73f9Sschwartz  */
79110e73f9Sschwartz 
80110e73f9Sschwartz static uint64_t counter_reg_offsets[] = {
81110e73f9Sschwartz 	JBC_PERFORMANCE_COUNTER_ZERO,
82110e73f9Sschwartz 	JBC_PERFORMANCE_COUNTER_ONE,
83110e73f9Sschwartz 	IMU_PERFORMANCE_COUNTER_ZERO,
84110e73f9Sschwartz 	IMU_PERFORMANCE_COUNTER_ONE,
85110e73f9Sschwartz 	MMU_PERFORMANCE_COUNTER_ZERO,
86110e73f9Sschwartz 	MMU_PERFORMANCE_COUNTER_ONE,
87110e73f9Sschwartz 	TLU_PERFORMANCE_COUNTER_ZERO,
88110e73f9Sschwartz 	TLU_PERFORMANCE_COUNTER_ONE,
89110e73f9Sschwartz 	TLU_PERFORMANCE_COUNTER_TWO,
90110e73f9Sschwartz 	LPU_LINK_PERFORMANCE_COUNTER1,
91110e73f9Sschwartz 	LPU_LINK_PERFORMANCE_COUNTER2
92110e73f9Sschwartz };
93110e73f9Sschwartz 
94110e73f9Sschwartz /*
95110e73f9Sschwartz  * Add the following to one of the LPU_LINK_PERFORMANCE_COUNTERx offsets to
96110e73f9Sschwartz  * write a value to that counter.
97110e73f9Sschwartz  */
98110e73f9Sschwartz #define	LPU_LINK_PERFCTR_WRITE_OFFSET	0x8
99110e73f9Sschwartz 
100110e73f9Sschwartz /*
101110e73f9Sschwartz  * Note that LPU_LINK_PERFORMANCE_COUNTER_CONTROL register is hard-reset to
102110e73f9Sschwartz  * zeros and this is the value we want.  This register isn't touched by this
103110e73f9Sschwartz  * module, and as long as it remains untouched by other modules we're OK.
104110e73f9Sschwartz  */
105110e73f9Sschwartz 
106110e73f9Sschwartz static ldi_ident_t ldi_identifier;
107110e73f9Sschwartz static boolean_t ldi_identifier_valid = B_FALSE;
108110e73f9Sschwartz 
1090ad689d6Sschwartz /* Called by _init to determine if it is OK to install driver. */
1100ad689d6Sschwartz int
fpc_platform_check()1110ad689d6Sschwartz fpc_platform_check()
1120ad689d6Sschwartz {
1130ad689d6Sschwartz 	return (SUCCESS);
1140ad689d6Sschwartz }
1150ad689d6Sschwartz 
1160ad689d6Sschwartz /* Called during attach to do module-wide initialization. */
117110e73f9Sschwartz int
fpc_platform_module_init(dev_info_t * dip)118110e73f9Sschwartz fpc_platform_module_init(dev_info_t *dip)
119110e73f9Sschwartz {
120110e73f9Sschwartz 	int status;
121110e73f9Sschwartz 
122110e73f9Sschwartz 	status = ldi_ident_from_dip(dip, &ldi_identifier);
123110e73f9Sschwartz 	if (status == 0)
124110e73f9Sschwartz 		ldi_identifier_valid = B_TRUE;
125110e73f9Sschwartz 	return ((status == 0) ? DDI_SUCCESS : DDI_FAILURE);
126110e73f9Sschwartz }
127110e73f9Sschwartz 
128110e73f9Sschwartz int
fpc_platform_node_init(dev_info_t * dip,int * avail)129110e73f9Sschwartz fpc_platform_node_init(dev_info_t *dip, int *avail)
130110e73f9Sschwartz {
131110e73f9Sschwartz 	int index;
132110e73f9Sschwartz 	char *name;
133110e73f9Sschwartz 	int nodename_size;
134110e73f9Sschwartz 	char *nodename = NULL;
135110e73f9Sschwartz 	fire4u_specific_t *platform_specific_data = NULL;
136110e73f9Sschwartz 	char *compatible = NULL;
137110e73f9Sschwartz 	px_regs_t *regs_p = NULL;
138110e73f9Sschwartz 	int regs_length = 0;
139110e73f9Sschwartz 
140110e73f9Sschwartz 	if (ddi_prop_lookup_string(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
141110e73f9Sschwartz 	    "compatible", &compatible) != DDI_PROP_SUCCESS)
142110e73f9Sschwartz 		return (DDI_SUCCESS);
143110e73f9Sschwartz 
144110e73f9Sschwartz 	if (strcmp(compatible, CHIP_COMPATIBLE_NAME) != 0) {
145110e73f9Sschwartz 		ddi_prop_free(compatible);
146110e73f9Sschwartz 		return (DDI_SUCCESS);
147110e73f9Sschwartz 	}
148110e73f9Sschwartz 	ddi_prop_free(compatible);
149110e73f9Sschwartz 
150110e73f9Sschwartz 	fpc_common_node_setup(dip, &index);
151110e73f9Sschwartz 
152110e73f9Sschwartz 	name = fpc_get_dev_name_by_number(index);
153110e73f9Sschwartz 	nodename_size = strlen(name) + strlen(PCI_MINOR_REG) + 2;
154110e73f9Sschwartz 	nodename = kmem_zalloc(nodename_size, KM_SLEEP);
155110e73f9Sschwartz 
156110e73f9Sschwartz 	platform_specific_data =
1572917a9c9Sschwartz 	    kmem_zalloc(sizeof (fire4u_specific_t), KM_SLEEP);
158110e73f9Sschwartz 
159110e73f9Sschwartz 	(void) strcpy(nodename, name);
160110e73f9Sschwartz 	(void) strcat(nodename, ":");
161110e73f9Sschwartz 	(void) strcat(nodename, PCI_MINOR_REG);
162110e73f9Sschwartz 	platform_specific_data->nodename = nodename;
163110e73f9Sschwartz 
164110e73f9Sschwartz 	/* Get register banks. */
165110e73f9Sschwartz 	if (ddi_getlongprop(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
1662917a9c9Sschwartz 	    "reg", (caddr_t)&regs_p, &regs_length) != DDI_SUCCESS) {
167110e73f9Sschwartz 		goto bad_regs_p;
168110e73f9Sschwartz 	}
169110e73f9Sschwartz 
170110e73f9Sschwartz 	if ((regs_length / sizeof (px_regs_t)) < 2) {
171110e73f9Sschwartz 		goto bad_regs_length;
172110e73f9Sschwartz 	}
173110e73f9Sschwartz 
174110e73f9Sschwartz 	platform_specific_data->jbus_bank_base =
175110e73f9Sschwartz 	    regs_p[JBUS_BANK].addr_lo & BANK_ADDR_MASK;
176110e73f9Sschwartz 
177110e73f9Sschwartz 	kmem_free(regs_p, regs_length);
178110e73f9Sschwartz 
179110e73f9Sschwartz 	if (index == 0)
180110e73f9Sschwartz 		*avail |= (PCIE_A_REGS_AVAIL | JBUS_REGS_AVAIL);
181110e73f9Sschwartz 	else
182110e73f9Sschwartz 		*avail |= PCIE_B_REGS_AVAIL;
183110e73f9Sschwartz 
184110e73f9Sschwartz 	(void) fpc_set_platform_data_by_number(index, platform_specific_data);
185110e73f9Sschwartz 
186110e73f9Sschwartz 	return (DDI_SUCCESS);
187110e73f9Sschwartz 
188110e73f9Sschwartz bad_regs_length:
189110e73f9Sschwartz 	if (regs_p)
190110e73f9Sschwartz 		kmem_free(regs_p, regs_length);
191110e73f9Sschwartz bad_regs_p:
192c5fab18eSToomas Soome 	kmem_free(platform_specific_data, sizeof (fire4u_specific_t));
193110e73f9Sschwartz 	if (nodename)
194110e73f9Sschwartz 		kmem_free(nodename, nodename_size);
195110e73f9Sschwartz 
196110e73f9Sschwartz 	return (DDI_FAILURE);
197110e73f9Sschwartz }
198110e73f9Sschwartz 
199110e73f9Sschwartz void
fpc_platform_node_fini(void * arg)200110e73f9Sschwartz fpc_platform_node_fini(void *arg)
201110e73f9Sschwartz {
202110e73f9Sschwartz 	fire4u_specific_t *plat_arg = (fire4u_specific_t *)arg;
203110e73f9Sschwartz 	if (plat_arg == NULL)
204110e73f9Sschwartz 		return;
205110e73f9Sschwartz 	if (plat_arg->nodename)
206110e73f9Sschwartz 		kmem_free(plat_arg->nodename, strlen(plat_arg->nodename)+1);
207110e73f9Sschwartz 	kmem_free(plat_arg, sizeof (fire4u_specific_t));
208110e73f9Sschwartz }
209110e73f9Sschwartz 
210110e73f9Sschwartz /*ARGSUSED*/
211110e73f9Sschwartz void
fpc_platform_module_fini(dev_info_t * dip)212110e73f9Sschwartz fpc_platform_module_fini(dev_info_t *dip)
213110e73f9Sschwartz {
214110e73f9Sschwartz 	if (ldi_identifier_valid)
215110e73f9Sschwartz 		ldi_ident_release(ldi_identifier);
216110e73f9Sschwartz }
217110e73f9Sschwartz 
218110e73f9Sschwartz fire_perfreg_handle_t
fpc_get_perfreg_handle(int devnum)219110e73f9Sschwartz fpc_get_perfreg_handle(int devnum)
220110e73f9Sschwartz {
221110e73f9Sschwartz 	int rval = EINVAL;
222110e73f9Sschwartz 
223110e73f9Sschwartz 	fire_counter_handle_impl_t *handle_impl =
224110e73f9Sschwartz 	    kmem_zalloc(sizeof (fire_counter_handle_impl_t), KM_SLEEP);
225110e73f9Sschwartz 
226110e73f9Sschwartz 	if ((handle_impl->devspec =
227110e73f9Sschwartz 	    fpc_get_platform_data_by_number(devnum)) != NULL) {
228110e73f9Sschwartz 		rval = ldi_open_by_name(handle_impl->devspec->nodename,
229*526073d8SMatt Barden 		    OPEN_FLAGS, kcred, &handle_impl->devhandle,
230110e73f9Sschwartz 		    ldi_identifier);
231110e73f9Sschwartz 	}
232110e73f9Sschwartz 
233110e73f9Sschwartz 	if (rval != SUCCESS) {
234110e73f9Sschwartz 		kmem_free(handle_impl, sizeof (fire_counter_handle_impl_t));
235110e73f9Sschwartz 		return ((fire_perfreg_handle_t)-1);
236110e73f9Sschwartz 	} else {
237110e73f9Sschwartz 		return ((fire_perfreg_handle_t)handle_impl);
238110e73f9Sschwartz 	}
239110e73f9Sschwartz }
240110e73f9Sschwartz 
241110e73f9Sschwartz int
fpc_free_counter_handle(fire_perfreg_handle_t handle)242110e73f9Sschwartz fpc_free_counter_handle(fire_perfreg_handle_t handle)
243110e73f9Sschwartz {
244110e73f9Sschwartz 	fire_counter_handle_impl_t *handle_impl =
245110e73f9Sschwartz 	    (fire_counter_handle_impl_t *)handle;
246*526073d8SMatt Barden 	(void) ldi_close(handle_impl->devhandle, OPEN_FLAGS, kcred);
247110e73f9Sschwartz 	kmem_free(handle_impl, sizeof (fire_counter_handle_impl_t));
248110e73f9Sschwartz 	return (SUCCESS);
249110e73f9Sschwartz }
250110e73f9Sschwartz 
251110e73f9Sschwartz int
fpc_event_io(fire_perfreg_handle_t handle,fire_perfcnt_t group,uint64_t * reg_data,boolean_t is_write)252110e73f9Sschwartz fpc_event_io(fire_perfreg_handle_t handle, fire_perfcnt_t group,
253110e73f9Sschwartz     uint64_t *reg_data, boolean_t is_write)
254110e73f9Sschwartz {
255110e73f9Sschwartz 	int rval;
256110e73f9Sschwartz 	int ioctl_rval;
257110e73f9Sschwartz 	pcitool_reg_t prg;
258110e73f9Sschwartz 	fire_counter_handle_impl_t *handle_impl =
259110e73f9Sschwartz 	    (fire_counter_handle_impl_t *)handle;
260110e73f9Sschwartz 	int cmd = is_write ? PCITOOL_NEXUS_SET_REG : PCITOOL_NEXUS_GET_REG;
261110e73f9Sschwartz 
2622917a9c9Sschwartz 	prg.user_version = PCITOOL_VERSION;
263110e73f9Sschwartz 
264110e73f9Sschwartz 	if (group == jbc) {
265110e73f9Sschwartz 		prg.barnum = JBUS_BANK;
266110e73f9Sschwartz 		prg.offset = counter_select_offsets[group] -
267110e73f9Sschwartz 		    handle_impl->devspec->jbus_bank_base;
268110e73f9Sschwartz 	} else {
269110e73f9Sschwartz 		prg.barnum = PCIE_BANK;
270110e73f9Sschwartz 
271110e73f9Sschwartz 		/*
272110e73f9Sschwartz 		 * Note that a pcie_bank_base isn't needed.  Pcie register
273110e73f9Sschwartz 		 * offsets are already relative to the start of their bank.  No
274110e73f9Sschwartz 		 * base needs to be subtracted to get the relative offset that
275110e73f9Sschwartz 		 * pcitool ioctls want.
276110e73f9Sschwartz 		 */
277110e73f9Sschwartz 		prg.offset = counter_select_offsets[group];
278110e73f9Sschwartz 	}
279110e73f9Sschwartz 	prg.acc_attr = PCITOOL_ACC_ATTR_SIZE_8 | PCITOOL_ACC_ATTR_ENDN_BIG;
280110e73f9Sschwartz 	prg.data = *reg_data;
281110e73f9Sschwartz 
282110e73f9Sschwartz 	/* Read original value. */
283110e73f9Sschwartz 	if (((rval = ldi_ioctl(handle_impl->devhandle, cmd, (intptr_t)&prg,
284*526073d8SMatt Barden 	    FKIOCTL, kcred, &ioctl_rval)) == SUCCESS) && (!is_write)) {
285110e73f9Sschwartz 		*reg_data = prg.data;
286110e73f9Sschwartz 	}
287110e73f9Sschwartz 
288110e73f9Sschwartz 	return (rval);
289110e73f9Sschwartz }
290110e73f9Sschwartz 
291110e73f9Sschwartz int
fpc_counter_io(fire_perfreg_handle_t handle,fire_perfcnt_t group,int counter_index,uint64_t * value,boolean_t is_write)292110e73f9Sschwartz fpc_counter_io(fire_perfreg_handle_t handle, fire_perfcnt_t group,
293110e73f9Sschwartz     int counter_index, uint64_t *value, boolean_t is_write)
294110e73f9Sschwartz {
295110e73f9Sschwartz 	int rval;
296110e73f9Sschwartz 	int ioctl_rval;
297110e73f9Sschwartz 	pcitool_reg_t prg;
298110e73f9Sschwartz 	fire_counter_handle_impl_t *handle_impl =
299110e73f9Sschwartz 	    (fire_counter_handle_impl_t *)handle;
300110e73f9Sschwartz 	int command =
301110e73f9Sschwartz 	    (is_write) ? PCITOOL_NEXUS_SET_REG : PCITOOL_NEXUS_GET_REG;
302110e73f9Sschwartz 
3032917a9c9Sschwartz 	prg.user_version = PCITOOL_VERSION;
304110e73f9Sschwartz 	/*
305110e73f9Sschwartz 	 * Note that stated PCIE offsets are relative to the beginning of their
306110e73f9Sschwartz 	 * register bank, while JBUS offsets are absolute.
307110e73f9Sschwartz 	 */
308110e73f9Sschwartz 	if (group == jbc) {
309110e73f9Sschwartz 		prg.barnum = JBUS_BANK;
310110e73f9Sschwartz 		prg.offset = counter_reg_offsets[counter_index] -
311110e73f9Sschwartz 		    handle_impl->devspec->jbus_bank_base;
312110e73f9Sschwartz 	} else {
313110e73f9Sschwartz 		prg.barnum = PCIE_BANK;
314110e73f9Sschwartz 		prg.offset = counter_reg_offsets[counter_index];
315110e73f9Sschwartz 	}
316110e73f9Sschwartz 
317110e73f9Sschwartz 	if ((group == lpu) && (is_write)) {
318110e73f9Sschwartz 		prg.offset += LPU_LINK_PERFCTR_WRITE_OFFSET;
319110e73f9Sschwartz 	}
320110e73f9Sschwartz 
321110e73f9Sschwartz 	prg.acc_attr = PCITOOL_ACC_ATTR_SIZE_8 | PCITOOL_ACC_ATTR_ENDN_BIG;
322110e73f9Sschwartz 	prg.data = *value;
323110e73f9Sschwartz 
324110e73f9Sschwartz 	if (((rval = ldi_ioctl(handle_impl->devhandle, command, (intptr_t)&prg,
325*526073d8SMatt Barden 	    FKIOCTL, kcred, &ioctl_rval)) == SUCCESS) && (!is_write)) {
326110e73f9Sschwartz 		*value = prg.data;
327110e73f9Sschwartz 	}
328110e73f9Sschwartz 
329110e73f9Sschwartz 	return (rval);
330110e73f9Sschwartz }
331