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