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.
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.
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]
22 * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
27 * Support for MSI, MSIX and INTx
31 #include <sys/debug.h>
33 #include <sys/pci_cap.h>
34 #include <sys/pci_intr_lib.h>
35 #include <sys/sunddi.h>
36 #include <sys/bitmap.h>
39 * MSI-X BIR Index Table:
41 * BAR indicator register (BIR) to Base Address register.
43 static uchar_t pci_msix_bir_index
[8] = {0x10, 0x14, 0x18, 0x1c,
44 0x20, 0x24, 0xff, 0xff};
46 /* default class to pil value mapping */
47 pci_class_val_t pci_default_pil
[] = {
48 {0x000000, 0xff0000, 0x1}, /* Class code for pre-2.0 devices */
49 {0x010000, 0xff0000, 0x5}, /* Mass Storage Controller */
50 {0x020000, 0xff0000, 0x6}, /* Network Controller */
51 {0x030000, 0xff0000, 0x9}, /* Display Controller */
52 {0x040000, 0xff0000, 0x8}, /* Multimedia Controller */
53 {0x050000, 0xff0000, 0x9}, /* Memory Controller */
54 {0x060000, 0xff0000, 0x9}, /* Bridge Controller */
55 {0x0c0000, 0xffff00, 0x9}, /* Serial Bus, FireWire (IEEE 1394) */
56 {0x0c0100, 0xffff00, 0x4}, /* Serial Bus, ACCESS.bus */
57 {0x0c0200, 0xffff00, 0x4}, /* Serial Bus, SSA */
58 {0x0c0300, 0xffff00, 0x9}, /* Serial Bus Universal Serial Bus */
60 * XXX - This is a temporary workaround and it will be removed
61 * after x86 interrupt scalability support.
63 #if defined(__i386) || defined(__amd64)
64 {0x0c0400, 0xffff00, 0x5}, /* Serial Bus, Fibre Channel */
66 {0x0c0400, 0xffff00, 0x6}, /* Serial Bus, Fibre Channel */
68 {0x0c0600, 0xffff00, 0x6} /* Serial Bus, Infiniband */
72 * Default class to intr_weight value mapping (% of CPU). A driver.conf
73 * entry on or above the pci node like
75 * pci-class-intr-weights= 0x020000, 0xff0000, 30;
77 * can be used to augment or override entries in the default table below.
79 * NB: The values below give NICs preference on redistribution, and provide
80 * NICs some isolation from other interrupt sources. We need better interfaces
81 * that allow the NIC driver to identify a specific NIC instance as high
82 * bandwidth, and thus deserving of separation from other low bandwidth
83 * NICs additional isolation from other interrupt sources.
85 * NB: We treat Infiniband like a NIC.
87 pci_class_val_t pci_default_intr_weight
[] = {
88 {0x020000, 0xff0000, 35}, /* Network Controller */
89 {0x010000, 0xff0000, 10}, /* Mass Storage Controller */
90 {0x0c0400, 0xffff00, 10}, /* Serial Bus, Fibre Channel */
91 {0x0c0600, 0xffff00, 50} /* Serial Bus, Infiniband */
95 * Library utility functions
101 * Helper function that returns with 'cfg_hdl', MSI/X ctrl pointer,
102 * and caps_ptr for MSI/X if these are found.
105 pci_get_msi_ctrl(dev_info_t
*dip
, int type
, ushort_t
*msi_ctrl
,
106 ushort_t
*caps_ptr
, ddi_acc_handle_t
*h
)
108 *msi_ctrl
= *caps_ptr
= 0;
110 if (pci_config_setup(dip
, h
) != DDI_SUCCESS
) {
111 DDI_INTR_NEXDBG((CE_CONT
, "pci_get_msi_ctrl: "
112 "%s%d can't get config handle",
113 ddi_driver_name(dip
), ddi_get_instance(dip
)));
115 return (DDI_FAILURE
);
118 if ((PCI_CAP_LOCATE(*h
, PCI_CAP_ID_MSI
, caps_ptr
) == DDI_SUCCESS
) &&
119 (type
== DDI_INTR_TYPE_MSI
)) {
120 if ((*msi_ctrl
= PCI_CAP_GET16(*h
, NULL
, *caps_ptr
,
121 PCI_MSI_CTRL
)) == PCI_CAP_EINVAL16
)
124 DDI_INTR_NEXDBG((CE_CONT
, "pci_get_msi_ctrl: MSI "
125 "caps_ptr=%x msi_ctrl=%x\n", *caps_ptr
, *msi_ctrl
));
127 return (DDI_SUCCESS
);
130 if ((PCI_CAP_LOCATE(*h
, PCI_CAP_ID_MSI_X
, caps_ptr
) == DDI_SUCCESS
) &&
131 (type
== DDI_INTR_TYPE_MSIX
)) {
132 if ((*msi_ctrl
= PCI_CAP_GET16(*h
, NULL
, *caps_ptr
,
133 PCI_MSIX_CTRL
)) == PCI_CAP_EINVAL16
)
136 DDI_INTR_NEXDBG((CE_CONT
, "pci_get_msi_ctrl: MSI-X "
137 "caps_ptr=%x msi_ctrl=%x\n", *caps_ptr
, *msi_ctrl
));
139 return (DDI_SUCCESS
);
143 pci_config_teardown(h
);
144 return (DDI_FAILURE
);
151 * Get the capabilities of the MSI/X interrupt
154 pci_msi_get_cap(dev_info_t
*rdip
, int type
, int *flagsp
)
156 ushort_t caps_ptr
, msi_ctrl
;
157 ddi_acc_handle_t cfg_hdle
;
159 DDI_INTR_NEXDBG((CE_CONT
, "pci_msi_get_cap: rdip = 0x%p\n",
164 if (pci_get_msi_ctrl(rdip
, type
, &msi_ctrl
,
165 &caps_ptr
, &cfg_hdle
) != DDI_SUCCESS
)
166 return (DDI_FAILURE
);
168 if (type
== DDI_INTR_TYPE_MSI
) {
169 if (msi_ctrl
& PCI_MSI_64BIT_MASK
)
170 *flagsp
|= DDI_INTR_FLAG_MSI64
;
171 if (msi_ctrl
& PCI_MSI_PVM_MASK
)
172 *flagsp
|= (DDI_INTR_FLAG_MASKABLE
|
173 DDI_INTR_FLAG_PENDING
);
175 *flagsp
|= DDI_INTR_FLAG_BLOCK
;
176 } else if (type
== DDI_INTR_TYPE_MSIX
) {
177 /* MSI-X supports PVM, 64bit by default */
178 *flagsp
|= (DDI_INTR_FLAG_MASKABLE
| DDI_INTR_FLAG_MSI64
|
179 DDI_INTR_FLAG_PENDING
);
182 *flagsp
|= DDI_INTR_FLAG_EDGE
;
184 DDI_INTR_NEXDBG((CE_CONT
, "pci_msi_get_cap: flags = 0x%x\n", *flagsp
));
186 pci_config_teardown(&cfg_hdle
);
187 return (DDI_SUCCESS
);
194 * Configure address/data and number MSI/Xs fields in the MSI/X
195 * capability structure.
199 pci_msi_configure(dev_info_t
*rdip
, int type
, int count
, int inum
,
200 uint64_t addr
, uint64_t data
)
202 ushort_t caps_ptr
, msi_ctrl
;
205 DDI_INTR_NEXDBG((CE_CONT
, "pci_msi_configure: rdip = 0x%p type 0x%x "
206 "count 0x%x inum 0x%x addr 0x%" PRIx64
" data 0x%" PRIx64
"\n",
207 (void *)rdip
, type
, count
, inum
, addr
, data
));
209 if (pci_get_msi_ctrl(rdip
, type
, &msi_ctrl
,
210 &caps_ptr
, &h
) != DDI_SUCCESS
)
211 return (DDI_FAILURE
);
213 if (type
== DDI_INTR_TYPE_MSI
) {
214 /* Set the bits to inform how many MSIs are enabled */
215 msi_ctrl
|= ((highbit(count
) -1) << PCI_MSI_MME_SHIFT
);
216 PCI_CAP_PUT16(h
, NULL
, caps_ptr
, PCI_MSI_CTRL
, msi_ctrl
);
218 DDI_INTR_NEXDBG((CE_CONT
, "pci_msi_configure: msi_ctrl = %x\n",
219 PCI_CAP_GET16(h
, NULL
, caps_ptr
, PCI_MSI_CTRL
)));
221 /* Set the "data" and "addr" bits */
222 PCI_CAP_PUT32(h
, NULL
, caps_ptr
, PCI_MSI_ADDR_OFFSET
, addr
);
224 DDI_INTR_NEXDBG((CE_CONT
, "pci_msi_configure: msi_addr = %x\n",
225 PCI_CAP_GET32(h
, NULL
, caps_ptr
, PCI_MSI_ADDR_OFFSET
)));
227 if (msi_ctrl
& PCI_MSI_64BIT_MASK
) {
228 PCI_CAP_PUT32(h
, NULL
, caps_ptr
, PCI_MSI_ADDR_OFFSET
231 DDI_INTR_NEXDBG((CE_CONT
, "pci_msi_configure: upper "
232 "32bit msi_addr = %x\n", PCI_CAP_GET32(h
, NULL
,
233 caps_ptr
, PCI_MSI_ADDR_OFFSET
+ 4)));
235 PCI_CAP_PUT16(h
, NULL
, caps_ptr
, PCI_MSI_64BIT_DATA
,
238 DDI_INTR_NEXDBG((CE_CONT
, "pci_msi_configure: msi_data "
239 "= %x\n", PCI_CAP_GET16(h
, NULL
, caps_ptr
,
240 PCI_MSI_64BIT_DATA
)));
242 PCI_CAP_PUT16(h
, NULL
, caps_ptr
, PCI_MSI_32BIT_DATA
,
245 DDI_INTR_NEXDBG((CE_CONT
, "pci_msi_configure: msi_data "
246 "= %x\n", PCI_CAP_GET16(h
, NULL
, caps_ptr
,
247 PCI_MSI_32BIT_DATA
)));
249 } else if (type
== DDI_INTR_TYPE_MSIX
) {
251 ddi_intr_msix_t
*msix_p
= i_ddi_get_msix(rdip
);
253 /* Offset into the "inum"th entry in the MSI-X table */
254 off
= (uintptr_t)msix_p
->msix_tbl_addr
+
255 (inum
* PCI_MSIX_VECTOR_SIZE
);
257 /* Set the "data" and "addr" bits */
258 ddi_put32(msix_p
->msix_tbl_hdl
,
259 (uint32_t *)(off
+ PCI_MSIX_DATA_OFFSET
), data
);
261 ddi_put64(msix_p
->msix_tbl_hdl
,
262 (uint64_t *)(off
+ PCI_MSIX_LOWER_ADDR_OFFSET
), addr
);
264 DDI_INTR_NEXDBG((CE_CONT
, "pci_msi_configure: "
265 "msix_addr 0x%" PRIx64
" msix_data 0x%x\n",
266 ddi_get64(msix_p
->msix_tbl_hdl
,
267 (uint64_t *)(off
+ PCI_MSIX_LOWER_ADDR_OFFSET
)),
268 ddi_get32(msix_p
->msix_tbl_hdl
,
269 (uint32_t *)(off
+ PCI_MSIX_DATA_OFFSET
))));
272 pci_config_teardown(&h
);
273 return (DDI_SUCCESS
);
278 * pci_msi_unconfigure:
280 * Unconfigure address/data and number MSI/Xs fields in the MSI/X
281 * capability structure.
285 pci_msi_unconfigure(dev_info_t
*rdip
, int type
, int inum
)
287 ushort_t msi_ctrl
, caps_ptr
;
290 DDI_INTR_NEXDBG((CE_CONT
, "pci_msi_unconfigure: rdip = 0x%p type 0x%x "
291 "inum 0x%x\n", (void *)rdip
, type
, inum
));
293 if (pci_get_msi_ctrl(rdip
, type
, &msi_ctrl
, &caps_ptr
, &h
) !=
295 return (DDI_FAILURE
);
297 if (type
== DDI_INTR_TYPE_MSI
) {
298 msi_ctrl
&= (~PCI_MSI_MME_MASK
);
299 PCI_CAP_PUT16(h
, NULL
, caps_ptr
, PCI_MSI_CTRL
, msi_ctrl
);
301 PCI_CAP_PUT32(h
, NULL
, caps_ptr
, PCI_MSI_ADDR_OFFSET
, 0);
303 if (msi_ctrl
& PCI_MSI_64BIT_MASK
) {
304 PCI_CAP_PUT16(h
, NULL
, caps_ptr
, PCI_MSI_64BIT_DATA
,
306 PCI_CAP_PUT32(h
, NULL
, caps_ptr
, PCI_MSI_ADDR_OFFSET
309 PCI_CAP_PUT16(h
, NULL
, caps_ptr
, PCI_MSI_32BIT_DATA
,
313 DDI_INTR_NEXDBG((CE_CONT
, "pci_msi_unconfigure: msi_ctrl "
314 "= %x\n", PCI_CAP_GET16(h
, NULL
, caps_ptr
, PCI_MSI_CTRL
)));
316 } else if (type
== DDI_INTR_TYPE_MSIX
) {
318 ddi_intr_msix_t
*msix_p
= i_ddi_get_msix(rdip
);
320 /* Offset into the "inum"th entry in the MSI-X table */
321 off
= (uintptr_t)msix_p
->msix_tbl_addr
+
322 (inum
* PCI_MSIX_VECTOR_SIZE
);
324 /* Reset the "data" and "addr" bits */
325 ddi_put32(msix_p
->msix_tbl_hdl
,
326 (uint32_t *)(off
+ PCI_MSIX_DATA_OFFSET
), 0);
328 ddi_put64(msix_p
->msix_tbl_hdl
,
329 (uint64_t *)(off
+ PCI_MSIX_LOWER_ADDR_OFFSET
), 0);
332 pci_config_teardown(&h
);
333 return (DDI_SUCCESS
);
338 * pci_is_msi_enabled:
340 * This function returns DDI_SUCCESS if MSI/X is already enabled, otherwise
341 * it returns DDI_FAILURE.
344 pci_is_msi_enabled(dev_info_t
*rdip
, int type
)
346 ushort_t caps_ptr
, msi_ctrl
;
347 ddi_acc_handle_t cfg_hdle
;
348 int ret
= DDI_FAILURE
;
350 DDI_INTR_NEXDBG((CE_CONT
, "pci_is_msi_enabled: rdip = 0x%p, "
351 "type = 0x%x\n", (void *)rdip
, type
));
353 if (pci_get_msi_ctrl(rdip
, type
, &msi_ctrl
,
354 &caps_ptr
, &cfg_hdle
) != DDI_SUCCESS
)
355 return (DDI_FAILURE
);
357 if ((type
== DDI_INTR_TYPE_MSI
) && (msi_ctrl
& PCI_MSI_ENABLE_BIT
))
360 if ((type
== DDI_INTR_TYPE_MSIX
) && (msi_ctrl
& PCI_MSIX_ENABLE_BIT
))
363 pci_config_teardown(&cfg_hdle
);
369 * pci_msi_enable_mode:
371 * This function sets the MSI_ENABLE bit in the capability structure
372 * (for MSI) and MSIX_ENABLE bit in the MSI-X capability structure.
374 * NOTE: It is the nexus driver's responsibility to clear the MSI/X
375 * interrupt's mask bit in the MSI/X capability structure before the
376 * interrupt can be used.
379 pci_msi_enable_mode(dev_info_t
*rdip
, int type
)
381 ushort_t caps_ptr
, msi_ctrl
;
382 ddi_acc_handle_t cfg_hdle
;
384 DDI_INTR_NEXDBG((CE_CONT
, "pci_msi_enable_mode: rdip = 0x%p\n",
387 if (pci_get_msi_ctrl(rdip
, type
, &msi_ctrl
,
388 &caps_ptr
, &cfg_hdle
) != DDI_SUCCESS
)
389 return (DDI_FAILURE
);
391 if (type
== DDI_INTR_TYPE_MSI
) {
392 if (msi_ctrl
& PCI_MSI_ENABLE_BIT
)
395 msi_ctrl
|= PCI_MSI_ENABLE_BIT
;
396 PCI_CAP_PUT16(cfg_hdle
, NULL
, caps_ptr
, PCI_MSI_CTRL
, msi_ctrl
);
398 } else if (type
== DDI_INTR_TYPE_MSIX
) {
399 if (msi_ctrl
& PCI_MSIX_ENABLE_BIT
)
402 msi_ctrl
|= PCI_MSIX_ENABLE_BIT
;
403 PCI_CAP_PUT16(cfg_hdle
, NULL
, caps_ptr
, PCI_MSIX_CTRL
,
408 DDI_INTR_NEXDBG((CE_CONT
, "pci_msi_enable_mode: msi_ctrl = %x\n",
411 pci_config_teardown(&cfg_hdle
);
412 return (DDI_SUCCESS
);
417 * pci_msi_disable_mode:
419 * This function resets the MSI_ENABLE bit in the capability structure
420 * (for MSI) and MSIX_ENABLE bit in the MSI-X capability structure.
422 * NOTE: It is the nexus driver's responsibility to set the MSI/X
423 * interrupt's mask bit in the MSI/X capability structure before the
424 * interrupt can be disabled.
427 pci_msi_disable_mode(dev_info_t
*rdip
, int type
)
429 ushort_t caps_ptr
, msi_ctrl
;
430 ddi_acc_handle_t cfg_hdle
;
432 DDI_INTR_NEXDBG((CE_CONT
, "pci_msi_disable_mode: rdip = 0x%p\n",
435 if (pci_get_msi_ctrl(rdip
, type
, &msi_ctrl
,
436 &caps_ptr
, &cfg_hdle
) != DDI_SUCCESS
)
437 return (DDI_FAILURE
);
439 /* Reset the "enable" bit */
440 if (type
== DDI_INTR_TYPE_MSI
) {
441 if (!(msi_ctrl
& PCI_MSI_ENABLE_BIT
))
443 msi_ctrl
&= ~PCI_MSI_ENABLE_BIT
;
444 PCI_CAP_PUT16(cfg_hdle
, NULL
, caps_ptr
, PCI_MSI_CTRL
, msi_ctrl
);
445 } else if (type
== DDI_INTR_TYPE_MSIX
) {
446 if (!(msi_ctrl
& PCI_MSIX_ENABLE_BIT
))
449 msi_ctrl
&= ~PCI_MSIX_ENABLE_BIT
;
450 PCI_CAP_PUT16(cfg_hdle
, NULL
, caps_ptr
, PCI_MSIX_CTRL
,
455 DDI_INTR_NEXDBG((CE_CONT
, "pci_msi_disable_mode: msi_ctrl = %x\n",
458 pci_config_teardown(&cfg_hdle
);
459 return (DDI_SUCCESS
);
466 * Set the mask bit in the MSI/X capability structure
470 pci_msi_set_mask(dev_info_t
*rdip
, int type
, int inum
)
473 int ret
= DDI_FAILURE
;
474 ushort_t caps_ptr
, msi_ctrl
;
475 ddi_acc_handle_t cfg_hdle
;
478 DDI_INTR_NEXDBG((CE_CONT
, "pci_msi_set_mask: rdip = 0x%p, "
479 "type = 0x%x\n", (void *)rdip
, type
));
481 if (pci_get_msi_ctrl(rdip
, type
, &msi_ctrl
,
482 &caps_ptr
, &cfg_hdle
) != DDI_SUCCESS
)
483 return (DDI_FAILURE
);
485 if (type
== DDI_INTR_TYPE_MSI
) {
486 if (!(msi_ctrl
& PCI_MSI_PVM_MASK
))
489 offset
= (msi_ctrl
& PCI_MSI_64BIT_MASK
) ?
490 PCI_MSI_64BIT_MASKBITS
: PCI_MSI_32BIT_MASK
;
492 if ((mask_bits
= PCI_CAP_GET32(cfg_hdle
, NULL
, caps_ptr
,
493 offset
)) == PCI_CAP_EINVAL32
)
496 mask_bits
|= (1 << inum
);
498 PCI_CAP_PUT32(cfg_hdle
, NULL
, caps_ptr
, offset
, mask_bits
);
500 } else if (type
== DDI_INTR_TYPE_MSIX
) {
502 ddi_intr_msix_t
*msix_p
;
504 /* Set function mask */
505 if (msi_ctrl
& PCI_MSIX_FUNCTION_MASK
) {
510 msix_p
= i_ddi_get_msix(rdip
);
512 /* Offset into the "inum"th entry in the MSI-X table */
513 off
= (uintptr_t)msix_p
->msix_tbl_addr
+ (inum
*
514 PCI_MSIX_VECTOR_SIZE
) + PCI_MSIX_VECTOR_CTRL_OFFSET
;
516 /* Set the Mask bit */
517 ddi_put32(msix_p
->msix_tbl_hdl
, (uint32_t *)off
, 0x1);
522 pci_config_teardown(&cfg_hdle
);
530 * Clear the mask bit in the MSI/X capability structure
534 pci_msi_clr_mask(dev_info_t
*rdip
, int type
, int inum
)
536 ushort_t caps_ptr
, msi_ctrl
;
537 ddi_acc_handle_t cfg_hdle
;
539 int ret
= DDI_FAILURE
;
542 DDI_INTR_NEXDBG((CE_CONT
, "pci_msi_clr_mask: rdip = 0x%p, "
543 "type = 0x%x\n", (void *)rdip
, type
));
545 if (pci_get_msi_ctrl(rdip
, type
, &msi_ctrl
,
546 &caps_ptr
, &cfg_hdle
) != DDI_SUCCESS
)
547 return (DDI_FAILURE
);
549 if (type
== DDI_INTR_TYPE_MSI
) {
550 if (!(msi_ctrl
& PCI_MSI_PVM_MASK
))
553 offset
= (msi_ctrl
& PCI_MSI_64BIT_MASK
) ?
554 PCI_MSI_64BIT_MASKBITS
: PCI_MSI_32BIT_MASK
;
555 if ((mask_bits
= PCI_CAP_GET32(cfg_hdle
, NULL
, caps_ptr
,
556 offset
)) == PCI_CAP_EINVAL32
)
559 mask_bits
&= ~(1 << inum
);
561 PCI_CAP_PUT32(cfg_hdle
, NULL
, caps_ptr
, offset
, mask_bits
);
563 } else if (type
== DDI_INTR_TYPE_MSIX
) {
565 ddi_intr_msix_t
*msix_p
;
567 if (msi_ctrl
& PCI_MSIX_FUNCTION_MASK
) {
572 msix_p
= i_ddi_get_msix(rdip
);
574 /* Offset into the "inum"th entry in the MSI-X table */
575 off
= (uintptr_t)msix_p
->msix_tbl_addr
+ (inum
*
576 PCI_MSIX_VECTOR_SIZE
) + PCI_MSIX_VECTOR_CTRL_OFFSET
;
578 /* Clear the Mask bit */
579 ddi_put32(msix_p
->msix_tbl_hdl
, (uint32_t *)off
, 0x0);
584 pci_config_teardown(&cfg_hdle
);
590 * pci_msi_get_pending:
592 * Get the pending bit from the MSI/X capability structure
596 pci_msi_get_pending(dev_info_t
*rdip
, int type
, int inum
, int *pendingp
)
598 ushort_t caps_ptr
, msi_ctrl
;
599 ddi_acc_handle_t cfg_hdle
;
601 int ret
= DDI_FAILURE
;
603 DDI_INTR_NEXDBG((CE_CONT
, "pci_msi_get_pending: rdip = 0x%p\n",
606 if (pci_get_msi_ctrl(rdip
, type
, &msi_ctrl
,
607 &caps_ptr
, &cfg_hdle
) != DDI_SUCCESS
)
608 return (DDI_FAILURE
);
610 if (type
== DDI_INTR_TYPE_MSI
) {
611 uint32_t pending_bits
;
613 if (!(msi_ctrl
& PCI_MSI_PVM_MASK
)) {
614 DDI_INTR_NEXDBG((CE_CONT
, "pci_msi_get_pending: "
615 "PVM is not supported\n"));
619 offset
= (msi_ctrl
& PCI_MSI_64BIT_MASK
) ?
620 PCI_MSI_64BIT_PENDING
: PCI_MSI_32BIT_PENDING
;
622 if ((pending_bits
= PCI_CAP_GET32(cfg_hdle
, NULL
, caps_ptr
,
623 offset
)) == PCI_CAP_EINVAL32
)
626 *pendingp
= pending_bits
& ~(1 >> inum
);
628 } else if (type
== DDI_INTR_TYPE_MSIX
) {
630 uint64_t pending_bits
;
631 ddi_intr_msix_t
*msix_p
= i_ddi_get_msix(rdip
);
633 /* Offset into the PBA array which has entry for "inum" */
634 off
= (uintptr_t)msix_p
->msix_pba_addr
+ (inum
/ 64);
636 /* Read the PBA array */
637 pending_bits
= ddi_get64(msix_p
->msix_pba_hdl
, (uint64_t *)off
);
639 *pendingp
= pending_bits
& ~(1 >> inum
);
644 pci_config_teardown(&cfg_hdle
);
650 * pci_msi_get_nintrs:
652 * For a given type (MSI/X) returns the number of interrupts supported
655 pci_msi_get_nintrs(dev_info_t
*rdip
, int type
, int *nintrs
)
657 ushort_t caps_ptr
, msi_ctrl
;
658 ddi_acc_handle_t cfg_hdle
;
660 DDI_INTR_NEXDBG((CE_CONT
, "pci_msi_get_nintrs: rdip = 0x%p\n",
663 if (pci_get_msi_ctrl(rdip
, type
, &msi_ctrl
,
664 &caps_ptr
, &cfg_hdle
) != DDI_SUCCESS
)
665 return (DDI_FAILURE
);
667 if (type
== DDI_INTR_TYPE_MSI
) {
668 *nintrs
= 1 << ((msi_ctrl
& PCI_MSI_MMC_MASK
) >>
670 } else if (type
== DDI_INTR_TYPE_MSIX
) {
671 if (msi_ctrl
& PCI_MSIX_TBL_SIZE_MASK
)
672 *nintrs
= (msi_ctrl
& PCI_MSIX_TBL_SIZE_MASK
) + 1;
675 DDI_INTR_NEXDBG((CE_CONT
, "pci_msi_get_nintrs: "
676 "nintr = 0x%x\n", *nintrs
));
678 pci_config_teardown(&cfg_hdle
);
679 return (DDI_SUCCESS
);
684 * pci_msi_set_nintrs:
686 * For a given type (MSI/X) sets the number of interrupts supported
688 * For MSI: Return an error if this func is called for navail > 32
689 * For MSI-X: Return an error if this func is called for navail > 2048
692 pci_msi_set_nintrs(dev_info_t
*rdip
, int type
, int navail
)
694 ushort_t caps_ptr
, msi_ctrl
;
695 ddi_acc_handle_t cfg_hdle
;
697 DDI_INTR_NEXDBG((CE_CONT
, "pci_msi_set_nintrs: rdip = 0x%p, "
698 "navail = 0x%x\n", (void *)rdip
, navail
));
700 /* Check for valid input argument */
701 if (((type
== DDI_INTR_TYPE_MSI
) && (navail
> PCI_MSI_MAX_INTRS
)) ||
702 ((type
== DDI_INTR_TYPE_MSIX
) && (navail
> PCI_MSIX_MAX_INTRS
)))
705 if (pci_get_msi_ctrl(rdip
, type
, &msi_ctrl
,
706 &caps_ptr
, &cfg_hdle
) != DDI_SUCCESS
)
707 return (DDI_FAILURE
);
709 if (type
== DDI_INTR_TYPE_MSI
) {
710 msi_ctrl
|= ((highbit(navail
) -1) << PCI_MSI_MME_SHIFT
);
712 PCI_CAP_PUT16(cfg_hdle
, NULL
, caps_ptr
, PCI_MSI_CTRL
, msi_ctrl
);
713 } else if (type
== DDI_INTR_TYPE_MSIX
) {
714 DDI_INTR_NEXDBG((CE_CONT
, "pci_msi_set_nintrs: unsupported\n"));
717 pci_config_teardown(&cfg_hdle
);
718 return (DDI_SUCCESS
);
723 * pci_msi_get_supported_type:
725 * Returns DDI_INTR_TYPE_MSI and/or DDI_INTR_TYPE_MSIX as supported
726 * types if device supports them. A DDI_FAILURE is returned otherwise.
729 pci_msi_get_supported_type(dev_info_t
*rdip
, int *typesp
)
731 ushort_t caps_ptr
, msi_ctrl
;
732 ddi_acc_handle_t cfg_hdle
;
734 DDI_INTR_NEXDBG((CE_CONT
, "pci_msi_get_supported_type: "
735 "rdip = 0x%p\n", (void *)rdip
));
739 if (pci_get_msi_ctrl(rdip
, DDI_INTR_TYPE_MSI
, &msi_ctrl
,
740 &caps_ptr
, &cfg_hdle
) == DDI_SUCCESS
) {
741 *typesp
|= DDI_INTR_TYPE_MSI
;
742 pci_config_teardown(&cfg_hdle
);
745 if (pci_get_msi_ctrl(rdip
, DDI_INTR_TYPE_MSIX
, &msi_ctrl
,
746 &caps_ptr
, &cfg_hdle
) == DDI_SUCCESS
) {
747 *typesp
|= DDI_INTR_TYPE_MSIX
;
748 pci_config_teardown(&cfg_hdle
);
751 DDI_INTR_NEXDBG((CE_CONT
, "pci_msi_get_supported_type: "
752 "rdip = 0x%p types 0x%x\n", (void *)rdip
, *typesp
));
754 return (*typesp
== 0 ? DDI_FAILURE
: DDI_SUCCESS
);
760 * This function initializes the various handles/addrs etc.
761 * needed for MSI-X support. It also allocates a private
762 * structure to keep track of these.
765 pci_msix_init(dev_info_t
*rdip
)
767 uint_t rnumber
, breg
, nregs
;
768 size_t msix_tbl_size
;
770 ushort_t caps_ptr
, msix_ctrl
;
771 ddi_intr_msix_t
*msix_p
;
772 ddi_acc_handle_t cfg_hdle
;
774 int reg_size
, addr_space
, offset
, *regs_list
;
777 DDI_INTR_NEXDBG((CE_CONT
, "pci_msix_init: rdip = %p\n", (void *)rdip
));
779 if (pci_get_msi_ctrl(rdip
, DDI_INTR_TYPE_MSIX
, &msix_ctrl
,
780 &caps_ptr
, &cfg_hdle
) != DDI_SUCCESS
)
783 msix_p
= kmem_zalloc(sizeof (ddi_intr_msix_t
), KM_SLEEP
);
786 * Initialize the devacc structure
788 msix_p
->msix_dev_attr
.devacc_attr_version
= DDI_DEVICE_ATTR_V0
;
789 msix_p
->msix_dev_attr
.devacc_attr_endian_flags
=
790 DDI_STRUCTURE_LE_ACC
;
791 msix_p
->msix_dev_attr
.devacc_attr_dataorder
= DDI_STRICTORDER_ACC
;
793 /* Map the entire MSI-X vector table */
794 msix_p
->msix_tbl_offset
= PCI_CAP_GET32(cfg_hdle
, NULL
, caps_ptr
,
795 PCI_MSIX_TBL_OFFSET
);
797 if ((breg
= pci_msix_bir_index
[msix_p
->msix_tbl_offset
&
798 PCI_MSIX_TBL_BIR_MASK
]) == 0xff)
801 msix_p
->msix_tbl_offset
= msix_p
->msix_tbl_offset
&
802 ~PCI_MSIX_TBL_BIR_MASK
;
803 msix_tbl_size
= ((msix_ctrl
& PCI_MSIX_TBL_SIZE_MASK
) + 1) *
804 PCI_MSIX_VECTOR_SIZE
;
806 DDI_INTR_NEXDBG((CE_CONT
, "pci_msix_init: MSI-X table offset 0x%x "
807 "breg 0x%x size 0x%lx\n", msix_p
->msix_tbl_offset
, breg
,
810 if ((ret
= ddi_prop_lookup_int_array(DDI_DEV_T_ANY
, rdip
,
811 DDI_PROP_DONTPASS
, "reg", (int **)®s_list
, &nregs
))
812 != DDI_PROP_SUCCESS
) {
813 DDI_INTR_NEXDBG((CE_CONT
, "pci_msix_init: "
814 "ddi_prop_lookup_int_array failed %d\n", ret
));
819 reg_size
= sizeof (pci_regspec_t
) / sizeof (int);
821 for (i
= 1, rnumber
= 0; i
< nregs
/reg_size
; i
++) {
822 rp
= (pci_regspec_t
*)®s_list
[i
* reg_size
];
823 addr_space
= rp
->pci_phys_hi
& PCI_ADDR_MASK
;
824 offset
= PCI_REG_REG_G(rp
->pci_phys_hi
);
826 if ((offset
== breg
) && ((addr_space
== PCI_ADDR_MEM32
) ||
827 (addr_space
== PCI_ADDR_MEM64
))) {
833 DDI_INTR_NEXDBG((CE_CONT
, "pci_msix_init: MSI-X rnum = %d\n", rnumber
));
836 DDI_INTR_NEXDBG((CE_CONT
, "pci_msix_init: "
837 "no mtaching reg number for offset 0x%x\n", breg
));
842 if ((ret
= ddi_regs_map_setup(rdip
, rnumber
,
843 (caddr_t
*)&msix_p
->msix_tbl_addr
, msix_p
->msix_tbl_offset
,
844 msix_tbl_size
, &msix_p
->msix_dev_attr
,
845 &msix_p
->msix_tbl_hdl
)) != DDI_SUCCESS
) {
846 DDI_INTR_NEXDBG((CE_CONT
, "pci_msix_init: MSI-X Table "
847 "ddi_regs_map_setup failed %d\n", ret
));
853 * Map in the MSI-X Pending Bit Array
855 msix_p
->msix_pba_offset
= PCI_CAP_GET32(cfg_hdle
, NULL
, caps_ptr
,
856 PCI_MSIX_PBA_OFFSET
);
858 if ((breg
= pci_msix_bir_index
[msix_p
->msix_pba_offset
&
859 PCI_MSIX_PBA_BIR_MASK
]) == 0xff)
862 msix_p
->msix_pba_offset
= msix_p
->msix_pba_offset
&
863 ~PCI_MSIX_PBA_BIR_MASK
;
864 pba_tbl_size
= ((msix_ctrl
& PCI_MSIX_TBL_SIZE_MASK
) + 1)/8;
866 DDI_INTR_NEXDBG((CE_CONT
, "pci_msix_init: PBA table offset 0x%x "
867 "breg 0x%x size 0x%lx\n", msix_p
->msix_pba_offset
, breg
,
870 for (i
= 1, rnumber
= 0; i
< nregs
/reg_size
; i
++) {
871 rp
= (pci_regspec_t
*)®s_list
[i
* reg_size
];
872 addr_space
= rp
->pci_phys_hi
& PCI_ADDR_MASK
;
873 offset
= PCI_REG_REG_G(rp
->pci_phys_hi
);
875 if ((offset
== breg
) && ((addr_space
== PCI_ADDR_MEM32
) ||
876 (addr_space
== PCI_ADDR_MEM64
))) {
882 DDI_INTR_NEXDBG((CE_CONT
, "pci_msix_init: PBA rnum = %d\n", rnumber
));
885 DDI_INTR_NEXDBG((CE_CONT
, "pci_msix_init: "
886 "no matching reg number for offset 0x%x\n", breg
));
891 if ((ret
= ddi_regs_map_setup(rdip
, rnumber
,
892 (caddr_t
*)&msix_p
->msix_pba_addr
, msix_p
->msix_pba_offset
,
893 pba_tbl_size
, &msix_p
->msix_dev_attr
,
894 &msix_p
->msix_pba_hdl
)) != DDI_SUCCESS
) {
895 DDI_INTR_NEXDBG((CE_CONT
, "pci_msix_init: PBA "
896 "ddi_regs_map_setup failed %d\n", ret
));
901 DDI_INTR_NEXDBG((CE_CONT
, "pci_msix_init: msix_p = 0x%p DONE!!\n",
904 ddi_prop_free(regs_list
);
908 ddi_regs_map_free(&msix_p
->msix_tbl_hdl
);
910 ddi_prop_free(regs_list
);
912 kmem_free(msix_p
, sizeof (ddi_intr_msix_t
));
915 pci_config_teardown(&cfg_hdle
);
922 * This function cleans up previously allocated handles/addrs etc.
923 * It is only called if no more MSI-X interrupts are being used.
926 pci_msix_fini(ddi_intr_msix_t
*msix_p
)
928 DDI_INTR_NEXDBG((CE_CONT
, "pci_msix_fini: msix_p = 0x%p\n",
931 ddi_regs_map_free(&msix_p
->msix_pba_hdl
);
932 ddi_regs_map_free(&msix_p
->msix_tbl_hdl
);
933 kmem_free(msix_p
, sizeof (ddi_intr_msix_t
));
939 * This function duplicates the address and data pair of one msi-x
940 * vector to another msi-x vector.
943 pci_msix_dup(dev_info_t
*rdip
, int org_inum
, int dup_inum
)
945 ddi_intr_msix_t
*msix_p
= i_ddi_get_msix(rdip
);
950 DDI_INTR_NEXDBG((CE_CONT
, "pci_msix_dup: dip = %p, inum = 0x%x, "
951 "to_vector = 0x%x\n", (void *)rdip
, org_inum
, dup_inum
));
953 /* Offset into the original inum's entry in the MSI-X table */
954 off
= (uintptr_t)msix_p
->msix_tbl_addr
+
955 (org_inum
* PCI_MSIX_VECTOR_SIZE
);
957 /* For the MSI-X number passed in, get the "data" and "addr" fields */
958 addr
= ddi_get64(msix_p
->msix_tbl_hdl
,
959 (uint64_t *)(off
+ PCI_MSIX_LOWER_ADDR_OFFSET
));
961 data
= ddi_get32(msix_p
->msix_tbl_hdl
,
962 (uint32_t *)(off
+ PCI_MSIX_DATA_OFFSET
));
964 /* Program new vector with these existing values */
965 return (pci_msi_configure(rdip
, DDI_INTR_TYPE_MSIX
, 1, dup_inum
, addr
,
971 * Next set of routines are for INTx (legacy) PCI interrupt
977 * For non-MSI devices that comply to PCI v2.3 or greater;
978 * read the command register. Bit 10 implies interrupt disable.
979 * Set this bit and then read the status register bit 3.
980 * Bit 3 of status register is Interrupt state.
981 * If it is set; then the device supports 'Masking'
983 * Reset the device back to the original state.
986 pci_intx_get_cap(dev_info_t
*dip
, int *flagsp
)
988 uint16_t cmdreg
, savereg
;
989 ddi_acc_handle_t cfg_hdl
;
995 DDI_INTR_NEXDBG((CE_CONT
, "pci_intx_get_cap: %s%d: called\n",
996 ddi_driver_name(dip
), ddi_get_instance(dip
)));
998 if (pci_config_setup(dip
, &cfg_hdl
) != DDI_SUCCESS
) {
999 DDI_INTR_NEXDBG((CE_CONT
, "pci_intx_get_cap: can't get "
1000 "config handle\n"));
1001 return (DDI_FAILURE
);
1004 savereg
= pci_config_get16(cfg_hdl
, PCI_CONF_COMM
);
1005 DDI_INTR_NEXDBG((CE_CONT
, "pci_intx_get_cap: "
1006 "command register was 0x%x\n", savereg
));
1008 /* Disable the interrupts */
1009 cmdreg
= savereg
| PCI_COMM_INTX_DISABLE
;
1010 pci_config_put16(cfg_hdl
, PCI_CONF_COMM
, cmdreg
);
1013 statreg
= pci_config_get16(cfg_hdl
, PCI_CONF_STAT
);
1014 DDI_INTR_NEXDBG((CE_CONT
, "pci_intx_get_cap: "
1015 "status register is 0x%x\n", statreg
));
1018 /* Read the bit back */
1019 cmdreg
= pci_config_get16(cfg_hdl
, PCI_CONF_COMM
);
1020 DDI_INTR_NEXDBG((CE_CONT
, "pci_intx_get_cap: "
1021 "command register is now 0x%x\n", cmdreg
));
1023 *flagsp
= DDI_INTR_FLAG_LEVEL
;
1025 if (cmdreg
& PCI_COMM_INTX_DISABLE
) {
1026 DDI_INTR_NEXDBG((CE_CONT
, "pci_intx_get_cap: "
1027 "masking supported\n"));
1028 *flagsp
|= (DDI_INTR_FLAG_MASKABLE
|
1029 DDI_INTR_FLAG_PENDING
);
1032 /* Restore the device back to the original state and return */
1033 pci_config_put16(cfg_hdl
, PCI_CONF_COMM
, savereg
);
1035 pci_config_teardown(&cfg_hdl
);
1036 return (DDI_SUCCESS
);
1041 * pci_intx_clr_mask:
1042 * For non-MSI devices that comply to PCI v2.3 or greater;
1043 * clear the bit10 in the command register.
1046 pci_intx_clr_mask(dev_info_t
*dip
)
1049 ddi_acc_handle_t cfg_hdl
;
1051 DDI_INTR_NEXDBG((CE_CONT
, "pci_intx_clr_mask: %s%d: called\n",
1052 ddi_driver_name(dip
), ddi_get_instance(dip
)));
1054 if (pci_config_setup(dip
, &cfg_hdl
) != DDI_SUCCESS
) {
1055 DDI_INTR_NEXDBG((CE_CONT
, "pci_intx_clr_mask: can't get "
1056 "config handle\n"));
1057 return (DDI_FAILURE
);
1060 cmdreg
= pci_config_get16(cfg_hdl
, PCI_CONF_COMM
);
1061 DDI_INTR_NEXDBG((CE_CONT
, "pci_intx_clr_mask: "
1062 "command register was 0x%x\n", cmdreg
));
1064 /* Enable the interrupts */
1065 cmdreg
&= ~PCI_COMM_INTX_DISABLE
;
1066 pci_config_put16(cfg_hdl
, PCI_CONF_COMM
, cmdreg
);
1067 pci_config_teardown(&cfg_hdl
);
1068 return (DDI_SUCCESS
);
1073 * pci_intx_set_mask:
1074 * For non-MSI devices that comply to PCI v2.3 or greater;
1075 * set the bit10 in the command register.
1078 pci_intx_set_mask(dev_info_t
*dip
)
1081 ddi_acc_handle_t cfg_hdl
;
1083 DDI_INTR_NEXDBG((CE_CONT
, "pci_intx_set_mask: %s%d: called\n",
1084 ddi_driver_name(dip
), ddi_get_instance(dip
)));
1086 if (pci_config_setup(dip
, &cfg_hdl
) != DDI_SUCCESS
) {
1087 DDI_INTR_NEXDBG((CE_CONT
, "pci_intx_set_mask: can't get "
1088 "config handle\n"));
1089 return (DDI_FAILURE
);
1092 cmdreg
= pci_config_get16(cfg_hdl
, PCI_CONF_COMM
);
1093 DDI_INTR_NEXDBG((CE_CONT
, "pci_intx_set_mask: "
1094 "command register was 0x%x\n", cmdreg
));
1096 /* Disable the interrupts */
1097 cmdreg
|= PCI_COMM_INTX_DISABLE
;
1098 pci_config_put16(cfg_hdl
, PCI_CONF_COMM
, cmdreg
);
1099 pci_config_teardown(&cfg_hdl
);
1100 return (DDI_SUCCESS
);
1104 * pci_intx_get_pending:
1105 * For non-MSI devices that comply to PCI v2.3 or greater;
1106 * read the status register. Bit 3 of status register is
1107 * Interrupt state. If it is set; then the interrupt is
1111 pci_intx_get_pending(dev_info_t
*dip
, int *pendingp
)
1114 ddi_acc_handle_t cfg_hdl
;
1117 DDI_INTR_NEXDBG((CE_CONT
, "pci_intx_get_pending: %s%d: called\n",
1118 ddi_driver_name(dip
), ddi_get_instance(dip
)));
1120 if (pci_config_setup(dip
, &cfg_hdl
) != DDI_SUCCESS
) {
1121 DDI_INTR_NEXDBG((CE_CONT
, "pci_intx_get_pending: can't get "
1122 "config handle\n"));
1123 return (DDI_FAILURE
);
1126 statreg
= pci_config_get16(cfg_hdl
, PCI_CONF_STAT
);
1128 if (statreg
& PCI_STAT_INTR
) {
1129 DDI_INTR_NEXDBG((CE_CONT
, "pci_intx_get_pending: "
1130 "interrupt is pending\n"));
1134 pci_config_teardown(&cfg_hdl
);
1135 return (DDI_SUCCESS
);
1140 * pci_intx_get_ispec:
1141 * Get intrspec for PCI devices (legacy support)
1142 * NOTE: This is moved here from x86 pci.c and is
1143 * needed here as pci-ide.c uses it as well
1147 pci_intx_get_ispec(dev_info_t
*dip
, dev_info_t
*rdip
, int inum
)
1150 uint_t num_intpriorities
;
1151 struct intrspec
*ispec
;
1152 ddi_acc_handle_t cfg_hdl
;
1153 struct ddi_parent_private_data
*pdptr
;
1155 if ((pdptr
= ddi_get_parent_data(rdip
)) == NULL
)
1158 ispec
= pdptr
->par_intr
;
1161 /* check if the intrspec_pri has been initialized */
1162 if (!ispec
->intrspec_pri
) {
1163 if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY
, rdip
,
1164 DDI_PROP_DONTPASS
, "interrupt-priorities",
1165 &intpriorities
, &num_intpriorities
) == DDI_PROP_SUCCESS
) {
1166 if (inum
< num_intpriorities
)
1167 ispec
->intrspec_pri
= intpriorities
[inum
];
1168 ddi_prop_free(intpriorities
);
1171 /* If still no priority, guess based on the class code */
1172 if (ispec
->intrspec_pri
== 0)
1173 ispec
->intrspec_pri
= pci_class_to_pil(rdip
);
1176 /* Get interrupt line value */
1177 if (!ispec
->intrspec_vec
) {
1178 if (pci_config_setup(rdip
, &cfg_hdl
) != DDI_SUCCESS
) {
1179 DDI_INTR_NEXDBG((CE_CONT
, "pci_intx_get_iline: "
1180 "can't get config handle\n"));
1181 return ((ddi_intrspec_t
)ispec
);
1184 ispec
->intrspec_vec
= pci_config_get8(cfg_hdl
, PCI_CONF_ILINE
);
1185 pci_config_teardown(&cfg_hdl
);
1188 return ((ddi_intrspec_t
)ispec
);
1192 pci_match_class_val(uint32_t key
, pci_class_val_t
*rec_p
, int nrec
,
1193 uint32_t default_val
)
1197 for (i
= 0; i
< nrec
; rec_p
++, i
++) {
1198 if ((rec_p
->class_code
& rec_p
->class_mask
) ==
1199 (key
& rec_p
->class_mask
))
1200 return (rec_p
->class_val
);
1203 return (default_val
);
1207 * Return the configuration value, based on class code and sub class code,
1208 * from the specified property based or default pci_class_val_t table.
1211 pci_class_to_val(dev_info_t
*rdip
, char *property_name
, pci_class_val_t
*rec_p
,
1212 int nrec
, uint32_t default_val
)
1215 uint32_t class_code
;
1216 pci_class_val_t
*conf
;
1217 uint32_t val
= default_val
;
1220 * Use the "class-code" property to get the base and sub class
1221 * codes for the requesting device.
1223 class_code
= (uint32_t)ddi_prop_get_int(DDI_DEV_T_ANY
, rdip
,
1224 DDI_PROP_DONTPASS
, "class-code", -1);
1226 if (class_code
== -1)
1229 /* look up the val from the default table */
1230 val
= pci_match_class_val(class_code
, rec_p
, nrec
, val
);
1233 /* see if there is a more specific property specified value */
1234 if (ddi_getlongprop(DDI_DEV_T_ANY
, rdip
, DDI_PROP_NOTPROM
,
1235 property_name
, (caddr_t
)&conf
, &property_len
))
1238 if ((property_len
% sizeof (pci_class_val_t
)) == 0)
1239 val
= pci_match_class_val(class_code
, conf
,
1240 property_len
/ sizeof (pci_class_val_t
), val
);
1241 kmem_free(conf
, property_len
);
1248 * Return the pil for a given PCI device.
1251 pci_class_to_pil(dev_info_t
*rdip
)
1255 /* Default pil is 1 */
1256 pil
= pci_class_to_val(rdip
,
1257 "pci-class-priorities", pci_default_pil
,
1258 sizeof (pci_default_pil
) / sizeof (pci_class_val_t
), 1);
1260 /* Range check the result */
1268 * pci_class_to_intr_weight:
1270 * Return the intr_weight for a given PCI device.
1273 pci_class_to_intr_weight(dev_info_t
*rdip
)
1275 int32_t intr_weight
;
1277 /* default weight is 0% */
1278 intr_weight
= pci_class_to_val(rdip
,
1279 "pci-class-intr-weights", pci_default_intr_weight
,
1280 sizeof (pci_default_intr_weight
) / sizeof (pci_class_val_t
), 0);
1282 /* range check the result */
1283 if (intr_weight
< 0)
1285 if (intr_weight
> 1000)
1288 return (intr_weight
);