xref: /illumos-gate/usr/src/uts/common/io/pci_intr_lib.c (revision 4aac33d31b41cc7e3ac6fb66747ff2cae63d08cf)
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 2007 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 type 0x%x "
244 	    "inum 0x%x\n", (void *)rdip, type, inum));
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,
282 		    (uint64_t *)(off + PCI_MSIX_LOWER_ADDR_OFFSET), 0);
283 	}
284 
285 	pci_config_teardown(&h);
286 	return (DDI_SUCCESS);
287 }
288 
289 
290 /*
291  * pci_is_msi_enabled:
292  *
293  * This function returns DDI_SUCCESS if MSI/X is already enabled, otherwise
294  * it returns DDI_FAILURE.
295  */
296 int
297 pci_is_msi_enabled(dev_info_t *rdip, int type)
298 {
299 	ushort_t		caps_ptr, msi_ctrl;
300 	ddi_acc_handle_t	cfg_hdle;
301 	int			ret = DDI_FAILURE;
302 
303 	DDI_INTR_NEXDBG((CE_CONT, "pci_is_msi_enabled: rdip = 0x%p, "
304 	    "type  = 0x%x\n", (void *)rdip, type));
305 
306 	if (pci_get_msi_ctrl(rdip, type, &msi_ctrl,
307 	    &caps_ptr, &cfg_hdle) != DDI_SUCCESS)
308 		return (DDI_FAILURE);
309 
310 	if ((type == DDI_INTR_TYPE_MSI) && (msi_ctrl & PCI_MSI_ENABLE_BIT))
311 		ret = DDI_SUCCESS;
312 
313 	if ((type == DDI_INTR_TYPE_MSIX) && (msi_ctrl & PCI_MSIX_ENABLE_BIT))
314 		ret = DDI_SUCCESS;
315 
316 	pci_config_teardown(&cfg_hdle);
317 	return (ret);
318 }
319 
320 
321 /*
322  * pci_msi_enable_mode:
323  *
324  * This function sets the MSI_ENABLE bit in the capability structure
325  * (for MSI) and MSIX_ENABLE bit in the MSI-X capability structure.
326  *
327  * NOTE: It is the nexus driver's responsibility to clear the MSI/X
328  * interrupt's mask bit in the MSI/X capability structure before the
329  * interrupt can be used.
330  */
331 int
332 pci_msi_enable_mode(dev_info_t *rdip, int type)
333 {
334 	ushort_t		caps_ptr, msi_ctrl;
335 	ddi_acc_handle_t	cfg_hdle;
336 
337 	DDI_INTR_NEXDBG((CE_CONT, "pci_msi_enable_mode: rdip = 0x%p\n",
338 	    (void *)rdip));
339 
340 	if (pci_get_msi_ctrl(rdip, type, &msi_ctrl,
341 	    &caps_ptr, &cfg_hdle) != DDI_SUCCESS)
342 		return (DDI_FAILURE);
343 
344 	if (type == DDI_INTR_TYPE_MSI) {
345 		if (msi_ctrl & PCI_MSI_ENABLE_BIT)
346 			goto finished;
347 
348 		msi_ctrl |= PCI_MSI_ENABLE_BIT;
349 		PCI_CAP_PUT16(cfg_hdle, NULL, caps_ptr, PCI_MSI_CTRL, msi_ctrl);
350 
351 	} else if (type == DDI_INTR_TYPE_MSIX) {
352 		if (msi_ctrl & PCI_MSIX_ENABLE_BIT)
353 			goto finished;
354 
355 		msi_ctrl |= PCI_MSIX_ENABLE_BIT;
356 		PCI_CAP_PUT16(cfg_hdle, NULL, caps_ptr, PCI_MSIX_CTRL,
357 		    msi_ctrl);
358 	}
359 
360 finished:
361 	DDI_INTR_NEXDBG((CE_CONT, "pci_msi_enable_mode: msi_ctrl = %x\n",
362 	    msi_ctrl));
363 
364 	pci_config_teardown(&cfg_hdle);
365 	return (DDI_SUCCESS);
366 }
367 
368 
369 /*
370  * pci_msi_disable_mode:
371  *
372  * This function resets the MSI_ENABLE bit in the capability structure
373  * (for MSI) and MSIX_ENABLE bit in the MSI-X capability structure.
374  *
375  * NOTE: It is the nexus driver's responsibility to set the MSI/X
376  * interrupt's mask bit in the MSI/X capability structure before the
377  * interrupt can be disabled.
378  */
379 int
380 pci_msi_disable_mode(dev_info_t *rdip, int type, uint_t flags)
381 {
382 	ushort_t		caps_ptr, msi_ctrl;
383 	ddi_acc_handle_t	cfg_hdle;
384 
385 	DDI_INTR_NEXDBG((CE_CONT, "pci_msi_disable_mode: rdip = 0x%p "
386 	    "flags = 0x%x\n", (void *)rdip, flags));
387 
388 	/*
389 	 * Do not turn off the master enable bit if other interrupts are
390 	 * still active.
391 	 */
392 	if ((flags != DDI_INTR_FLAG_BLOCK) &&
393 	    ((i_ddi_intr_get_current_nintrs(rdip) - 1) > 0))
394 		return (DDI_SUCCESS);
395 
396 	if (pci_get_msi_ctrl(rdip, type, &msi_ctrl,
397 	    &caps_ptr, &cfg_hdle) != DDI_SUCCESS)
398 		return (DDI_FAILURE);
399 
400 	/* Reset the "enable" bit */
401 	if (type == DDI_INTR_TYPE_MSI) {
402 		if (!(msi_ctrl & PCI_MSI_ENABLE_BIT))
403 			goto finished;
404 		msi_ctrl &= ~PCI_MSI_ENABLE_BIT;
405 		PCI_CAP_PUT16(cfg_hdle, NULL, caps_ptr, PCI_MSI_CTRL, msi_ctrl);
406 	} else if (type == DDI_INTR_TYPE_MSIX) {
407 		if (!(msi_ctrl & PCI_MSIX_ENABLE_BIT))
408 			goto finished;
409 
410 		msi_ctrl &= ~PCI_MSIX_ENABLE_BIT;
411 		PCI_CAP_PUT16(cfg_hdle, NULL, caps_ptr, PCI_MSIX_CTRL,
412 		    msi_ctrl);
413 	}
414 
415 finished:
416 	DDI_INTR_NEXDBG((CE_CONT, "pci_msi_disable_mode: msi_ctrl = %x\n",
417 	    msi_ctrl));
418 
419 	pci_config_teardown(&cfg_hdle);
420 	return (DDI_SUCCESS);
421 }
422 
423 
424 /*
425  * pci_msi_set_mask:
426  *
427  * Set the mask bit in the MSI/X capability structure
428  */
429 /* ARGSUSED */
430 int
431 pci_msi_set_mask(dev_info_t *rdip, int type, int inum)
432 {
433 	int			offset;
434 	int			ret = DDI_FAILURE;
435 	ushort_t		caps_ptr, msi_ctrl;
436 	ddi_acc_handle_t	cfg_hdle;
437 	uint32_t		mask_bits;
438 
439 	DDI_INTR_NEXDBG((CE_CONT, "pci_msi_set_mask: rdip = 0x%p, "
440 	    "type = 0x%x\n", (void *)rdip, type));
441 
442 	if (pci_get_msi_ctrl(rdip, type, &msi_ctrl,
443 	    &caps_ptr, &cfg_hdle) != DDI_SUCCESS)
444 		return (DDI_FAILURE);
445 
446 	if (type == DDI_INTR_TYPE_MSI) {
447 		if (!(msi_ctrl &  PCI_MSI_PVM_MASK))
448 			goto done;
449 
450 		offset = (msi_ctrl &  PCI_MSI_64BIT_MASK) ?
451 		    PCI_MSI_64BIT_MASKBITS : PCI_MSI_32BIT_MASK;
452 
453 		if ((mask_bits = PCI_CAP_GET32(cfg_hdle, NULL, caps_ptr,
454 			offset)) == PCI_CAP_EINVAL32)
455 			goto done;
456 
457 		mask_bits |= (1 << inum);
458 
459 		PCI_CAP_PUT32(cfg_hdle, NULL, caps_ptr, offset, mask_bits);
460 
461 	} else if (type == DDI_INTR_TYPE_MSIX) {
462 		uintptr_t		off;
463 		ddi_intr_msix_t		*msix_p;
464 
465 		/* Set function mask */
466 		if (msi_ctrl & PCI_MSIX_FUNCTION_MASK) {
467 			ret = DDI_SUCCESS;
468 			goto done;
469 		}
470 
471 		msix_p = i_ddi_get_msix(rdip);
472 
473 		/* Offset into the "inum"th entry in the MSI-X table */
474 		off = (uintptr_t)msix_p->msix_tbl_addr + (inum *
475 		    PCI_MSIX_VECTOR_SIZE) + PCI_MSIX_VECTOR_CTRL_OFFSET;
476 
477 		/* Set the Mask bit */
478 		ddi_put32(msix_p->msix_tbl_hdl, (uint32_t *)off, 0x1);
479 	}
480 
481 	ret = DDI_SUCCESS;
482 done:
483 	pci_config_teardown(&cfg_hdle);
484 	return (ret);
485 }
486 
487 
488 /*
489  * pci_msi_clr_mask:
490  *
491  * Clear the mask bit in the MSI/X capability structure
492  */
493 /* ARGSUSED */
494 int
495 pci_msi_clr_mask(dev_info_t *rdip, int type, int inum)
496 {
497 	ushort_t		caps_ptr, msi_ctrl;
498 	ddi_acc_handle_t	cfg_hdle;
499 	int			offset;
500 	int			ret = DDI_FAILURE;
501 	uint32_t		mask_bits;
502 
503 	DDI_INTR_NEXDBG((CE_CONT, "pci_msi_clr_mask: rdip = 0x%p, "
504 	    "type = 0x%x\n", (void *)rdip, type));
505 
506 	if (pci_get_msi_ctrl(rdip, type, &msi_ctrl,
507 	    &caps_ptr, &cfg_hdle) != DDI_SUCCESS)
508 		return (DDI_FAILURE);
509 
510 	if (type == DDI_INTR_TYPE_MSI) {
511 		if (!(msi_ctrl &  PCI_MSI_PVM_MASK))
512 			goto done;
513 
514 		offset = (msi_ctrl &  PCI_MSI_64BIT_MASK) ?
515 		    PCI_MSI_64BIT_MASKBITS : PCI_MSI_32BIT_MASK;
516 		if ((mask_bits = PCI_CAP_GET32(cfg_hdle, NULL, caps_ptr,
517 			offset)) == PCI_CAP_EINVAL32)
518 			goto done;
519 
520 		mask_bits &= ~(1 << inum);
521 
522 		PCI_CAP_PUT32(cfg_hdle, NULL, caps_ptr, offset, mask_bits);
523 
524 	} else if (type == DDI_INTR_TYPE_MSIX) {
525 		uintptr_t		off;
526 		ddi_intr_msix_t		*msix_p;
527 
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 *
537 		    PCI_MSIX_VECTOR_SIZE) + PCI_MSIX_VECTOR_CTRL_OFFSET;
538 
539 		/* Clear the Mask bit */
540 		ddi_put32(msix_p->msix_tbl_hdl, (uint32_t *)off, 0x0);
541 	}
542 
543 	ret = DDI_SUCCESS;
544 done:
545 	pci_config_teardown(&cfg_hdle);
546 	return (ret);
547 }
548 
549 
550 /*
551  * pci_msi_get_pending:
552  *
553  * Get the pending bit from the MSI/X capability structure
554  */
555 /* ARGSUSED */
556 int
557 pci_msi_get_pending(dev_info_t *rdip, int type, int inum, int *pendingp)
558 {
559 	ushort_t		caps_ptr, msi_ctrl;
560 	ddi_acc_handle_t	cfg_hdle;
561 	int			offset;
562 	int			ret = DDI_FAILURE;
563 
564 	DDI_INTR_NEXDBG((CE_CONT, "pci_msi_get_pending: rdip = 0x%p\n",
565 	    (void *)rdip));
566 
567 	if (pci_get_msi_ctrl(rdip, type, &msi_ctrl,
568 	    &caps_ptr, &cfg_hdle) != DDI_SUCCESS)
569 		return (DDI_FAILURE);
570 
571 	if (type == DDI_INTR_TYPE_MSI) {
572 		uint32_t	pending_bits;
573 
574 		if (!(msi_ctrl &  PCI_MSI_PVM_MASK)) {
575 			DDI_INTR_NEXDBG((CE_CONT, "pci_msi_get_pending: "
576 			    "PVM is not supported\n"));
577 			goto done;
578 		}
579 
580 		offset = (msi_ctrl &  PCI_MSI_64BIT_MASK) ?
581 		    PCI_MSI_64BIT_PENDING : PCI_MSI_32BIT_PENDING;
582 
583 		if ((pending_bits = PCI_CAP_GET32(cfg_hdle, NULL, caps_ptr,
584 			offset)) == PCI_CAP_EINVAL32)
585 			goto done;
586 
587 		*pendingp = pending_bits & ~(1 >> inum);
588 
589 	} else if (type == DDI_INTR_TYPE_MSIX) {
590 		uintptr_t	off;
591 		uint64_t	pending_bits;
592 		ddi_intr_msix_t	*msix_p = i_ddi_get_msix(rdip);
593 
594 		/* Offset into the PBA array which has entry for "inum" */
595 		off = (uintptr_t)msix_p->msix_pba_addr + (inum / 64);
596 
597 		/* Read the PBA array */
598 		pending_bits = ddi_get64(msix_p->msix_pba_hdl, (uint64_t *)off);
599 
600 		*pendingp = pending_bits & ~(1 >> inum);
601 	}
602 
603 	ret = DDI_SUCCESS;
604 done:
605 	pci_config_teardown(&cfg_hdle);
606 	return (ret);
607 }
608 
609 
610 /*
611  * pci_msi_get_nintrs:
612  *
613  * For a given type (MSI/X) returns the number of interrupts supported
614  */
615 int
616 pci_msi_get_nintrs(dev_info_t *rdip, int type, int *nintrs)
617 {
618 	ushort_t		caps_ptr, msi_ctrl;
619 	ddi_acc_handle_t	cfg_hdle;
620 
621 	DDI_INTR_NEXDBG((CE_CONT, "pci_msi_get_nintrs: rdip = 0x%p\n",
622 	    (void *)rdip));
623 
624 	if (pci_get_msi_ctrl(rdip, type, &msi_ctrl,
625 	    &caps_ptr, &cfg_hdle) != DDI_SUCCESS)
626 		return (DDI_FAILURE);
627 
628 	if (type == DDI_INTR_TYPE_MSI) {
629 		*nintrs = 1 << ((msi_ctrl & PCI_MSI_MMC_MASK) >>
630 		    PCI_MSI_MMC_SHIFT);
631 	} else if (type == DDI_INTR_TYPE_MSIX) {
632 		if (msi_ctrl &  PCI_MSIX_TBL_SIZE_MASK)
633 			*nintrs = (msi_ctrl & PCI_MSIX_TBL_SIZE_MASK) + 1;
634 	}
635 
636 	DDI_INTR_NEXDBG((CE_CONT, "pci_msi_get_nintrs: "
637 	    "nintr = 0x%x\n", *nintrs));
638 
639 	pci_config_teardown(&cfg_hdle);
640 	return (DDI_SUCCESS);
641 }
642 
643 
644 /*
645  * pci_msi_set_nintrs:
646  *
647  * For a given type (MSI/X) sets the number of interrupts supported
648  * by the system.
649  * For MSI: Return an error if this func is called for navail > 32
650  * For MSI-X: Return an error if this func is called for navail > 2048
651  */
652 int
653 pci_msi_set_nintrs(dev_info_t *rdip, int type, int navail)
654 {
655 	ushort_t		caps_ptr, msi_ctrl;
656 	ddi_acc_handle_t	cfg_hdle;
657 
658 	DDI_INTR_NEXDBG((CE_CONT, "pci_msi_set_nintrs: rdip = 0x%p, "
659 	    "navail = 0x%x\n", (void *)rdip, navail));
660 
661 	/* Check for valid input argument */
662 	if (((type == DDI_INTR_TYPE_MSI) && (navail > PCI_MSI_MAX_INTRS)) ||
663 	    ((type == DDI_INTR_TYPE_MSIX) && (navail >  PCI_MSIX_MAX_INTRS)))
664 		return (DDI_EINVAL);
665 
666 	if (pci_get_msi_ctrl(rdip, type, &msi_ctrl,
667 	    &caps_ptr, &cfg_hdle) != DDI_SUCCESS)
668 		return (DDI_FAILURE);
669 
670 	if (type == DDI_INTR_TYPE_MSI) {
671 		msi_ctrl |= ((highbit(navail) -1) << PCI_MSI_MME_SHIFT);
672 
673 		PCI_CAP_PUT16(cfg_hdle, NULL, caps_ptr, PCI_MSI_CTRL, msi_ctrl);
674 	} else if (type == DDI_INTR_TYPE_MSIX) {
675 		DDI_INTR_NEXDBG((CE_CONT, "pci_msi_set_nintrs: unsupported\n"));
676 	}
677 
678 	pci_config_teardown(&cfg_hdle);
679 	return (DDI_SUCCESS);
680 }
681 
682 
683 /*
684  * pci_msi_get_supported_type:
685  *
686  * Returns DDI_INTR_TYPE_MSI and/or DDI_INTR_TYPE_MSIX as supported
687  * types if device supports them. A DDI_FAILURE is returned otherwise.
688  */
689 int
690 pci_msi_get_supported_type(dev_info_t *rdip, int *typesp)
691 {
692 	ushort_t		caps_ptr, msi_ctrl;
693 	ddi_acc_handle_t	cfg_hdle;
694 
695 	DDI_INTR_NEXDBG((CE_CONT, "pci_msi_get_supported_type: "
696 	    "rdip = 0x%p\n", (void *)rdip));
697 
698 	*typesp = 0;
699 
700 	if (pci_get_msi_ctrl(rdip, DDI_INTR_TYPE_MSI, &msi_ctrl,
701 	    &caps_ptr, &cfg_hdle) == DDI_SUCCESS) {
702 		*typesp |= DDI_INTR_TYPE_MSI;
703 		pci_config_teardown(&cfg_hdle);
704 	}
705 
706 	if (pci_get_msi_ctrl(rdip, DDI_INTR_TYPE_MSIX, &msi_ctrl,
707 	    &caps_ptr, &cfg_hdle) == DDI_SUCCESS) {
708 		*typesp |= DDI_INTR_TYPE_MSIX;
709 		pci_config_teardown(&cfg_hdle);
710 	}
711 
712 	DDI_INTR_NEXDBG((CE_CONT, "pci_msi_get_supported_type: "
713 	    "rdip = 0x%p types 0x%x\n", (void *)rdip, *typesp));
714 
715 	return (*typesp == 0 ? DDI_FAILURE : DDI_SUCCESS);
716 }
717 
718 
719 /*
720  * pci_msix_init:
721  *	This function initializes the various handles/addrs etc.
722  *	needed for MSI-X support. It also allocates a private
723  *	structure to keep track of these.
724  */
725 ddi_intr_msix_t *
726 pci_msix_init(dev_info_t *rdip)
727 {
728 	uint_t			rnumber, breg, nregs;
729 	size_t			msix_tbl_size;
730 	size_t			pba_tbl_size;
731 	ushort_t		caps_ptr, msix_ctrl;
732 	ddi_intr_msix_t		*msix_p;
733 	ddi_acc_handle_t	cfg_hdle;
734 	pci_regspec_t		*rp;
735 	int			reg_size, addr_space, offset, *regs_list;
736 	int			i, ret;
737 
738 	DDI_INTR_NEXDBG((CE_CONT, "pci_msix_init: rdip = %p\n", (void *)rdip));
739 
740 	if (pci_get_msi_ctrl(rdip, DDI_INTR_TYPE_MSIX, &msix_ctrl,
741 	    &caps_ptr, &cfg_hdle) != DDI_SUCCESS)
742 		return (NULL);
743 
744 	msix_p = kmem_zalloc(sizeof (ddi_intr_msix_t), KM_SLEEP);
745 
746 	/*
747 	 * Initialize the devacc structure
748 	 */
749 	msix_p->msix_dev_attr.devacc_attr_version = DDI_DEVICE_ATTR_V0;
750 	msix_p->msix_dev_attr.devacc_attr_endian_flags =
751 	    DDI_STRUCTURE_LE_ACC;
752 	msix_p->msix_dev_attr.devacc_attr_dataorder = DDI_STRICTORDER_ACC;
753 
754 	/* Map the entire MSI-X vector table */
755 	msix_p->msix_tbl_offset = PCI_CAP_GET32(cfg_hdle, NULL, caps_ptr,
756 		PCI_MSIX_TBL_OFFSET);
757 
758 	if ((breg = pci_msix_bir_index[msix_p->msix_tbl_offset &
759 	    PCI_MSIX_TBL_BIR_MASK]) == 0xff)
760 		goto fail1;
761 
762 	msix_p->msix_tbl_offset = msix_p->msix_tbl_offset &
763 	    ~PCI_MSIX_TBL_BIR_MASK;
764 	msix_tbl_size = ((msix_ctrl & PCI_MSIX_TBL_SIZE_MASK) + 1) *
765 	    PCI_MSIX_VECTOR_SIZE;
766 
767 	DDI_INTR_NEXDBG((CE_CONT, "pci_msix_init: MSI-X table offset 0x%x "
768 	    "breg 0x%x size 0x%lx\n", msix_p->msix_tbl_offset, breg,
769 	    msix_tbl_size));
770 
771 	if ((ret = ddi_prop_lookup_int_array(DDI_DEV_T_ANY, rdip,
772 	    DDI_PROP_DONTPASS, "reg", (int **)&regs_list, &nregs))
773 	    != DDI_PROP_SUCCESS) {
774 		DDI_INTR_NEXDBG((CE_CONT, "pci_msix_init: "
775 		    "ddi_prop_lookup_int_array failed %d\n", ret));
776 
777 		goto fail1;
778 	}
779 
780 	reg_size = sizeof (pci_regspec_t) / sizeof (int);
781 
782 	for (i = 1, rnumber = 0; i < nregs/reg_size; i++) {
783 		rp = (pci_regspec_t *)&regs_list[i * reg_size];
784 		addr_space = rp->pci_phys_hi & PCI_ADDR_MASK;
785 		offset = PCI_REG_REG_G(rp->pci_phys_hi);
786 
787 		if ((offset == breg) && ((addr_space == PCI_ADDR_MEM32) ||
788 		    (addr_space == PCI_ADDR_MEM64))) {
789 			rnumber = i;
790 			break;
791 		}
792 	}
793 
794 	DDI_INTR_NEXDBG((CE_CONT, "pci_msix_init: MSI-X rnum = %d\n", rnumber));
795 
796 	if (rnumber == 0) {
797 		DDI_INTR_NEXDBG((CE_CONT, "pci_msix_init: "
798 		    "no mtaching reg number for offset 0x%x\n", breg));
799 
800 		goto fail2;
801 	}
802 
803 	if ((ret = ddi_regs_map_setup(rdip, rnumber,
804 	    (caddr_t *)&msix_p->msix_tbl_addr, msix_p->msix_tbl_offset,
805 	    msix_tbl_size, &msix_p->msix_dev_attr,
806 	    &msix_p->msix_tbl_hdl)) != DDI_SUCCESS) {
807 		DDI_INTR_NEXDBG((CE_CONT, "pci_msix_init: MSI-X Table "
808 		    "ddi_regs_map_setup failed %d\n", ret));
809 
810 		goto fail2;
811 	}
812 
813 	/*
814 	 * Map in the MSI-X Pending Bit Array
815 	 */
816 	msix_p->msix_pba_offset = PCI_CAP_GET32(cfg_hdle, NULL, caps_ptr,
817 		PCI_MSIX_PBA_OFFSET);
818 
819 	if ((breg = pci_msix_bir_index[msix_p->msix_pba_offset &
820 	    PCI_MSIX_PBA_BIR_MASK]) == 0xff)
821 		goto fail3;
822 
823 	msix_p->msix_pba_offset = msix_p->msix_pba_offset &
824 	    ~PCI_MSIX_PBA_BIR_MASK;
825 	pba_tbl_size = ((msix_ctrl & PCI_MSIX_TBL_SIZE_MASK) + 1)/8;
826 
827 	DDI_INTR_NEXDBG((CE_CONT, "pci_msix_init: PBA table offset 0x%x "
828 	    "breg 0x%x size 0x%lx\n", msix_p->msix_pba_offset, breg,
829 	    pba_tbl_size));
830 
831 	for (i = 1, rnumber = 0; i < nregs/reg_size; i++) {
832 		rp = (pci_regspec_t *)&regs_list[i * reg_size];
833 		addr_space = rp->pci_phys_hi & PCI_ADDR_MASK;
834 		offset = PCI_REG_REG_G(rp->pci_phys_hi);
835 
836 		if ((offset == breg) && ((addr_space == PCI_ADDR_MEM32) ||
837 		    (addr_space == PCI_ADDR_MEM64))) {
838 			ddi_prop_free(regs_list);
839 			rnumber = i;
840 			break;
841 		}
842 	}
843 
844 	DDI_INTR_NEXDBG((CE_CONT, "pci_msix_init: PBA rnum = %d\n", rnumber));
845 
846 	if (rnumber == 0) {
847 		DDI_INTR_NEXDBG((CE_CONT, "pci_msix_init: "
848 		    "no matching reg number for offset 0x%x\n", breg));
849 
850 		goto fail3;
851 	}
852 
853 	if ((ret = ddi_regs_map_setup(rdip, rnumber,
854 	    (caddr_t *)&msix_p->msix_pba_addr, msix_p->msix_pba_offset,
855 	    pba_tbl_size, &msix_p->msix_dev_attr,
856 	    &msix_p->msix_pba_hdl)) != DDI_SUCCESS) {
857 		DDI_INTR_NEXDBG((CE_CONT, "pci_msix_init: PBA "
858 		    "ddi_regs_map_setup failed %d\n", ret));
859 
860 		goto fail3;
861 	}
862 
863 	DDI_INTR_NEXDBG((CE_CONT, "pci_msix_init: msix_p = 0x%p DONE!!\n",
864 	    (void *)msix_p));
865 
866 	goto done;
867 
868 fail3:
869 	ddi_regs_map_free(&msix_p->msix_tbl_hdl);
870 fail2:
871 	ddi_prop_free(regs_list);
872 fail1:
873 	kmem_free(msix_p, sizeof (ddi_intr_msix_t));
874 	msix_p = NULL;
875 done:
876 	pci_config_teardown(&cfg_hdle);
877 	return (msix_p);
878 }
879 
880 
881 /*
882  * pci_msix_fini:
883  *	This function cleans up previously allocated handles/addrs etc.
884  *	It is only called if no more MSI-X interrupts are being used.
885  */
886 void
887 pci_msix_fini(ddi_intr_msix_t *msix_p)
888 {
889 	DDI_INTR_NEXDBG((CE_CONT, "pci_msix_fini: msix_p = 0x%p\n",
890 	    (void *)msix_p));
891 
892 	ddi_regs_map_free(&msix_p->msix_pba_hdl);
893 	ddi_regs_map_free(&msix_p->msix_tbl_hdl);
894 	kmem_free(msix_p, sizeof (ddi_intr_msix_t));
895 }
896 
897 
898 /*
899  * pci_msix_dup:
900  *	This function duplicates the address and data pair of one msi-x
901  *	vector to another msi-x vector.
902  */
903 int
904 pci_msix_dup(dev_info_t *rdip, int org_inum, int dup_inum)
905 {
906 	ddi_intr_msix_t	*msix_p = i_ddi_get_msix(rdip);
907 	uint64_t	addr;
908 	uint64_t	data;
909 	uintptr_t	off;
910 
911 	DDI_INTR_NEXDBG((CE_CONT, "pci_msix_dup: dip = %p, inum = 0x%x, "
912 	    "to_vector = 0x%x\n", (void *)rdip, org_inum, dup_inum));
913 
914 	/* Offset into the original inum's entry in the MSI-X table */
915 	off = (uintptr_t)msix_p->msix_tbl_addr +
916 	    (org_inum * PCI_MSIX_VECTOR_SIZE);
917 
918 	/* For the MSI-X number passed in, get the "data" and "addr" fields */
919 	addr = ddi_get64(msix_p->msix_tbl_hdl,
920 	    (uint64_t *)(off + PCI_MSIX_LOWER_ADDR_OFFSET));
921 
922 	data = ddi_get32(msix_p->msix_tbl_hdl,
923 	    (uint32_t *)(off + PCI_MSIX_DATA_OFFSET));
924 
925 	/* Program new vector with these existing values */
926 	return (pci_msi_configure(rdip, DDI_INTR_TYPE_MSIX, 1, dup_inum, addr,
927 	    data));
928 }
929 
930 
931 /*
932  * Next set of routines are for INTx (legacy) PCI interrupt
933  * support only.
934  */
935 
936 /*
937  * pci_intx_get_cap:
938  *	For non-MSI devices that comply to PCI v2.3 or greater;
939  *	read the command register. Bit 10 implies interrupt disable.
940  *	Set this bit and then read the status register bit 3.
941  *	Bit 3 of status register is Interrupt state.
942  *	If it is set; then the device supports 'Masking'
943  *
944  *	Reset the device back to the original state.
945  */
946 int
947 pci_intx_get_cap(dev_info_t *dip, int *flagsp)
948 {
949 	uint16_t		cmdreg, savereg;
950 	ddi_acc_handle_t	cfg_hdl;
951 #ifdef	DEBUG
952 	uint16_t		statreg;
953 #endif /* DEBUG */
954 
955 	*flagsp = 0;
956 	DDI_INTR_NEXDBG((CE_CONT, "pci_intx_get_cap: %s%d: called\n",
957 	    ddi_driver_name(dip), ddi_get_instance(dip)));
958 
959 	if (pci_config_setup(dip, &cfg_hdl) != DDI_SUCCESS) {
960 		DDI_INTR_NEXDBG((CE_CONT, "pci_intx_get_cap: can't get "
961 		    "config handle\n"));
962 		return (DDI_FAILURE);
963 	}
964 
965 	savereg = pci_config_get16(cfg_hdl, PCI_CONF_COMM);
966 	DDI_INTR_NEXDBG((CE_CONT, "pci_intx_get_cap: "
967 	    "command register was 0x%x\n", savereg));
968 
969 	/* Disable the interrupts */
970 	cmdreg = savereg | PCI_COMM_INTX_DISABLE;
971 	pci_config_put16(cfg_hdl, PCI_CONF_COMM, cmdreg);
972 
973 #ifdef	DEBUG
974 	statreg = pci_config_get16(cfg_hdl, PCI_CONF_STAT);
975 	DDI_INTR_NEXDBG((CE_CONT, "pci_intx_get_cap: "
976 	    "status register is 0x%x\n", statreg));
977 #endif /* DEBUG */
978 
979 	/* Read the bit back */
980 	cmdreg = pci_config_get16(cfg_hdl, PCI_CONF_COMM);
981 	DDI_INTR_NEXDBG((CE_CONT, "pci_intx_get_cap: "
982 	    "command register is now 0x%x\n", cmdreg));
983 
984 	*flagsp = DDI_INTR_FLAG_LEVEL;
985 
986 	if (cmdreg & PCI_COMM_INTX_DISABLE) {
987 		DDI_INTR_NEXDBG((CE_CONT, "pci_intx_get_cap: "
988 		    "masking supported\n"));
989 		*flagsp |= (DDI_INTR_FLAG_MASKABLE |
990 		    DDI_INTR_FLAG_PENDING);
991 	}
992 
993 	/* Restore the device back to the original state and return */
994 	pci_config_put16(cfg_hdl, PCI_CONF_COMM, savereg);
995 
996 	pci_config_teardown(&cfg_hdl);
997 	return (DDI_SUCCESS);
998 }
999 
1000 
1001 /*
1002  * pci_intx_clr_mask:
1003  *	For non-MSI devices that comply to PCI v2.3 or greater;
1004  *	clear the bit10 in the command register.
1005  */
1006 int
1007 pci_intx_clr_mask(dev_info_t *dip)
1008 {
1009 	uint16_t		cmdreg;
1010 	ddi_acc_handle_t	cfg_hdl;
1011 
1012 	DDI_INTR_NEXDBG((CE_CONT, "pci_intx_clr_mask: %s%d: called\n",
1013 	    ddi_driver_name(dip), ddi_get_instance(dip)));
1014 
1015 	if (pci_config_setup(dip, &cfg_hdl) != DDI_SUCCESS) {
1016 		DDI_INTR_NEXDBG((CE_CONT, "pci_intx_clr_mask: can't get "
1017 		    "config handle\n"));
1018 		return (DDI_FAILURE);
1019 	}
1020 
1021 	cmdreg = pci_config_get16(cfg_hdl, PCI_CONF_COMM);
1022 	DDI_INTR_NEXDBG((CE_CONT, "pci_intx_clr_mask: "
1023 	    "command register was 0x%x\n", cmdreg));
1024 
1025 	/* Enable the interrupts */
1026 	cmdreg &= ~PCI_COMM_INTX_DISABLE;
1027 	pci_config_put16(cfg_hdl, PCI_CONF_COMM, cmdreg);
1028 	pci_config_teardown(&cfg_hdl);
1029 	return (DDI_SUCCESS);
1030 }
1031 
1032 
1033 /*
1034  * pci_intx_set_mask:
1035  *	For non-MSI devices that comply to PCI v2.3 or greater;
1036  *	set the bit10 in the command register.
1037  */
1038 int
1039 pci_intx_set_mask(dev_info_t *dip)
1040 {
1041 	uint16_t		cmdreg;
1042 	ddi_acc_handle_t	cfg_hdl;
1043 
1044 	DDI_INTR_NEXDBG((CE_CONT, "pci_intx_set_mask: %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_set_mask: can't get "
1049 		    "config handle\n"));
1050 		return (DDI_FAILURE);
1051 	}
1052 
1053 	cmdreg = pci_config_get16(cfg_hdl, PCI_CONF_COMM);
1054 	DDI_INTR_NEXDBG((CE_CONT, "pci_intx_set_mask: "
1055 	    "command register was 0x%x\n", cmdreg));
1056 
1057 	/* Disable the interrupts */
1058 	cmdreg |= PCI_COMM_INTX_DISABLE;
1059 	pci_config_put16(cfg_hdl, PCI_CONF_COMM, cmdreg);
1060 	pci_config_teardown(&cfg_hdl);
1061 	return (DDI_SUCCESS);
1062 }
1063 
1064 /*
1065  * pci_intx_get_pending:
1066  *	For non-MSI devices that comply to PCI v2.3 or greater;
1067  *	read the status register. Bit 3 of status register is
1068  *	Interrupt state. If it is set; then the interrupt is
1069  *	'Pending'.
1070  */
1071 int
1072 pci_intx_get_pending(dev_info_t *dip, int *pendingp)
1073 {
1074 	uint16_t		statreg;
1075 	ddi_acc_handle_t	cfg_hdl;
1076 
1077 	*pendingp = 0;
1078 	DDI_INTR_NEXDBG((CE_CONT, "pci_intx_get_pending: %s%d: called\n",
1079 	    ddi_driver_name(dip), ddi_get_instance(dip)));
1080 
1081 	if (pci_config_setup(dip, &cfg_hdl) != DDI_SUCCESS) {
1082 		DDI_INTR_NEXDBG((CE_CONT, "pci_intx_get_pending: can't get "
1083 		    "config handle\n"));
1084 		return (DDI_FAILURE);
1085 	}
1086 
1087 	statreg = pci_config_get16(cfg_hdl, PCI_CONF_STAT);
1088 
1089 	if (statreg & PCI_STAT_INTR) {
1090 		DDI_INTR_NEXDBG((CE_CONT, "pci_intx_get_pending: "
1091 		    "interrupt is pending\n"));
1092 		*pendingp = 1;
1093 	}
1094 
1095 	pci_config_teardown(&cfg_hdl);
1096 	return (DDI_SUCCESS);
1097 }
1098 
1099 
1100 /*
1101  * pci_devclass_to_ipl:
1102  *	translate from device class to ipl
1103  *	NOTE: This function is added here as pci_intx_get_ispec()
1104  *	calls this to figure out the priority.
1105  *	It is moved over from x86 pci.c
1106  */
1107 int
1108 pci_devclass_to_ipl(int class)
1109 {
1110 	int	ipl;
1111 	int	base_class = (class & 0xff0000) >> 16;
1112 	int	sub_class = (class & 0xff00) >> 8;
1113 
1114 	/*
1115 	 * Use the class code values to construct an ipl for the device.
1116 	 */
1117 
1118 	switch (base_class) {
1119 	default:
1120 	case PCI_CLASS_NONE:
1121 		ipl = 1;
1122 		break;
1123 	case PCI_CLASS_MASS:
1124 		ipl = 0x5;
1125 		break;
1126 	case PCI_CLASS_NET:
1127 		ipl = 0x6;
1128 		break;
1129 	case PCI_CLASS_DISPLAY:
1130 		ipl = 0x9;
1131 		break;
1132 	case PCI_CLASS_SERIALBUS:
1133 		ipl = (sub_class == PCI_SERIAL_IB) ? 6 : 1;
1134 		break;
1135 	/*
1136 	 * for high priority interrupt handlers, use level 12
1137 	 * as the highest for device drivers
1138 	 */
1139 	case PCI_CLASS_MM:
1140 		ipl = 0xc;
1141 		break;
1142 	case PCI_CLASS_MEM:
1143 		ipl = 0xc;
1144 		break;
1145 	case PCI_CLASS_BRIDGE:
1146 		ipl = 0xc;
1147 		break;
1148 	}
1149 
1150 	return (ipl);
1151 }
1152 
1153 
1154 /*
1155  * pci_intx_get_ispec:
1156  *	Get intrspec for PCI devices (legacy support)
1157  *	NOTE: This is moved here from x86 pci.c and is
1158  *	needed here as pci-ide.c uses it as well
1159  */
1160 /*ARGSUSED*/
1161 ddi_intrspec_t
1162 pci_intx_get_ispec(dev_info_t *dip, dev_info_t *rdip, int inum)
1163 {
1164 	int				class, *intpriorities;
1165 	uint_t				num_intpriorities;
1166 	struct intrspec			*ispec;
1167 	ddi_acc_handle_t		cfg_hdl;
1168 	struct ddi_parent_private_data	*pdptr;
1169 
1170 	if ((pdptr = ddi_get_parent_data(rdip)) == NULL)
1171 		return (NULL);
1172 
1173 	ispec = pdptr->par_intr;
1174 	ASSERT(ispec);
1175 
1176 	/* check if the intrspec_pri has been initialized */
1177 	if (!ispec->intrspec_pri) {
1178 		if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, rdip,
1179 		    DDI_PROP_DONTPASS, "interrupt-priorities",
1180 		    &intpriorities, &num_intpriorities) == DDI_PROP_SUCCESS) {
1181 			if (inum < num_intpriorities)
1182 				ispec->intrspec_pri = intpriorities[inum];
1183 			ddi_prop_free(intpriorities);
1184 		}
1185 
1186 		/* If still no priority, guess based on the class code */
1187 		if (ispec->intrspec_pri == 0) {
1188 			/* get 'class' property to derive the intr priority */
1189 			class = ddi_prop_get_int(DDI_DEV_T_ANY, rdip,
1190 			    DDI_PROP_DONTPASS, "class-code", -1);
1191 			ispec->intrspec_pri = (class == -1) ? 1 :
1192 			    pci_devclass_to_ipl(class);
1193 		}
1194 	}
1195 
1196 	/* Get interrupt line value */
1197 	if (!ispec->intrspec_vec) {
1198 		if (pci_config_setup(rdip, &cfg_hdl) != DDI_SUCCESS) {
1199 			DDI_INTR_NEXDBG((CE_CONT, "pci_intx_get_iline: "
1200 			    "can't get config handle\n"));
1201 			return ((ddi_intrspec_t)ispec);
1202 		}
1203 
1204 		ispec->intrspec_vec = pci_config_get8(cfg_hdl, PCI_CONF_ILINE);
1205 		pci_config_teardown(&cfg_hdl);
1206 	}
1207 
1208 	return ((ddi_intrspec_t)ispec);
1209 }
1210