xref: /titanic_51/usr/src/uts/common/io/pci_intr_lib.c (revision d485aa23b5e424dd136afdf657683389f93f72d6)
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 	*flagsp = DDI_INTR_FLAG_LEVEL;
951 
952 	if (cmdreg & PCI_COMM_INTX_DISABLE) {
953 		DDI_INTR_NEXDBG((CE_CONT, "pci_intx_get_cap: "
954 		    "masking supported\n"));
955 		*flagsp |= (DDI_INTR_FLAG_MASKABLE |
956 		    DDI_INTR_FLAG_PENDING);
957 	}
958 
959 	/* Restore the device back to the original state and return */
960 	pci_config_put16(cfg_hdl, PCI_CONF_COMM, savereg);
961 
962 	pci_config_teardown(&cfg_hdl);
963 	return (DDI_SUCCESS);
964 }
965 
966 
967 /*
968  * pci_intx_clr_mask:
969  *	For non-MSI devices that comply to PCI v2.3 or greater;
970  *	clear the bit10 in the command register.
971  */
972 int
973 pci_intx_clr_mask(dev_info_t *dip)
974 {
975 	uint16_t		cmdreg;
976 	ddi_acc_handle_t	cfg_hdl;
977 
978 	DDI_INTR_NEXDBG((CE_CONT, "pci_intx_clr_mask: %s%d: called\n",
979 	    ddi_driver_name(dip), ddi_get_instance(dip)));
980 
981 	if (pci_config_setup(dip, &cfg_hdl) != DDI_SUCCESS) {
982 		DDI_INTR_NEXDBG((CE_CONT, "pci_intx_clr_mask: can't get "
983 		    "config handle\n"));
984 		return (DDI_FAILURE);
985 	}
986 
987 	cmdreg = pci_config_get16(cfg_hdl, PCI_CONF_COMM);
988 	DDI_INTR_NEXDBG((CE_CONT, "pci_intx_clr_mask: "
989 	    "command register was 0x%x\n", cmdreg));
990 
991 	/* Enable the interrupts */
992 	cmdreg &= ~PCI_COMM_INTX_DISABLE;
993 	pci_config_put16(cfg_hdl, PCI_CONF_COMM, cmdreg);
994 	pci_config_teardown(&cfg_hdl);
995 	return (DDI_SUCCESS);
996 }
997 
998 
999 /*
1000  * pci_intx_set_mask:
1001  *	For non-MSI devices that comply to PCI v2.3 or greater;
1002  *	set the bit10 in the command register.
1003  */
1004 int
1005 pci_intx_set_mask(dev_info_t *dip)
1006 {
1007 	uint16_t		cmdreg;
1008 	ddi_acc_handle_t	cfg_hdl;
1009 
1010 	DDI_INTR_NEXDBG((CE_CONT, "pci_intx_set_mask: %s%d: called\n",
1011 	    ddi_driver_name(dip), ddi_get_instance(dip)));
1012 
1013 	if (pci_config_setup(dip, &cfg_hdl) != DDI_SUCCESS) {
1014 		DDI_INTR_NEXDBG((CE_CONT, "pci_intx_set_mask: can't get "
1015 		    "config handle\n"));
1016 		return (DDI_FAILURE);
1017 	}
1018 
1019 	cmdreg = pci_config_get16(cfg_hdl, PCI_CONF_COMM);
1020 	DDI_INTR_NEXDBG((CE_CONT, "pci_intx_set_mask: "
1021 	    "command register was 0x%x\n", cmdreg));
1022 
1023 	/* Disable the interrupts */
1024 	cmdreg |= PCI_COMM_INTX_DISABLE;
1025 	pci_config_put16(cfg_hdl, PCI_CONF_COMM, cmdreg);
1026 	pci_config_teardown(&cfg_hdl);
1027 	return (DDI_SUCCESS);
1028 }
1029 
1030 /*
1031  * pci_intx_get_pending:
1032  *	For non-MSI devices that comply to PCI v2.3 or greater;
1033  *	read the status register. Bit 3 of status register is
1034  *	Interrupt state. If it is set; then the interrupt is
1035  *	'Pending'.
1036  */
1037 int
1038 pci_intx_get_pending(dev_info_t *dip, int *pendingp)
1039 {
1040 	uint16_t		statreg;
1041 	ddi_acc_handle_t	cfg_hdl;
1042 
1043 	*pendingp = 0;
1044 	DDI_INTR_NEXDBG((CE_CONT, "pci_intx_get_pending: %s%d: called\n",
1045 	    ddi_driver_name(dip), ddi_get_instance(dip)));
1046 
1047 	if (pci_config_setup(dip, &cfg_hdl) != DDI_SUCCESS) {
1048 		DDI_INTR_NEXDBG((CE_CONT, "pci_intx_get_pending: can't get "
1049 		    "config handle\n"));
1050 		return (DDI_FAILURE);
1051 	}
1052 
1053 	statreg = pci_config_get16(cfg_hdl, PCI_CONF_STAT);
1054 
1055 	if (statreg & PCI_STAT_INTR) {
1056 		DDI_INTR_NEXDBG((CE_CONT, "pci_intx_get_pending: "
1057 		    "interrupt is pending\n"));
1058 		*pendingp = 1;
1059 	}
1060 
1061 	pci_config_teardown(&cfg_hdl);
1062 	return (DDI_SUCCESS);
1063 }
1064 
1065 
1066 /*
1067  * pci_devclass_to_ipl:
1068  *	translate from device class to ipl
1069  *	NOTE: This function is  added here as pci_intx_get_ispec()
1070  *	calls this to figure out the priority.
1071  *	It is moved over from x86 pci.c
1072  */
1073 int
1074 pci_devclass_to_ipl(int class)
1075 {
1076 	int	base_cl;
1077 	int	ipl;
1078 
1079 	base_cl = (class & 0xff0000) >> 16;
1080 
1081 	/*
1082 	 * Use the class code values to construct an ipl for the device.
1083 	 */
1084 	switch (base_cl) {
1085 	default:
1086 	case PCI_CLASS_NONE:
1087 		ipl = 1;
1088 		break;
1089 	case PCI_CLASS_MASS:
1090 		ipl = 0x5;
1091 		break;
1092 	case PCI_CLASS_NET:
1093 		ipl = 0x6;
1094 		break;
1095 	case PCI_CLASS_DISPLAY:
1096 		ipl = 0x9;
1097 		break;
1098 	/*
1099 	 * for high priority interrupt handlers, use level 12
1100 	 * as the highest for device drivers
1101 	 */
1102 	case PCI_CLASS_MM:
1103 		ipl = 0xc;
1104 		break;
1105 	case PCI_CLASS_MEM:
1106 		ipl = 0xc;
1107 		break;
1108 	case PCI_CLASS_BRIDGE:
1109 		ipl = 0xc;
1110 		break;
1111 	}
1112 	return (ipl);
1113 }
1114 
1115 
1116 /*
1117  * pci_intx_get_ispec:
1118  *	Get intrspec for PCI devices (legacy support)
1119  *	NOTE: This is moved here from x86 pci.c and is
1120  *	needed here as pci-ide.c uses it as well
1121  */
1122 /*ARGSUSED*/
1123 ddi_intrspec_t
1124 pci_intx_get_ispec(dev_info_t *dip, dev_info_t *rdip, int inum)
1125 {
1126 	int				class, *intpriorities;
1127 	uint_t				num_intpriorities;
1128 	struct intrspec			*ispec;
1129 	ddi_acc_handle_t		cfg_hdl;
1130 	struct ddi_parent_private_data	*pdptr;
1131 
1132 	if ((pdptr = ddi_get_parent_data(rdip)) == NULL)
1133 		return (NULL);
1134 
1135 	ispec = pdptr->par_intr;
1136 	ASSERT(ispec);
1137 
1138 	/* check if the intrspec_pri has been initialized */
1139 	if (!ispec->intrspec_pri) {
1140 		if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, rdip,
1141 		    DDI_PROP_DONTPASS, "interrupt-priorities",
1142 		    &intpriorities, &num_intpriorities) == DDI_PROP_SUCCESS) {
1143 			if (inum < num_intpriorities)
1144 				ispec->intrspec_pri = intpriorities[inum];
1145 			ddi_prop_free(intpriorities);
1146 		}
1147 
1148 		/* If still no priority, guess based on the class code */
1149 		if (ispec->intrspec_pri == 0) {
1150 			/* get 'class' property to derive the intr priority */
1151 			class = ddi_prop_get_int(DDI_DEV_T_ANY, rdip,
1152 			    DDI_PROP_DONTPASS, "class-code", -1);
1153 			ispec->intrspec_pri = (class == -1) ? 1 :
1154 			    pci_devclass_to_ipl(class);
1155 		}
1156 	}
1157 
1158 	/* Get interrupt line value */
1159 	if (!ispec->intrspec_vec) {
1160 		if (pci_config_setup(rdip, &cfg_hdl) != DDI_SUCCESS) {
1161 			DDI_INTR_NEXDBG((CE_CONT, "pci_intx_get_iline: "
1162 			    "can't get config handle\n"));
1163 			return (NULL);
1164 		}
1165 
1166 		ispec->intrspec_vec = pci_config_get8(cfg_hdl, PCI_CONF_ILINE);
1167 		pci_config_teardown(&cfg_hdl);
1168 	}
1169 
1170 	return ((ddi_intrspec_t)ispec);
1171 }
1172