xref: /illumos-gate/usr/src/uts/i86pc/io/pci/pci_common.c (revision a07094369b21309434206d9b3601d162693466fc)
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 #include <sys/pci_cfgspace.h>
55 #include <sys/pci_impl.h>
56 
57 /*
58  * Function prototypes
59  */
60 static int	pci_get_priority(dev_info_t *, ddi_intr_handle_impl_t *, int *);
61 static int	pci_get_nintrs(dev_info_t *, int, int *);
62 static int	pci_enable_intr(dev_info_t *, dev_info_t *,
63 		    ddi_intr_handle_impl_t *, uint32_t);
64 static void	pci_disable_intr(dev_info_t *, dev_info_t *,
65 		    ddi_intr_handle_impl_t *, uint32_t);
66 
67 /* Extern decalration for pcplusmp module */
68 extern int	(*psm_intr_ops)(dev_info_t *, ddi_intr_handle_impl_t *,
69 		    psm_intr_op_t, int *);
70 
71 
72 /*
73  * pci_name_child:
74  *
75  *	Assign the address portion of the node name
76  */
77 int
78 pci_common_name_child(dev_info_t *child, char *name, int namelen)
79 {
80 	int		dev, func, length;
81 	char		**unit_addr;
82 	uint_t		n;
83 	pci_regspec_t	*pci_rp;
84 
85 	if (ndi_dev_is_persistent_node(child) == 0) {
86 		/*
87 		 * For .conf node, use "unit-address" property
88 		 */
89 		if (ddi_prop_lookup_string_array(DDI_DEV_T_ANY, child,
90 		    DDI_PROP_DONTPASS, "unit-address", &unit_addr, &n) !=
91 		    DDI_PROP_SUCCESS) {
92 			cmn_err(CE_WARN, "cannot find unit-address in %s.conf",
93 			    ddi_get_name(child));
94 			return (DDI_FAILURE);
95 		}
96 		if (n != 1 || *unit_addr == NULL || **unit_addr == 0) {
97 			cmn_err(CE_WARN, "unit-address property in %s.conf"
98 			    " not well-formed", ddi_get_name(child));
99 			ddi_prop_free(unit_addr);
100 			return (DDI_FAILURE);
101 		}
102 		(void) snprintf(name, namelen, "%s", *unit_addr);
103 		ddi_prop_free(unit_addr);
104 		return (DDI_SUCCESS);
105 	}
106 
107 	if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, child, DDI_PROP_DONTPASS,
108 	    "reg", (int **)&pci_rp, (uint_t *)&length) != DDI_PROP_SUCCESS) {
109 		cmn_err(CE_WARN, "cannot find reg property in %s",
110 		    ddi_get_name(child));
111 		return (DDI_FAILURE);
112 	}
113 
114 	/* copy the device identifications */
115 	dev = PCI_REG_DEV_G(pci_rp->pci_phys_hi);
116 	func = PCI_REG_FUNC_G(pci_rp->pci_phys_hi);
117 
118 	/*
119 	 * free the memory allocated by ddi_prop_lookup_int_array
120 	 */
121 	ddi_prop_free(pci_rp);
122 
123 	if (func != 0) {
124 		(void) snprintf(name, namelen, "%x,%x", dev, func);
125 	} else {
126 		(void) snprintf(name, namelen, "%x", dev);
127 	}
128 
129 	return (DDI_SUCCESS);
130 }
131 
132 /*
133  * Interrupt related code:
134  *
135  * The following busop is common to npe and pci drivers
136  *	bus_introp
137  */
138 
139 /*
140  * Create the ddi_parent_private_data for a pseudo child.
141  */
142 void
143 pci_common_set_parent_private_data(dev_info_t *dip)
144 {
145 	struct ddi_parent_private_data *pdptr;
146 
147 	pdptr = (struct ddi_parent_private_data *)kmem_zalloc(
148 	    (sizeof (struct ddi_parent_private_data) +
149 	sizeof (struct intrspec)), KM_SLEEP);
150 	pdptr->par_intr = (struct intrspec *)(pdptr + 1);
151 	pdptr->par_nintr = 1;
152 	ddi_set_parent_data(dip, pdptr);
153 }
154 
155 /*
156  * pci_get_priority:
157  *	Figure out the priority of the device
158  */
159 static int
160 pci_get_priority(dev_info_t *dip, ddi_intr_handle_impl_t *hdlp, int *pri)
161 {
162 	struct intrspec *ispec;
163 
164 	DDI_INTR_NEXDBG((CE_CONT, "pci_get_priority: dip = 0x%p, hdlp = %p\n",
165 	    (void *)dip, (void *)hdlp));
166 
167 	if ((ispec = (struct intrspec *)pci_intx_get_ispec(dip, dip,
168 	    hdlp->ih_inum)) == NULL) {
169 		if (DDI_INTR_IS_MSI_OR_MSIX(hdlp->ih_type)) {
170 			int class = ddi_prop_get_int(DDI_DEV_T_ANY, dip,
171 			    DDI_PROP_DONTPASS, "class-code", -1);
172 
173 			*pri = (class == -1) ? 1 : pci_devclass_to_ipl(class);
174 			pci_common_set_parent_private_data(hdlp->ih_dip);
175 			ispec = (struct intrspec *)pci_intx_get_ispec(dip, dip,
176 			    hdlp->ih_inum);
177 			return (DDI_SUCCESS);
178 		}
179 		return (DDI_FAILURE);
180 	}
181 
182 	*pri = ispec->intrspec_pri;
183 	return (DDI_SUCCESS);
184 }
185 
186 
187 /*
188  * pci_get_nintrs:
189  *	Figure out how many interrupts the device supports
190  */
191 static int
192 pci_get_nintrs(dev_info_t *dip, int type, int *nintrs)
193 {
194 	int	ret;
195 
196 	*nintrs = 0;
197 
198 	if (DDI_INTR_IS_MSI_OR_MSIX(type))
199 		ret = pci_msi_get_nintrs(dip, type, nintrs);
200 	else {
201 		ret = DDI_FAILURE;
202 		if (ddi_prop_get_int(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
203 		    "interrupts", -1) != -1) {
204 			*nintrs = 1;
205 			ret = DDI_SUCCESS;
206 		}
207 	}
208 
209 	return (ret);
210 }
211 
212 static int pcie_pci_intr_pri_counter = 0;
213 
214 /*
215  * pci_common_intr_ops: bus_intr_op() function for interrupt support
216  */
217 int
218 pci_common_intr_ops(dev_info_t *pdip, dev_info_t *rdip, ddi_intr_op_t intr_op,
219     ddi_intr_handle_impl_t *hdlp, void *result)
220 {
221 	int			priority = 0;
222 	int			psm_status = 0;
223 	int			pci_status = 0;
224 	int			pci_rval, psm_rval = PSM_FAILURE;
225 	int			types = 0;
226 	int			pciepci = 0;
227 	int			i, j;
228 	int			behavior;
229 	ddi_intrspec_t		isp;
230 	struct intrspec		*ispec;
231 	ddi_intr_handle_impl_t	tmp_hdl;
232 	ddi_intr_msix_t		*msix_p;
233 	ihdl_plat_t		*ihdl_plat_datap;
234 
235 	DDI_INTR_NEXDBG((CE_CONT,
236 	    "pci_common_intr_ops: pdip 0x%p, rdip 0x%p, op %x handle 0x%p\n",
237 	    (void *)pdip, (void *)rdip, intr_op, (void *)hdlp));
238 
239 	/* Process the request */
240 	switch (intr_op) {
241 	case DDI_INTROP_SUPPORTED_TYPES:
242 		/* Fixed supported by default */
243 		*(int *)result = DDI_INTR_TYPE_FIXED;
244 
245 		/* Figure out if MSI or MSI-X is supported? */
246 		if (pci_msi_get_supported_type(rdip, &types) != DDI_SUCCESS)
247 			return (DDI_SUCCESS);
248 
249 		if (psm_intr_ops != NULL) {
250 			/* MSI or MSI-X is supported, OR it in */
251 			*(int *)result |= types;
252 
253 			tmp_hdl.ih_type = *(int *)result;
254 			(void) (*psm_intr_ops)(rdip, &tmp_hdl,
255 			    PSM_INTR_OP_CHECK_MSI, result);
256 			DDI_INTR_NEXDBG((CE_CONT, "pci_common_intr_ops: "
257 			    "rdip: 0x%p supported types: 0x%x\n", (void *)rdip,
258 			    *(int *)result));
259 		}
260 		break;
261 	case DDI_INTROP_NINTRS:
262 		if (pci_get_nintrs(rdip, hdlp->ih_type, result) != DDI_SUCCESS)
263 			return (DDI_FAILURE);
264 		break;
265 	case DDI_INTROP_ALLOC:
266 		/*
267 		 * MSI or MSIX (figure out number of vectors available)
268 		 * FIXED interrupts: just return available interrupts
269 		 */
270 		if (DDI_INTR_IS_MSI_OR_MSIX(hdlp->ih_type) &&
271 		    (psm_intr_ops != NULL) &&
272 		    (pci_get_priority(rdip, hdlp, &priority) == DDI_SUCCESS)) {
273 			/*
274 			 * Following check is a special case for 'pcie_pci'.
275 			 * This makes sure vectors with the right priority
276 			 * are allocated for pcie_pci during ALLOC time.
277 			 */
278 			if (strcmp(ddi_driver_name(rdip), "pcie_pci") == 0) {
279 				hdlp->ih_pri =
280 				    (pcie_pci_intr_pri_counter % 2) ? 4 : 7;
281 				pciepci = 1;
282 			} else
283 				hdlp->ih_pri = priority;
284 			behavior = hdlp->ih_scratch2;
285 			(void) (*psm_intr_ops)(rdip, hdlp,
286 			    PSM_INTR_OP_ALLOC_VECTORS, result);
287 
288 			/* verify behavior flag and take appropriate action */
289 			if ((behavior == DDI_INTR_ALLOC_STRICT) &&
290 			    (*(int *)result < hdlp->ih_scratch1)) {
291 				DDI_INTR_NEXDBG((CE_CONT,
292 				    "pci_common_intr_ops: behavior %x, "
293 				    "couldn't get enough intrs\n", behavior));
294 				hdlp->ih_scratch1 = *(int *)result;
295 				(void) (*psm_intr_ops)(rdip, hdlp,
296 				    PSM_INTR_OP_FREE_VECTORS, NULL);
297 				return (DDI_EAGAIN);
298 			}
299 
300 			if (hdlp->ih_type == DDI_INTR_TYPE_MSIX) {
301 				if (!(msix_p = i_ddi_get_msix(hdlp->ih_dip))) {
302 					msix_p = pci_msix_init(hdlp->ih_dip);
303 					if (msix_p)
304 						i_ddi_set_msix(hdlp->ih_dip,
305 						    msix_p);
306 				}
307 				msix_p->msix_intrs_in_use += *(int *)result;
308 			}
309 
310 			if (pciepci) {
311 				/* update priority in ispec */
312 				isp = pci_intx_get_ispec(pdip, rdip,
313 					(int)hdlp->ih_inum);
314 				ispec = (struct intrspec *)isp;
315 				if (ispec)
316 					ispec->intrspec_pri = hdlp->ih_pri;
317 				++pcie_pci_intr_pri_counter;
318 			}
319 
320 		} else if (hdlp->ih_type == DDI_INTR_TYPE_FIXED) {
321 			/* Figure out if this device supports MASKING */
322 			pci_rval = pci_intx_get_cap(rdip, &pci_status);
323 			if (pci_rval == DDI_SUCCESS && pci_status)
324 				hdlp->ih_cap |= pci_status;
325 			*(int *)result = 1;	/* DDI_INTR_TYPE_FIXED */
326 		} else
327 			return (DDI_FAILURE);
328 		break;
329 	case DDI_INTROP_FREE:
330 		if (DDI_INTR_IS_MSI_OR_MSIX(hdlp->ih_type) &&
331 		    (psm_intr_ops != NULL)) {
332 			(void) (*psm_intr_ops)(rdip, hdlp,
333 			    PSM_INTR_OP_FREE_VECTORS, NULL);
334 
335 			if (hdlp->ih_type == DDI_INTR_TYPE_MSIX) {
336 				msix_p = i_ddi_get_msix(hdlp->ih_dip);
337 				if (msix_p &&
338 				    --msix_p->msix_intrs_in_use == 0) {
339 					pci_msix_fini(msix_p);
340 					i_ddi_set_msix(hdlp->ih_dip, NULL);
341 				}
342 			}
343 		}
344 		break;
345 	case DDI_INTROP_GETPRI:
346 		/* Get the priority */
347 		if (pci_get_priority(rdip, hdlp, &priority) != DDI_SUCCESS)
348 			return (DDI_FAILURE);
349 		DDI_INTR_NEXDBG((CE_CONT, "pci_common_intr_ops: "
350 		    "priority = 0x%x\n", priority));
351 		*(int *)result = priority;
352 		break;
353 	case DDI_INTROP_SETPRI:
354 		/* Validate the interrupt priority passed */
355 		if (*(int *)result > LOCK_LEVEL)
356 			return (DDI_FAILURE);
357 
358 		/* Ensure that PSM is all initialized */
359 		if (psm_intr_ops == NULL)
360 			return (DDI_FAILURE);
361 
362 		/* Change the priority */
363 		if ((*psm_intr_ops)(rdip, hdlp, PSM_INTR_OP_SET_PRI, result) ==
364 		    PSM_FAILURE)
365 			return (DDI_FAILURE);
366 
367 		/* update ispec */
368 		isp = pci_intx_get_ispec(pdip, rdip, (int)hdlp->ih_inum);
369 		ispec = (struct intrspec *)isp;
370 		if (ispec)
371 			ispec->intrspec_pri = *(int *)result;
372 		break;
373 	case DDI_INTROP_ADDISR:
374 		/* update ispec */
375 		isp = pci_intx_get_ispec(pdip, rdip, (int)hdlp->ih_inum);
376 		ispec = (struct intrspec *)isp;
377 		if (ispec) {
378 			ispec->intrspec_func = hdlp->ih_cb_func;
379 			ihdl_plat_datap = (ihdl_plat_t *)hdlp->ih_private;
380 			pci_kstat_create(&ihdl_plat_datap->ip_ksp, pdip, hdlp);
381 		}
382 		break;
383 	case DDI_INTROP_REMISR:
384 		/* Get the interrupt structure pointer */
385 		isp = pci_intx_get_ispec(pdip, rdip, (int)hdlp->ih_inum);
386 		ispec = (struct intrspec *)isp;
387 		if (ispec) {
388 			ispec->intrspec_func = (uint_t (*)()) 0;
389 			ihdl_plat_datap = (ihdl_plat_t *)hdlp->ih_private;
390 			if (ihdl_plat_datap->ip_ksp != NULL)
391 				pci_kstat_delete(ihdl_plat_datap->ip_ksp);
392 		}
393 		break;
394 	case DDI_INTROP_GETCAP:
395 		/*
396 		 * First check the config space and/or
397 		 * MSI capability register(s)
398 		 */
399 		if (DDI_INTR_IS_MSI_OR_MSIX(hdlp->ih_type))
400 			pci_rval = pci_msi_get_cap(rdip, hdlp->ih_type,
401 			    &pci_status);
402 		else if (hdlp->ih_type == DDI_INTR_TYPE_FIXED)
403 			pci_rval = pci_intx_get_cap(rdip, &pci_status);
404 
405 		/* next check with pcplusmp */
406 		if (psm_intr_ops != NULL)
407 			psm_rval = (*psm_intr_ops)(rdip, hdlp,
408 			    PSM_INTR_OP_GET_CAP, &psm_status);
409 
410 		DDI_INTR_NEXDBG((CE_CONT, "pci: GETCAP returned psm_rval = %x, "
411 		    "psm_status = %x, pci_rval = %x, pci_status = %x\n",
412 		    psm_rval, psm_status, pci_rval, pci_status));
413 
414 		if (psm_rval == PSM_FAILURE && pci_rval == DDI_FAILURE) {
415 			*(int *)result = 0;
416 			return (DDI_FAILURE);
417 		}
418 
419 		if (psm_rval == PSM_SUCCESS)
420 			*(int *)result = psm_status;
421 
422 		if (pci_rval == DDI_SUCCESS)
423 			*(int *)result |= pci_status;
424 
425 		DDI_INTR_NEXDBG((CE_CONT, "pci: GETCAP returned = %x\n",
426 		    *(int *)result));
427 		break;
428 	case DDI_INTROP_SETCAP:
429 		DDI_INTR_NEXDBG((CE_CONT, "pci_common_intr_ops: "
430 		    "SETCAP cap=0x%x\n", *(int *)result));
431 		if (psm_intr_ops == NULL)
432 			return (DDI_FAILURE);
433 
434 		if ((*psm_intr_ops)(rdip, hdlp, PSM_INTR_OP_SET_CAP, result)) {
435 			DDI_INTR_NEXDBG((CE_CONT, "GETCAP: psm_intr_ops"
436 			    " returned failure\n"));
437 			return (DDI_FAILURE);
438 		}
439 		break;
440 	case DDI_INTROP_ENABLE:
441 		DDI_INTR_NEXDBG((CE_CONT, "pci_common_intr_ops: ENABLE\n"));
442 		if (psm_intr_ops == NULL)
443 			return (DDI_FAILURE);
444 
445 		if (pci_enable_intr(pdip, rdip, hdlp, hdlp->ih_inum) !=
446 		    DDI_SUCCESS)
447 			return (DDI_FAILURE);
448 
449 		DDI_INTR_NEXDBG((CE_CONT, "pci_common_intr_ops: ENABLE "
450 		    "vector=0x%x\n", hdlp->ih_vector));
451 		break;
452 	case DDI_INTROP_DISABLE:
453 		DDI_INTR_NEXDBG((CE_CONT, "pci_common_intr_ops: DISABLE\n"));
454 		if (psm_intr_ops == NULL)
455 			return (DDI_FAILURE);
456 
457 		pci_disable_intr(pdip, rdip, hdlp, hdlp->ih_inum);
458 		DDI_INTR_NEXDBG((CE_CONT, "pci_common_intr_ops: DISABLE "
459 		    "vector = %x\n", hdlp->ih_vector));
460 		break;
461 	case DDI_INTROP_BLOCKENABLE:
462 		DDI_INTR_NEXDBG((CE_CONT, "pci_common_intr_ops: "
463 		    "BLOCKENABLE\n"));
464 		if (hdlp->ih_type != DDI_INTR_TYPE_MSI) {
465 			DDI_INTR_NEXDBG((CE_CONT, "BLOCKENABLE: not MSI\n"));
466 			return (DDI_FAILURE);
467 		}
468 
469 		/* Check if psm_intr_ops is NULL? */
470 		if (psm_intr_ops == NULL)
471 			return (DDI_FAILURE);
472 
473 		for (i = 0; i < hdlp->ih_scratch1; i++) {
474 			if (pci_enable_intr(pdip, rdip, hdlp,
475 			    hdlp->ih_inum + i) != DDI_SUCCESS) {
476 				DDI_INTR_NEXDBG((CE_CONT, "BLOCKENABLE: "
477 				    "pci_enable_intr failed for %d\n", i));
478 				for (j = 0; j < i; j++)
479 					pci_disable_intr(pdip, rdip, hdlp,
480 					    hdlp->ih_inum + j);
481 				return (DDI_FAILURE);
482 			}
483 			DDI_INTR_NEXDBG((CE_CONT, "pci_common_intr_ops: "
484 			    "BLOCKENABLE inum %x done\n", hdlp->ih_inum + i));
485 		}
486 		break;
487 	case DDI_INTROP_BLOCKDISABLE:
488 		DDI_INTR_NEXDBG((CE_CONT, "pci_common_intr_ops: "
489 		    "BLOCKDISABLE\n"));
490 		if (hdlp->ih_type != DDI_INTR_TYPE_MSI) {
491 			DDI_INTR_NEXDBG((CE_CONT, "BLOCKDISABLE: not MSI\n"));
492 			return (DDI_FAILURE);
493 		}
494 
495 		/* Check if psm_intr_ops is present */
496 		if (psm_intr_ops == NULL)
497 			return (DDI_FAILURE);
498 
499 		for (i = 0; i < hdlp->ih_scratch1; i++) {
500 			pci_disable_intr(pdip, rdip, hdlp, hdlp->ih_inum + i);
501 			DDI_INTR_NEXDBG((CE_CONT, "pci_common_intr_ops: "
502 			    "BLOCKDISABLE inum %x done\n", hdlp->ih_inum + i));
503 		}
504 		break;
505 	case DDI_INTROP_SETMASK:
506 	case DDI_INTROP_CLRMASK:
507 		/*
508 		 * First handle in the config space
509 		 */
510 		if (intr_op == DDI_INTROP_SETMASK) {
511 			if (DDI_INTR_IS_MSI_OR_MSIX(hdlp->ih_type))
512 				pci_status = pci_msi_set_mask(rdip,
513 				    hdlp->ih_type, hdlp->ih_inum);
514 			else if (hdlp->ih_type == DDI_INTR_TYPE_FIXED)
515 				pci_status = pci_intx_set_mask(rdip);
516 		} else {
517 			if (DDI_INTR_IS_MSI_OR_MSIX(hdlp->ih_type))
518 				pci_status = pci_msi_clr_mask(rdip,
519 				    hdlp->ih_type, hdlp->ih_inum);
520 			else if (hdlp->ih_type == DDI_INTR_TYPE_FIXED)
521 				pci_status = pci_intx_clr_mask(rdip);
522 		}
523 
524 		/* For MSI/X; no need to check with pcplusmp */
525 		if (hdlp->ih_type != DDI_INTR_TYPE_FIXED)
526 			return (pci_status);
527 
528 		/* For fixed interrupts only: handle config space first */
529 		if (hdlp->ih_type == DDI_INTR_TYPE_FIXED &&
530 		    pci_status == DDI_SUCCESS)
531 			break;
532 
533 		/* For fixed interrupts only: confer with pcplusmp next */
534 		if (psm_intr_ops != NULL) {
535 			/* If interrupt is shared; do nothing */
536 			psm_rval = (*psm_intr_ops)(rdip, hdlp,
537 			    PSM_INTR_OP_GET_SHARED, &psm_status);
538 
539 			if (psm_rval == PSM_FAILURE || psm_status == 1)
540 				return (pci_status);
541 
542 			/* Now, pcplusmp should try to set/clear the mask */
543 			if (intr_op == DDI_INTROP_SETMASK)
544 				psm_rval = (*psm_intr_ops)(rdip, hdlp,
545 				    PSM_INTR_OP_SET_MASK, NULL);
546 			else
547 				psm_rval = (*psm_intr_ops)(rdip, hdlp,
548 				    PSM_INTR_OP_CLEAR_MASK, NULL);
549 		}
550 		return ((psm_rval == PSM_FAILURE) ? DDI_FAILURE : DDI_SUCCESS);
551 	case DDI_INTROP_GETPENDING:
552 		/*
553 		 * First check the config space and/or
554 		 * MSI capability register(s)
555 		 */
556 		if (DDI_INTR_IS_MSI_OR_MSIX(hdlp->ih_type))
557 			pci_rval = pci_msi_get_pending(rdip, hdlp->ih_type,
558 			    hdlp->ih_inum, &pci_status);
559 		else if (hdlp->ih_type == DDI_INTR_TYPE_FIXED)
560 			pci_rval = pci_intx_get_pending(rdip, &pci_status);
561 
562 		/* On failure; next try with pcplusmp */
563 		if (pci_rval != DDI_SUCCESS && psm_intr_ops != NULL)
564 			psm_rval = (*psm_intr_ops)(rdip, hdlp,
565 			    PSM_INTR_OP_GET_PENDING, &psm_status);
566 
567 		DDI_INTR_NEXDBG((CE_CONT, "pci: GETPENDING returned "
568 		    "psm_rval = %x, psm_status = %x, pci_rval = %x, "
569 		    "pci_status = %x\n", psm_rval, psm_status, pci_rval,
570 		    pci_status));
571 		if (psm_rval == PSM_FAILURE && pci_rval == DDI_FAILURE) {
572 			*(int *)result = 0;
573 			return (DDI_FAILURE);
574 		}
575 
576 		if (psm_rval != PSM_FAILURE)
577 			*(int *)result = psm_status;
578 		else if (pci_rval != DDI_FAILURE)
579 			*(int *)result = pci_status;
580 		DDI_INTR_NEXDBG((CE_CONT, "pci: GETPENDING returned = %x\n",
581 		    *(int *)result));
582 		break;
583 	case DDI_INTROP_NAVAIL:
584 		if ((psm_intr_ops != NULL) && (pci_get_priority(rdip,
585 		    hdlp, &priority) == DDI_SUCCESS)) {
586 			/* Priority in the handle not initialized yet */
587 			hdlp->ih_pri = priority;
588 			(void) (*psm_intr_ops)(rdip, hdlp,
589 			    PSM_INTR_OP_NAVAIL_VECTORS, result);
590 		} else {
591 			*(int *)result = 1;
592 		}
593 		DDI_INTR_NEXDBG((CE_CONT, "pci: NAVAIL returned = %x\n",
594 		    *(int *)result));
595 		break;
596 	default:
597 		return (i_ddi_intr_ops(pdip, rdip, intr_op, hdlp, result));
598 	}
599 
600 	return (DDI_SUCCESS);
601 }
602 
603 int
604 pci_get_intr_from_vecirq(apic_get_intr_t *intrinfo_p,
605     int vecirq, boolean_t is_irq)
606 {
607 	ddi_intr_handle_impl_t	get_info_ii_hdl;
608 
609 	if (is_irq)
610 		intrinfo_p->avgi_req_flags |= PSMGI_INTRBY_IRQ;
611 
612 	/*
613 	 * For this locally-declared and used handle, ih_private will contain a
614 	 * pointer to apic_get_intr_t, not an ihdl_plat_t as used for
615 	 * global interrupt handling.
616 	 */
617 	get_info_ii_hdl.ih_private = intrinfo_p;
618 	get_info_ii_hdl.ih_vector = (ushort_t)vecirq;
619 
620 	if ((*psm_intr_ops)(NULL, &get_info_ii_hdl,
621 	    PSM_INTR_OP_GET_INTR, NULL) == PSM_FAILURE)
622 		return (DDI_FAILURE);
623 
624 	return (DDI_SUCCESS);
625 }
626 
627 
628 int
629 pci_get_cpu_from_vecirq(int vecirq, boolean_t is_irq)
630 {
631 	int rval;
632 
633 	apic_get_intr_t	intrinfo;
634 	intrinfo.avgi_req_flags = PSMGI_REQ_CPUID;
635 	rval = pci_get_intr_from_vecirq(&intrinfo, vecirq, is_irq);
636 
637 	if (rval == DDI_SUCCESS)
638 		return (intrinfo.avgi_cpu_id);
639 	else
640 		return (-1);
641 }
642 
643 
644 static int
645 pci_enable_intr(dev_info_t *pdip, dev_info_t *rdip,
646     ddi_intr_handle_impl_t *hdlp, uint32_t inum)
647 {
648 	struct intrspec	*ispec;
649 	int		irq;
650 	ihdl_plat_t	*ihdl_plat_datap = (ihdl_plat_t *)hdlp->ih_private;
651 
652 	DDI_INTR_NEXDBG((CE_CONT, "pci_enable_intr: hdlp %p inum %x\n",
653 	    (void *)hdlp, inum));
654 
655 	/* Translate the interrupt if needed */
656 	ispec = (struct intrspec *)pci_intx_get_ispec(pdip, rdip, (int)inum);
657 	if (DDI_INTR_IS_MSI_OR_MSIX(hdlp->ih_type) && ispec)
658 		ispec->intrspec_vec = inum;
659 	ihdl_plat_datap->ip_ispecp = ispec;
660 
661 	/* translate the interrupt if needed */
662 	(void) (*psm_intr_ops)(rdip, hdlp, PSM_INTR_OP_XLATE_VECTOR, &irq);
663 	DDI_INTR_NEXDBG((CE_CONT, "pci_enable_intr: priority=%x irq=%x\n",
664 	    hdlp->ih_pri, irq));
665 
666 	/* Add the interrupt handler */
667 	if (!add_avintr((void *)hdlp, hdlp->ih_pri, hdlp->ih_cb_func,
668 	    DEVI(rdip)->devi_name, irq, hdlp->ih_cb_arg1,
669 	    hdlp->ih_cb_arg2, &ihdl_plat_datap->ip_ticks, rdip))
670 		return (DDI_FAILURE);
671 
672 	/* Note this really is an irq. */
673 	hdlp->ih_vector = (ushort_t)irq;
674 
675 	return (DDI_SUCCESS);
676 }
677 
678 
679 static void
680 pci_disable_intr(dev_info_t *pdip, dev_info_t *rdip,
681     ddi_intr_handle_impl_t *hdlp, uint32_t inum)
682 {
683 	int		irq;
684 	struct intrspec	*ispec;
685 	ihdl_plat_t	*ihdl_plat_datap = (ihdl_plat_t *)hdlp->ih_private;
686 
687 	DDI_INTR_NEXDBG((CE_CONT, "pci_disable_intr: \n"));
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 
822 
823 /*
824  * These are the get and put functions to be shared with drivers. The
825  * mutex locking is done inside the functions referenced, rather than
826  * here, and is thus shared across PCI child drivers and any other
827  * consumers of PCI config space (such as the ACPI subsystem).
828  *
829  * The configuration space addresses come in as pointers.  This is fine on
830  * a 32-bit system, where the VM space and configuration space are the same
831  * size.  It's not such a good idea on a 64-bit system, where memory
832  * addresses are twice as large as configuration space addresses.  At some
833  * point in the call tree we need to take a stand and say "you are 32-bit
834  * from this time forth", and this seems like a nice self-contained place.
835  */
836 
837 uint8_t
838 pci_config_rd8(ddi_acc_impl_t *hdlp, uint8_t *addr)
839 {
840 	pci_acc_cfblk_t *cfp;
841 	uint8_t	rval;
842 	int reg;
843 
844 	ASSERT64(((uintptr_t)addr >> 32) == 0);
845 
846 	reg = (int)(uintptr_t)addr;
847 
848 	cfp = (pci_acc_cfblk_t *)&hdlp->ahi_common.ah_bus_private;
849 
850 	rval = (*pci_getb_func)(cfp->c_busnum, cfp->c_devnum, cfp->c_funcnum,
851 	    reg);
852 
853 	return (rval);
854 }
855 
856 void
857 pci_config_rep_rd8(ddi_acc_impl_t *hdlp, uint8_t *host_addr,
858 	uint8_t *dev_addr, size_t repcount, uint_t flags)
859 {
860 	uint8_t *h, *d;
861 
862 	h = host_addr;
863 	d = dev_addr;
864 
865 	if (flags == DDI_DEV_AUTOINCR)
866 		for (; repcount; repcount--)
867 			*h++ = pci_config_rd8(hdlp, d++);
868 	else
869 		for (; repcount; repcount--)
870 			*h++ = pci_config_rd8(hdlp, d);
871 }
872 
873 uint16_t
874 pci_config_rd16(ddi_acc_impl_t *hdlp, uint16_t *addr)
875 {
876 	pci_acc_cfblk_t *cfp;
877 	uint16_t rval;
878 	int reg;
879 
880 	ASSERT64(((uintptr_t)addr >> 32) == 0);
881 
882 	reg = (int)(uintptr_t)addr;
883 
884 	cfp = (pci_acc_cfblk_t *)&hdlp->ahi_common.ah_bus_private;
885 
886 	rval = (*pci_getw_func)(cfp->c_busnum, cfp->c_devnum, cfp->c_funcnum,
887 	    reg);
888 
889 	return (rval);
890 }
891 
892 void
893 pci_config_rep_rd16(ddi_acc_impl_t *hdlp, uint16_t *host_addr,
894 	uint16_t *dev_addr, size_t repcount, uint_t flags)
895 {
896 	uint16_t *h, *d;
897 
898 	h = host_addr;
899 	d = dev_addr;
900 
901 	if (flags == DDI_DEV_AUTOINCR)
902 		for (; repcount; repcount--)
903 			*h++ = pci_config_rd16(hdlp, d++);
904 	else
905 		for (; repcount; repcount--)
906 			*h++ = pci_config_rd16(hdlp, d);
907 }
908 
909 uint32_t
910 pci_config_rd32(ddi_acc_impl_t *hdlp, uint32_t *addr)
911 {
912 	pci_acc_cfblk_t *cfp;
913 	uint32_t rval;
914 	int reg;
915 
916 	ASSERT64(((uintptr_t)addr >> 32) == 0);
917 
918 	reg = (int)(uintptr_t)addr;
919 
920 	cfp = (pci_acc_cfblk_t *)&hdlp->ahi_common.ah_bus_private;
921 
922 	rval = (*pci_getl_func)(cfp->c_busnum, cfp->c_devnum,
923 	    cfp->c_funcnum, reg);
924 
925 	return (rval);
926 }
927 
928 void
929 pci_config_rep_rd32(ddi_acc_impl_t *hdlp, uint32_t *host_addr,
930 	uint32_t *dev_addr, size_t repcount, uint_t flags)
931 {
932 	uint32_t *h, *d;
933 
934 	h = host_addr;
935 	d = dev_addr;
936 
937 	if (flags == DDI_DEV_AUTOINCR)
938 		for (; repcount; repcount--)
939 			*h++ = pci_config_rd32(hdlp, d++);
940 	else
941 		for (; repcount; repcount--)
942 			*h++ = pci_config_rd32(hdlp, d);
943 }
944 
945 
946 void
947 pci_config_wr8(ddi_acc_impl_t *hdlp, uint8_t *addr, uint8_t value)
948 {
949 	pci_acc_cfblk_t *cfp;
950 	int reg;
951 
952 	ASSERT64(((uintptr_t)addr >> 32) == 0);
953 
954 	reg = (int)(uintptr_t)addr;
955 
956 	cfp = (pci_acc_cfblk_t *)&hdlp->ahi_common.ah_bus_private;
957 
958 	(*pci_putb_func)(cfp->c_busnum, cfp->c_devnum,
959 	    cfp->c_funcnum, reg, value);
960 }
961 
962 void
963 pci_config_rep_wr8(ddi_acc_impl_t *hdlp, uint8_t *host_addr,
964 	uint8_t *dev_addr, size_t repcount, uint_t flags)
965 {
966 	uint8_t *h, *d;
967 
968 	h = host_addr;
969 	d = dev_addr;
970 
971 	if (flags == DDI_DEV_AUTOINCR)
972 		for (; repcount; repcount--)
973 			pci_config_wr8(hdlp, d++, *h++);
974 	else
975 		for (; repcount; repcount--)
976 			pci_config_wr8(hdlp, d, *h++);
977 }
978 
979 void
980 pci_config_wr16(ddi_acc_impl_t *hdlp, uint16_t *addr, uint16_t value)
981 {
982 	pci_acc_cfblk_t *cfp;
983 	int reg;
984 
985 	ASSERT64(((uintptr_t)addr >> 32) == 0);
986 
987 	reg = (int)(uintptr_t)addr;
988 
989 	cfp = (pci_acc_cfblk_t *)&hdlp->ahi_common.ah_bus_private;
990 
991 	(*pci_putw_func)(cfp->c_busnum, cfp->c_devnum,
992 	    cfp->c_funcnum, reg, value);
993 }
994 
995 void
996 pci_config_rep_wr16(ddi_acc_impl_t *hdlp, uint16_t *host_addr,
997 	uint16_t *dev_addr, size_t repcount, uint_t flags)
998 {
999 	uint16_t *h, *d;
1000 
1001 	h = host_addr;
1002 	d = dev_addr;
1003 
1004 	if (flags == DDI_DEV_AUTOINCR)
1005 		for (; repcount; repcount--)
1006 			pci_config_wr16(hdlp, d++, *h++);
1007 	else
1008 		for (; repcount; repcount--)
1009 			pci_config_wr16(hdlp, d, *h++);
1010 }
1011 
1012 void
1013 pci_config_wr32(ddi_acc_impl_t *hdlp, uint32_t *addr, uint32_t value)
1014 {
1015 	pci_acc_cfblk_t *cfp;
1016 	int reg;
1017 
1018 	ASSERT64(((uintptr_t)addr >> 32) == 0);
1019 
1020 	reg = (int)(uintptr_t)addr;
1021 
1022 	cfp = (pci_acc_cfblk_t *)&hdlp->ahi_common.ah_bus_private;
1023 
1024 	(*pci_putl_func)(cfp->c_busnum, cfp->c_devnum,
1025 	    cfp->c_funcnum, reg, value);
1026 }
1027 
1028 void
1029 pci_config_rep_wr32(ddi_acc_impl_t *hdlp, uint32_t *host_addr,
1030 	uint32_t *dev_addr, size_t repcount, uint_t flags)
1031 {
1032 	uint32_t *h, *d;
1033 
1034 	h = host_addr;
1035 	d = dev_addr;
1036 
1037 	if (flags == DDI_DEV_AUTOINCR)
1038 		for (; repcount; repcount--)
1039 			pci_config_wr32(hdlp, d++, *h++);
1040 	else
1041 		for (; repcount; repcount--)
1042 			pci_config_wr32(hdlp, d, *h++);
1043 }
1044 
1045 uint64_t
1046 pci_config_rd64(ddi_acc_impl_t *hdlp, uint64_t *addr)
1047 {
1048 	uint32_t lw_val;
1049 	uint32_t hi_val;
1050 	uint32_t *dp;
1051 	uint64_t val;
1052 
1053 	dp = (uint32_t *)addr;
1054 	lw_val = pci_config_rd32(hdlp, dp);
1055 	dp++;
1056 	hi_val = pci_config_rd32(hdlp, dp);
1057 	val = ((uint64_t)hi_val << 32) | lw_val;
1058 	return (val);
1059 }
1060 
1061 void
1062 pci_config_wr64(ddi_acc_impl_t *hdlp, uint64_t *addr, uint64_t value)
1063 {
1064 	uint32_t lw_val;
1065 	uint32_t hi_val;
1066 	uint32_t *dp;
1067 
1068 	dp = (uint32_t *)addr;
1069 	lw_val = (uint32_t)(value & 0xffffffff);
1070 	hi_val = (uint32_t)(value >> 32);
1071 	pci_config_wr32(hdlp, dp, lw_val);
1072 	dp++;
1073 	pci_config_wr32(hdlp, dp, hi_val);
1074 }
1075 
1076 void
1077 pci_config_rep_rd64(ddi_acc_impl_t *hdlp, uint64_t *host_addr,
1078 	uint64_t *dev_addr, size_t repcount, uint_t flags)
1079 {
1080 	if (flags == DDI_DEV_AUTOINCR) {
1081 		for (; repcount; repcount--)
1082 			*host_addr++ = pci_config_rd64(hdlp, dev_addr++);
1083 	} else {
1084 		for (; repcount; repcount--)
1085 			*host_addr++ = pci_config_rd64(hdlp, dev_addr);
1086 	}
1087 }
1088 
1089 void
1090 pci_config_rep_wr64(ddi_acc_impl_t *hdlp, uint64_t *host_addr,
1091 	uint64_t *dev_addr, size_t repcount, uint_t flags)
1092 {
1093 	if (flags == DDI_DEV_AUTOINCR) {
1094 		for (; repcount; repcount--)
1095 			pci_config_wr64(hdlp, host_addr++, *dev_addr++);
1096 	} else {
1097 		for (; repcount; repcount--)
1098 			pci_config_wr64(hdlp, host_addr++, *dev_addr);
1099 	}
1100 }
1101 
1102 
1103 /*
1104  * Enable Legacy PCI config space access for the following four north bridges
1105  *	Host bridge: AMD HyperTransport Technology Configuration
1106  *	Host bridge: AMD Address Map
1107  *	Host bridge: AMD DRAM Controller
1108  *	Host bridge: AMD Miscellaneous Control
1109  */
1110 int
1111 is_amd_northbridge(dev_info_t *dip)
1112 {
1113 	int vendor_id, device_id;
1114 
1115 	vendor_id = ddi_prop_get_int(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
1116 			"vendor-id", -1);
1117 	device_id = ddi_prop_get_int(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
1118 			"device-id", -1);
1119 
1120 	if (IS_AMD_NTBRIDGE(vendor_id, device_id))
1121 		return (0);
1122 
1123 	return (1);
1124 }
1125