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