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