xref: /onnv-gate/usr/src/uts/i86pc/io/pci/pci_common.c (revision 881:e6bc7f4b8a33)
1*881Sjohnny /*
2*881Sjohnny  * CDDL HEADER START
3*881Sjohnny  *
4*881Sjohnny  * The contents of this file are subject to the terms of the
5*881Sjohnny  * Common Development and Distribution License, Version 1.0 only
6*881Sjohnny  * (the "License").  You may not use this file except in compliance
7*881Sjohnny  * with the License.
8*881Sjohnny  *
9*881Sjohnny  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10*881Sjohnny  * or http://www.opensolaris.org/os/licensing.
11*881Sjohnny  * See the License for the specific language governing permissions
12*881Sjohnny  * and limitations under the License.
13*881Sjohnny  *
14*881Sjohnny  * When distributing Covered Code, include this CDDL HEADER in each
15*881Sjohnny  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16*881Sjohnny  * If applicable, add the following below this CDDL HEADER, with the
17*881Sjohnny  * fields enclosed by brackets "[]" replaced with your own identifying
18*881Sjohnny  * information: Portions Copyright [yyyy] [name of copyright owner]
19*881Sjohnny  *
20*881Sjohnny  * CDDL HEADER END
21*881Sjohnny  */
22*881Sjohnny 
23*881Sjohnny /*
24*881Sjohnny  * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
25*881Sjohnny  * Use is subject to license terms.
26*881Sjohnny  */
27*881Sjohnny 
28*881Sjohnny #pragma ident	"%Z%%M%	%I%	%E% SMI"
29*881Sjohnny 
30*881Sjohnny /*
31*881Sjohnny  *	File that has code which is common between pci(7d) and npe(7d)
32*881Sjohnny  *	It shares the following:
33*881Sjohnny  *	- interrupt code
34*881Sjohnny  *	- pci_tools ioctl code
35*881Sjohnny  *	- name_child code
36*881Sjohnny  *	- set_parent_private_data code
37*881Sjohnny  */
38*881Sjohnny 
39*881Sjohnny #include <sys/conf.h>
40*881Sjohnny #include <sys/pci.h>
41*881Sjohnny #include <sys/sunndi.h>
42*881Sjohnny #include <sys/hotplug/pci/pcihp.h>
43*881Sjohnny #include <sys/pci_intr_lib.h>
44*881Sjohnny #include <sys/psm.h>
45*881Sjohnny #include <sys/policy.h>
46*881Sjohnny #include <sys/sysmacros.h>
47*881Sjohnny #include <sys/pci_tools.h>
48*881Sjohnny #include <io/pci/pci_var.h>
49*881Sjohnny #include <io/pci/pci_tools_ext.h>
50*881Sjohnny #include <io/pci/pci_common.h>
51*881Sjohnny 
52*881Sjohnny /*
53*881Sjohnny  * Function prototypes
54*881Sjohnny  */
55*881Sjohnny static int	pci_get_priority(dev_info_t *, ddi_intr_handle_impl_t *, int *);
56*881Sjohnny static int	pci_get_nintrs(dev_info_t *, int, int *);
57*881Sjohnny static int	pci_enable_intr(dev_info_t *, dev_info_t *,
58*881Sjohnny 		    ddi_intr_handle_impl_t *, uint32_t);
59*881Sjohnny static void	pci_disable_intr(dev_info_t *, dev_info_t *,
60*881Sjohnny 		    ddi_intr_handle_impl_t *, uint32_t);
61*881Sjohnny 
62*881Sjohnny /* Extern decalration for pcplusmp module */
63*881Sjohnny extern int	(*psm_intr_ops)(dev_info_t *, ddi_intr_handle_impl_t *,
64*881Sjohnny 		    psm_intr_op_t, int *);
65*881Sjohnny 
66*881Sjohnny 
67*881Sjohnny /*
68*881Sjohnny  * pci_name_child:
69*881Sjohnny  *
70*881Sjohnny  *	Assign the address portion of the node name
71*881Sjohnny  */
72*881Sjohnny int
73*881Sjohnny pci_common_name_child(dev_info_t *child, char *name, int namelen)
74*881Sjohnny {
75*881Sjohnny 	int		dev, func, length;
76*881Sjohnny 	char		**unit_addr;
77*881Sjohnny 	uint_t		n;
78*881Sjohnny 	pci_regspec_t	*pci_rp;
79*881Sjohnny 
80*881Sjohnny 	if (ndi_dev_is_persistent_node(child) == 0) {
81*881Sjohnny 		/*
82*881Sjohnny 		 * For .conf node, use "unit-address" property
83*881Sjohnny 		 */
84*881Sjohnny 		if (ddi_prop_lookup_string_array(DDI_DEV_T_ANY, child,
85*881Sjohnny 		    DDI_PROP_DONTPASS, "unit-address", &unit_addr, &n) !=
86*881Sjohnny 		    DDI_PROP_SUCCESS) {
87*881Sjohnny 			cmn_err(CE_WARN, "cannot find unit-address in %s.conf",
88*881Sjohnny 			    ddi_get_name(child));
89*881Sjohnny 			return (DDI_FAILURE);
90*881Sjohnny 		}
91*881Sjohnny 		if (n != 1 || *unit_addr == NULL || **unit_addr == 0) {
92*881Sjohnny 			cmn_err(CE_WARN, "unit-address property in %s.conf"
93*881Sjohnny 			    " not well-formed", ddi_get_name(child));
94*881Sjohnny 			ddi_prop_free(unit_addr);
95*881Sjohnny 			return (DDI_FAILURE);
96*881Sjohnny 		}
97*881Sjohnny 		(void) snprintf(name, namelen, "%s", *unit_addr);
98*881Sjohnny 		ddi_prop_free(unit_addr);
99*881Sjohnny 		return (DDI_SUCCESS);
100*881Sjohnny 	}
101*881Sjohnny 
102*881Sjohnny 	if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, child, DDI_PROP_DONTPASS,
103*881Sjohnny 	    "reg", (int **)&pci_rp, (uint_t *)&length) != DDI_PROP_SUCCESS) {
104*881Sjohnny 		cmn_err(CE_WARN, "cannot find reg property in %s",
105*881Sjohnny 		    ddi_get_name(child));
106*881Sjohnny 		return (DDI_FAILURE);
107*881Sjohnny 	}
108*881Sjohnny 
109*881Sjohnny 	/* copy the device identifications */
110*881Sjohnny 	dev = PCI_REG_DEV_G(pci_rp->pci_phys_hi);
111*881Sjohnny 	func = PCI_REG_FUNC_G(pci_rp->pci_phys_hi);
112*881Sjohnny 
113*881Sjohnny 	/*
114*881Sjohnny 	 * free the memory allocated by ddi_prop_lookup_int_array
115*881Sjohnny 	 */
116*881Sjohnny 	ddi_prop_free(pci_rp);
117*881Sjohnny 
118*881Sjohnny 	if (func != 0) {
119*881Sjohnny 		(void) snprintf(name, namelen, "%x,%x", dev, func);
120*881Sjohnny 	} else {
121*881Sjohnny 		(void) snprintf(name, namelen, "%x", dev);
122*881Sjohnny 	}
123*881Sjohnny 
124*881Sjohnny 	return (DDI_SUCCESS);
125*881Sjohnny }
126*881Sjohnny 
127*881Sjohnny /*
128*881Sjohnny  * Interrupt related code:
129*881Sjohnny  *
130*881Sjohnny  * The following busop is common to npe and pci drivers
131*881Sjohnny  *	bus_introp
132*881Sjohnny  */
133*881Sjohnny 
134*881Sjohnny /*
135*881Sjohnny  * Create the ddi_parent_private_data for a pseudo child.
136*881Sjohnny  */
137*881Sjohnny void
138*881Sjohnny pci_common_set_parent_private_data(dev_info_t *dip)
139*881Sjohnny {
140*881Sjohnny 	struct ddi_parent_private_data *pdptr;
141*881Sjohnny 
142*881Sjohnny 	pdptr = (struct ddi_parent_private_data *)kmem_zalloc(
143*881Sjohnny 	    (sizeof (struct ddi_parent_private_data) +
144*881Sjohnny 	sizeof (struct intrspec)), KM_SLEEP);
145*881Sjohnny 	pdptr->par_intr = (struct intrspec *)(pdptr + 1);
146*881Sjohnny 	pdptr->par_nintr = 1;
147*881Sjohnny 	ddi_set_parent_data(dip, pdptr);
148*881Sjohnny }
149*881Sjohnny 
150*881Sjohnny /*
151*881Sjohnny  * pci_get_priority:
152*881Sjohnny  *	Figure out the priority of the device
153*881Sjohnny  */
154*881Sjohnny static int
155*881Sjohnny pci_get_priority(dev_info_t *dip, ddi_intr_handle_impl_t *hdlp, int *pri)
156*881Sjohnny {
157*881Sjohnny 	struct intrspec *ispec;
158*881Sjohnny 
159*881Sjohnny 	DDI_INTR_NEXDBG((CE_CONT, "pci_get_priority: dip = 0x%p, hdlp = %p\n",
160*881Sjohnny 	    (void *)dip, (void *)hdlp));
161*881Sjohnny 
162*881Sjohnny 	if ((ispec = (struct intrspec *)pci_intx_get_ispec(dip, dip,
163*881Sjohnny 	    hdlp->ih_inum)) == NULL) {
164*881Sjohnny 		if (DDI_INTR_IS_MSI_OR_MSIX(hdlp->ih_type)) {
165*881Sjohnny 			int class = ddi_prop_get_int(DDI_DEV_T_ANY, dip,
166*881Sjohnny 			    DDI_PROP_DONTPASS, "class-code", -1);
167*881Sjohnny 
168*881Sjohnny 			*pri = (class == -1) ? 1 : pci_devclass_to_ipl(class);
169*881Sjohnny 			pci_common_set_parent_private_data(hdlp->ih_dip);
170*881Sjohnny 			ispec = (struct intrspec *)pci_intx_get_ispec(dip, dip,
171*881Sjohnny 			    hdlp->ih_inum);
172*881Sjohnny 			return (DDI_SUCCESS);
173*881Sjohnny 		}
174*881Sjohnny 		return (DDI_FAILURE);
175*881Sjohnny 	}
176*881Sjohnny 
177*881Sjohnny 	*pri = ispec->intrspec_pri;
178*881Sjohnny 	return (DDI_SUCCESS);
179*881Sjohnny }
180*881Sjohnny 
181*881Sjohnny 
182*881Sjohnny /*
183*881Sjohnny  * pci_get_nintrs:
184*881Sjohnny  *	Figure out how many interrupts the device supports
185*881Sjohnny  */
186*881Sjohnny static int
187*881Sjohnny pci_get_nintrs(dev_info_t *dip, int type, int *nintrs)
188*881Sjohnny {
189*881Sjohnny 	int	ret;
190*881Sjohnny 
191*881Sjohnny 	*nintrs = 0;
192*881Sjohnny 
193*881Sjohnny 	if (DDI_INTR_IS_MSI_OR_MSIX(type))
194*881Sjohnny 		ret = pci_msi_get_nintrs(dip, type, nintrs);
195*881Sjohnny 	else {
196*881Sjohnny 		ret = DDI_FAILURE;
197*881Sjohnny 		if (ddi_prop_get_int(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
198*881Sjohnny 		    "interrupts", -1) != -1) {
199*881Sjohnny 			*nintrs = 1;
200*881Sjohnny 			ret = DDI_SUCCESS;
201*881Sjohnny 		}
202*881Sjohnny 	}
203*881Sjohnny 
204*881Sjohnny 	return (ret);
205*881Sjohnny }
206*881Sjohnny 
207*881Sjohnny static int pcie_pci_intr_pri_counter = 0;
208*881Sjohnny 
209*881Sjohnny /*
210*881Sjohnny  * pci_common_intr_ops: bus_intr_op() function for interrupt support
211*881Sjohnny  */
212*881Sjohnny int
213*881Sjohnny pci_common_intr_ops(dev_info_t *pdip, dev_info_t *rdip, ddi_intr_op_t intr_op,
214*881Sjohnny     ddi_intr_handle_impl_t *hdlp, void *result)
215*881Sjohnny {
216*881Sjohnny 	int			priority = 0;
217*881Sjohnny 	int			psm_status = 0;
218*881Sjohnny 	int			pci_status = 0;
219*881Sjohnny 	int			pci_rval, psm_rval = PSM_FAILURE;
220*881Sjohnny 	int			types = 0;
221*881Sjohnny 	int			pciepci = 0;
222*881Sjohnny 	int			i, j;
223*881Sjohnny 	int			behavior;
224*881Sjohnny 	ddi_intrspec_t		isp;
225*881Sjohnny 	struct intrspec		*ispec;
226*881Sjohnny 	ddi_intr_handle_impl_t	tmp_hdl;
227*881Sjohnny 	ddi_intr_msix_t		*msix_p;
228*881Sjohnny 
229*881Sjohnny 	DDI_INTR_NEXDBG((CE_CONT,
230*881Sjohnny 	    "pci_common_intr_ops: pdip 0x%p, rdip 0x%p, op %x handle 0x%p\n",
231*881Sjohnny 	    (void *)pdip, (void *)rdip, intr_op, (void *)hdlp));
232*881Sjohnny 
233*881Sjohnny 	/* Process the request */
234*881Sjohnny 	switch (intr_op) {
235*881Sjohnny 	case DDI_INTROP_SUPPORTED_TYPES:
236*881Sjohnny 		/* Fixed supported by default */
237*881Sjohnny 		*(int *)result = DDI_INTR_TYPE_FIXED;
238*881Sjohnny 
239*881Sjohnny 		/* Figure out if MSI or MSI-X is supported? */
240*881Sjohnny 		if (pci_msi_get_supported_type(rdip, &types) != DDI_SUCCESS)
241*881Sjohnny 			return (DDI_SUCCESS);
242*881Sjohnny 
243*881Sjohnny 		if (psm_intr_ops != NULL) {
244*881Sjohnny 			/* MSI or MSI-X is supported, OR it in */
245*881Sjohnny 			*(int *)result |= types;
246*881Sjohnny 
247*881Sjohnny 			tmp_hdl.ih_type = *(int *)result;
248*881Sjohnny 			(void) (*psm_intr_ops)(rdip, &tmp_hdl,
249*881Sjohnny 			    PSM_INTR_OP_CHECK_MSI, result);
250*881Sjohnny 			DDI_INTR_NEXDBG((CE_CONT, "pci_common_intr_ops: "
251*881Sjohnny 			    "rdip: 0x%p supported types: 0x%x\n", (void *)rdip,
252*881Sjohnny 			    *(int *)result));
253*881Sjohnny 		}
254*881Sjohnny 		break;
255*881Sjohnny 	case DDI_INTROP_NINTRS:
256*881Sjohnny 		if (pci_get_nintrs(rdip, hdlp->ih_type, result) != DDI_SUCCESS)
257*881Sjohnny 			return (DDI_FAILURE);
258*881Sjohnny 		break;
259*881Sjohnny 	case DDI_INTROP_ALLOC:
260*881Sjohnny 		/*
261*881Sjohnny 		 * MSI or MSIX (figure out number of vectors available)
262*881Sjohnny 		 * FIXED interrupts: just return available interrupts
263*881Sjohnny 		 */
264*881Sjohnny 		if (DDI_INTR_IS_MSI_OR_MSIX(hdlp->ih_type) &&
265*881Sjohnny 		    (psm_intr_ops != NULL) &&
266*881Sjohnny 		    (pci_get_priority(rdip, hdlp, &priority) == DDI_SUCCESS)) {
267*881Sjohnny 			/*
268*881Sjohnny 			 * Following check is a special case for 'pcie_pci'.
269*881Sjohnny 			 * This makes sure vectors with the right priority
270*881Sjohnny 			 * are allocated for pcie_pci during ALLOC time.
271*881Sjohnny 			 */
272*881Sjohnny 			if (strcmp(ddi_driver_name(rdip), "pcie_pci") == 0) {
273*881Sjohnny 				hdlp->ih_pri =
274*881Sjohnny 				    (pcie_pci_intr_pri_counter % 2) ? 4 : 7;
275*881Sjohnny 				pciepci = 1;
276*881Sjohnny 			} else
277*881Sjohnny 				hdlp->ih_pri = priority;
278*881Sjohnny 			behavior = hdlp->ih_scratch2;
279*881Sjohnny 			(void) (*psm_intr_ops)(rdip, hdlp,
280*881Sjohnny 			    PSM_INTR_OP_ALLOC_VECTORS, result);
281*881Sjohnny 
282*881Sjohnny 			/* verify behavior flag and take appropriate action */
283*881Sjohnny 			if ((behavior == DDI_INTR_ALLOC_STRICT) &&
284*881Sjohnny 			    (*(int *)result < hdlp->ih_scratch1)) {
285*881Sjohnny 				DDI_INTR_NEXDBG((CE_CONT,
286*881Sjohnny 				    "pci_common_intr_ops: behavior %x, "
287*881Sjohnny 				    "couldn't get enough intrs\n", behavior));
288*881Sjohnny 				hdlp->ih_scratch1 = *(int *)result;
289*881Sjohnny 				(void) (*psm_intr_ops)(rdip, hdlp,
290*881Sjohnny 				    PSM_INTR_OP_FREE_VECTORS, NULL);
291*881Sjohnny 				return (DDI_EAGAIN);
292*881Sjohnny 			}
293*881Sjohnny 
294*881Sjohnny 			if (hdlp->ih_type == DDI_INTR_TYPE_MSIX) {
295*881Sjohnny 				if (!(msix_p = i_ddi_get_msix(hdlp->ih_dip))) {
296*881Sjohnny 					msix_p = pci_msix_init(hdlp->ih_dip);
297*881Sjohnny 					if (msix_p)
298*881Sjohnny 						i_ddi_set_msix(hdlp->ih_dip,
299*881Sjohnny 						    msix_p);
300*881Sjohnny 				}
301*881Sjohnny 				msix_p->msix_intrs_in_use += *(int *)result;
302*881Sjohnny 			}
303*881Sjohnny 
304*881Sjohnny 			if (pciepci) {
305*881Sjohnny 				/* update priority in ispec */
306*881Sjohnny 				isp = pci_intx_get_ispec(pdip, rdip,
307*881Sjohnny 					(int)hdlp->ih_inum);
308*881Sjohnny 				ispec = (struct intrspec *)isp;
309*881Sjohnny 				if (ispec)
310*881Sjohnny 					ispec->intrspec_pri = hdlp->ih_pri;
311*881Sjohnny 				++pcie_pci_intr_pri_counter;
312*881Sjohnny 			}
313*881Sjohnny 
314*881Sjohnny 		} else if (hdlp->ih_type == DDI_INTR_TYPE_FIXED) {
315*881Sjohnny 			/* Figure out if this device supports MASKING */
316*881Sjohnny 			pci_rval = pci_intx_get_cap(rdip, &pci_status);
317*881Sjohnny 			if (pci_rval == DDI_SUCCESS && pci_status)
318*881Sjohnny 				hdlp->ih_cap |= pci_status;
319*881Sjohnny 			*(int *)result = 1;	/* DDI_INTR_TYPE_FIXED */
320*881Sjohnny 		} else
321*881Sjohnny 			return (DDI_FAILURE);
322*881Sjohnny 		break;
323*881Sjohnny 	case DDI_INTROP_FREE:
324*881Sjohnny 		if (DDI_INTR_IS_MSI_OR_MSIX(hdlp->ih_type) &&
325*881Sjohnny 		    (psm_intr_ops != NULL)) {
326*881Sjohnny 			(void) (*psm_intr_ops)(rdip, hdlp,
327*881Sjohnny 			    PSM_INTR_OP_FREE_VECTORS, NULL);
328*881Sjohnny 
329*881Sjohnny 			if (hdlp->ih_type == DDI_INTR_TYPE_MSIX) {
330*881Sjohnny 				msix_p = i_ddi_get_msix(hdlp->ih_dip);
331*881Sjohnny 				if (msix_p &&
332*881Sjohnny 				    --msix_p->msix_intrs_in_use == 0) {
333*881Sjohnny 					pci_msix_fini(msix_p);
334*881Sjohnny 					i_ddi_set_msix(hdlp->ih_dip, NULL);
335*881Sjohnny 				}
336*881Sjohnny 			}
337*881Sjohnny 		}
338*881Sjohnny 		break;
339*881Sjohnny 	case DDI_INTROP_GETPRI:
340*881Sjohnny 		/* Get the priority */
341*881Sjohnny 		if (pci_get_priority(rdip, hdlp, &priority) != DDI_SUCCESS)
342*881Sjohnny 			return (DDI_FAILURE);
343*881Sjohnny 		DDI_INTR_NEXDBG((CE_CONT, "pci_common_intr_ops: "
344*881Sjohnny 		    "priority = 0x%x\n", priority));
345*881Sjohnny 		*(int *)result = priority;
346*881Sjohnny 		break;
347*881Sjohnny 	case DDI_INTROP_SETPRI:
348*881Sjohnny 		/* Validate the interrupt priority passed */
349*881Sjohnny 		if (*(int *)result > LOCK_LEVEL)
350*881Sjohnny 			return (DDI_FAILURE);
351*881Sjohnny 
352*881Sjohnny 		/* Ensure that PSM is all initialized */
353*881Sjohnny 		if (psm_intr_ops == NULL)
354*881Sjohnny 			return (DDI_FAILURE);
355*881Sjohnny 
356*881Sjohnny 		/* Change the priority */
357*881Sjohnny 		if ((*psm_intr_ops)(rdip, hdlp, PSM_INTR_OP_SET_PRI, result) ==
358*881Sjohnny 		    PSM_FAILURE)
359*881Sjohnny 			return (DDI_FAILURE);
360*881Sjohnny 
361*881Sjohnny 		/* update ispec */
362*881Sjohnny 		isp = pci_intx_get_ispec(pdip, rdip, (int)hdlp->ih_inum);
363*881Sjohnny 		ispec = (struct intrspec *)isp;
364*881Sjohnny 		if (ispec)
365*881Sjohnny 			ispec->intrspec_pri = *(int *)result;
366*881Sjohnny 		break;
367*881Sjohnny 	case DDI_INTROP_ADDISR:
368*881Sjohnny 		/* update ispec */
369*881Sjohnny 		isp = pci_intx_get_ispec(pdip, rdip, (int)hdlp->ih_inum);
370*881Sjohnny 		ispec = (struct intrspec *)isp;
371*881Sjohnny 		if (ispec)
372*881Sjohnny 			ispec->intrspec_func = hdlp->ih_cb_func;
373*881Sjohnny 		break;
374*881Sjohnny 	case DDI_INTROP_REMISR:
375*881Sjohnny 		/* Get the interrupt structure pointer */
376*881Sjohnny 		isp = pci_intx_get_ispec(pdip, rdip, (int)hdlp->ih_inum);
377*881Sjohnny 		ispec = (struct intrspec *)isp;
378*881Sjohnny 		if (ispec)
379*881Sjohnny 			ispec->intrspec_func = (uint_t (*)()) 0;
380*881Sjohnny 		break;
381*881Sjohnny 	case DDI_INTROP_GETCAP:
382*881Sjohnny 		/*
383*881Sjohnny 		 * First check the config space and/or
384*881Sjohnny 		 * MSI capability register(s)
385*881Sjohnny 		 */
386*881Sjohnny 		if (DDI_INTR_IS_MSI_OR_MSIX(hdlp->ih_type))
387*881Sjohnny 			pci_rval = pci_msi_get_cap(rdip, hdlp->ih_type,
388*881Sjohnny 			    &pci_status);
389*881Sjohnny 		else if (hdlp->ih_type == DDI_INTR_TYPE_FIXED)
390*881Sjohnny 			pci_rval = pci_intx_get_cap(rdip, &pci_status);
391*881Sjohnny 
392*881Sjohnny 		/* next check with pcplusmp */
393*881Sjohnny 		if (psm_intr_ops != NULL)
394*881Sjohnny 			psm_rval = (*psm_intr_ops)(rdip, hdlp,
395*881Sjohnny 			    PSM_INTR_OP_GET_CAP, &psm_status);
396*881Sjohnny 
397*881Sjohnny 		DDI_INTR_NEXDBG((CE_CONT, "pci: GETCAP returned psm_rval = %x, "
398*881Sjohnny 		    "psm_status = %x, pci_rval = %x, pci_status = %x\n",
399*881Sjohnny 		    psm_rval, psm_status, pci_rval, pci_status));
400*881Sjohnny 
401*881Sjohnny 		if (psm_rval == PSM_FAILURE && pci_rval == DDI_FAILURE) {
402*881Sjohnny 			*(int *)result = 0;
403*881Sjohnny 			return (DDI_FAILURE);
404*881Sjohnny 		}
405*881Sjohnny 
406*881Sjohnny 		if (psm_rval == PSM_SUCCESS)
407*881Sjohnny 			*(int *)result = psm_status;
408*881Sjohnny 
409*881Sjohnny 		if (pci_rval == DDI_SUCCESS)
410*881Sjohnny 			*(int *)result |= pci_status;
411*881Sjohnny 
412*881Sjohnny 		DDI_INTR_NEXDBG((CE_CONT, "pci: GETCAP returned = %x\n",
413*881Sjohnny 		    *(int *)result));
414*881Sjohnny 		break;
415*881Sjohnny 	case DDI_INTROP_SETCAP:
416*881Sjohnny 		DDI_INTR_NEXDBG((CE_CONT, "pci_common_intr_ops: "
417*881Sjohnny 		    "SETCAP cap=0x%x\n", *(int *)result));
418*881Sjohnny 		if (psm_intr_ops == NULL)
419*881Sjohnny 			return (DDI_FAILURE);
420*881Sjohnny 
421*881Sjohnny 		if ((*psm_intr_ops)(rdip, hdlp, PSM_INTR_OP_SET_CAP, result)) {
422*881Sjohnny 			DDI_INTR_NEXDBG((CE_CONT, "GETCAP: psm_intr_ops"
423*881Sjohnny 			    " returned failure\n"));
424*881Sjohnny 			return (DDI_FAILURE);
425*881Sjohnny 		}
426*881Sjohnny 		break;
427*881Sjohnny 	case DDI_INTROP_ENABLE:
428*881Sjohnny 		DDI_INTR_NEXDBG((CE_CONT, "pci_common_intr_ops: ENABLE\n"));
429*881Sjohnny 		if (psm_intr_ops == NULL)
430*881Sjohnny 			return (DDI_FAILURE);
431*881Sjohnny 
432*881Sjohnny 		if (pci_enable_intr(pdip, rdip, hdlp, hdlp->ih_inum) !=
433*881Sjohnny 		    DDI_SUCCESS)
434*881Sjohnny 			return (DDI_FAILURE);
435*881Sjohnny 
436*881Sjohnny 		DDI_INTR_NEXDBG((CE_CONT, "pci_common_intr_ops: ENABLE "
437*881Sjohnny 		    "vector=0x%x\n", hdlp->ih_vector));
438*881Sjohnny 		break;
439*881Sjohnny 	case DDI_INTROP_DISABLE:
440*881Sjohnny 		DDI_INTR_NEXDBG((CE_CONT, "pci_common_intr_ops: DISABLE\n"));
441*881Sjohnny 		if (psm_intr_ops == NULL)
442*881Sjohnny 			return (DDI_FAILURE);
443*881Sjohnny 
444*881Sjohnny 		pci_disable_intr(pdip, rdip, hdlp, hdlp->ih_inum);
445*881Sjohnny 		DDI_INTR_NEXDBG((CE_CONT, "pci_common_intr_ops: DISABLE "
446*881Sjohnny 		    "vector = %x\n", hdlp->ih_vector));
447*881Sjohnny 		break;
448*881Sjohnny 	case DDI_INTROP_BLOCKENABLE:
449*881Sjohnny 		DDI_INTR_NEXDBG((CE_CONT, "pci_common_intr_ops: "
450*881Sjohnny 		    "BLOCKENABLE\n"));
451*881Sjohnny 		if (hdlp->ih_type != DDI_INTR_TYPE_MSI) {
452*881Sjohnny 			DDI_INTR_NEXDBG((CE_CONT, "BLOCKENABLE: not MSI\n"));
453*881Sjohnny 			return (DDI_FAILURE);
454*881Sjohnny 		}
455*881Sjohnny 
456*881Sjohnny 		/* Check if psm_intr_ops is NULL? */
457*881Sjohnny 		if (psm_intr_ops == NULL)
458*881Sjohnny 			return (DDI_FAILURE);
459*881Sjohnny 
460*881Sjohnny 		for (i = 0; i < hdlp->ih_scratch1; i++) {
461*881Sjohnny 			if (pci_enable_intr(pdip, rdip, hdlp,
462*881Sjohnny 			    hdlp->ih_inum + i) != DDI_SUCCESS) {
463*881Sjohnny 				DDI_INTR_NEXDBG((CE_CONT, "BLOCKENABLE: "
464*881Sjohnny 				    "pci_enable_intr failed for %d\n", i));
465*881Sjohnny 				for (j = 0; j < i; j++)
466*881Sjohnny 					pci_disable_intr(pdip, rdip, hdlp,
467*881Sjohnny 					    hdlp->ih_inum + j);
468*881Sjohnny 				return (DDI_FAILURE);
469*881Sjohnny 			}
470*881Sjohnny 			DDI_INTR_NEXDBG((CE_CONT, "pci_common_intr_ops: "
471*881Sjohnny 			    "BLOCKENABLE inum %x done\n", hdlp->ih_inum + i));
472*881Sjohnny 		}
473*881Sjohnny 		break;
474*881Sjohnny 	case DDI_INTROP_BLOCKDISABLE:
475*881Sjohnny 		DDI_INTR_NEXDBG((CE_CONT, "pci_common_intr_ops: "
476*881Sjohnny 		    "BLOCKDISABLE\n"));
477*881Sjohnny 		if (hdlp->ih_type != DDI_INTR_TYPE_MSI) {
478*881Sjohnny 			DDI_INTR_NEXDBG((CE_CONT, "BLOCKDISABLE: not MSI\n"));
479*881Sjohnny 			return (DDI_FAILURE);
480*881Sjohnny 		}
481*881Sjohnny 
482*881Sjohnny 		/* Check if psm_intr_ops is present */
483*881Sjohnny 		if (psm_intr_ops == NULL)
484*881Sjohnny 			return (DDI_FAILURE);
485*881Sjohnny 
486*881Sjohnny 		for (i = 0; i < hdlp->ih_scratch1; i++) {
487*881Sjohnny 			pci_disable_intr(pdip, rdip, hdlp, hdlp->ih_inum + i);
488*881Sjohnny 			DDI_INTR_NEXDBG((CE_CONT, "pci_common_intr_ops: "
489*881Sjohnny 			    "BLOCKDISABLE inum %x done\n", hdlp->ih_inum + i));
490*881Sjohnny 		}
491*881Sjohnny 		break;
492*881Sjohnny 	case DDI_INTROP_SETMASK:
493*881Sjohnny 	case DDI_INTROP_CLRMASK:
494*881Sjohnny 		/*
495*881Sjohnny 		 * First handle in the config space
496*881Sjohnny 		 */
497*881Sjohnny 		if (intr_op == DDI_INTROP_SETMASK) {
498*881Sjohnny 			if (DDI_INTR_IS_MSI_OR_MSIX(hdlp->ih_type))
499*881Sjohnny 				pci_status = pci_msi_set_mask(rdip,
500*881Sjohnny 				    hdlp->ih_type, hdlp->ih_inum);
501*881Sjohnny 			else if (hdlp->ih_type == DDI_INTR_TYPE_FIXED)
502*881Sjohnny 				pci_status = pci_intx_set_mask(rdip);
503*881Sjohnny 		} else {
504*881Sjohnny 			if (DDI_INTR_IS_MSI_OR_MSIX(hdlp->ih_type))
505*881Sjohnny 				pci_status = pci_msi_clr_mask(rdip,
506*881Sjohnny 				    hdlp->ih_type, hdlp->ih_inum);
507*881Sjohnny 			else if (hdlp->ih_type == DDI_INTR_TYPE_FIXED)
508*881Sjohnny 				pci_status = pci_intx_clr_mask(rdip);
509*881Sjohnny 		}
510*881Sjohnny 
511*881Sjohnny 		/* For MSI/X; no need to check with pcplusmp */
512*881Sjohnny 		if (hdlp->ih_type != DDI_INTR_TYPE_FIXED)
513*881Sjohnny 			return (pci_status);
514*881Sjohnny 
515*881Sjohnny 		/* For fixed interrupts only: handle config space first */
516*881Sjohnny 		if (hdlp->ih_type == DDI_INTR_TYPE_FIXED &&
517*881Sjohnny 		    pci_status == DDI_SUCCESS)
518*881Sjohnny 			break;
519*881Sjohnny 
520*881Sjohnny 		/* For fixed interrupts only: confer with pcplusmp next */
521*881Sjohnny 		if (psm_intr_ops != NULL) {
522*881Sjohnny 			/* If interrupt is shared; do nothing */
523*881Sjohnny 			psm_rval = (*psm_intr_ops)(rdip, hdlp,
524*881Sjohnny 			    PSM_INTR_OP_GET_SHARED, &psm_status);
525*881Sjohnny 
526*881Sjohnny 			if (psm_rval == PSM_FAILURE || psm_status == 1)
527*881Sjohnny 				return (pci_status);
528*881Sjohnny 
529*881Sjohnny 			/* Now, pcplusmp should try to set/clear the mask */
530*881Sjohnny 			if (intr_op == DDI_INTROP_SETMASK)
531*881Sjohnny 				psm_rval = (*psm_intr_ops)(rdip, hdlp,
532*881Sjohnny 				    PSM_INTR_OP_SET_MASK, NULL);
533*881Sjohnny 			else
534*881Sjohnny 				psm_rval = (*psm_intr_ops)(rdip, hdlp,
535*881Sjohnny 				    PSM_INTR_OP_CLEAR_MASK, NULL);
536*881Sjohnny 		}
537*881Sjohnny 		return ((psm_rval == PSM_FAILURE) ? DDI_FAILURE : DDI_SUCCESS);
538*881Sjohnny 	case DDI_INTROP_GETPENDING:
539*881Sjohnny 		/*
540*881Sjohnny 		 * First check the config space and/or
541*881Sjohnny 		 * MSI capability register(s)
542*881Sjohnny 		 */
543*881Sjohnny 		if (DDI_INTR_IS_MSI_OR_MSIX(hdlp->ih_type))
544*881Sjohnny 			pci_rval = pci_msi_get_pending(rdip, hdlp->ih_type,
545*881Sjohnny 			    hdlp->ih_inum, &pci_status);
546*881Sjohnny 		else if (hdlp->ih_type == DDI_INTR_TYPE_FIXED)
547*881Sjohnny 			pci_rval = pci_intx_get_pending(rdip, &pci_status);
548*881Sjohnny 
549*881Sjohnny 		/* On failure; next try with pcplusmp */
550*881Sjohnny 		if (pci_rval != DDI_SUCCESS && psm_intr_ops != NULL)
551*881Sjohnny 			psm_rval = (*psm_intr_ops)(rdip, hdlp,
552*881Sjohnny 			    PSM_INTR_OP_GET_PENDING, &psm_status);
553*881Sjohnny 
554*881Sjohnny 		DDI_INTR_NEXDBG((CE_CONT, "pci: GETPENDING returned "
555*881Sjohnny 		    "psm_rval = %x, psm_status = %x, pci_rval = %x, "
556*881Sjohnny 		    "pci_status = %x\n", psm_rval, psm_status, pci_rval,
557*881Sjohnny 		    pci_status));
558*881Sjohnny 		if (psm_rval == PSM_FAILURE && pci_rval == DDI_FAILURE) {
559*881Sjohnny 			*(int *)result = 0;
560*881Sjohnny 			return (DDI_FAILURE);
561*881Sjohnny 		}
562*881Sjohnny 
563*881Sjohnny 		if (psm_rval != PSM_FAILURE)
564*881Sjohnny 			*(int *)result = psm_status;
565*881Sjohnny 		else if (pci_rval != DDI_FAILURE)
566*881Sjohnny 			*(int *)result = pci_status;
567*881Sjohnny 		DDI_INTR_NEXDBG((CE_CONT, "pci: GETPENDING returned = %x\n",
568*881Sjohnny 		    *(int *)result));
569*881Sjohnny 		break;
570*881Sjohnny 	case DDI_INTROP_NAVAIL:
571*881Sjohnny 		if ((psm_intr_ops != NULL) && (pci_get_priority(rdip,
572*881Sjohnny 		    hdlp, &priority) == DDI_SUCCESS)) {
573*881Sjohnny 			/* Priority in the handle not initialized yet */
574*881Sjohnny 			hdlp->ih_pri = priority;
575*881Sjohnny 			(void) (*psm_intr_ops)(rdip, hdlp,
576*881Sjohnny 			    PSM_INTR_OP_NAVAIL_VECTORS, result);
577*881Sjohnny 		} else {
578*881Sjohnny 			*(int *)result = 1;
579*881Sjohnny 		}
580*881Sjohnny 		DDI_INTR_NEXDBG((CE_CONT, "pci: NAVAIL returned = %x\n",
581*881Sjohnny 		    *(int *)result));
582*881Sjohnny 		break;
583*881Sjohnny 	default:
584*881Sjohnny 		return (i_ddi_intr_ops(pdip, rdip, intr_op, hdlp, result));
585*881Sjohnny 	}
586*881Sjohnny 
587*881Sjohnny 	return (DDI_SUCCESS);
588*881Sjohnny }
589*881Sjohnny 
590*881Sjohnny 
591*881Sjohnny static int
592*881Sjohnny pci_enable_intr(dev_info_t *pdip, dev_info_t *rdip,
593*881Sjohnny     ddi_intr_handle_impl_t *hdlp, uint32_t inum)
594*881Sjohnny {
595*881Sjohnny 	int		vector;
596*881Sjohnny 	struct intrspec	*ispec;
597*881Sjohnny 
598*881Sjohnny 	DDI_INTR_NEXDBG((CE_CONT, "pci_enable_intr: hdlp %p inum %x\n",
599*881Sjohnny 	    (void *)hdlp, inum));
600*881Sjohnny 
601*881Sjohnny 	/* Translate the interrupt if needed */
602*881Sjohnny 	ispec = (struct intrspec *)pci_intx_get_ispec(pdip, rdip, (int)inum);
603*881Sjohnny 	if (DDI_INTR_IS_MSI_OR_MSIX(hdlp->ih_type) && ispec)
604*881Sjohnny 		ispec->intrspec_vec = inum;
605*881Sjohnny 	hdlp->ih_private = (void *)ispec;
606*881Sjohnny 
607*881Sjohnny 	/* translate the interrupt if needed */
608*881Sjohnny 	(void) (*psm_intr_ops)(rdip, hdlp, PSM_INTR_OP_XLATE_VECTOR, &vector);
609*881Sjohnny 	DDI_INTR_NEXDBG((CE_CONT, "pci_enable_intr: priority=%x vector=%x\n",
610*881Sjohnny 	    hdlp->ih_pri, vector));
611*881Sjohnny 
612*881Sjohnny 	/* Add the interrupt handler */
613*881Sjohnny 	if (!add_avintr((void *)hdlp, hdlp->ih_pri, hdlp->ih_cb_func,
614*881Sjohnny 	    DEVI(rdip)->devi_name, vector, hdlp->ih_cb_arg1,
615*881Sjohnny 	    hdlp->ih_cb_arg2, rdip))
616*881Sjohnny 		return (DDI_FAILURE);
617*881Sjohnny 
618*881Sjohnny 	return (DDI_SUCCESS);
619*881Sjohnny }
620*881Sjohnny 
621*881Sjohnny 
622*881Sjohnny static void
623*881Sjohnny pci_disable_intr(dev_info_t *pdip, dev_info_t *rdip,
624*881Sjohnny     ddi_intr_handle_impl_t *hdlp, uint32_t inum)
625*881Sjohnny {
626*881Sjohnny 	int		vector;
627*881Sjohnny 	struct intrspec	*ispec;
628*881Sjohnny 
629*881Sjohnny 	DDI_INTR_NEXDBG((CE_CONT, "pci_disable_intr: \n"));
630*881Sjohnny 	ispec = (struct intrspec *)pci_intx_get_ispec(pdip, rdip, (int)inum);
631*881Sjohnny 	if (DDI_INTR_IS_MSI_OR_MSIX(hdlp->ih_type) && ispec)
632*881Sjohnny 		ispec->intrspec_vec = inum;
633*881Sjohnny 	hdlp->ih_private = (void *)ispec;
634*881Sjohnny 
635*881Sjohnny 	/* translate the interrupt if needed */
636*881Sjohnny 	(void) (*psm_intr_ops)(rdip, hdlp, PSM_INTR_OP_XLATE_VECTOR, &vector);
637*881Sjohnny 
638*881Sjohnny 	/* Disable the interrupt handler */
639*881Sjohnny 	rem_avintr((void *)hdlp, hdlp->ih_pri, hdlp->ih_cb_func, vector);
640*881Sjohnny }
641*881Sjohnny 
642*881Sjohnny /*
643*881Sjohnny  * Miscellaneous library function
644*881Sjohnny  */
645*881Sjohnny int
646*881Sjohnny pci_common_get_reg_prop(dev_info_t *dip, pci_regspec_t *pci_rp)
647*881Sjohnny {
648*881Sjohnny 	int		i;
649*881Sjohnny 	int 		number;
650*881Sjohnny 	int		assigned_addr_len;
651*881Sjohnny 	uint_t		phys_hi = pci_rp->pci_phys_hi;
652*881Sjohnny 	pci_regspec_t	*assigned_addr;
653*881Sjohnny 
654*881Sjohnny 	if (((phys_hi & PCI_REG_ADDR_M) == PCI_ADDR_CONFIG) ||
655*881Sjohnny 	    (phys_hi & PCI_RELOCAT_B))
656*881Sjohnny 		return (DDI_SUCCESS);
657*881Sjohnny 
658*881Sjohnny 	/*
659*881Sjohnny 	 * the "reg" property specifies relocatable, get and interpret the
660*881Sjohnny 	 * "assigned-addresses" property.
661*881Sjohnny 	 */
662*881Sjohnny 	if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
663*881Sjohnny 	    "assigned-addresses", (int **)&assigned_addr,
664*881Sjohnny 	    (uint_t *)&assigned_addr_len) != DDI_PROP_SUCCESS)
665*881Sjohnny 		return (DDI_FAILURE);
666*881Sjohnny 
667*881Sjohnny 	/*
668*881Sjohnny 	 * Scan the "assigned-addresses" for one that matches the specified
669*881Sjohnny 	 * "reg" property entry.
670*881Sjohnny 	 */
671*881Sjohnny 	phys_hi &= PCI_CONF_ADDR_MASK;
672*881Sjohnny 	number = assigned_addr_len / (sizeof (pci_regspec_t) / sizeof (int));
673*881Sjohnny 	for (i = 0; i < number; i++) {
674*881Sjohnny 		if ((assigned_addr[i].pci_phys_hi & PCI_CONF_ADDR_MASK) ==
675*881Sjohnny 		    phys_hi) {
676*881Sjohnny 			pci_rp->pci_phys_mid = assigned_addr[i].pci_phys_mid;
677*881Sjohnny 			pci_rp->pci_phys_low = assigned_addr[i].pci_phys_low;
678*881Sjohnny 			ddi_prop_free(assigned_addr);
679*881Sjohnny 			return (DDI_SUCCESS);
680*881Sjohnny 		}
681*881Sjohnny 	}
682*881Sjohnny 
683*881Sjohnny 	ddi_prop_free(assigned_addr);
684*881Sjohnny 	return (DDI_FAILURE);
685*881Sjohnny }
686*881Sjohnny 
687*881Sjohnny 
688*881Sjohnny /*
689*881Sjohnny  * For pci_tools
690*881Sjohnny  */
691*881Sjohnny 
692*881Sjohnny int
693*881Sjohnny pci_common_ioctl(dev_info_t *dip, dev_t dev, int cmd, intptr_t arg,
694*881Sjohnny     int mode, cred_t *credp, int *rvalp)
695*881Sjohnny {
696*881Sjohnny 	int rv = ENOTTY;
697*881Sjohnny 
698*881Sjohnny 	minor_t minor = getminor(dev);
699*881Sjohnny 
700*881Sjohnny 	switch (PCIHP_AP_MINOR_NUM_TO_PCI_DEVNUM(minor)) {
701*881Sjohnny 	case PCI_TOOL_REG_MINOR_NUM:
702*881Sjohnny 
703*881Sjohnny 		switch (cmd) {
704*881Sjohnny 		case PCITOOL_DEVICE_SET_REG:
705*881Sjohnny 		case PCITOOL_DEVICE_GET_REG:
706*881Sjohnny 
707*881Sjohnny 			/* Require full privileges. */
708*881Sjohnny 			if (secpolicy_kmdb(credp))
709*881Sjohnny 				rv = EPERM;
710*881Sjohnny 			else
711*881Sjohnny 				rv = pcitool_dev_reg_ops(dip, (void *)arg,
712*881Sjohnny 				    cmd, mode);
713*881Sjohnny 			break;
714*881Sjohnny 
715*881Sjohnny 		case PCITOOL_NEXUS_SET_REG:
716*881Sjohnny 		case PCITOOL_NEXUS_GET_REG:
717*881Sjohnny 
718*881Sjohnny 			/* Require full privileges. */
719*881Sjohnny 			if (secpolicy_kmdb(credp))
720*881Sjohnny 				rv = EPERM;
721*881Sjohnny 			else
722*881Sjohnny 				rv = pcitool_bus_reg_ops(dip, (void *)arg,
723*881Sjohnny 				    cmd, mode);
724*881Sjohnny 			break;
725*881Sjohnny 		}
726*881Sjohnny 		break;
727*881Sjohnny 
728*881Sjohnny 	case PCI_TOOL_INTR_MINOR_NUM:
729*881Sjohnny 
730*881Sjohnny 		switch (cmd) {
731*881Sjohnny 		case PCITOOL_DEVICE_SET_INTR:
732*881Sjohnny 
733*881Sjohnny 			/* Require PRIV_SYS_RES_CONFIG, same as psradm */
734*881Sjohnny 			if (secpolicy_ponline(credp)) {
735*881Sjohnny 				rv = EPERM;
736*881Sjohnny 				break;
737*881Sjohnny 			}
738*881Sjohnny 
739*881Sjohnny 		/*FALLTHRU*/
740*881Sjohnny 		/* These require no special privileges. */
741*881Sjohnny 		case PCITOOL_DEVICE_GET_INTR:
742*881Sjohnny 		case PCITOOL_DEVICE_NUM_INTR:
743*881Sjohnny 			rv = pcitool_intr_admn(dip, (void *)arg, cmd, mode);
744*881Sjohnny 			break;
745*881Sjohnny 		}
746*881Sjohnny 		break;
747*881Sjohnny 
748*881Sjohnny 	/*
749*881Sjohnny 	 * All non-PCItool ioctls go through here, including:
750*881Sjohnny 	 *   devctl ioctls with minor number PCIHP_DEVCTL_MINOR and
751*881Sjohnny 	 *   those for attachment points with where minor number is the
752*881Sjohnny 	 *   device number.
753*881Sjohnny 	 */
754*881Sjohnny 	default:
755*881Sjohnny 		rv = (pcihp_get_cb_ops())->cb_ioctl(dev, cmd, arg, mode,
756*881Sjohnny 		    credp, rvalp);
757*881Sjohnny 		break;
758*881Sjohnny 	}
759*881Sjohnny 
760*881Sjohnny 	return (rv);
761*881Sjohnny }
762