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