xref: /titanic_51/usr/src/uts/common/io/pci_intr_lib.c (revision 5f8e16172ef40e14cf931b329fedb86ea369a42c)
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 (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 /*
22  * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 #pragma ident	"%Z%%M%	%I%	%E% SMI"
27 
28 /*
29  * Support for MSI, MSIX and INTx
30  */
31 
32 #include <sys/conf.h>
33 #include <sys/debug.h>
34 #include <sys/pci.h>
35 #include <sys/pci_cap.h>
36 #include <sys/sunddi.h>
37 #include <sys/bitmap.h>
38 
39 /*
40  * MSI-X BIR Index Table:
41  *
42  * BAR indicator register (BIR) to Base Address register.
43  */
44 static	uchar_t pci_msix_bir_index[8] = {0x10, 0x14, 0x18, 0x1c,
45 					0x20, 0x24, 0xff, 0xff};
46 
47 /*
48  * Library utility functions
49  */
50 
51 /*
52  * pci_get_msi_ctrl:
53  *
54  *	Helper function that returns with 'cfg_hdl', MSI/X ctrl pointer,
55  *	and caps_ptr for MSI/X if these are found.
56  */
57 static int
58 pci_get_msi_ctrl(dev_info_t *dip, int type, ushort_t *msi_ctrl,
59     ushort_t *caps_ptr, ddi_acc_handle_t *h)
60 {
61 	*msi_ctrl = *caps_ptr = 0;
62 
63 	if (pci_config_setup(dip, h) != DDI_SUCCESS) {
64 		DDI_INTR_NEXDBG((CE_CONT, "pci_get_msi_ctrl: "
65 		    "%s%d can't get config handle",
66 		    ddi_driver_name(dip), ddi_get_instance(dip)));
67 
68 		return (DDI_FAILURE);
69 	}
70 
71 	if ((PCI_CAP_LOCATE(*h, PCI_CAP_ID_MSI, caps_ptr) == DDI_SUCCESS) &&
72 		(type == DDI_INTR_TYPE_MSI)) {
73 		if ((*msi_ctrl = PCI_CAP_GET16(*h, NULL, *caps_ptr,
74 			PCI_MSI_CTRL)) == PCI_CAP_EINVAL16)
75 			goto done;
76 
77 		DDI_INTR_NEXDBG((CE_CONT, "pci_get_msi_ctrl: MSI "
78 		    "caps_ptr=%x msi_ctrl=%x\n", *caps_ptr, *msi_ctrl));
79 
80 		return (DDI_SUCCESS);
81 	}
82 
83 	if ((PCI_CAP_LOCATE(*h, PCI_CAP_ID_MSI_X, caps_ptr) == DDI_SUCCESS) &&
84 		(type == DDI_INTR_TYPE_MSIX)) {
85 		if ((*msi_ctrl = PCI_CAP_GET16(*h, NULL, *caps_ptr,
86 			PCI_MSIX_CTRL)) == PCI_CAP_EINVAL16)
87 			goto done;
88 
89 		DDI_INTR_NEXDBG((CE_CONT, "pci_get_msi_ctrl: MSI-X "
90 		    "caps_ptr=%x msi_ctrl=%x\n", *caps_ptr, *msi_ctrl));
91 
92 		return (DDI_SUCCESS);
93 	}
94 
95 done:
96 	pci_config_teardown(h);
97 	return (DDI_FAILURE);
98 }
99 
100 
101 /*
102  * pci_msi_get_cap:
103  *
104  * Get the capabilities of the MSI/X interrupt
105  */
106 int
107 pci_msi_get_cap(dev_info_t *rdip, int type, int *flagsp)
108 {
109 	ushort_t		caps_ptr, msi_ctrl;
110 	ddi_acc_handle_t	cfg_hdle;
111 
112 	DDI_INTR_NEXDBG((CE_CONT, "pci_msi_get_cap: rdip = 0x%p\n",
113 	    (void *)rdip));
114 
115 	*flagsp = 0;
116 
117 	if (pci_get_msi_ctrl(rdip, type, &msi_ctrl,
118 	    &caps_ptr, &cfg_hdle) != DDI_SUCCESS)
119 		return (DDI_FAILURE);
120 
121 	if (type == DDI_INTR_TYPE_MSI) {
122 		if (msi_ctrl &  PCI_MSI_64BIT_MASK)
123 			*flagsp |= DDI_INTR_FLAG_MSI64;
124 		if (msi_ctrl & PCI_MSI_PVM_MASK)
125 			*flagsp |= (DDI_INTR_FLAG_MASKABLE |
126 			    DDI_INTR_FLAG_PENDING);
127 		else
128 			*flagsp |= DDI_INTR_FLAG_BLOCK;
129 	} else if (type == DDI_INTR_TYPE_MSIX) {
130 		/* MSI-X supports PVM, 64bit by default */
131 		*flagsp |= (DDI_INTR_FLAG_MASKABLE | DDI_INTR_FLAG_MSI64 |
132 		    DDI_INTR_FLAG_PENDING);
133 	}
134 
135 	*flagsp |= DDI_INTR_FLAG_EDGE;
136 
137 	DDI_INTR_NEXDBG((CE_CONT, "pci_msi_get_cap: flags = 0x%x\n", *flagsp));
138 
139 	pci_config_teardown(&cfg_hdle);
140 	return (DDI_SUCCESS);
141 }
142 
143 
144 /*
145  * pci_msi_configure:
146  *
147  * Configure address/data and number MSI/Xs fields in the MSI/X
148  * capability structure.
149  */
150 /* ARGSUSED */
151 int
152 pci_msi_configure(dev_info_t *rdip, int type, int count, int inum,
153     uint64_t addr, uint64_t data)
154 {
155 	ushort_t		caps_ptr, msi_ctrl;
156 	ddi_acc_handle_t	h;
157 
158 	DDI_INTR_NEXDBG((CE_CONT, "pci_msi_configure: rdip = 0x%p type 0x%x "
159 	    "count 0x%x inum 0x%x addr 0x%" PRIx64 " data 0x%" PRIx64 "\n",
160 	    (void *)rdip, type, count, inum, addr, data));
161 
162 	if (pci_get_msi_ctrl(rdip, type, &msi_ctrl,
163 	    &caps_ptr, &h) != DDI_SUCCESS)
164 		return (DDI_FAILURE);
165 
166 	if (type == DDI_INTR_TYPE_MSI) {
167 		/* Set the bits to inform how many MSIs are enabled */
168 		msi_ctrl |= ((highbit(count) -1) << PCI_MSI_MME_SHIFT);
169 		PCI_CAP_PUT16(h, NULL, caps_ptr, PCI_MSI_CTRL, msi_ctrl);
170 
171 		DDI_INTR_NEXDBG((CE_CONT, "pci_msi_configure: msi_ctrl = %x\n",
172 		    PCI_CAP_GET16(h, NULL, caps_ptr, PCI_MSI_CTRL)));
173 
174 		/* Set the "data" and "addr" bits */
175 		PCI_CAP_PUT32(h, NULL, caps_ptr, PCI_MSI_ADDR_OFFSET, addr);
176 
177 		DDI_INTR_NEXDBG((CE_CONT, "pci_msi_configure: msi_addr = %x\n",
178 			PCI_CAP_GET32(h, NULL, caps_ptr, PCI_MSI_ADDR_OFFSET)));
179 
180 		if (msi_ctrl &  PCI_MSI_64BIT_MASK) {
181 			PCI_CAP_PUT32(h, NULL, caps_ptr, PCI_MSI_ADDR_OFFSET
182 				+ 4, addr >> 32);
183 
184 			DDI_INTR_NEXDBG((CE_CONT, "pci_msi_configure: upper "
185 				"32bit msi_addr = %x\n", PCI_CAP_GET32(h, NULL,
186 				caps_ptr, PCI_MSI_ADDR_OFFSET + 4)));
187 
188 			PCI_CAP_PUT16(h, NULL, caps_ptr, PCI_MSI_64BIT_DATA,
189 				data);
190 
191 			DDI_INTR_NEXDBG((CE_CONT, "pci_msi_configure: msi_data "
192 				"= %x\n", PCI_CAP_GET16(h, NULL, caps_ptr,
193 				PCI_MSI_64BIT_DATA)));
194 		} else {
195 			PCI_CAP_PUT16(h, NULL, caps_ptr, PCI_MSI_32BIT_DATA,
196 				data);
197 
198 			DDI_INTR_NEXDBG((CE_CONT, "pci_msi_configure: msi_data "
199 				"= %x\n", PCI_CAP_GET16(h, NULL, caps_ptr,
200 				PCI_MSI_32BIT_DATA)));
201 		}
202 	} else if (type == DDI_INTR_TYPE_MSIX) {
203 		uintptr_t	off;
204 		ddi_intr_msix_t	*msix_p = i_ddi_get_msix(rdip);
205 
206 		/* Offset into the "inum"th entry in the MSI-X table */
207 		off = (uintptr_t)msix_p->msix_tbl_addr +
208 		    (inum * PCI_MSIX_VECTOR_SIZE);
209 
210 		/* Set the "data" and "addr" bits */
211 		ddi_put32(msix_p->msix_tbl_hdl,
212 		    (uint32_t *)(off + PCI_MSIX_DATA_OFFSET), data);
213 
214 		ddi_put64(msix_p->msix_tbl_hdl,
215 		    (uint64_t *)(off + PCI_MSIX_LOWER_ADDR_OFFSET), addr);
216 
217 		DDI_INTR_NEXDBG((CE_CONT, "pci_msi_configure: "
218 		    "msix_addr 0x%" PRIx64 " msix_data 0x%x\n",
219 		    ddi_get64(msix_p->msix_tbl_hdl,
220 		    (uint64_t *)(off + PCI_MSIX_LOWER_ADDR_OFFSET)),
221 		    ddi_get32(msix_p->msix_tbl_hdl,
222 		    (uint32_t *)(off + PCI_MSIX_DATA_OFFSET))));
223 	}
224 
225 	pci_config_teardown(&h);
226 	return (DDI_SUCCESS);
227 }
228 
229 
230 /*
231  * pci_msi_unconfigure:
232  *
233  * Unconfigure address/data and number MSI/Xs fields in the MSI/X
234  * capability structure.
235  */
236 /* ARGSUSED */
237 int
238 pci_msi_unconfigure(dev_info_t *rdip, int type, int inum)
239 {
240 	ushort_t		msi_ctrl, caps_ptr;
241 	ddi_acc_handle_t	h;
242 
243 	DDI_INTR_NEXDBG((CE_CONT, "pci_msi_unconfigure: rdip = 0x%p\n",
244 	    (void *)rdip));
245 
246 	if (pci_get_msi_ctrl(rdip, type, &msi_ctrl, &caps_ptr, &h) !=
247 		DDI_SUCCESS)
248 		return (DDI_FAILURE);
249 
250 	if (type == DDI_INTR_TYPE_MSI) {
251 		msi_ctrl &= (~PCI_MSI_MME_MASK);
252 		PCI_CAP_PUT16(h, NULL, caps_ptr, PCI_MSI_CTRL, msi_ctrl);
253 
254 		PCI_CAP_PUT32(h, NULL, caps_ptr, PCI_MSI_ADDR_OFFSET, 0);
255 
256 		if (msi_ctrl &  PCI_MSI_64BIT_MASK) {
257 			PCI_CAP_PUT16(h, NULL, caps_ptr, PCI_MSI_64BIT_DATA,
258 				0);
259 			PCI_CAP_PUT32(h, NULL, caps_ptr, PCI_MSI_ADDR_OFFSET
260 				+ 4, 0);
261 		} else {
262 			PCI_CAP_PUT16(h, NULL, caps_ptr, PCI_MSI_32BIT_DATA,
263 				0);
264 		}
265 
266 		DDI_INTR_NEXDBG((CE_CONT, "pci_msi_unconfigure: msi_ctrl "
267 		    "= %x\n", PCI_CAP_GET16(h, NULL, caps_ptr, PCI_MSI_CTRL)));
268 
269 	} else if (type == DDI_INTR_TYPE_MSIX) {
270 		uintptr_t	off;
271 		ddi_intr_msix_t	*msix_p = i_ddi_get_msix(rdip);
272 
273 		/* Offset into the "inum"th entry in the MSI-X table */
274 		off = (uintptr_t)msix_p->msix_tbl_addr +
275 		    (inum * PCI_MSIX_VECTOR_SIZE);
276 
277 		/* Reset the "data" and "addr" bits */
278 		ddi_put32(msix_p->msix_tbl_hdl,
279 		    (uint32_t *)(off + PCI_MSIX_DATA_OFFSET), 0);
280 
281 		ddi_put64(msix_p->msix_tbl_hdl, (uint64_t *)off, 0);
282 	}
283 
284 	pci_config_teardown(&h);
285 	return (DDI_SUCCESS);
286 }
287 
288 
289 /*
290  * pci_is_msi_enabled:
291  *
292  * This function returns DDI_SUCCESS if MSI/X is already enabled, otherwise
293  * it returns DDI_FAILURE.
294  */
295 int
296 pci_is_msi_enabled(dev_info_t *rdip, int type)
297 {
298 	ushort_t		caps_ptr, msi_ctrl;
299 	ddi_acc_handle_t	cfg_hdle;
300 	int			ret = DDI_FAILURE;
301 
302 	DDI_INTR_NEXDBG((CE_CONT, "pci_is_msi_enabled: rdip = 0x%p, "
303 	    "type  = 0x%x\n", (void *)rdip, type));
304 
305 	if (pci_get_msi_ctrl(rdip, type, &msi_ctrl,
306 	    &caps_ptr, &cfg_hdle) != DDI_SUCCESS)
307 		return (DDI_FAILURE);
308 
309 	if ((type == DDI_INTR_TYPE_MSI) && (msi_ctrl & PCI_MSI_ENABLE_BIT))
310 		ret = DDI_SUCCESS;
311 
312 	if ((type == DDI_INTR_TYPE_MSIX) && (msi_ctrl & PCI_MSIX_ENABLE_BIT))
313 		ret = DDI_SUCCESS;
314 
315 	pci_config_teardown(&cfg_hdle);
316 	return (ret);
317 }
318 
319 
320 /*
321  * pci_msi_enable_mode:
322  *
323  * This function sets the MSI_ENABLE bit in the capability structure
324  * (for MSI) and MSIX_ENABLE bit in the MSI-X capability structure.
325  */
326 int
327 pci_msi_enable_mode(dev_info_t *rdip, int type, int inum)
328 {
329 	ushort_t		caps_ptr, msi_ctrl;
330 	ddi_acc_handle_t	cfg_hdle;
331 
332 	DDI_INTR_NEXDBG((CE_CONT, "pci_msi_enable_mode: rdip = 0x%p, "
333 	    "inum  = 0x%x\n", (void *)rdip, inum));
334 
335 	if (pci_get_msi_ctrl(rdip, type, &msi_ctrl,
336 	    &caps_ptr, &cfg_hdle) != DDI_SUCCESS)
337 		return (DDI_FAILURE);
338 
339 	if (type == DDI_INTR_TYPE_MSI) {
340 		if (msi_ctrl & PCI_MSI_ENABLE_BIT)
341 			goto finished;
342 
343 		msi_ctrl |= PCI_MSI_ENABLE_BIT;
344 		PCI_CAP_PUT16(cfg_hdle, NULL, caps_ptr, PCI_MSI_CTRL, msi_ctrl);
345 
346 	} else if (type == DDI_INTR_TYPE_MSIX) {
347 		uintptr_t	off;
348 		ddi_intr_msix_t	*msix_p;
349 
350 		if (msi_ctrl & PCI_MSIX_ENABLE_BIT)
351 			goto finished;
352 
353 		msi_ctrl |= PCI_MSIX_ENABLE_BIT;
354 		PCI_CAP_PUT16(cfg_hdle, NULL, caps_ptr, PCI_MSIX_CTRL,
355 			msi_ctrl);
356 
357 		msix_p = i_ddi_get_msix(rdip);
358 
359 		/* Offset into the "inum"th entry in the MSI-X table */
360 		off = (uintptr_t)msix_p->msix_tbl_addr + (inum *
361 		    PCI_MSIX_VECTOR_SIZE) + PCI_MSIX_VECTOR_CTRL_OFFSET;
362 
363 		/* Clear the Mask bit */
364 		ddi_put32(msix_p->msix_tbl_hdl, (uint32_t *)off, 0x0);
365 
366 		DDI_INTR_NEXDBG((CE_CONT, "pci_msi_enable: "
367 		    "msix_vector_mask 0x%x\n",
368 		    ddi_get32(msix_p->msix_tbl_hdl, (uint32_t *)off)));
369 	}
370 
371 finished:
372 	DDI_INTR_NEXDBG((CE_CONT, "pci_msi_enable_mode: msi_ctrl = %x\n",
373 	    msi_ctrl));
374 
375 	pci_config_teardown(&cfg_hdle);
376 	return (DDI_SUCCESS);
377 }
378 
379 
380 /*
381  * pci_msi_disable_mode:
382  *
383  * This function resets the MSI_ENABLE bit in the capability structure
384  * (for MSI) and MSIX_ENABLE bit in the MSI-X capability structure.
385  */
386 int
387 pci_msi_disable_mode(dev_info_t *rdip, int type, int inum)
388 {
389 	ushort_t		caps_ptr, msi_ctrl;
390 	ddi_acc_handle_t	cfg_hdle;
391 
392 	DDI_INTR_NEXDBG((CE_CONT, "pci_msi_disable_mode: rdip = 0x%p "
393 	    "inum = 0x%x\n", (void *)rdip, inum));
394 
395 	if (pci_get_msi_ctrl(rdip, type, &msi_ctrl,
396 	    &caps_ptr, &cfg_hdle) != DDI_SUCCESS)
397 		return (DDI_FAILURE);
398 
399 	/* Reset the "enable" bit */
400 	if (type == DDI_INTR_TYPE_MSI) {
401 		if (!(msi_ctrl & PCI_MSI_ENABLE_BIT))
402 			goto finished;
403 		msi_ctrl &= ~PCI_MSI_ENABLE_BIT;
404 		PCI_CAP_PUT16(cfg_hdle, NULL, caps_ptr, PCI_MSI_CTRL, msi_ctrl);
405 	} else if (type == DDI_INTR_TYPE_MSIX) {
406 		uintptr_t		off;
407 		ddi_intr_msix_t		*msix_p;
408 
409 		if (!(msi_ctrl & PCI_MSIX_ENABLE_BIT))
410 			goto finished;
411 
412 		msix_p = i_ddi_get_msix(rdip);
413 
414 		/* Offset into the "inum"th entry in the MSI-X table */
415 		off = (uintptr_t)msix_p->msix_tbl_addr + (inum *
416 		    PCI_MSIX_VECTOR_SIZE) + PCI_MSIX_VECTOR_CTRL_OFFSET;
417 
418 		/* Set the Mask bit */
419 		ddi_put32(msix_p->msix_tbl_hdl, (uint32_t *)off, 0x1);
420 	}
421 
422 finished:
423 	DDI_INTR_NEXDBG((CE_CONT, "pci_msi_disable_mode: msi_ctrl = %x\n",
424 	    msi_ctrl));
425 
426 	pci_config_teardown(&cfg_hdle);
427 	return (DDI_SUCCESS);
428 }
429 
430 
431 /*
432  * pci_msi_set_mask:
433  *
434  * Set the mask bit in the MSI/X capability structure
435  */
436 /* ARGSUSED */
437 int
438 pci_msi_set_mask(dev_info_t *rdip, int type, int inum)
439 {
440 	int			offset;
441 	int			ret = DDI_FAILURE;
442 	ushort_t		caps_ptr, msi_ctrl;
443 	ddi_acc_handle_t	cfg_hdle;
444 	uint32_t		mask_bits;
445 
446 	DDI_INTR_NEXDBG((CE_CONT, "pci_msi_set_mask: rdip = 0x%p, "
447 	    "type = 0x%x\n", (void *)rdip, type));
448 
449 	if (pci_get_msi_ctrl(rdip, type, &msi_ctrl,
450 	    &caps_ptr, &cfg_hdle) != DDI_SUCCESS)
451 		return (DDI_FAILURE);
452 
453 	if (type == DDI_INTR_TYPE_MSI) {
454 		if (!(msi_ctrl &  PCI_MSI_PVM_MASK))
455 			goto done;
456 
457 		offset = (msi_ctrl &  PCI_MSI_64BIT_MASK) ?
458 		    PCI_MSI_64BIT_MASKBITS : PCI_MSI_32BIT_MASK;
459 
460 		if ((mask_bits = PCI_CAP_GET32(cfg_hdle, NULL, caps_ptr,
461 			offset)) == PCI_CAP_EINVAL32)
462 			goto done;
463 
464 		mask_bits |= (1 << inum);
465 
466 		PCI_CAP_PUT32(cfg_hdle, NULL, caps_ptr, offset, mask_bits);
467 
468 	} else if (type == DDI_INTR_TYPE_MSIX) {
469 		uintptr_t		off;
470 		ddi_intr_msix_t		*msix_p;
471 
472 		/* Set function mask */
473 		if (msi_ctrl & PCI_MSIX_FUNCTION_MASK) {
474 			ret = DDI_SUCCESS;
475 			goto done;
476 		}
477 
478 		msix_p = i_ddi_get_msix(rdip);
479 
480 		/* Offset into the "inum"th entry in the MSI-X table */
481 		off = (uintptr_t)msix_p->msix_tbl_addr + (inum *
482 		    PCI_MSIX_VECTOR_SIZE) + PCI_MSIX_VECTOR_CTRL_OFFSET;
483 
484 		/* Set the Mask bit */
485 		ddi_put32(msix_p->msix_tbl_hdl, (uint32_t *)off, 0x1);
486 	}
487 
488 	ret = DDI_SUCCESS;
489 done:
490 	pci_config_teardown(&cfg_hdle);
491 	return (ret);
492 }
493 
494 
495 /*
496  * pci_msi_clr_mask:
497  *
498  * Clear the mask bit in the MSI/X capability structure
499  */
500 /* ARGSUSED */
501 int
502 pci_msi_clr_mask(dev_info_t *rdip, int type, int inum)
503 {
504 	ushort_t		caps_ptr, msi_ctrl;
505 	ddi_acc_handle_t	cfg_hdle;
506 	int			offset;
507 	int			ret = DDI_FAILURE;
508 	uint32_t		mask_bits;
509 
510 	DDI_INTR_NEXDBG((CE_CONT, "pci_msi_clr_mask: rdip = 0x%p, "
511 	    "type = 0x%x\n", (void *)rdip, type));
512 
513 	if (pci_get_msi_ctrl(rdip, type, &msi_ctrl,
514 	    &caps_ptr, &cfg_hdle) != DDI_SUCCESS)
515 		return (DDI_FAILURE);
516 
517 	if (type == DDI_INTR_TYPE_MSI) {
518 		if (!(msi_ctrl &  PCI_MSI_PVM_MASK))
519 			goto done;
520 
521 		offset = (msi_ctrl &  PCI_MSI_64BIT_MASK) ?
522 		    PCI_MSI_64BIT_MASKBITS : PCI_MSI_32BIT_MASK;
523 		if ((mask_bits = PCI_CAP_GET32(cfg_hdle, NULL, caps_ptr,
524 			offset)) == PCI_CAP_EINVAL32)
525 			goto done;
526 
527 		mask_bits &= ~(1 << inum);
528 
529 		PCI_CAP_PUT32(cfg_hdle, NULL, caps_ptr, offset, mask_bits);
530 
531 	} else if (type == DDI_INTR_TYPE_MSIX) {
532 		uintptr_t		off;
533 		ddi_intr_msix_t		*msix_p;
534 
535 		if (msi_ctrl & PCI_MSIX_FUNCTION_MASK) {
536 			ret = DDI_SUCCESS;
537 			goto done;
538 		}
539 
540 		msix_p = i_ddi_get_msix(rdip);
541 
542 		/* Offset into the "inum"th entry in the MSI-X table */
543 		off = (uintptr_t)msix_p->msix_tbl_addr + (inum *
544 		    PCI_MSIX_VECTOR_SIZE) + PCI_MSIX_VECTOR_CTRL_OFFSET;
545 
546 		/* Clear the Mask bit */
547 		ddi_put32(msix_p->msix_tbl_hdl, (uint32_t *)off, 0x0);
548 	}
549 
550 	ret = DDI_SUCCESS;
551 done:
552 	pci_config_teardown(&cfg_hdle);
553 	return (ret);
554 }
555 
556 
557 /*
558  * pci_msi_get_pending:
559  *
560  * Get the pending bit from the MSI/X capability structure
561  */
562 /* ARGSUSED */
563 int
564 pci_msi_get_pending(dev_info_t *rdip, int type, int inum, int *pendingp)
565 {
566 	ushort_t		caps_ptr, msi_ctrl;
567 	ddi_acc_handle_t	cfg_hdle;
568 	int			offset;
569 	int			ret = DDI_FAILURE;
570 
571 	DDI_INTR_NEXDBG((CE_CONT, "pci_msi_get_pending: rdip = 0x%p\n",
572 	    (void *)rdip));
573 
574 	if (pci_get_msi_ctrl(rdip, type, &msi_ctrl,
575 	    &caps_ptr, &cfg_hdle) != DDI_SUCCESS)
576 		return (DDI_FAILURE);
577 
578 	if (type == DDI_INTR_TYPE_MSI) {
579 		uint32_t	pending_bits;
580 
581 		if (!(msi_ctrl &  PCI_MSI_PVM_MASK)) {
582 			DDI_INTR_NEXDBG((CE_CONT, "pci_msi_get_pending: "
583 			    "PVM is not supported\n"));
584 			goto done;
585 		}
586 
587 		offset = (msi_ctrl &  PCI_MSI_64BIT_MASK) ?
588 		    PCI_MSI_64BIT_PENDING : PCI_MSI_32BIT_PENDING;
589 
590 		if ((pending_bits = PCI_CAP_GET32(cfg_hdle, NULL, caps_ptr,
591 			offset)) == PCI_CAP_EINVAL32)
592 			goto done;
593 
594 		*pendingp = pending_bits & ~(1 >> inum);
595 
596 	} else if (type == DDI_INTR_TYPE_MSIX) {
597 		uintptr_t	off;
598 		uint64_t	pending_bits;
599 		ddi_intr_msix_t	*msix_p = i_ddi_get_msix(rdip);
600 
601 		/* Offset into the PBA array which has entry for "inum" */
602 		off = (uintptr_t)msix_p->msix_pba_addr + (inum / 64);
603 
604 		/* Read the PBA array */
605 		pending_bits = ddi_get64(msix_p->msix_pba_hdl, (uint64_t *)off);
606 
607 		*pendingp = pending_bits & ~(1 >> inum);
608 	}
609 
610 	ret = DDI_SUCCESS;
611 done:
612 	pci_config_teardown(&cfg_hdle);
613 	return (ret);
614 }
615 
616 
617 /*
618  * pci_msi_get_nintrs:
619  *
620  * For a given type (MSI/X) returns the number of interrupts supported
621  */
622 int
623 pci_msi_get_nintrs(dev_info_t *rdip, int type, int *nintrs)
624 {
625 	ushort_t		caps_ptr, msi_ctrl;
626 	ddi_acc_handle_t	cfg_hdle;
627 
628 	DDI_INTR_NEXDBG((CE_CONT, "pci_msi_get_nintrs: rdip = 0x%p\n",
629 	    (void *)rdip));
630 
631 	if (pci_get_msi_ctrl(rdip, type, &msi_ctrl,
632 	    &caps_ptr, &cfg_hdle) != DDI_SUCCESS)
633 		return (DDI_FAILURE);
634 
635 	if (type == DDI_INTR_TYPE_MSI) {
636 		*nintrs = 1 << ((msi_ctrl & PCI_MSI_MMC_MASK) >>
637 		    PCI_MSI_MMC_SHIFT);
638 	} else if (type == DDI_INTR_TYPE_MSIX) {
639 		if (msi_ctrl &  PCI_MSIX_TBL_SIZE_MASK)
640 			*nintrs = (msi_ctrl & PCI_MSIX_TBL_SIZE_MASK) + 1;
641 	}
642 
643 	DDI_INTR_NEXDBG((CE_CONT, "pci_msi_get_nintrs: "
644 	    "nintr = 0x%x\n", *nintrs));
645 
646 	pci_config_teardown(&cfg_hdle);
647 	return (DDI_SUCCESS);
648 }
649 
650 
651 /*
652  * pci_msi_set_nintrs:
653  *
654  * For a given type (MSI/X) sets the number of interrupts supported
655  * by the system.
656  * For MSI: Return an error if this func is called for navail > 32
657  * For MSI-X: Return an error if this func is called for navail > 2048
658  */
659 int
660 pci_msi_set_nintrs(dev_info_t *rdip, int type, int navail)
661 {
662 	ushort_t		caps_ptr, msi_ctrl;
663 	ddi_acc_handle_t	cfg_hdle;
664 
665 	DDI_INTR_NEXDBG((CE_CONT, "pci_msi_set_nintrs: rdip = 0x%p, "
666 	    "navail = 0x%x\n", (void *)rdip, navail));
667 
668 	/* Check for valid input argument */
669 	if (((type == DDI_INTR_TYPE_MSI) && (navail > PCI_MSI_MAX_INTRS)) ||
670 	    ((type == DDI_INTR_TYPE_MSIX) && (navail >  PCI_MSIX_MAX_INTRS)))
671 		return (DDI_EINVAL);
672 
673 	if (pci_get_msi_ctrl(rdip, type, &msi_ctrl,
674 	    &caps_ptr, &cfg_hdle) != DDI_SUCCESS)
675 		return (DDI_FAILURE);
676 
677 	if (type == DDI_INTR_TYPE_MSI) {
678 		msi_ctrl |= ((highbit(navail) -1) << PCI_MSI_MME_SHIFT);
679 
680 		PCI_CAP_PUT16(cfg_hdle, NULL, caps_ptr, PCI_MSI_CTRL, msi_ctrl);
681 	} else if (type == DDI_INTR_TYPE_MSIX) {
682 		DDI_INTR_NEXDBG((CE_CONT, "pci_msi_set_nintrs: unsupported\n"));
683 	}
684 
685 	pci_config_teardown(&cfg_hdle);
686 	return (DDI_SUCCESS);
687 }
688 
689 
690 /*
691  * pci_msi_get_supported_type:
692  *
693  * Returns DDI_INTR_TYPE_MSI and/or DDI_INTR_TYPE_MSIX as supported
694  * types if device supports them. A DDI_FAILURE is returned otherwise.
695  */
696 int
697 pci_msi_get_supported_type(dev_info_t *rdip, int *typesp)
698 {
699 	ushort_t		caps_ptr, msi_ctrl;
700 	ddi_acc_handle_t	cfg_hdle;
701 
702 	DDI_INTR_NEXDBG((CE_CONT, "pci_msi_get_supported_type: "
703 	    "rdip = 0x%p\n", (void *)rdip));
704 
705 	*typesp = 0;
706 
707 	if (pci_get_msi_ctrl(rdip, DDI_INTR_TYPE_MSI, &msi_ctrl,
708 	    &caps_ptr, &cfg_hdle) == DDI_SUCCESS) {
709 		*typesp |= DDI_INTR_TYPE_MSI;
710 		pci_config_teardown(&cfg_hdle);
711 	}
712 
713 	if (pci_get_msi_ctrl(rdip, DDI_INTR_TYPE_MSIX, &msi_ctrl,
714 	    &caps_ptr, &cfg_hdle) == DDI_SUCCESS) {
715 		*typesp |= DDI_INTR_TYPE_MSIX;
716 		pci_config_teardown(&cfg_hdle);
717 	}
718 
719 	DDI_INTR_NEXDBG((CE_CONT, "pci_msi_get_supported_type: "
720 	    "rdip = 0x%p types 0x%x\n", (void *)rdip, *typesp));
721 
722 	return (*typesp == 0 ? DDI_FAILURE : DDI_SUCCESS);
723 }
724 
725 
726 /*
727  * pci_msix_init:
728  *	This function initializes the various handles/addrs etc.
729  *	needed for MSI-X support. It also allocates a private
730  *	structure to keep track of these.
731  */
732 ddi_intr_msix_t *
733 pci_msix_init(dev_info_t *rdip)
734 {
735 	uint_t			rnumber, breg, nregs;
736 	size_t			msix_tbl_size;
737 	size_t			pba_tbl_size;
738 	ushort_t		caps_ptr, msix_ctrl;
739 	ddi_intr_msix_t		*msix_p;
740 	ddi_acc_handle_t	cfg_hdle;
741 	pci_regspec_t		*rp;
742 	int			reg_size, addr_space, offset, *regs_list;
743 	int			i, ret;
744 
745 	DDI_INTR_NEXDBG((CE_CONT, "pci_msix_init: rdip = %p\n", (void *)rdip));
746 
747 	if (pci_get_msi_ctrl(rdip, DDI_INTR_TYPE_MSIX, &msix_ctrl,
748 	    &caps_ptr, &cfg_hdle) != DDI_SUCCESS)
749 		return (NULL);
750 
751 	msix_p = kmem_zalloc(sizeof (ddi_intr_msix_t), KM_SLEEP);
752 
753 	/*
754 	 * Initialize the devacc structure
755 	 */
756 	msix_p->msix_dev_attr.devacc_attr_version = DDI_DEVICE_ATTR_V0;
757 	msix_p->msix_dev_attr.devacc_attr_endian_flags =
758 	    DDI_STRUCTURE_LE_ACC;
759 	msix_p->msix_dev_attr.devacc_attr_dataorder = DDI_STRICTORDER_ACC;
760 
761 	/* Map the entire MSI-X vector table */
762 	msix_p->msix_tbl_offset = PCI_CAP_GET32(cfg_hdle, NULL, caps_ptr,
763 		PCI_MSIX_TBL_OFFSET);
764 
765 	if ((breg = pci_msix_bir_index[msix_p->msix_tbl_offset &
766 	    PCI_MSIX_TBL_BIR_MASK]) == 0xff)
767 		goto fail1;
768 
769 	msix_p->msix_tbl_offset = msix_p->msix_tbl_offset &
770 	    ~PCI_MSIX_TBL_BIR_MASK;
771 	msix_tbl_size = ((msix_ctrl & PCI_MSIX_TBL_SIZE_MASK) + 1) *
772 	    PCI_MSIX_VECTOR_SIZE;
773 
774 	DDI_INTR_NEXDBG((CE_CONT, "pci_msix_init: MSI-X table offset 0x%x "
775 	    "breg 0x%x size 0x%lx\n", msix_p->msix_tbl_offset, breg,
776 	    msix_tbl_size));
777 
778 	if ((ret = ddi_prop_lookup_int_array(DDI_DEV_T_ANY, rdip,
779 	    DDI_PROP_DONTPASS, "reg", (int **)&regs_list, &nregs))
780 	    != DDI_PROP_SUCCESS) {
781 		DDI_INTR_NEXDBG((CE_CONT, "pci_msix_init: "
782 		    "ddi_prop_lookup_int_array failed %d\n", ret));
783 
784 		goto fail1;
785 	}
786 
787 	reg_size = sizeof (pci_regspec_t) / sizeof (int);
788 
789 	for (i = 1, rnumber = 0; i < nregs/reg_size; i++) {
790 		rp = (pci_regspec_t *)&regs_list[i * reg_size];
791 		addr_space = rp->pci_phys_hi & PCI_ADDR_MASK;
792 		offset = PCI_REG_REG_G(rp->pci_phys_hi);
793 
794 		if ((offset == breg) && ((addr_space == PCI_ADDR_MEM32) ||
795 		    (addr_space == PCI_ADDR_MEM64))) {
796 			rnumber = i;
797 			break;
798 		}
799 	}
800 
801 	DDI_INTR_NEXDBG((CE_CONT, "pci_msix_init: MSI-X rnum = %d\n", rnumber));
802 
803 	if (rnumber == 0) {
804 		DDI_INTR_NEXDBG((CE_CONT, "pci_msix_init: "
805 		    "no mtaching reg number for offset 0x%x\n", breg));
806 
807 		goto fail2;
808 	}
809 
810 	if ((ret = ddi_regs_map_setup(rdip, rnumber,
811 	    (caddr_t *)&msix_p->msix_tbl_addr, msix_p->msix_tbl_offset,
812 	    msix_tbl_size, &msix_p->msix_dev_attr,
813 	    &msix_p->msix_tbl_hdl)) != DDI_SUCCESS) {
814 		DDI_INTR_NEXDBG((CE_CONT, "pci_msix_init: MSI-X Table "
815 		    "ddi_regs_map_setup failed %d\n", ret));
816 
817 		goto fail2;
818 	}
819 
820 	/*
821 	 * Map in the MSI-X Pending Bit Array
822 	 */
823 	msix_p->msix_pba_offset = PCI_CAP_GET32(cfg_hdle, NULL, caps_ptr,
824 		PCI_MSIX_PBA_OFFSET);
825 
826 	if ((breg = pci_msix_bir_index[msix_p->msix_pba_offset &
827 	    PCI_MSIX_PBA_BIR_MASK]) == 0xff)
828 		goto fail3;
829 
830 	msix_p->msix_pba_offset = msix_p->msix_pba_offset &
831 	    ~PCI_MSIX_PBA_BIR_MASK;
832 	pba_tbl_size = ((msix_ctrl & PCI_MSIX_TBL_SIZE_MASK) + 1)/8;
833 
834 	DDI_INTR_NEXDBG((CE_CONT, "pci_msix_init: PBA table offset 0x%x "
835 	    "breg 0x%x size 0x%lx\n", msix_p->msix_pba_offset, breg,
836 	    pba_tbl_size));
837 
838 	for (i = 1, rnumber = 0; i < nregs/reg_size; i++) {
839 		rp = (pci_regspec_t *)&regs_list[i * reg_size];
840 		addr_space = rp->pci_phys_hi & PCI_ADDR_MASK;
841 		offset = PCI_REG_REG_G(rp->pci_phys_hi);
842 
843 		if ((offset == breg) && ((addr_space == PCI_ADDR_MEM32) ||
844 		    (addr_space == PCI_ADDR_MEM64))) {
845 			ddi_prop_free(regs_list);
846 			rnumber = i;
847 			break;
848 		}
849 	}
850 
851 	DDI_INTR_NEXDBG((CE_CONT, "pci_msix_init: PBA rnum = %d\n", rnumber));
852 
853 	if (rnumber == 0) {
854 		DDI_INTR_NEXDBG((CE_CONT, "pci_msix_init: "
855 		    "no matching reg number for offset 0x%x\n", breg));
856 
857 		goto fail3;
858 	}
859 
860 	if ((ret = ddi_regs_map_setup(rdip, rnumber,
861 	    (caddr_t *)&msix_p->msix_pba_addr, msix_p->msix_pba_offset,
862 	    pba_tbl_size, &msix_p->msix_dev_attr,
863 	    &msix_p->msix_pba_hdl)) != DDI_SUCCESS) {
864 		DDI_INTR_NEXDBG((CE_CONT, "pci_msix_init: PBA "
865 		    "ddi_regs_map_setup failed %d\n", ret));
866 
867 		goto fail3;
868 	}
869 
870 	DDI_INTR_NEXDBG((CE_CONT, "pci_msix_init: msix_p = 0x%p DONE!!\n",
871 	    (void *)msix_p));
872 
873 	goto done;
874 
875 fail3:
876 	ddi_regs_map_free(&msix_p->msix_tbl_hdl);
877 fail2:
878 	ddi_prop_free(regs_list);
879 fail1:
880 	kmem_free(msix_p, sizeof (ddi_intr_msix_t));
881 	msix_p = NULL;
882 done:
883 	pci_config_teardown(&cfg_hdle);
884 	return (msix_p);
885 }
886 
887 
888 /*
889  * pci_msix_fini:
890  *	This function cleans up previously allocated handles/addrs etc.
891  *	It is only called if no more MSI-X interrupts are being used.
892  */
893 void
894 pci_msix_fini(ddi_intr_msix_t *msix_p)
895 {
896 	DDI_INTR_NEXDBG((CE_CONT, "pci_msix_fini: msix_p = 0x%p\n",
897 	    (void *)msix_p));
898 
899 	ddi_regs_map_free(&msix_p->msix_pba_hdl);
900 	ddi_regs_map_free(&msix_p->msix_tbl_hdl);
901 	kmem_free(msix_p, sizeof (ddi_intr_msix_t));
902 }
903 
904 
905 /*
906  * pci_msix_dup:
907  *	This function duplicates the address and data pair of one msi-x
908  *	vector to another msi-x vector.
909  */
910 int
911 pci_msix_dup(dev_info_t *rdip, int org_inum, int dup_inum)
912 {
913 	ddi_intr_msix_t	*msix_p = i_ddi_get_msix(rdip);
914 	uint64_t	addr;
915 	uint64_t	data;
916 	uintptr_t	off;
917 
918 	DDI_INTR_NEXDBG((CE_CONT, "pci_msix_dup: dip = %p, inum = 0x%x, "
919 	    "to_vector = 0x%x\n", (void *)rdip, org_inum, dup_inum));
920 
921 	/* Offset into the original inum's entry in the MSI-X table */
922 	off = (uintptr_t)msix_p->msix_tbl_addr +
923 	    (org_inum * PCI_MSIX_VECTOR_SIZE);
924 
925 	/* For the MSI-X number passed in, get the "data" and "addr" fields */
926 	addr = ddi_get64(msix_p->msix_tbl_hdl,
927 	    (uint64_t *)(off + PCI_MSIX_LOWER_ADDR_OFFSET));
928 
929 	data = ddi_get32(msix_p->msix_tbl_hdl,
930 	    (uint32_t *)(off + PCI_MSIX_DATA_OFFSET));
931 
932 	/* Program new vector with these existing values */
933 	return (pci_msi_configure(rdip, DDI_INTR_TYPE_MSIX, 1, dup_inum, addr,
934 	    data));
935 }
936 
937 
938 /*
939  * Next set of routines are for INTx (legacy) PCI interrupt
940  * support only.
941  */
942 
943 /*
944  * pci_intx_get_cap:
945  *	For non-MSI devices that comply to PCI v2.3 or greater;
946  *	read the command register. Bit 10 implies interrupt disable.
947  *	Set this bit and then read the status register bit 3.
948  *	Bit 3 of status register is Interrupt state.
949  *	If it is set; then the device supports 'Masking'
950  *
951  *	Reset the device back to the original state.
952  */
953 int
954 pci_intx_get_cap(dev_info_t *dip, int *flagsp)
955 {
956 	uint16_t		cmdreg, savereg;
957 	ddi_acc_handle_t	cfg_hdl;
958 #ifdef	DEBUG
959 	uint16_t		statreg;
960 #endif /* DEBUG */
961 
962 	*flagsp = 0;
963 	DDI_INTR_NEXDBG((CE_CONT, "pci_intx_get_cap: %s%d: called\n",
964 	    ddi_driver_name(dip), ddi_get_instance(dip)));
965 
966 	if (pci_config_setup(dip, &cfg_hdl) != DDI_SUCCESS) {
967 		DDI_INTR_NEXDBG((CE_CONT, "pci_intx_get_cap: can't get "
968 		    "config handle\n"));
969 		return (DDI_FAILURE);
970 	}
971 
972 	savereg = pci_config_get16(cfg_hdl, PCI_CONF_COMM);
973 	DDI_INTR_NEXDBG((CE_CONT, "pci_intx_get_cap: "
974 	    "command register was 0x%x\n", savereg));
975 
976 	/* Disable the interrupts */
977 	cmdreg = savereg | PCI_COMM_INTX_DISABLE;
978 	pci_config_put16(cfg_hdl, PCI_CONF_COMM, cmdreg);
979 
980 #ifdef	DEBUG
981 	statreg = pci_config_get16(cfg_hdl, PCI_CONF_STAT);
982 	DDI_INTR_NEXDBG((CE_CONT, "pci_intx_get_cap: "
983 	    "status register is 0x%x\n", statreg));
984 #endif /* DEBUG */
985 
986 	/* Read the bit back */
987 	cmdreg = pci_config_get16(cfg_hdl, PCI_CONF_COMM);
988 	DDI_INTR_NEXDBG((CE_CONT, "pci_intx_get_cap: "
989 	    "command register is now 0x%x\n", cmdreg));
990 
991 	*flagsp = DDI_INTR_FLAG_LEVEL;
992 
993 	if (cmdreg & PCI_COMM_INTX_DISABLE) {
994 		DDI_INTR_NEXDBG((CE_CONT, "pci_intx_get_cap: "
995 		    "masking supported\n"));
996 		*flagsp |= (DDI_INTR_FLAG_MASKABLE |
997 		    DDI_INTR_FLAG_PENDING);
998 	}
999 
1000 	/* Restore the device back to the original state and return */
1001 	pci_config_put16(cfg_hdl, PCI_CONF_COMM, savereg);
1002 
1003 	pci_config_teardown(&cfg_hdl);
1004 	return (DDI_SUCCESS);
1005 }
1006 
1007 
1008 /*
1009  * pci_intx_clr_mask:
1010  *	For non-MSI devices that comply to PCI v2.3 or greater;
1011  *	clear the bit10 in the command register.
1012  */
1013 int
1014 pci_intx_clr_mask(dev_info_t *dip)
1015 {
1016 	uint16_t		cmdreg;
1017 	ddi_acc_handle_t	cfg_hdl;
1018 
1019 	DDI_INTR_NEXDBG((CE_CONT, "pci_intx_clr_mask: %s%d: called\n",
1020 	    ddi_driver_name(dip), ddi_get_instance(dip)));
1021 
1022 	if (pci_config_setup(dip, &cfg_hdl) != DDI_SUCCESS) {
1023 		DDI_INTR_NEXDBG((CE_CONT, "pci_intx_clr_mask: can't get "
1024 		    "config handle\n"));
1025 		return (DDI_FAILURE);
1026 	}
1027 
1028 	cmdreg = pci_config_get16(cfg_hdl, PCI_CONF_COMM);
1029 	DDI_INTR_NEXDBG((CE_CONT, "pci_intx_clr_mask: "
1030 	    "command register was 0x%x\n", cmdreg));
1031 
1032 	/* Enable the interrupts */
1033 	cmdreg &= ~PCI_COMM_INTX_DISABLE;
1034 	pci_config_put16(cfg_hdl, PCI_CONF_COMM, cmdreg);
1035 	pci_config_teardown(&cfg_hdl);
1036 	return (DDI_SUCCESS);
1037 }
1038 
1039 
1040 /*
1041  * pci_intx_set_mask:
1042  *	For non-MSI devices that comply to PCI v2.3 or greater;
1043  *	set the bit10 in the command register.
1044  */
1045 int
1046 pci_intx_set_mask(dev_info_t *dip)
1047 {
1048 	uint16_t		cmdreg;
1049 	ddi_acc_handle_t	cfg_hdl;
1050 
1051 	DDI_INTR_NEXDBG((CE_CONT, "pci_intx_set_mask: %s%d: called\n",
1052 	    ddi_driver_name(dip), ddi_get_instance(dip)));
1053 
1054 	if (pci_config_setup(dip, &cfg_hdl) != DDI_SUCCESS) {
1055 		DDI_INTR_NEXDBG((CE_CONT, "pci_intx_set_mask: can't get "
1056 		    "config handle\n"));
1057 		return (DDI_FAILURE);
1058 	}
1059 
1060 	cmdreg = pci_config_get16(cfg_hdl, PCI_CONF_COMM);
1061 	DDI_INTR_NEXDBG((CE_CONT, "pci_intx_set_mask: "
1062 	    "command register was 0x%x\n", cmdreg));
1063 
1064 	/* Disable the interrupts */
1065 	cmdreg |= PCI_COMM_INTX_DISABLE;
1066 	pci_config_put16(cfg_hdl, PCI_CONF_COMM, cmdreg);
1067 	pci_config_teardown(&cfg_hdl);
1068 	return (DDI_SUCCESS);
1069 }
1070 
1071 /*
1072  * pci_intx_get_pending:
1073  *	For non-MSI devices that comply to PCI v2.3 or greater;
1074  *	read the status register. Bit 3 of status register is
1075  *	Interrupt state. If it is set; then the interrupt is
1076  *	'Pending'.
1077  */
1078 int
1079 pci_intx_get_pending(dev_info_t *dip, int *pendingp)
1080 {
1081 	uint16_t		statreg;
1082 	ddi_acc_handle_t	cfg_hdl;
1083 
1084 	*pendingp = 0;
1085 	DDI_INTR_NEXDBG((CE_CONT, "pci_intx_get_pending: %s%d: called\n",
1086 	    ddi_driver_name(dip), ddi_get_instance(dip)));
1087 
1088 	if (pci_config_setup(dip, &cfg_hdl) != DDI_SUCCESS) {
1089 		DDI_INTR_NEXDBG((CE_CONT, "pci_intx_get_pending: can't get "
1090 		    "config handle\n"));
1091 		return (DDI_FAILURE);
1092 	}
1093 
1094 	statreg = pci_config_get16(cfg_hdl, PCI_CONF_STAT);
1095 
1096 	if (statreg & PCI_STAT_INTR) {
1097 		DDI_INTR_NEXDBG((CE_CONT, "pci_intx_get_pending: "
1098 		    "interrupt is pending\n"));
1099 		*pendingp = 1;
1100 	}
1101 
1102 	pci_config_teardown(&cfg_hdl);
1103 	return (DDI_SUCCESS);
1104 }
1105 
1106 
1107 /*
1108  * pci_devclass_to_ipl:
1109  *	translate from device class to ipl
1110  *	NOTE: This function is  added here as pci_intx_get_ispec()
1111  *	calls this to figure out the priority.
1112  *	It is moved over from x86 pci.c
1113  */
1114 int
1115 pci_devclass_to_ipl(int class)
1116 {
1117 	int	base_cl;
1118 	int	ipl;
1119 
1120 	base_cl = (class & 0xff0000) >> 16;
1121 
1122 	/*
1123 	 * Use the class code values to construct an ipl for the device.
1124 	 */
1125 	switch (base_cl) {
1126 	default:
1127 	case PCI_CLASS_NONE:
1128 		ipl = 1;
1129 		break;
1130 	case PCI_CLASS_MASS:
1131 		ipl = 0x5;
1132 		break;
1133 	case PCI_CLASS_NET:
1134 		ipl = 0x6;
1135 		break;
1136 	case PCI_CLASS_DISPLAY:
1137 		ipl = 0x9;
1138 		break;
1139 	/*
1140 	 * for high priority interrupt handlers, use level 12
1141 	 * as the highest for device drivers
1142 	 */
1143 	case PCI_CLASS_MM:
1144 		ipl = 0xc;
1145 		break;
1146 	case PCI_CLASS_MEM:
1147 		ipl = 0xc;
1148 		break;
1149 	case PCI_CLASS_BRIDGE:
1150 		ipl = 0xc;
1151 		break;
1152 	}
1153 	return (ipl);
1154 }
1155 
1156 
1157 /*
1158  * pci_intx_get_ispec:
1159  *	Get intrspec for PCI devices (legacy support)
1160  *	NOTE: This is moved here from x86 pci.c and is
1161  *	needed here as pci-ide.c uses it as well
1162  */
1163 /*ARGSUSED*/
1164 ddi_intrspec_t
1165 pci_intx_get_ispec(dev_info_t *dip, dev_info_t *rdip, int inum)
1166 {
1167 	int				class, *intpriorities;
1168 	uint_t				num_intpriorities;
1169 	struct intrspec			*ispec;
1170 	ddi_acc_handle_t		cfg_hdl;
1171 	struct ddi_parent_private_data	*pdptr;
1172 
1173 	if ((pdptr = ddi_get_parent_data(rdip)) == NULL)
1174 		return (NULL);
1175 
1176 	ispec = pdptr->par_intr;
1177 	ASSERT(ispec);
1178 
1179 	/* check if the intrspec_pri has been initialized */
1180 	if (!ispec->intrspec_pri) {
1181 		if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, rdip,
1182 		    DDI_PROP_DONTPASS, "interrupt-priorities",
1183 		    &intpriorities, &num_intpriorities) == DDI_PROP_SUCCESS) {
1184 			if (inum < num_intpriorities)
1185 				ispec->intrspec_pri = intpriorities[inum];
1186 			ddi_prop_free(intpriorities);
1187 		}
1188 
1189 		/* If still no priority, guess based on the class code */
1190 		if (ispec->intrspec_pri == 0) {
1191 			/* get 'class' property to derive the intr priority */
1192 			class = ddi_prop_get_int(DDI_DEV_T_ANY, rdip,
1193 			    DDI_PROP_DONTPASS, "class-code", -1);
1194 			ispec->intrspec_pri = (class == -1) ? 1 :
1195 			    pci_devclass_to_ipl(class);
1196 		}
1197 	}
1198 
1199 	/* Get interrupt line value */
1200 	if (!ispec->intrspec_vec) {
1201 		if (pci_config_setup(rdip, &cfg_hdl) != DDI_SUCCESS) {
1202 			DDI_INTR_NEXDBG((CE_CONT, "pci_intx_get_iline: "
1203 			    "can't get config handle\n"));
1204 			return ((ddi_intrspec_t)ispec);
1205 		}
1206 
1207 		ispec->intrspec_vec = pci_config_get8(cfg_hdl, PCI_CONF_ILINE);
1208 		pci_config_teardown(&cfg_hdl);
1209 	}
1210 
1211 	return ((ddi_intrspec_t)ispec);
1212 }
1213