bmake-ify mega_sas
[unleashed.git] / usr / src / uts / common / io / pci_intr_lib.c
blob2d2e5d41235f455ccbfcada63f1868e3a76daf02
1 /*
2 * CDDL HEADER START
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]
19 * CDDL HEADER END
22 * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
24 * Copyright 2013 Pluribus Networks, Inc.
28 * Support for MSI, MSIX and INTx
31 #include <sys/conf.h>
32 #include <sys/debug.h>
33 #include <sys/pci.h>
34 #include <sys/pci_cap.h>
35 #include <sys/pci_intr_lib.h>
36 #include <sys/sunddi.h>
37 #include <sys/bitmap.h>
40 * MSI-X BIR Index Table:
42 * BAR indicator register (BIR) to Base Address register.
44 static uchar_t pci_msix_bir_index[8] = {0x10, 0x14, 0x18, 0x1c,
45 0x20, 0x24, 0xff, 0xff};
47 /* default class to pil value mapping */
48 pci_class_val_t pci_default_pil [] = {
49 {0x000000, 0xff0000, 0x1}, /* Class code for pre-2.0 devices */
50 {0x010000, 0xff0000, 0x5}, /* Mass Storage Controller */
51 {0x020000, 0xff0000, 0x6}, /* Network Controller */
52 {0x030000, 0xff0000, 0x9}, /* Display Controller */
53 {0x040000, 0xff0000, 0x8}, /* Multimedia Controller */
54 {0x050000, 0xff0000, 0x9}, /* Memory Controller */
55 {0x060000, 0xff0000, 0x9}, /* Bridge Controller */
56 {0x0c0000, 0xffff00, 0x9}, /* Serial Bus, FireWire (IEEE 1394) */
57 {0x0c0100, 0xffff00, 0x4}, /* Serial Bus, ACCESS.bus */
58 {0x0c0200, 0xffff00, 0x4}, /* Serial Bus, SSA */
59 {0x0c0300, 0xffff00, 0x9}, /* Serial Bus Universal Serial Bus */
61 * XXX - This is a temporary workaround and it will be removed
62 * after x86 interrupt scalability support.
64 #if defined(__i386) || defined(__amd64)
65 {0x0c0400, 0xffff00, 0x5}, /* Serial Bus, Fibre Channel */
66 #else
67 {0x0c0400, 0xffff00, 0x6}, /* Serial Bus, Fibre Channel */
68 #endif
69 {0x0c0600, 0xffff00, 0x6} /* Serial Bus, Infiniband */
73 * Default class to intr_weight value mapping (% of CPU). A driver.conf
74 * entry on or above the pci node like
76 * pci-class-intr-weights= 0x020000, 0xff0000, 30;
78 * can be used to augment or override entries in the default table below.
80 * NB: The values below give NICs preference on redistribution, and provide
81 * NICs some isolation from other interrupt sources. We need better interfaces
82 * that allow the NIC driver to identify a specific NIC instance as high
83 * bandwidth, and thus deserving of separation from other low bandwidth
84 * NICs additional isolation from other interrupt sources.
86 * NB: We treat Infiniband like a NIC.
88 pci_class_val_t pci_default_intr_weight [] = {
89 {0x020000, 0xff0000, 35}, /* Network Controller */
90 {0x010000, 0xff0000, 10}, /* Mass Storage Controller */
91 {0x0c0400, 0xffff00, 10}, /* Serial Bus, Fibre Channel */
92 {0x0c0600, 0xffff00, 50} /* Serial Bus, Infiniband */
96 * Library utility functions
100 * pci_get_msi_ctrl:
102 * Helper function that returns with 'cfg_hdl', MSI/X ctrl pointer,
103 * and caps_ptr for MSI/X if these are found.
105 static int
106 pci_get_msi_ctrl(dev_info_t *dip, int type, ushort_t *msi_ctrl,
107 ushort_t *caps_ptr, ddi_acc_handle_t *h)
109 *msi_ctrl = *caps_ptr = 0;
111 if (pci_config_setup(dip, h) != DDI_SUCCESS) {
112 DDI_INTR_NEXDBG((CE_CONT, "pci_get_msi_ctrl: "
113 "%s%d can't get config handle",
114 ddi_driver_name(dip), ddi_get_instance(dip)));
116 return (DDI_FAILURE);
119 if ((PCI_CAP_LOCATE(*h, PCI_CAP_ID_MSI, caps_ptr) == DDI_SUCCESS) &&
120 (type == DDI_INTR_TYPE_MSI)) {
121 if ((*msi_ctrl = PCI_CAP_GET16(*h, 0, *caps_ptr,
122 PCI_MSI_CTRL)) == PCI_CAP_EINVAL16)
123 goto done;
125 DDI_INTR_NEXDBG((CE_CONT, "pci_get_msi_ctrl: MSI "
126 "caps_ptr=%x msi_ctrl=%x\n", *caps_ptr, *msi_ctrl));
128 return (DDI_SUCCESS);
131 if ((PCI_CAP_LOCATE(*h, PCI_CAP_ID_MSI_X, caps_ptr) == DDI_SUCCESS) &&
132 (type == DDI_INTR_TYPE_MSIX)) {
133 if ((*msi_ctrl = PCI_CAP_GET16(*h, 0, *caps_ptr,
134 PCI_MSIX_CTRL)) == PCI_CAP_EINVAL16)
135 goto done;
137 DDI_INTR_NEXDBG((CE_CONT, "pci_get_msi_ctrl: MSI-X "
138 "caps_ptr=%x msi_ctrl=%x\n", *caps_ptr, *msi_ctrl));
140 return (DDI_SUCCESS);
143 done:
144 pci_config_teardown(h);
145 return (DDI_FAILURE);
150 * pci_msi_get_cap:
152 * Get the capabilities of the MSI/X interrupt
155 pci_msi_get_cap(dev_info_t *rdip, int type, int *flagsp)
157 ushort_t caps_ptr, msi_ctrl;
158 ddi_acc_handle_t cfg_hdle;
160 DDI_INTR_NEXDBG((CE_CONT, "pci_msi_get_cap: rdip = 0x%p\n",
161 (void *)rdip));
163 *flagsp = 0;
165 if (pci_get_msi_ctrl(rdip, type, &msi_ctrl,
166 &caps_ptr, &cfg_hdle) != DDI_SUCCESS)
167 return (DDI_FAILURE);
169 if (type == DDI_INTR_TYPE_MSI) {
170 if (msi_ctrl & PCI_MSI_64BIT_MASK)
171 *flagsp |= DDI_INTR_FLAG_MSI64;
172 if (msi_ctrl & PCI_MSI_PVM_MASK)
173 *flagsp |= (DDI_INTR_FLAG_MASKABLE |
174 DDI_INTR_FLAG_PENDING);
175 else
176 *flagsp |= DDI_INTR_FLAG_BLOCK;
177 } else if (type == DDI_INTR_TYPE_MSIX) {
178 /* MSI-X supports PVM, 64bit by default */
179 *flagsp |= (DDI_INTR_FLAG_MASKABLE | DDI_INTR_FLAG_MSI64 |
180 DDI_INTR_FLAG_PENDING);
183 *flagsp |= DDI_INTR_FLAG_EDGE;
185 DDI_INTR_NEXDBG((CE_CONT, "pci_msi_get_cap: flags = 0x%x\n", *flagsp));
187 pci_config_teardown(&cfg_hdle);
188 return (DDI_SUCCESS);
193 * pci_msi_configure:
195 * Configure address/data and number MSI/Xs fields in the MSI/X
196 * capability structure.
198 /* ARGSUSED */
200 pci_msi_configure(dev_info_t *rdip, int type, int count, int inum,
201 uint64_t addr, uint64_t data)
203 ushort_t caps_ptr, msi_ctrl;
204 ddi_acc_handle_t h;
206 DDI_INTR_NEXDBG((CE_CONT, "pci_msi_configure: rdip = 0x%p type 0x%x "
207 "count 0x%x inum 0x%x addr 0x%" PRIx64 " data 0x%" PRIx64 "\n",
208 (void *)rdip, type, count, inum, addr, data));
210 if (pci_get_msi_ctrl(rdip, type, &msi_ctrl,
211 &caps_ptr, &h) != DDI_SUCCESS)
212 return (DDI_FAILURE);
214 if (type == DDI_INTR_TYPE_MSI) {
215 /* Set the bits to inform how many MSIs are enabled */
216 msi_ctrl |= ((highbit(count) -1) << PCI_MSI_MME_SHIFT);
217 PCI_CAP_PUT16(h, 0, caps_ptr, PCI_MSI_CTRL, msi_ctrl);
219 DDI_INTR_NEXDBG((CE_CONT, "pci_msi_configure: msi_ctrl = %x\n",
220 PCI_CAP_GET16(h, 0, caps_ptr, PCI_MSI_CTRL)));
222 /* Set the "data" and "addr" bits */
223 PCI_CAP_PUT32(h, 0, caps_ptr, PCI_MSI_ADDR_OFFSET, addr);
225 DDI_INTR_NEXDBG((CE_CONT, "pci_msi_configure: msi_addr = %x\n",
226 PCI_CAP_GET32(h, 0, caps_ptr, PCI_MSI_ADDR_OFFSET)));
228 if (msi_ctrl & PCI_MSI_64BIT_MASK) {
229 PCI_CAP_PUT32(h, 0, caps_ptr, PCI_MSI_ADDR_OFFSET
230 + 4, addr >> 32);
232 DDI_INTR_NEXDBG((CE_CONT, "pci_msi_configure: upper "
233 "32bit msi_addr = %x\n", PCI_CAP_GET32(h, 0,
234 caps_ptr, PCI_MSI_ADDR_OFFSET + 4)));
236 PCI_CAP_PUT16(h, 0, caps_ptr, PCI_MSI_64BIT_DATA,
237 data);
239 DDI_INTR_NEXDBG((CE_CONT, "pci_msi_configure: msi_data "
240 "= %x\n", PCI_CAP_GET16(h, 0, caps_ptr,
241 PCI_MSI_64BIT_DATA)));
242 } else {
243 PCI_CAP_PUT16(h, 0, caps_ptr, PCI_MSI_32BIT_DATA,
244 data);
246 DDI_INTR_NEXDBG((CE_CONT, "pci_msi_configure: msi_data "
247 "= %x\n", PCI_CAP_GET16(h, 0, caps_ptr,
248 PCI_MSI_32BIT_DATA)));
250 } else if (type == DDI_INTR_TYPE_MSIX) {
251 uintptr_t off;
252 ddi_intr_msix_t *msix_p = i_ddi_get_msix(rdip);
254 /* Offset into the "inum"th entry in the MSI-X table */
255 off = (uintptr_t)msix_p->msix_tbl_addr +
256 (inum * PCI_MSIX_VECTOR_SIZE);
258 /* Set the "data" and "addr" bits */
259 ddi_put32(msix_p->msix_tbl_hdl,
260 (uint32_t *)(off + PCI_MSIX_DATA_OFFSET), data);
263 * Note that the spec only requires 32-bit accesses
264 * to be supported. Apparently some chipsets don't
265 * support 64-bit accesses.
267 ddi_put32(msix_p->msix_tbl_hdl,
268 (uint32_t *)(off + PCI_MSIX_LOWER_ADDR_OFFSET), addr);
269 ddi_put32(msix_p->msix_tbl_hdl,
270 (uint32_t *)(off + PCI_MSIX_UPPER_ADDR_OFFSET),
271 addr >> 32);
273 DDI_INTR_NEXDBG((CE_CONT, "pci_msi_configure: "
274 "msix_addr 0x%x.%x msix_data 0x%x\n",
275 ddi_get32(msix_p->msix_tbl_hdl,
276 (uint32_t *)(off + PCI_MSIX_UPPER_ADDR_OFFSET)),
277 ddi_get32(msix_p->msix_tbl_hdl,
278 (uint32_t *)(off + PCI_MSIX_LOWER_ADDR_OFFSET)),
279 ddi_get32(msix_p->msix_tbl_hdl,
280 (uint32_t *)(off + PCI_MSIX_DATA_OFFSET))));
283 pci_config_teardown(&h);
284 return (DDI_SUCCESS);
289 * pci_msi_unconfigure:
291 * Unconfigure address/data and number MSI/Xs fields in the MSI/X
292 * capability structure.
294 /* ARGSUSED */
296 pci_msi_unconfigure(dev_info_t *rdip, int type, int inum)
298 ushort_t msi_ctrl, caps_ptr;
299 ddi_acc_handle_t h;
301 DDI_INTR_NEXDBG((CE_CONT, "pci_msi_unconfigure: rdip = 0x%p type 0x%x "
302 "inum 0x%x\n", (void *)rdip, type, inum));
304 if (pci_get_msi_ctrl(rdip, type, &msi_ctrl, &caps_ptr, &h) !=
305 DDI_SUCCESS)
306 return (DDI_FAILURE);
308 if (type == DDI_INTR_TYPE_MSI) {
309 msi_ctrl &= (~PCI_MSI_MME_MASK);
310 PCI_CAP_PUT16(h, 0, caps_ptr, PCI_MSI_CTRL, msi_ctrl);
312 PCI_CAP_PUT32(h, 0, caps_ptr, PCI_MSI_ADDR_OFFSET, 0);
314 if (msi_ctrl & PCI_MSI_64BIT_MASK) {
315 PCI_CAP_PUT16(h, 0, caps_ptr, PCI_MSI_64BIT_DATA,
317 PCI_CAP_PUT32(h, 0, caps_ptr, PCI_MSI_ADDR_OFFSET
318 + 4, 0);
319 } else {
320 PCI_CAP_PUT16(h, 0, caps_ptr, PCI_MSI_32BIT_DATA,
324 DDI_INTR_NEXDBG((CE_CONT, "pci_msi_unconfigure: msi_ctrl "
325 "= %x\n", PCI_CAP_GET16(h, 0, caps_ptr, PCI_MSI_CTRL)));
327 } else if (type == DDI_INTR_TYPE_MSIX) {
328 uintptr_t off;
329 ddi_intr_msix_t *msix_p = i_ddi_get_msix(rdip);
331 /* Offset into the "inum"th entry in the MSI-X table */
332 off = (uintptr_t)msix_p->msix_tbl_addr +
333 (inum * PCI_MSIX_VECTOR_SIZE);
335 /* Reset the "data" and "addr" bits */
336 ddi_put32(msix_p->msix_tbl_hdl,
337 (uint32_t *)(off + PCI_MSIX_DATA_OFFSET), 0);
340 * Note that the spec only requires 32-bit accesses
341 * to be supported. Apparently some chipsets don't
342 * support 64-bit accesses.
344 ddi_put32(msix_p->msix_tbl_hdl,
345 (uint32_t *)(off + PCI_MSIX_LOWER_ADDR_OFFSET), 0);
346 ddi_put32(msix_p->msix_tbl_hdl,
347 (uint32_t *)(off + PCI_MSIX_UPPER_ADDR_OFFSET), 0);
350 pci_config_teardown(&h);
351 return (DDI_SUCCESS);
356 * pci_is_msi_enabled:
358 * This function returns DDI_SUCCESS if MSI/X is already enabled, otherwise
359 * it returns DDI_FAILURE.
362 pci_is_msi_enabled(dev_info_t *rdip, int type)
364 ushort_t caps_ptr, msi_ctrl;
365 ddi_acc_handle_t cfg_hdle;
366 int ret = DDI_FAILURE;
368 DDI_INTR_NEXDBG((CE_CONT, "pci_is_msi_enabled: rdip = 0x%p, "
369 "type = 0x%x\n", (void *)rdip, type));
371 if (pci_get_msi_ctrl(rdip, type, &msi_ctrl,
372 &caps_ptr, &cfg_hdle) != DDI_SUCCESS)
373 return (DDI_FAILURE);
375 if ((type == DDI_INTR_TYPE_MSI) && (msi_ctrl & PCI_MSI_ENABLE_BIT))
376 ret = DDI_SUCCESS;
378 if ((type == DDI_INTR_TYPE_MSIX) && (msi_ctrl & PCI_MSIX_ENABLE_BIT))
379 ret = DDI_SUCCESS;
381 pci_config_teardown(&cfg_hdle);
382 return (ret);
387 * pci_msi_enable_mode:
389 * This function sets the MSI_ENABLE bit in the capability structure
390 * (for MSI) and MSIX_ENABLE bit in the MSI-X capability structure.
392 * NOTE: It is the nexus driver's responsibility to clear the MSI/X
393 * interrupt's mask bit in the MSI/X capability structure before the
394 * interrupt can be used.
397 pci_msi_enable_mode(dev_info_t *rdip, int type)
399 ushort_t caps_ptr, msi_ctrl;
400 ddi_acc_handle_t cfg_hdle;
402 DDI_INTR_NEXDBG((CE_CONT, "pci_msi_enable_mode: rdip = 0x%p\n",
403 (void *)rdip));
405 if (pci_get_msi_ctrl(rdip, type, &msi_ctrl,
406 &caps_ptr, &cfg_hdle) != DDI_SUCCESS)
407 return (DDI_FAILURE);
409 if (type == DDI_INTR_TYPE_MSI) {
410 if (msi_ctrl & PCI_MSI_ENABLE_BIT)
411 goto finished;
413 msi_ctrl |= PCI_MSI_ENABLE_BIT;
414 PCI_CAP_PUT16(cfg_hdle, 0, caps_ptr, PCI_MSI_CTRL, msi_ctrl);
416 } else if (type == DDI_INTR_TYPE_MSIX) {
417 if (msi_ctrl & PCI_MSIX_ENABLE_BIT)
418 goto finished;
420 msi_ctrl |= PCI_MSIX_ENABLE_BIT;
421 PCI_CAP_PUT16(cfg_hdle, 0, caps_ptr, PCI_MSIX_CTRL,
422 msi_ctrl);
425 finished:
426 DDI_INTR_NEXDBG((CE_CONT, "pci_msi_enable_mode: msi_ctrl = %x\n",
427 msi_ctrl));
429 pci_config_teardown(&cfg_hdle);
430 return (DDI_SUCCESS);
435 * pci_msi_disable_mode:
437 * This function resets the MSI_ENABLE bit in the capability structure
438 * (for MSI) and MSIX_ENABLE bit in the MSI-X capability structure.
440 * NOTE: It is the nexus driver's responsibility to set the MSI/X
441 * interrupt's mask bit in the MSI/X capability structure before the
442 * interrupt can be disabled.
445 pci_msi_disable_mode(dev_info_t *rdip, int type)
447 ushort_t caps_ptr, msi_ctrl;
448 ddi_acc_handle_t cfg_hdle;
450 DDI_INTR_NEXDBG((CE_CONT, "pci_msi_disable_mode: rdip = 0x%p\n",
451 (void *)rdip));
453 if (pci_get_msi_ctrl(rdip, type, &msi_ctrl,
454 &caps_ptr, &cfg_hdle) != DDI_SUCCESS)
455 return (DDI_FAILURE);
457 /* Reset the "enable" bit */
458 if (type == DDI_INTR_TYPE_MSI) {
459 if (!(msi_ctrl & PCI_MSI_ENABLE_BIT))
460 goto finished;
461 msi_ctrl &= ~PCI_MSI_ENABLE_BIT;
462 PCI_CAP_PUT16(cfg_hdle, 0, caps_ptr, PCI_MSI_CTRL, msi_ctrl);
463 } else if (type == DDI_INTR_TYPE_MSIX) {
464 if (!(msi_ctrl & PCI_MSIX_ENABLE_BIT))
465 goto finished;
467 msi_ctrl &= ~PCI_MSIX_ENABLE_BIT;
468 PCI_CAP_PUT16(cfg_hdle, 0, caps_ptr, PCI_MSIX_CTRL,
469 msi_ctrl);
472 finished:
473 DDI_INTR_NEXDBG((CE_CONT, "pci_msi_disable_mode: msi_ctrl = %x\n",
474 msi_ctrl));
476 pci_config_teardown(&cfg_hdle);
477 return (DDI_SUCCESS);
482 * pci_msi_set_mask:
484 * Set the mask bit in the MSI/X capability structure
486 /* ARGSUSED */
488 pci_msi_set_mask(dev_info_t *rdip, int type, int inum)
490 int offset;
491 int ret = DDI_FAILURE;
492 ushort_t caps_ptr, msi_ctrl;
493 ddi_acc_handle_t cfg_hdle;
494 uint32_t mask_bits;
496 DDI_INTR_NEXDBG((CE_CONT, "pci_msi_set_mask: rdip = 0x%p, "
497 "type = 0x%x\n", (void *)rdip, type));
499 if (pci_get_msi_ctrl(rdip, type, &msi_ctrl,
500 &caps_ptr, &cfg_hdle) != DDI_SUCCESS)
501 return (DDI_FAILURE);
503 if (type == DDI_INTR_TYPE_MSI) {
504 if (!(msi_ctrl & PCI_MSI_PVM_MASK))
505 goto done;
507 offset = (msi_ctrl & PCI_MSI_64BIT_MASK) ?
508 PCI_MSI_64BIT_MASKBITS : PCI_MSI_32BIT_MASK;
510 if ((mask_bits = PCI_CAP_GET32(cfg_hdle, 0, caps_ptr,
511 offset)) == PCI_CAP_EINVAL32)
512 goto done;
514 mask_bits |= (1 << inum);
516 PCI_CAP_PUT32(cfg_hdle, 0, caps_ptr, offset, mask_bits);
518 } else if (type == DDI_INTR_TYPE_MSIX) {
519 uintptr_t off;
520 ddi_intr_msix_t *msix_p;
522 /* Set function mask */
523 if (msi_ctrl & PCI_MSIX_FUNCTION_MASK) {
524 ret = DDI_SUCCESS;
525 goto done;
528 msix_p = i_ddi_get_msix(rdip);
530 /* Offset into the "inum"th entry in the MSI-X table */
531 off = (uintptr_t)msix_p->msix_tbl_addr + (inum *
532 PCI_MSIX_VECTOR_SIZE) + PCI_MSIX_VECTOR_CTRL_OFFSET;
534 /* Set the Mask bit */
535 ddi_put32(msix_p->msix_tbl_hdl, (uint32_t *)off, 0x1);
538 ret = DDI_SUCCESS;
539 done:
540 pci_config_teardown(&cfg_hdle);
541 return (ret);
546 * pci_msi_clr_mask:
548 * Clear the mask bit in the MSI/X capability structure
550 /* ARGSUSED */
552 pci_msi_clr_mask(dev_info_t *rdip, int type, int inum)
554 ushort_t caps_ptr, msi_ctrl;
555 ddi_acc_handle_t cfg_hdle;
556 int offset;
557 int ret = DDI_FAILURE;
558 uint32_t mask_bits;
560 DDI_INTR_NEXDBG((CE_CONT, "pci_msi_clr_mask: rdip = 0x%p, "
561 "type = 0x%x\n", (void *)rdip, type));
563 if (pci_get_msi_ctrl(rdip, type, &msi_ctrl,
564 &caps_ptr, &cfg_hdle) != DDI_SUCCESS)
565 return (DDI_FAILURE);
567 if (type == DDI_INTR_TYPE_MSI) {
568 if (!(msi_ctrl & PCI_MSI_PVM_MASK))
569 goto done;
571 offset = (msi_ctrl & PCI_MSI_64BIT_MASK) ?
572 PCI_MSI_64BIT_MASKBITS : PCI_MSI_32BIT_MASK;
573 if ((mask_bits = PCI_CAP_GET32(cfg_hdle, 0, caps_ptr,
574 offset)) == PCI_CAP_EINVAL32)
575 goto done;
577 mask_bits &= ~(1 << inum);
579 PCI_CAP_PUT32(cfg_hdle, 0, caps_ptr, offset, mask_bits);
581 } else if (type == DDI_INTR_TYPE_MSIX) {
582 uintptr_t off;
583 ddi_intr_msix_t *msix_p;
585 if (msi_ctrl & PCI_MSIX_FUNCTION_MASK) {
586 ret = DDI_SUCCESS;
587 goto done;
590 msix_p = i_ddi_get_msix(rdip);
592 /* Offset into the "inum"th entry in the MSI-X table */
593 off = (uintptr_t)msix_p->msix_tbl_addr + (inum *
594 PCI_MSIX_VECTOR_SIZE) + PCI_MSIX_VECTOR_CTRL_OFFSET;
596 /* Clear the Mask bit */
597 ddi_put32(msix_p->msix_tbl_hdl, (uint32_t *)off, 0x0);
600 ret = DDI_SUCCESS;
601 done:
602 pci_config_teardown(&cfg_hdle);
603 return (ret);
608 * pci_msi_get_pending:
610 * Get the pending bit from the MSI/X capability structure
612 /* ARGSUSED */
614 pci_msi_get_pending(dev_info_t *rdip, int type, int inum, int *pendingp)
616 ushort_t caps_ptr, msi_ctrl;
617 ddi_acc_handle_t cfg_hdle;
618 int offset;
619 int ret = DDI_FAILURE;
621 DDI_INTR_NEXDBG((CE_CONT, "pci_msi_get_pending: rdip = 0x%p\n",
622 (void *)rdip));
624 if (pci_get_msi_ctrl(rdip, type, &msi_ctrl,
625 &caps_ptr, &cfg_hdle) != DDI_SUCCESS)
626 return (DDI_FAILURE);
628 if (type == DDI_INTR_TYPE_MSI) {
629 uint32_t pending_bits;
631 if (!(msi_ctrl & PCI_MSI_PVM_MASK)) {
632 DDI_INTR_NEXDBG((CE_CONT, "pci_msi_get_pending: "
633 "PVM is not supported\n"));
634 goto done;
637 offset = (msi_ctrl & PCI_MSI_64BIT_MASK) ?
638 PCI_MSI_64BIT_PENDING : PCI_MSI_32BIT_PENDING;
640 if ((pending_bits = PCI_CAP_GET32(cfg_hdle, 0, caps_ptr,
641 offset)) == PCI_CAP_EINVAL32)
642 goto done;
644 *pendingp = pending_bits & ~(1 >> inum);
646 } else if (type == DDI_INTR_TYPE_MSIX) {
647 uintptr_t off;
648 uint64_t pending_bits;
649 ddi_intr_msix_t *msix_p = i_ddi_get_msix(rdip);
651 /* Offset into the PBA array which has entry for "inum" */
652 off = (uintptr_t)msix_p->msix_pba_addr + (inum / 64);
654 /* Read the PBA array */
655 pending_bits = ddi_get64(msix_p->msix_pba_hdl, (uint64_t *)off);
657 *pendingp = pending_bits & ~(1 >> inum);
660 ret = DDI_SUCCESS;
661 done:
662 pci_config_teardown(&cfg_hdle);
663 return (ret);
668 * pci_msi_get_nintrs:
670 * For a given type (MSI/X) returns the number of interrupts supported
673 pci_msi_get_nintrs(dev_info_t *rdip, int type, int *nintrs)
675 ushort_t caps_ptr, msi_ctrl;
676 ddi_acc_handle_t cfg_hdle;
678 DDI_INTR_NEXDBG((CE_CONT, "pci_msi_get_nintrs: rdip = 0x%p\n",
679 (void *)rdip));
681 if (pci_get_msi_ctrl(rdip, type, &msi_ctrl,
682 &caps_ptr, &cfg_hdle) != DDI_SUCCESS)
683 return (DDI_FAILURE);
685 if (type == DDI_INTR_TYPE_MSI) {
686 *nintrs = 1 << ((msi_ctrl & PCI_MSI_MMC_MASK) >>
687 PCI_MSI_MMC_SHIFT);
688 } else if (type == DDI_INTR_TYPE_MSIX) {
689 if (msi_ctrl & PCI_MSIX_TBL_SIZE_MASK)
690 *nintrs = (msi_ctrl & PCI_MSIX_TBL_SIZE_MASK) + 1;
693 DDI_INTR_NEXDBG((CE_CONT, "pci_msi_get_nintrs: "
694 "nintr = 0x%x\n", *nintrs));
696 pci_config_teardown(&cfg_hdle);
697 return (DDI_SUCCESS);
702 * pci_msi_set_nintrs:
704 * For a given type (MSI/X) sets the number of interrupts supported
705 * by the system.
706 * For MSI: Return an error if this func is called for navail > 32
707 * For MSI-X: Return an error if this func is called for navail > 2048
710 pci_msi_set_nintrs(dev_info_t *rdip, int type, int navail)
712 ushort_t caps_ptr, msi_ctrl;
713 ddi_acc_handle_t cfg_hdle;
715 DDI_INTR_NEXDBG((CE_CONT, "pci_msi_set_nintrs: rdip = 0x%p, "
716 "navail = 0x%x\n", (void *)rdip, navail));
718 /* Check for valid input argument */
719 if (((type == DDI_INTR_TYPE_MSI) && (navail > PCI_MSI_MAX_INTRS)) ||
720 ((type == DDI_INTR_TYPE_MSIX) && (navail > PCI_MSIX_MAX_INTRS)))
721 return (DDI_EINVAL);
723 if (pci_get_msi_ctrl(rdip, type, &msi_ctrl,
724 &caps_ptr, &cfg_hdle) != DDI_SUCCESS)
725 return (DDI_FAILURE);
727 if (type == DDI_INTR_TYPE_MSI) {
728 msi_ctrl |= ((highbit(navail) -1) << PCI_MSI_MME_SHIFT);
730 PCI_CAP_PUT16(cfg_hdle, 0, caps_ptr, PCI_MSI_CTRL, msi_ctrl);
731 } else if (type == DDI_INTR_TYPE_MSIX) {
732 DDI_INTR_NEXDBG((CE_CONT, "pci_msi_set_nintrs: unsupported\n"));
735 pci_config_teardown(&cfg_hdle);
736 return (DDI_SUCCESS);
741 * pci_msi_get_supported_type:
743 * Returns DDI_INTR_TYPE_MSI and/or DDI_INTR_TYPE_MSIX as supported
744 * types if device supports them. A DDI_FAILURE is returned otherwise.
747 pci_msi_get_supported_type(dev_info_t *rdip, int *typesp)
749 ushort_t caps_ptr, msi_ctrl;
750 ddi_acc_handle_t cfg_hdle;
752 DDI_INTR_NEXDBG((CE_CONT, "pci_msi_get_supported_type: "
753 "rdip = 0x%p\n", (void *)rdip));
755 *typesp = 0;
757 if (pci_get_msi_ctrl(rdip, DDI_INTR_TYPE_MSI, &msi_ctrl,
758 &caps_ptr, &cfg_hdle) == DDI_SUCCESS) {
759 *typesp |= DDI_INTR_TYPE_MSI;
760 pci_config_teardown(&cfg_hdle);
763 if (pci_get_msi_ctrl(rdip, DDI_INTR_TYPE_MSIX, &msi_ctrl,
764 &caps_ptr, &cfg_hdle) == DDI_SUCCESS) {
765 *typesp |= DDI_INTR_TYPE_MSIX;
766 pci_config_teardown(&cfg_hdle);
769 DDI_INTR_NEXDBG((CE_CONT, "pci_msi_get_supported_type: "
770 "rdip = 0x%p types 0x%x\n", (void *)rdip, *typesp));
772 return (*typesp == 0 ? DDI_FAILURE : DDI_SUCCESS);
777 * pci_msix_init:
778 * This function initializes the various handles/addrs etc.
779 * needed for MSI-X support. It also allocates a private
780 * structure to keep track of these.
782 ddi_intr_msix_t *
783 pci_msix_init(dev_info_t *rdip)
785 uint_t rnumber, breg, nregs;
786 size_t msix_tbl_size;
787 size_t pba_tbl_size;
788 ushort_t caps_ptr, msix_ctrl;
789 ddi_intr_msix_t *msix_p;
790 ddi_acc_handle_t cfg_hdle;
791 pci_regspec_t *rp;
792 int reg_size, addr_space, offset, *regs_list;
793 int i, ret;
795 DDI_INTR_NEXDBG((CE_CONT, "pci_msix_init: rdip = %p\n", (void *)rdip));
797 if (pci_get_msi_ctrl(rdip, DDI_INTR_TYPE_MSIX, &msix_ctrl,
798 &caps_ptr, &cfg_hdle) != DDI_SUCCESS)
799 return (NULL);
801 msix_p = kmem_zalloc(sizeof (ddi_intr_msix_t), KM_SLEEP);
804 * Initialize the devacc structure
806 msix_p->msix_dev_attr.devacc_attr_version = DDI_DEVICE_ATTR_V0;
807 msix_p->msix_dev_attr.devacc_attr_endian_flags =
808 DDI_STRUCTURE_LE_ACC;
809 msix_p->msix_dev_attr.devacc_attr_dataorder = DDI_STRICTORDER_ACC;
811 /* Map the entire MSI-X vector table */
812 msix_p->msix_tbl_offset = PCI_CAP_GET32(cfg_hdle, 0, caps_ptr,
813 PCI_MSIX_TBL_OFFSET);
815 if ((breg = pci_msix_bir_index[msix_p->msix_tbl_offset &
816 PCI_MSIX_TBL_BIR_MASK]) == 0xff)
817 goto fail1;
819 msix_p->msix_tbl_offset = msix_p->msix_tbl_offset &
820 ~PCI_MSIX_TBL_BIR_MASK;
821 msix_tbl_size = ((msix_ctrl & PCI_MSIX_TBL_SIZE_MASK) + 1) *
822 PCI_MSIX_VECTOR_SIZE;
824 DDI_INTR_NEXDBG((CE_CONT, "pci_msix_init: MSI-X table offset 0x%x "
825 "breg 0x%x size 0x%lx\n", msix_p->msix_tbl_offset, breg,
826 msix_tbl_size));
828 if ((ret = ddi_prop_lookup_int_array(DDI_DEV_T_ANY, rdip,
829 DDI_PROP_DONTPASS, "reg", (int **)&regs_list, &nregs))
830 != DDI_PROP_SUCCESS) {
831 DDI_INTR_NEXDBG((CE_CONT, "pci_msix_init: "
832 "ddi_prop_lookup_int_array failed %d\n", ret));
834 goto fail1;
837 reg_size = sizeof (pci_regspec_t) / sizeof (int);
839 for (i = 1, rnumber = 0; i < nregs/reg_size; i++) {
840 rp = (pci_regspec_t *)&regs_list[i * reg_size];
841 addr_space = rp->pci_phys_hi & PCI_ADDR_MASK;
842 offset = PCI_REG_REG_G(rp->pci_phys_hi);
844 if ((offset == breg) && ((addr_space == PCI_ADDR_MEM32) ||
845 (addr_space == PCI_ADDR_MEM64))) {
846 rnumber = i;
847 break;
851 DDI_INTR_NEXDBG((CE_CONT, "pci_msix_init: MSI-X rnum = %d\n", rnumber));
853 if (rnumber == 0) {
854 DDI_INTR_NEXDBG((CE_CONT, "pci_msix_init: "
855 "no mtaching reg number for offset 0x%x\n", breg));
857 goto fail2;
860 if ((ret = ddi_regs_map_setup(rdip, rnumber,
861 (caddr_t *)&msix_p->msix_tbl_addr, msix_p->msix_tbl_offset,
862 msix_tbl_size, &msix_p->msix_dev_attr,
863 &msix_p->msix_tbl_hdl)) != DDI_SUCCESS) {
864 DDI_INTR_NEXDBG((CE_CONT, "pci_msix_init: MSI-X Table "
865 "ddi_regs_map_setup failed %d\n", ret));
867 goto fail2;
871 * Map in the MSI-X Pending Bit Array
873 msix_p->msix_pba_offset = PCI_CAP_GET32(cfg_hdle, 0, caps_ptr,
874 PCI_MSIX_PBA_OFFSET);
876 if ((breg = pci_msix_bir_index[msix_p->msix_pba_offset &
877 PCI_MSIX_PBA_BIR_MASK]) == 0xff)
878 goto fail3;
880 msix_p->msix_pba_offset = msix_p->msix_pba_offset &
881 ~PCI_MSIX_PBA_BIR_MASK;
882 pba_tbl_size = ((msix_ctrl & PCI_MSIX_TBL_SIZE_MASK) + 1)/8;
884 DDI_INTR_NEXDBG((CE_CONT, "pci_msix_init: PBA table offset 0x%x "
885 "breg 0x%x size 0x%lx\n", msix_p->msix_pba_offset, breg,
886 pba_tbl_size));
888 for (i = 1, rnumber = 0; i < nregs/reg_size; i++) {
889 rp = (pci_regspec_t *)&regs_list[i * reg_size];
890 addr_space = rp->pci_phys_hi & PCI_ADDR_MASK;
891 offset = PCI_REG_REG_G(rp->pci_phys_hi);
893 if ((offset == breg) && ((addr_space == PCI_ADDR_MEM32) ||
894 (addr_space == PCI_ADDR_MEM64))) {
895 rnumber = i;
896 break;
900 DDI_INTR_NEXDBG((CE_CONT, "pci_msix_init: PBA rnum = %d\n", rnumber));
902 if (rnumber == 0) {
903 DDI_INTR_NEXDBG((CE_CONT, "pci_msix_init: "
904 "no matching reg number for offset 0x%x\n", breg));
906 goto fail3;
909 if ((ret = ddi_regs_map_setup(rdip, rnumber,
910 (caddr_t *)&msix_p->msix_pba_addr, msix_p->msix_pba_offset,
911 pba_tbl_size, &msix_p->msix_dev_attr,
912 &msix_p->msix_pba_hdl)) != DDI_SUCCESS) {
913 DDI_INTR_NEXDBG((CE_CONT, "pci_msix_init: PBA "
914 "ddi_regs_map_setup failed %d\n", ret));
916 goto fail3;
919 DDI_INTR_NEXDBG((CE_CONT, "pci_msix_init: msix_p = 0x%p DONE!!\n",
920 (void *)msix_p));
922 ddi_prop_free(regs_list);
923 goto done;
925 fail3:
926 ddi_regs_map_free(&msix_p->msix_tbl_hdl);
927 fail2:
928 ddi_prop_free(regs_list);
929 fail1:
930 kmem_free(msix_p, sizeof (ddi_intr_msix_t));
931 msix_p = NULL;
932 done:
933 pci_config_teardown(&cfg_hdle);
934 return (msix_p);
939 * pci_msix_fini:
940 * This function cleans up previously allocated handles/addrs etc.
941 * It is only called if no more MSI-X interrupts are being used.
943 void
944 pci_msix_fini(ddi_intr_msix_t *msix_p)
946 DDI_INTR_NEXDBG((CE_CONT, "pci_msix_fini: msix_p = 0x%p\n",
947 (void *)msix_p));
949 ddi_regs_map_free(&msix_p->msix_pba_hdl);
950 ddi_regs_map_free(&msix_p->msix_tbl_hdl);
951 kmem_free(msix_p, sizeof (ddi_intr_msix_t));
956 * pci_msix_dup:
957 * This function duplicates the address and data pair of one msi-x
958 * vector to another msi-x vector.
961 pci_msix_dup(dev_info_t *rdip, int org_inum, int dup_inum)
963 ddi_intr_msix_t *msix_p = i_ddi_get_msix(rdip);
964 uint64_t addr;
965 uint64_t data;
966 uintptr_t off;
968 DDI_INTR_NEXDBG((CE_CONT, "pci_msix_dup: dip = %p, inum = 0x%x, "
969 "to_vector = 0x%x\n", (void *)rdip, org_inum, dup_inum));
971 /* Offset into the original inum's entry in the MSI-X table */
972 off = (uintptr_t)msix_p->msix_tbl_addr +
973 (org_inum * PCI_MSIX_VECTOR_SIZE);
976 * For the MSI-X number passed in, get the "data" and "addr" fields.
978 * Note that the spec only requires 32-bit accesses to be supported.
979 * Apparently some chipsets don't support 64-bit accesses.
981 addr = ddi_get32(msix_p->msix_tbl_hdl,
982 (uint32_t *)(off + PCI_MSIX_UPPER_ADDR_OFFSET));
983 addr = (addr << 32) | ddi_get32(msix_p->msix_tbl_hdl,
984 (uint32_t *)(off + PCI_MSIX_LOWER_ADDR_OFFSET));
986 data = ddi_get32(msix_p->msix_tbl_hdl,
987 (uint32_t *)(off + PCI_MSIX_DATA_OFFSET));
989 /* Program new vector with these existing values */
990 return (pci_msi_configure(rdip, DDI_INTR_TYPE_MSIX, 1, dup_inum, addr,
991 data));
996 * Next set of routines are for INTx (legacy) PCI interrupt
997 * support only.
1001 * pci_intx_get_cap:
1002 * For non-MSI devices that comply to PCI v2.3 or greater;
1003 * read the command register. Bit 10 implies interrupt disable.
1004 * Set this bit and then read the status register bit 3.
1005 * Bit 3 of status register is Interrupt state.
1006 * If it is set; then the device supports 'Masking'
1008 * Reset the device back to the original state.
1011 pci_intx_get_cap(dev_info_t *dip, int *flagsp)
1013 uint16_t cmdreg, savereg;
1014 ddi_acc_handle_t cfg_hdl;
1015 #ifdef DEBUG
1016 uint16_t statreg;
1017 #endif /* DEBUG */
1019 *flagsp = 0;
1020 DDI_INTR_NEXDBG((CE_CONT, "pci_intx_get_cap: %s%d: called\n",
1021 ddi_driver_name(dip), ddi_get_instance(dip)));
1023 if (pci_config_setup(dip, &cfg_hdl) != DDI_SUCCESS) {
1024 DDI_INTR_NEXDBG((CE_CONT, "pci_intx_get_cap: can't get "
1025 "config handle\n"));
1026 return (DDI_FAILURE);
1029 savereg = pci_config_get16(cfg_hdl, PCI_CONF_COMM);
1030 DDI_INTR_NEXDBG((CE_CONT, "pci_intx_get_cap: "
1031 "command register was 0x%x\n", savereg));
1033 /* Disable the interrupts */
1034 cmdreg = savereg | PCI_COMM_INTX_DISABLE;
1035 pci_config_put16(cfg_hdl, PCI_CONF_COMM, cmdreg);
1037 #ifdef DEBUG
1038 statreg = pci_config_get16(cfg_hdl, PCI_CONF_STAT);
1039 DDI_INTR_NEXDBG((CE_CONT, "pci_intx_get_cap: "
1040 "status register is 0x%x\n", statreg));
1041 #endif /* DEBUG */
1043 /* Read the bit back */
1044 cmdreg = pci_config_get16(cfg_hdl, PCI_CONF_COMM);
1045 DDI_INTR_NEXDBG((CE_CONT, "pci_intx_get_cap: "
1046 "command register is now 0x%x\n", cmdreg));
1048 *flagsp = DDI_INTR_FLAG_LEVEL;
1050 if (cmdreg & PCI_COMM_INTX_DISABLE) {
1051 DDI_INTR_NEXDBG((CE_CONT, "pci_intx_get_cap: "
1052 "masking supported\n"));
1053 *flagsp |= (DDI_INTR_FLAG_MASKABLE |
1054 DDI_INTR_FLAG_PENDING);
1057 /* Restore the device back to the original state and return */
1058 pci_config_put16(cfg_hdl, PCI_CONF_COMM, savereg);
1060 pci_config_teardown(&cfg_hdl);
1061 return (DDI_SUCCESS);
1066 * pci_intx_clr_mask:
1067 * For non-MSI devices that comply to PCI v2.3 or greater;
1068 * clear the bit10 in the command register.
1071 pci_intx_clr_mask(dev_info_t *dip)
1073 uint16_t cmdreg;
1074 ddi_acc_handle_t cfg_hdl;
1076 DDI_INTR_NEXDBG((CE_CONT, "pci_intx_clr_mask: %s%d: called\n",
1077 ddi_driver_name(dip), ddi_get_instance(dip)));
1079 if (pci_config_setup(dip, &cfg_hdl) != DDI_SUCCESS) {
1080 DDI_INTR_NEXDBG((CE_CONT, "pci_intx_clr_mask: can't get "
1081 "config handle\n"));
1082 return (DDI_FAILURE);
1085 cmdreg = pci_config_get16(cfg_hdl, PCI_CONF_COMM);
1086 DDI_INTR_NEXDBG((CE_CONT, "pci_intx_clr_mask: "
1087 "command register was 0x%x\n", cmdreg));
1089 /* Enable the interrupts */
1090 cmdreg &= ~PCI_COMM_INTX_DISABLE;
1091 pci_config_put16(cfg_hdl, PCI_CONF_COMM, cmdreg);
1092 pci_config_teardown(&cfg_hdl);
1093 return (DDI_SUCCESS);
1098 * pci_intx_set_mask:
1099 * For non-MSI devices that comply to PCI v2.3 or greater;
1100 * set the bit10 in the command register.
1103 pci_intx_set_mask(dev_info_t *dip)
1105 uint16_t cmdreg;
1106 ddi_acc_handle_t cfg_hdl;
1108 DDI_INTR_NEXDBG((CE_CONT, "pci_intx_set_mask: %s%d: called\n",
1109 ddi_driver_name(dip), ddi_get_instance(dip)));
1111 if (pci_config_setup(dip, &cfg_hdl) != DDI_SUCCESS) {
1112 DDI_INTR_NEXDBG((CE_CONT, "pci_intx_set_mask: can't get "
1113 "config handle\n"));
1114 return (DDI_FAILURE);
1117 cmdreg = pci_config_get16(cfg_hdl, PCI_CONF_COMM);
1118 DDI_INTR_NEXDBG((CE_CONT, "pci_intx_set_mask: "
1119 "command register was 0x%x\n", cmdreg));
1121 /* Disable the interrupts */
1122 cmdreg |= PCI_COMM_INTX_DISABLE;
1123 pci_config_put16(cfg_hdl, PCI_CONF_COMM, cmdreg);
1124 pci_config_teardown(&cfg_hdl);
1125 return (DDI_SUCCESS);
1129 * pci_intx_get_pending:
1130 * For non-MSI devices that comply to PCI v2.3 or greater;
1131 * read the status register. Bit 3 of status register is
1132 * Interrupt state. If it is set; then the interrupt is
1133 * 'Pending'.
1136 pci_intx_get_pending(dev_info_t *dip, int *pendingp)
1138 uint16_t statreg;
1139 ddi_acc_handle_t cfg_hdl;
1141 *pendingp = 0;
1142 DDI_INTR_NEXDBG((CE_CONT, "pci_intx_get_pending: %s%d: called\n",
1143 ddi_driver_name(dip), ddi_get_instance(dip)));
1145 if (pci_config_setup(dip, &cfg_hdl) != DDI_SUCCESS) {
1146 DDI_INTR_NEXDBG((CE_CONT, "pci_intx_get_pending: can't get "
1147 "config handle\n"));
1148 return (DDI_FAILURE);
1151 statreg = pci_config_get16(cfg_hdl, PCI_CONF_STAT);
1153 if (statreg & PCI_STAT_INTR) {
1154 DDI_INTR_NEXDBG((CE_CONT, "pci_intx_get_pending: "
1155 "interrupt is pending\n"));
1156 *pendingp = 1;
1159 pci_config_teardown(&cfg_hdl);
1160 return (DDI_SUCCESS);
1165 * pci_intx_get_ispec:
1166 * Get intrspec for PCI devices (legacy support)
1167 * NOTE: This is moved here from x86 pci.c and is
1168 * needed here as pci-ide.c uses it as well
1170 /*ARGSUSED*/
1171 ddi_intrspec_t
1172 pci_intx_get_ispec(dev_info_t *dip, dev_info_t *rdip, int inum)
1174 int *intpriorities;
1175 uint_t num_intpriorities;
1176 struct intrspec *ispec;
1177 ddi_acc_handle_t cfg_hdl;
1178 struct ddi_parent_private_data *pdptr;
1180 if ((pdptr = ddi_get_parent_data(rdip)) == NULL)
1181 return (NULL);
1183 ispec = pdptr->par_intr;
1184 ASSERT(ispec);
1186 /* check if the intrspec_pri has been initialized */
1187 if (!ispec->intrspec_pri) {
1188 if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, rdip,
1189 DDI_PROP_DONTPASS, "interrupt-priorities",
1190 &intpriorities, &num_intpriorities) == DDI_PROP_SUCCESS) {
1191 if (inum < num_intpriorities)
1192 ispec->intrspec_pri = intpriorities[inum];
1193 ddi_prop_free(intpriorities);
1196 /* If still no priority, guess based on the class code */
1197 if (ispec->intrspec_pri == 0)
1198 ispec->intrspec_pri = pci_class_to_pil(rdip);
1201 /* Get interrupt line value */
1202 if (!ispec->intrspec_vec) {
1203 if (pci_config_setup(rdip, &cfg_hdl) != DDI_SUCCESS) {
1204 DDI_INTR_NEXDBG((CE_CONT, "pci_intx_get_iline: "
1205 "can't get config handle\n"));
1206 return ((ddi_intrspec_t)ispec);
1209 ispec->intrspec_vec = pci_config_get8(cfg_hdl, PCI_CONF_ILINE);
1210 pci_config_teardown(&cfg_hdl);
1213 return ((ddi_intrspec_t)ispec);
1216 static uint32_t
1217 pci_match_class_val(uint32_t key, pci_class_val_t *rec_p, int nrec,
1218 uint32_t default_val)
1220 int i;
1222 for (i = 0; i < nrec; rec_p++, i++) {
1223 if ((rec_p->class_code & rec_p->class_mask) ==
1224 (key & rec_p->class_mask))
1225 return (rec_p->class_val);
1228 return (default_val);
1232 * Return the configuration value, based on class code and sub class code,
1233 * from the specified property based or default pci_class_val_t table.
1235 uint32_t
1236 pci_class_to_val(dev_info_t *rdip, char *property_name, pci_class_val_t *rec_p,
1237 int nrec, uint32_t default_val)
1239 int property_len;
1240 uint32_t class_code;
1241 pci_class_val_t *conf;
1242 uint32_t val = default_val;
1245 * Use the "class-code" property to get the base and sub class
1246 * codes for the requesting device.
1248 class_code = (uint32_t)ddi_prop_get_int(DDI_DEV_T_ANY, rdip,
1249 DDI_PROP_DONTPASS, "class-code", -1);
1251 if (class_code == -1)
1252 return (val);
1254 /* look up the val from the default table */
1255 val = pci_match_class_val(class_code, rec_p, nrec, val);
1258 /* see if there is a more specific property specified value */
1259 if (ddi_getlongprop(DDI_DEV_T_ANY, rdip, DDI_PROP_NOTPROM,
1260 property_name, (caddr_t)&conf, &property_len))
1261 return (val);
1263 if ((property_len % sizeof (pci_class_val_t)) == 0)
1264 val = pci_match_class_val(class_code, conf,
1265 property_len / sizeof (pci_class_val_t), val);
1266 kmem_free(conf, property_len);
1267 return (val);
1271 * pci_class_to_pil:
1273 * Return the pil for a given PCI device.
1275 uint32_t
1276 pci_class_to_pil(dev_info_t *rdip)
1278 uint32_t pil;
1280 /* Default pil is 1 */
1281 pil = pci_class_to_val(rdip,
1282 "pci-class-priorities", pci_default_pil,
1283 sizeof (pci_default_pil) / sizeof (pci_class_val_t), 1);
1285 /* Range check the result */
1286 if (pil >= 0xf)
1287 pil = 1;
1289 return (pil);
1293 * pci_class_to_intr_weight:
1295 * Return the intr_weight for a given PCI device.
1297 int32_t
1298 pci_class_to_intr_weight(dev_info_t *rdip)
1300 int32_t intr_weight;
1302 /* default weight is 0% */
1303 intr_weight = pci_class_to_val(rdip,
1304 "pci-class-intr-weights", pci_default_intr_weight,
1305 sizeof (pci_default_intr_weight) / sizeof (pci_class_val_t), 0);
1307 /* range check the result */
1308 if (intr_weight < 0)
1309 intr_weight = 0;
1310 if (intr_weight > 1000)
1311 intr_weight = 1000;
1313 return (intr_weight);