17c478bd9Sstevel@tonic-gate /*
27c478bd9Sstevel@tonic-gate * CDDL HEADER START
37c478bd9Sstevel@tonic-gate *
47c478bd9Sstevel@tonic-gate * The contents of this file are subject to the terms of the
527255037Spjha * Common Development and Distribution License (the "License").
627255037Spjha * You may not use this file except in compliance with the License.
77c478bd9Sstevel@tonic-gate *
87c478bd9Sstevel@tonic-gate * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
97c478bd9Sstevel@tonic-gate * or http://www.opensolaris.org/os/licensing.
107c478bd9Sstevel@tonic-gate * See the License for the specific language governing permissions
117c478bd9Sstevel@tonic-gate * and limitations under the License.
127c478bd9Sstevel@tonic-gate *
137c478bd9Sstevel@tonic-gate * When distributing Covered Code, include this CDDL HEADER in each
147c478bd9Sstevel@tonic-gate * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
157c478bd9Sstevel@tonic-gate * If applicable, add the following below this CDDL HEADER, with the
167c478bd9Sstevel@tonic-gate * fields enclosed by brackets "[]" replaced with your own identifying
177c478bd9Sstevel@tonic-gate * information: Portions Copyright [yyyy] [name of copyright owner]
187c478bd9Sstevel@tonic-gate *
197c478bd9Sstevel@tonic-gate * CDDL HEADER END
207c478bd9Sstevel@tonic-gate */
217c478bd9Sstevel@tonic-gate /*
22614edcaeSEvan Yan * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
237c478bd9Sstevel@tonic-gate * Use is subject to license terms.
24*f8c90ccaSGarrett D'Amore * Copyright 2013 Pluribus Networks, Inc.
257c478bd9Sstevel@tonic-gate */
267c478bd9Sstevel@tonic-gate
277c478bd9Sstevel@tonic-gate /*
287c478bd9Sstevel@tonic-gate * Support for MSI, MSIX and INTx
297c478bd9Sstevel@tonic-gate */
307c478bd9Sstevel@tonic-gate
317c478bd9Sstevel@tonic-gate #include <sys/conf.h>
327c478bd9Sstevel@tonic-gate #include <sys/debug.h>
337c478bd9Sstevel@tonic-gate #include <sys/pci.h>
3427255037Spjha #include <sys/pci_cap.h>
35614edcaeSEvan Yan #include <sys/pci_intr_lib.h>
367c478bd9Sstevel@tonic-gate #include <sys/sunddi.h>
377c478bd9Sstevel@tonic-gate #include <sys/bitmap.h>
387c478bd9Sstevel@tonic-gate
397c478bd9Sstevel@tonic-gate /*
409c75c6bfSgovinda * MSI-X BIR Index Table:
419c75c6bfSgovinda *
429c75c6bfSgovinda * BAR indicator register (BIR) to Base Address register.
439c75c6bfSgovinda */
449c75c6bfSgovinda static uchar_t pci_msix_bir_index[8] = {0x10, 0x14, 0x18, 0x1c,
459c75c6bfSgovinda 0x20, 0x24, 0xff, 0xff};
469c75c6bfSgovinda
47614edcaeSEvan Yan /* default class to pil value mapping */
48614edcaeSEvan Yan pci_class_val_t pci_default_pil [] = {
49614edcaeSEvan Yan {0x000000, 0xff0000, 0x1}, /* Class code for pre-2.0 devices */
50614edcaeSEvan Yan {0x010000, 0xff0000, 0x5}, /* Mass Storage Controller */
51614edcaeSEvan Yan {0x020000, 0xff0000, 0x6}, /* Network Controller */
52614edcaeSEvan Yan {0x030000, 0xff0000, 0x9}, /* Display Controller */
53614edcaeSEvan Yan {0x040000, 0xff0000, 0x8}, /* Multimedia Controller */
54614edcaeSEvan Yan {0x050000, 0xff0000, 0x9}, /* Memory Controller */
55614edcaeSEvan Yan {0x060000, 0xff0000, 0x9}, /* Bridge Controller */
56614edcaeSEvan Yan {0x0c0000, 0xffff00, 0x9}, /* Serial Bus, FireWire (IEEE 1394) */
57614edcaeSEvan Yan {0x0c0100, 0xffff00, 0x4}, /* Serial Bus, ACCESS.bus */
58614edcaeSEvan Yan {0x0c0200, 0xffff00, 0x4}, /* Serial Bus, SSA */
59614edcaeSEvan Yan {0x0c0300, 0xffff00, 0x9}, /* Serial Bus Universal Serial Bus */
604f3b09fdSEvan Yan /*
614f3b09fdSEvan Yan * XXX - This is a temporary workaround and it will be removed
624f3b09fdSEvan Yan * after x86 interrupt scalability support.
634f3b09fdSEvan Yan */
644f3b09fdSEvan Yan #if defined(__i386) || defined(__amd64)
654f3b09fdSEvan Yan {0x0c0400, 0xffff00, 0x5}, /* Serial Bus, Fibre Channel */
664f3b09fdSEvan Yan #else
67614edcaeSEvan Yan {0x0c0400, 0xffff00, 0x6}, /* Serial Bus, Fibre Channel */
684f3b09fdSEvan Yan #endif
69614edcaeSEvan Yan {0x0c0600, 0xffff00, 0x6} /* Serial Bus, Infiniband */
70614edcaeSEvan Yan };
71614edcaeSEvan Yan
72614edcaeSEvan Yan /*
73614edcaeSEvan Yan * Default class to intr_weight value mapping (% of CPU). A driver.conf
74614edcaeSEvan Yan * entry on or above the pci node like
75614edcaeSEvan Yan *
76614edcaeSEvan Yan * pci-class-intr-weights= 0x020000, 0xff0000, 30;
77614edcaeSEvan Yan *
78614edcaeSEvan Yan * can be used to augment or override entries in the default table below.
79614edcaeSEvan Yan *
80614edcaeSEvan Yan * NB: The values below give NICs preference on redistribution, and provide
81614edcaeSEvan Yan * NICs some isolation from other interrupt sources. We need better interfaces
82614edcaeSEvan Yan * that allow the NIC driver to identify a specific NIC instance as high
83614edcaeSEvan Yan * bandwidth, and thus deserving of separation from other low bandwidth
84614edcaeSEvan Yan * NICs additional isolation from other interrupt sources.
85614edcaeSEvan Yan *
86614edcaeSEvan Yan * NB: We treat Infiniband like a NIC.
87614edcaeSEvan Yan */
88614edcaeSEvan Yan pci_class_val_t pci_default_intr_weight [] = {
89614edcaeSEvan Yan {0x020000, 0xff0000, 35}, /* Network Controller */
90614edcaeSEvan Yan {0x010000, 0xff0000, 10}, /* Mass Storage Controller */
91614edcaeSEvan Yan {0x0c0400, 0xffff00, 10}, /* Serial Bus, Fibre Channel */
92614edcaeSEvan Yan {0x0c0600, 0xffff00, 50} /* Serial Bus, Infiniband */
93614edcaeSEvan Yan };
94614edcaeSEvan Yan
959c75c6bfSgovinda /*
967c478bd9Sstevel@tonic-gate * Library utility functions
977c478bd9Sstevel@tonic-gate */
987c478bd9Sstevel@tonic-gate
997c478bd9Sstevel@tonic-gate /*
1007c478bd9Sstevel@tonic-gate * pci_get_msi_ctrl:
1017c478bd9Sstevel@tonic-gate *
1027c478bd9Sstevel@tonic-gate * Helper function that returns with 'cfg_hdl', MSI/X ctrl pointer,
1037c478bd9Sstevel@tonic-gate * and caps_ptr for MSI/X if these are found.
1047c478bd9Sstevel@tonic-gate */
1057c478bd9Sstevel@tonic-gate static int
pci_get_msi_ctrl(dev_info_t * dip,int type,ushort_t * msi_ctrl,ushort_t * caps_ptr,ddi_acc_handle_t * h)1067c478bd9Sstevel@tonic-gate pci_get_msi_ctrl(dev_info_t *dip, int type, ushort_t *msi_ctrl,
10727255037Spjha ushort_t *caps_ptr, ddi_acc_handle_t *h)
1087c478bd9Sstevel@tonic-gate {
1097c478bd9Sstevel@tonic-gate *msi_ctrl = *caps_ptr = 0;
1107c478bd9Sstevel@tonic-gate
11127255037Spjha if (pci_config_setup(dip, h) != DDI_SUCCESS) {
1127c478bd9Sstevel@tonic-gate DDI_INTR_NEXDBG((CE_CONT, "pci_get_msi_ctrl: "
11327255037Spjha "%s%d can't get config handle",
1147c478bd9Sstevel@tonic-gate ddi_driver_name(dip), ddi_get_instance(dip)));
1157c478bd9Sstevel@tonic-gate
1167c478bd9Sstevel@tonic-gate return (DDI_FAILURE);
1177c478bd9Sstevel@tonic-gate }
1187c478bd9Sstevel@tonic-gate
11927255037Spjha if ((PCI_CAP_LOCATE(*h, PCI_CAP_ID_MSI, caps_ptr) == DDI_SUCCESS) &&
12027255037Spjha (type == DDI_INTR_TYPE_MSI)) {
12127255037Spjha if ((*msi_ctrl = PCI_CAP_GET16(*h, NULL, *caps_ptr,
122dc5d169bSpjha PCI_MSI_CTRL)) == PCI_CAP_EINVAL16)
12327255037Spjha goto done;
1247c478bd9Sstevel@tonic-gate
1257c478bd9Sstevel@tonic-gate DDI_INTR_NEXDBG((CE_CONT, "pci_get_msi_ctrl: MSI "
1267c478bd9Sstevel@tonic-gate "caps_ptr=%x msi_ctrl=%x\n", *caps_ptr, *msi_ctrl));
1277c478bd9Sstevel@tonic-gate
1287c478bd9Sstevel@tonic-gate return (DDI_SUCCESS);
1297c478bd9Sstevel@tonic-gate }
1307c478bd9Sstevel@tonic-gate
13127255037Spjha if ((PCI_CAP_LOCATE(*h, PCI_CAP_ID_MSI_X, caps_ptr) == DDI_SUCCESS) &&
13227255037Spjha (type == DDI_INTR_TYPE_MSIX)) {
13327255037Spjha if ((*msi_ctrl = PCI_CAP_GET16(*h, NULL, *caps_ptr,
134dc5d169bSpjha PCI_MSIX_CTRL)) == PCI_CAP_EINVAL16)
13527255037Spjha goto done;
1367c478bd9Sstevel@tonic-gate
1377c478bd9Sstevel@tonic-gate DDI_INTR_NEXDBG((CE_CONT, "pci_get_msi_ctrl: MSI-X "
1387c478bd9Sstevel@tonic-gate "caps_ptr=%x msi_ctrl=%x\n", *caps_ptr, *msi_ctrl));
1397c478bd9Sstevel@tonic-gate
1407c478bd9Sstevel@tonic-gate return (DDI_SUCCESS);
1417c478bd9Sstevel@tonic-gate }
1427c478bd9Sstevel@tonic-gate
14327255037Spjha done:
14427255037Spjha pci_config_teardown(h);
1457c478bd9Sstevel@tonic-gate return (DDI_FAILURE);
1467c478bd9Sstevel@tonic-gate }
1477c478bd9Sstevel@tonic-gate
1487c478bd9Sstevel@tonic-gate
1497c478bd9Sstevel@tonic-gate /*
1507c478bd9Sstevel@tonic-gate * pci_msi_get_cap:
1517c478bd9Sstevel@tonic-gate *
1527c478bd9Sstevel@tonic-gate * Get the capabilities of the MSI/X interrupt
1537c478bd9Sstevel@tonic-gate */
1547c478bd9Sstevel@tonic-gate int
pci_msi_get_cap(dev_info_t * rdip,int type,int * flagsp)1557c478bd9Sstevel@tonic-gate pci_msi_get_cap(dev_info_t *rdip, int type, int *flagsp)
1567c478bd9Sstevel@tonic-gate {
1577c478bd9Sstevel@tonic-gate ushort_t caps_ptr, msi_ctrl;
1587c478bd9Sstevel@tonic-gate ddi_acc_handle_t cfg_hdle;
1597c478bd9Sstevel@tonic-gate
1607c478bd9Sstevel@tonic-gate DDI_INTR_NEXDBG((CE_CONT, "pci_msi_get_cap: rdip = 0x%p\n",
1617c478bd9Sstevel@tonic-gate (void *)rdip));
1627c478bd9Sstevel@tonic-gate
1637c478bd9Sstevel@tonic-gate *flagsp = 0;
1647c478bd9Sstevel@tonic-gate
1657c478bd9Sstevel@tonic-gate if (pci_get_msi_ctrl(rdip, type, &msi_ctrl,
1667c478bd9Sstevel@tonic-gate &caps_ptr, &cfg_hdle) != DDI_SUCCESS)
1677c478bd9Sstevel@tonic-gate return (DDI_FAILURE);
1687c478bd9Sstevel@tonic-gate
1697c478bd9Sstevel@tonic-gate if (type == DDI_INTR_TYPE_MSI) {
1707c478bd9Sstevel@tonic-gate if (msi_ctrl & PCI_MSI_64BIT_MASK)
1717c478bd9Sstevel@tonic-gate *flagsp |= DDI_INTR_FLAG_MSI64;
1727c478bd9Sstevel@tonic-gate if (msi_ctrl & PCI_MSI_PVM_MASK)
1737c478bd9Sstevel@tonic-gate *flagsp |= (DDI_INTR_FLAG_MASKABLE |
1747c478bd9Sstevel@tonic-gate DDI_INTR_FLAG_PENDING);
1757c478bd9Sstevel@tonic-gate else
1767c478bd9Sstevel@tonic-gate *flagsp |= DDI_INTR_FLAG_BLOCK;
1777c478bd9Sstevel@tonic-gate } else if (type == DDI_INTR_TYPE_MSIX) {
1787c478bd9Sstevel@tonic-gate /* MSI-X supports PVM, 64bit by default */
1797c478bd9Sstevel@tonic-gate *flagsp |= (DDI_INTR_FLAG_MASKABLE | DDI_INTR_FLAG_MSI64 |
1807c478bd9Sstevel@tonic-gate DDI_INTR_FLAG_PENDING);
1817c478bd9Sstevel@tonic-gate }
1827c478bd9Sstevel@tonic-gate
1837c478bd9Sstevel@tonic-gate *flagsp |= DDI_INTR_FLAG_EDGE;
1847c478bd9Sstevel@tonic-gate
1857c478bd9Sstevel@tonic-gate DDI_INTR_NEXDBG((CE_CONT, "pci_msi_get_cap: flags = 0x%x\n", *flagsp));
1867c478bd9Sstevel@tonic-gate
1877c478bd9Sstevel@tonic-gate pci_config_teardown(&cfg_hdle);
1887c478bd9Sstevel@tonic-gate return (DDI_SUCCESS);
1897c478bd9Sstevel@tonic-gate }
1907c478bd9Sstevel@tonic-gate
1917c478bd9Sstevel@tonic-gate
1927c478bd9Sstevel@tonic-gate /*
1937c478bd9Sstevel@tonic-gate * pci_msi_configure:
1947c478bd9Sstevel@tonic-gate *
1957c478bd9Sstevel@tonic-gate * Configure address/data and number MSI/Xs fields in the MSI/X
1967c478bd9Sstevel@tonic-gate * capability structure.
1977c478bd9Sstevel@tonic-gate */
1987c478bd9Sstevel@tonic-gate /* ARGSUSED */
1997c478bd9Sstevel@tonic-gate int
pci_msi_configure(dev_info_t * rdip,int type,int count,int inum,uint64_t addr,uint64_t data)2007c478bd9Sstevel@tonic-gate pci_msi_configure(dev_info_t *rdip, int type, int count, int inum,
2017c478bd9Sstevel@tonic-gate uint64_t addr, uint64_t data)
2027c478bd9Sstevel@tonic-gate {
2037c478bd9Sstevel@tonic-gate ushort_t caps_ptr, msi_ctrl;
20427255037Spjha ddi_acc_handle_t h;
2057c478bd9Sstevel@tonic-gate
2069c75c6bfSgovinda DDI_INTR_NEXDBG((CE_CONT, "pci_msi_configure: rdip = 0x%p type 0x%x "
2079c75c6bfSgovinda "count 0x%x inum 0x%x addr 0x%" PRIx64 " data 0x%" PRIx64 "\n",
2089c75c6bfSgovinda (void *)rdip, type, count, inum, addr, data));
2097c478bd9Sstevel@tonic-gate
2107c478bd9Sstevel@tonic-gate if (pci_get_msi_ctrl(rdip, type, &msi_ctrl,
21127255037Spjha &caps_ptr, &h) != DDI_SUCCESS)
2127c478bd9Sstevel@tonic-gate return (DDI_FAILURE);
2137c478bd9Sstevel@tonic-gate
2147c478bd9Sstevel@tonic-gate if (type == DDI_INTR_TYPE_MSI) {
2157c478bd9Sstevel@tonic-gate /* Set the bits to inform how many MSIs are enabled */
2167c478bd9Sstevel@tonic-gate msi_ctrl |= ((highbit(count) -1) << PCI_MSI_MME_SHIFT);
21727255037Spjha PCI_CAP_PUT16(h, NULL, caps_ptr, PCI_MSI_CTRL, msi_ctrl);
2187c478bd9Sstevel@tonic-gate
2199c75c6bfSgovinda DDI_INTR_NEXDBG((CE_CONT, "pci_msi_configure: msi_ctrl = %x\n",
22027255037Spjha PCI_CAP_GET16(h, NULL, caps_ptr, PCI_MSI_CTRL)));
2219c75c6bfSgovinda
2227c478bd9Sstevel@tonic-gate /* Set the "data" and "addr" bits */
22327255037Spjha PCI_CAP_PUT32(h, NULL, caps_ptr, PCI_MSI_ADDR_OFFSET, addr);
2247c478bd9Sstevel@tonic-gate
2259c75c6bfSgovinda DDI_INTR_NEXDBG((CE_CONT, "pci_msi_configure: msi_addr = %x\n",
22627255037Spjha PCI_CAP_GET32(h, NULL, caps_ptr, PCI_MSI_ADDR_OFFSET)));
2279c75c6bfSgovinda
2287c478bd9Sstevel@tonic-gate if (msi_ctrl & PCI_MSI_64BIT_MASK) {
22927255037Spjha PCI_CAP_PUT32(h, NULL, caps_ptr, PCI_MSI_ADDR_OFFSET
23027255037Spjha + 4, addr >> 32);
2319c75c6bfSgovinda
23227255037Spjha DDI_INTR_NEXDBG((CE_CONT, "pci_msi_configure: upper "
23327255037Spjha "32bit msi_addr = %x\n", PCI_CAP_GET32(h, NULL,
23427255037Spjha caps_ptr, PCI_MSI_ADDR_OFFSET + 4)));
2359c75c6bfSgovinda
23627255037Spjha PCI_CAP_PUT16(h, NULL, caps_ptr, PCI_MSI_64BIT_DATA,
23727255037Spjha data);
2389c75c6bfSgovinda
23927255037Spjha DDI_INTR_NEXDBG((CE_CONT, "pci_msi_configure: msi_data "
24027255037Spjha "= %x\n", PCI_CAP_GET16(h, NULL, caps_ptr,
24127255037Spjha PCI_MSI_64BIT_DATA)));
2427c478bd9Sstevel@tonic-gate } else {
24327255037Spjha PCI_CAP_PUT16(h, NULL, caps_ptr, PCI_MSI_32BIT_DATA,
24427255037Spjha data);
2459c75c6bfSgovinda
24627255037Spjha DDI_INTR_NEXDBG((CE_CONT, "pci_msi_configure: msi_data "
24727255037Spjha "= %x\n", PCI_CAP_GET16(h, NULL, caps_ptr,
24827255037Spjha PCI_MSI_32BIT_DATA)));
2497c478bd9Sstevel@tonic-gate }
2507c478bd9Sstevel@tonic-gate } else if (type == DDI_INTR_TYPE_MSIX) {
251abdbd06dSagiri uintptr_t off;
2527c478bd9Sstevel@tonic-gate ddi_intr_msix_t *msix_p = i_ddi_get_msix(rdip);
2537c478bd9Sstevel@tonic-gate
2547c478bd9Sstevel@tonic-gate /* Offset into the "inum"th entry in the MSI-X table */
255abdbd06dSagiri off = (uintptr_t)msix_p->msix_tbl_addr +
2569c75c6bfSgovinda (inum * PCI_MSIX_VECTOR_SIZE);
2577c478bd9Sstevel@tonic-gate
2587c478bd9Sstevel@tonic-gate /* Set the "data" and "addr" bits */
2597c478bd9Sstevel@tonic-gate ddi_put32(msix_p->msix_tbl_hdl,
2609c75c6bfSgovinda (uint32_t *)(off + PCI_MSIX_DATA_OFFSET), data);
2617c478bd9Sstevel@tonic-gate
262*f8c90ccaSGarrett D'Amore /*
263*f8c90ccaSGarrett D'Amore * Note that the spec only requires 32-bit accesses
264*f8c90ccaSGarrett D'Amore * to be supported. Apparently some chipsets don't
265*f8c90ccaSGarrett D'Amore * support 64-bit accesses.
266*f8c90ccaSGarrett D'Amore */
267*f8c90ccaSGarrett D'Amore ddi_put32(msix_p->msix_tbl_hdl,
268*f8c90ccaSGarrett D'Amore (uint32_t *)(off + PCI_MSIX_LOWER_ADDR_OFFSET), addr);
269*f8c90ccaSGarrett D'Amore ddi_put32(msix_p->msix_tbl_hdl,
270*f8c90ccaSGarrett D'Amore (uint32_t *)(off + PCI_MSIX_UPPER_ADDR_OFFSET),
271*f8c90ccaSGarrett D'Amore addr >> 32);
2729c75c6bfSgovinda
2739c75c6bfSgovinda DDI_INTR_NEXDBG((CE_CONT, "pci_msi_configure: "
274*f8c90ccaSGarrett D'Amore "msix_addr 0x%x.%x msix_data 0x%x\n",
275*f8c90ccaSGarrett D'Amore ddi_get32(msix_p->msix_tbl_hdl,
276*f8c90ccaSGarrett D'Amore (uint32_t *)(off + PCI_MSIX_UPPER_ADDR_OFFSET)),
277*f8c90ccaSGarrett D'Amore ddi_get32(msix_p->msix_tbl_hdl,
278*f8c90ccaSGarrett D'Amore (uint32_t *)(off + PCI_MSIX_LOWER_ADDR_OFFSET)),
2799c75c6bfSgovinda ddi_get32(msix_p->msix_tbl_hdl,
2809c75c6bfSgovinda (uint32_t *)(off + PCI_MSIX_DATA_OFFSET))));
2817c478bd9Sstevel@tonic-gate }
2827c478bd9Sstevel@tonic-gate
28327255037Spjha pci_config_teardown(&h);
2847c478bd9Sstevel@tonic-gate return (DDI_SUCCESS);
2857c478bd9Sstevel@tonic-gate }
2867c478bd9Sstevel@tonic-gate
2877c478bd9Sstevel@tonic-gate
2887c478bd9Sstevel@tonic-gate /*
2897c478bd9Sstevel@tonic-gate * pci_msi_unconfigure:
2907c478bd9Sstevel@tonic-gate *
2917c478bd9Sstevel@tonic-gate * Unconfigure address/data and number MSI/Xs fields in the MSI/X
2927c478bd9Sstevel@tonic-gate * capability structure.
2937c478bd9Sstevel@tonic-gate */
2947c478bd9Sstevel@tonic-gate /* ARGSUSED */
2957c478bd9Sstevel@tonic-gate int
pci_msi_unconfigure(dev_info_t * rdip,int type,int inum)2967c478bd9Sstevel@tonic-gate pci_msi_unconfigure(dev_info_t *rdip, int type, int inum)
2977c478bd9Sstevel@tonic-gate {
2987c478bd9Sstevel@tonic-gate ushort_t msi_ctrl, caps_ptr;
29927255037Spjha ddi_acc_handle_t h;
3007c478bd9Sstevel@tonic-gate
30195003185Segillett DDI_INTR_NEXDBG((CE_CONT, "pci_msi_unconfigure: rdip = 0x%p type 0x%x "
30295003185Segillett "inum 0x%x\n", (void *)rdip, type, inum));
3037c478bd9Sstevel@tonic-gate
30427255037Spjha if (pci_get_msi_ctrl(rdip, type, &msi_ctrl, &caps_ptr, &h) !=
30527255037Spjha DDI_SUCCESS)
3067c478bd9Sstevel@tonic-gate return (DDI_FAILURE);
3077c478bd9Sstevel@tonic-gate
3087c478bd9Sstevel@tonic-gate if (type == DDI_INTR_TYPE_MSI) {
3097c478bd9Sstevel@tonic-gate msi_ctrl &= (~PCI_MSI_MME_MASK);
31027255037Spjha PCI_CAP_PUT16(h, NULL, caps_ptr, PCI_MSI_CTRL, msi_ctrl);
3117c478bd9Sstevel@tonic-gate
31227255037Spjha PCI_CAP_PUT32(h, NULL, caps_ptr, PCI_MSI_ADDR_OFFSET, 0);
31327255037Spjha
3147c478bd9Sstevel@tonic-gate if (msi_ctrl & PCI_MSI_64BIT_MASK) {
31527255037Spjha PCI_CAP_PUT16(h, NULL, caps_ptr, PCI_MSI_64BIT_DATA,
31627255037Spjha 0);
31727255037Spjha PCI_CAP_PUT32(h, NULL, caps_ptr, PCI_MSI_ADDR_OFFSET
31827255037Spjha + 4, 0);
3197c478bd9Sstevel@tonic-gate } else {
32027255037Spjha PCI_CAP_PUT16(h, NULL, caps_ptr, PCI_MSI_32BIT_DATA,
32127255037Spjha 0);
3227c478bd9Sstevel@tonic-gate }
3237c478bd9Sstevel@tonic-gate
32427255037Spjha DDI_INTR_NEXDBG((CE_CONT, "pci_msi_unconfigure: msi_ctrl "
32527255037Spjha "= %x\n", PCI_CAP_GET16(h, NULL, caps_ptr, PCI_MSI_CTRL)));
3267c478bd9Sstevel@tonic-gate
3277c478bd9Sstevel@tonic-gate } else if (type == DDI_INTR_TYPE_MSIX) {
328abdbd06dSagiri uintptr_t off;
3297c478bd9Sstevel@tonic-gate ddi_intr_msix_t *msix_p = i_ddi_get_msix(rdip);
3307c478bd9Sstevel@tonic-gate
3317c478bd9Sstevel@tonic-gate /* Offset into the "inum"th entry in the MSI-X table */
332abdbd06dSagiri off = (uintptr_t)msix_p->msix_tbl_addr +
3339c75c6bfSgovinda (inum * PCI_MSIX_VECTOR_SIZE);
3347c478bd9Sstevel@tonic-gate
3357c478bd9Sstevel@tonic-gate /* Reset the "data" and "addr" bits */
3367c478bd9Sstevel@tonic-gate ddi_put32(msix_p->msix_tbl_hdl,
3379c75c6bfSgovinda (uint32_t *)(off + PCI_MSIX_DATA_OFFSET), 0);
3387c478bd9Sstevel@tonic-gate
339*f8c90ccaSGarrett D'Amore /*
340*f8c90ccaSGarrett D'Amore * Note that the spec only requires 32-bit accesses
341*f8c90ccaSGarrett D'Amore * to be supported. Apparently some chipsets don't
342*f8c90ccaSGarrett D'Amore * support 64-bit accesses.
343*f8c90ccaSGarrett D'Amore */
344*f8c90ccaSGarrett D'Amore ddi_put32(msix_p->msix_tbl_hdl,
345*f8c90ccaSGarrett D'Amore (uint32_t *)(off + PCI_MSIX_LOWER_ADDR_OFFSET), 0);
346*f8c90ccaSGarrett D'Amore ddi_put32(msix_p->msix_tbl_hdl,
347*f8c90ccaSGarrett D'Amore (uint32_t *)(off + PCI_MSIX_UPPER_ADDR_OFFSET), 0);
3487c478bd9Sstevel@tonic-gate }
3497c478bd9Sstevel@tonic-gate
35027255037Spjha pci_config_teardown(&h);
3517c478bd9Sstevel@tonic-gate return (DDI_SUCCESS);
3527c478bd9Sstevel@tonic-gate }
3537c478bd9Sstevel@tonic-gate
3547c478bd9Sstevel@tonic-gate
3557c478bd9Sstevel@tonic-gate /*
3567c478bd9Sstevel@tonic-gate * pci_is_msi_enabled:
3577c478bd9Sstevel@tonic-gate *
3587c478bd9Sstevel@tonic-gate * This function returns DDI_SUCCESS if MSI/X is already enabled, otherwise
3597c478bd9Sstevel@tonic-gate * it returns DDI_FAILURE.
3607c478bd9Sstevel@tonic-gate */
3617c478bd9Sstevel@tonic-gate int
pci_is_msi_enabled(dev_info_t * rdip,int type)3627c478bd9Sstevel@tonic-gate pci_is_msi_enabled(dev_info_t *rdip, int type)
3637c478bd9Sstevel@tonic-gate {
3647c478bd9Sstevel@tonic-gate ushort_t caps_ptr, msi_ctrl;
3657c478bd9Sstevel@tonic-gate ddi_acc_handle_t cfg_hdle;
3667c478bd9Sstevel@tonic-gate int ret = DDI_FAILURE;
3677c478bd9Sstevel@tonic-gate
3687c478bd9Sstevel@tonic-gate DDI_INTR_NEXDBG((CE_CONT, "pci_is_msi_enabled: rdip = 0x%p, "
3697c478bd9Sstevel@tonic-gate "type = 0x%x\n", (void *)rdip, type));
3707c478bd9Sstevel@tonic-gate
3717c478bd9Sstevel@tonic-gate if (pci_get_msi_ctrl(rdip, type, &msi_ctrl,
3727c478bd9Sstevel@tonic-gate &caps_ptr, &cfg_hdle) != DDI_SUCCESS)
3737c478bd9Sstevel@tonic-gate return (DDI_FAILURE);
3747c478bd9Sstevel@tonic-gate
3757c478bd9Sstevel@tonic-gate if ((type == DDI_INTR_TYPE_MSI) && (msi_ctrl & PCI_MSI_ENABLE_BIT))
3767c478bd9Sstevel@tonic-gate ret = DDI_SUCCESS;
3777c478bd9Sstevel@tonic-gate
3787c478bd9Sstevel@tonic-gate if ((type == DDI_INTR_TYPE_MSIX) && (msi_ctrl & PCI_MSIX_ENABLE_BIT))
3797c478bd9Sstevel@tonic-gate ret = DDI_SUCCESS;
3807c478bd9Sstevel@tonic-gate
3817c478bd9Sstevel@tonic-gate pci_config_teardown(&cfg_hdle);
3827c478bd9Sstevel@tonic-gate return (ret);
3837c478bd9Sstevel@tonic-gate }
3847c478bd9Sstevel@tonic-gate
3857c478bd9Sstevel@tonic-gate
3867c478bd9Sstevel@tonic-gate /*
3877c478bd9Sstevel@tonic-gate * pci_msi_enable_mode:
3887c478bd9Sstevel@tonic-gate *
3897c478bd9Sstevel@tonic-gate * This function sets the MSI_ENABLE bit in the capability structure
3907c478bd9Sstevel@tonic-gate * (for MSI) and MSIX_ENABLE bit in the MSI-X capability structure.
39195003185Segillett *
39295003185Segillett * NOTE: It is the nexus driver's responsibility to clear the MSI/X
39395003185Segillett * interrupt's mask bit in the MSI/X capability structure before the
39495003185Segillett * interrupt can be used.
3957c478bd9Sstevel@tonic-gate */
3967c478bd9Sstevel@tonic-gate int
pci_msi_enable_mode(dev_info_t * rdip,int type)39795003185Segillett pci_msi_enable_mode(dev_info_t *rdip, int type)
3987c478bd9Sstevel@tonic-gate {
3997c478bd9Sstevel@tonic-gate ushort_t caps_ptr, msi_ctrl;
4007c478bd9Sstevel@tonic-gate ddi_acc_handle_t cfg_hdle;
4017c478bd9Sstevel@tonic-gate
40295003185Segillett DDI_INTR_NEXDBG((CE_CONT, "pci_msi_enable_mode: rdip = 0x%p\n",
40395003185Segillett (void *)rdip));
4047c478bd9Sstevel@tonic-gate
4057c478bd9Sstevel@tonic-gate if (pci_get_msi_ctrl(rdip, type, &msi_ctrl,
4067c478bd9Sstevel@tonic-gate &caps_ptr, &cfg_hdle) != DDI_SUCCESS)
4077c478bd9Sstevel@tonic-gate return (DDI_FAILURE);
4087c478bd9Sstevel@tonic-gate
4097c478bd9Sstevel@tonic-gate if (type == DDI_INTR_TYPE_MSI) {
4107c478bd9Sstevel@tonic-gate if (msi_ctrl & PCI_MSI_ENABLE_BIT)
4117c478bd9Sstevel@tonic-gate goto finished;
4127c478bd9Sstevel@tonic-gate
4137c478bd9Sstevel@tonic-gate msi_ctrl |= PCI_MSI_ENABLE_BIT;
41427255037Spjha PCI_CAP_PUT16(cfg_hdle, NULL, caps_ptr, PCI_MSI_CTRL, msi_ctrl);
4157c478bd9Sstevel@tonic-gate
4167c478bd9Sstevel@tonic-gate } else if (type == DDI_INTR_TYPE_MSIX) {
4177c478bd9Sstevel@tonic-gate if (msi_ctrl & PCI_MSIX_ENABLE_BIT)
4187c478bd9Sstevel@tonic-gate goto finished;
4197c478bd9Sstevel@tonic-gate
4207c478bd9Sstevel@tonic-gate msi_ctrl |= PCI_MSIX_ENABLE_BIT;
42127255037Spjha PCI_CAP_PUT16(cfg_hdle, NULL, caps_ptr, PCI_MSIX_CTRL,
42227255037Spjha msi_ctrl);
4237c478bd9Sstevel@tonic-gate }
4247c478bd9Sstevel@tonic-gate
4257c478bd9Sstevel@tonic-gate finished:
4267c478bd9Sstevel@tonic-gate DDI_INTR_NEXDBG((CE_CONT, "pci_msi_enable_mode: msi_ctrl = %x\n",
4277c478bd9Sstevel@tonic-gate msi_ctrl));
4287c478bd9Sstevel@tonic-gate
4297c478bd9Sstevel@tonic-gate pci_config_teardown(&cfg_hdle);
4307c478bd9Sstevel@tonic-gate return (DDI_SUCCESS);
4317c478bd9Sstevel@tonic-gate }
4327c478bd9Sstevel@tonic-gate
4337c478bd9Sstevel@tonic-gate
4347c478bd9Sstevel@tonic-gate /*
4357c478bd9Sstevel@tonic-gate * pci_msi_disable_mode:
4367c478bd9Sstevel@tonic-gate *
4377c478bd9Sstevel@tonic-gate * This function resets the MSI_ENABLE bit in the capability structure
4387c478bd9Sstevel@tonic-gate * (for MSI) and MSIX_ENABLE bit in the MSI-X capability structure.
43995003185Segillett *
44095003185Segillett * NOTE: It is the nexus driver's responsibility to set the MSI/X
44195003185Segillett * interrupt's mask bit in the MSI/X capability structure before the
44295003185Segillett * interrupt can be disabled.
4437c478bd9Sstevel@tonic-gate */
4447c478bd9Sstevel@tonic-gate int
pci_msi_disable_mode(dev_info_t * rdip,int type)44509b1eac2SEvan Yan pci_msi_disable_mode(dev_info_t *rdip, int type)
4467c478bd9Sstevel@tonic-gate {
4477c478bd9Sstevel@tonic-gate ushort_t caps_ptr, msi_ctrl;
4487c478bd9Sstevel@tonic-gate ddi_acc_handle_t cfg_hdle;
4497c478bd9Sstevel@tonic-gate
45009b1eac2SEvan Yan DDI_INTR_NEXDBG((CE_CONT, "pci_msi_disable_mode: rdip = 0x%p\n",
45109b1eac2SEvan Yan (void *)rdip));
4527c478bd9Sstevel@tonic-gate
4537c478bd9Sstevel@tonic-gate if (pci_get_msi_ctrl(rdip, type, &msi_ctrl,
4547c478bd9Sstevel@tonic-gate &caps_ptr, &cfg_hdle) != DDI_SUCCESS)
4557c478bd9Sstevel@tonic-gate return (DDI_FAILURE);
4567c478bd9Sstevel@tonic-gate
4577c478bd9Sstevel@tonic-gate /* Reset the "enable" bit */
4587c478bd9Sstevel@tonic-gate if (type == DDI_INTR_TYPE_MSI) {
4597c478bd9Sstevel@tonic-gate if (!(msi_ctrl & PCI_MSI_ENABLE_BIT))
4607c478bd9Sstevel@tonic-gate goto finished;
4617c478bd9Sstevel@tonic-gate msi_ctrl &= ~PCI_MSI_ENABLE_BIT;
46227255037Spjha PCI_CAP_PUT16(cfg_hdle, NULL, caps_ptr, PCI_MSI_CTRL, msi_ctrl);
4637c478bd9Sstevel@tonic-gate } else if (type == DDI_INTR_TYPE_MSIX) {
4647c478bd9Sstevel@tonic-gate if (!(msi_ctrl & PCI_MSIX_ENABLE_BIT))
4657c478bd9Sstevel@tonic-gate goto finished;
4667c478bd9Sstevel@tonic-gate
46795003185Segillett msi_ctrl &= ~PCI_MSIX_ENABLE_BIT;
46895003185Segillett PCI_CAP_PUT16(cfg_hdle, NULL, caps_ptr, PCI_MSIX_CTRL,
46995003185Segillett msi_ctrl);
4707c478bd9Sstevel@tonic-gate }
4717c478bd9Sstevel@tonic-gate
4727c478bd9Sstevel@tonic-gate finished:
4737c478bd9Sstevel@tonic-gate DDI_INTR_NEXDBG((CE_CONT, "pci_msi_disable_mode: msi_ctrl = %x\n",
4747c478bd9Sstevel@tonic-gate msi_ctrl));
4757c478bd9Sstevel@tonic-gate
4767c478bd9Sstevel@tonic-gate pci_config_teardown(&cfg_hdle);
4777c478bd9Sstevel@tonic-gate return (DDI_SUCCESS);
4787c478bd9Sstevel@tonic-gate }
4797c478bd9Sstevel@tonic-gate
4807c478bd9Sstevel@tonic-gate
4817c478bd9Sstevel@tonic-gate /*
4827c478bd9Sstevel@tonic-gate * pci_msi_set_mask:
4837c478bd9Sstevel@tonic-gate *
4847c478bd9Sstevel@tonic-gate * Set the mask bit in the MSI/X capability structure
4857c478bd9Sstevel@tonic-gate */
4867c478bd9Sstevel@tonic-gate /* ARGSUSED */
4877c478bd9Sstevel@tonic-gate int
pci_msi_set_mask(dev_info_t * rdip,int type,int inum)4887c478bd9Sstevel@tonic-gate pci_msi_set_mask(dev_info_t *rdip, int type, int inum)
4897c478bd9Sstevel@tonic-gate {
4907c478bd9Sstevel@tonic-gate int offset;
4917c478bd9Sstevel@tonic-gate int ret = DDI_FAILURE;
4927c478bd9Sstevel@tonic-gate ushort_t caps_ptr, msi_ctrl;
4937c478bd9Sstevel@tonic-gate ddi_acc_handle_t cfg_hdle;
4949c75c6bfSgovinda uint32_t mask_bits;
4957c478bd9Sstevel@tonic-gate
4967c478bd9Sstevel@tonic-gate DDI_INTR_NEXDBG((CE_CONT, "pci_msi_set_mask: rdip = 0x%p, "
4977c478bd9Sstevel@tonic-gate "type = 0x%x\n", (void *)rdip, type));
4987c478bd9Sstevel@tonic-gate
4997c478bd9Sstevel@tonic-gate if (pci_get_msi_ctrl(rdip, type, &msi_ctrl,
5007c478bd9Sstevel@tonic-gate &caps_ptr, &cfg_hdle) != DDI_SUCCESS)
5017c478bd9Sstevel@tonic-gate return (DDI_FAILURE);
5027c478bd9Sstevel@tonic-gate
5037c478bd9Sstevel@tonic-gate if (type == DDI_INTR_TYPE_MSI) {
5047c478bd9Sstevel@tonic-gate if (!(msi_ctrl & PCI_MSI_PVM_MASK))
5057c478bd9Sstevel@tonic-gate goto done;
5067c478bd9Sstevel@tonic-gate
5077c478bd9Sstevel@tonic-gate offset = (msi_ctrl & PCI_MSI_64BIT_MASK) ?
5087c478bd9Sstevel@tonic-gate PCI_MSI_64BIT_MASKBITS : PCI_MSI_32BIT_MASK;
5097c478bd9Sstevel@tonic-gate
51027255037Spjha if ((mask_bits = PCI_CAP_GET32(cfg_hdle, NULL, caps_ptr,
511dc5d169bSpjha offset)) == PCI_CAP_EINVAL32)
51227255037Spjha goto done;
5137c478bd9Sstevel@tonic-gate
5147c478bd9Sstevel@tonic-gate mask_bits |= (1 << inum);
5157c478bd9Sstevel@tonic-gate
51627255037Spjha PCI_CAP_PUT32(cfg_hdle, NULL, caps_ptr, offset, mask_bits);
5177c478bd9Sstevel@tonic-gate
5187c478bd9Sstevel@tonic-gate } else if (type == DDI_INTR_TYPE_MSIX) {
519abdbd06dSagiri uintptr_t off;
5207c478bd9Sstevel@tonic-gate ddi_intr_msix_t *msix_p;
5217c478bd9Sstevel@tonic-gate
5227c478bd9Sstevel@tonic-gate /* Set function mask */
5237c478bd9Sstevel@tonic-gate if (msi_ctrl & PCI_MSIX_FUNCTION_MASK) {
5247c478bd9Sstevel@tonic-gate ret = DDI_SUCCESS;
5257c478bd9Sstevel@tonic-gate goto done;
5267c478bd9Sstevel@tonic-gate }
5277c478bd9Sstevel@tonic-gate
5287c478bd9Sstevel@tonic-gate msix_p = i_ddi_get_msix(rdip);
5297c478bd9Sstevel@tonic-gate
5307c478bd9Sstevel@tonic-gate /* Offset into the "inum"th entry in the MSI-X table */
5319c75c6bfSgovinda off = (uintptr_t)msix_p->msix_tbl_addr + (inum *
5327c478bd9Sstevel@tonic-gate PCI_MSIX_VECTOR_SIZE) + PCI_MSIX_VECTOR_CTRL_OFFSET;
5337c478bd9Sstevel@tonic-gate
5347c478bd9Sstevel@tonic-gate /* Set the Mask bit */
5359c75c6bfSgovinda ddi_put32(msix_p->msix_tbl_hdl, (uint32_t *)off, 0x1);
5367c478bd9Sstevel@tonic-gate }
5377c478bd9Sstevel@tonic-gate
5387c478bd9Sstevel@tonic-gate ret = DDI_SUCCESS;
5397c478bd9Sstevel@tonic-gate done:
5407c478bd9Sstevel@tonic-gate pci_config_teardown(&cfg_hdle);
5417c478bd9Sstevel@tonic-gate return (ret);
5427c478bd9Sstevel@tonic-gate }
5437c478bd9Sstevel@tonic-gate
5447c478bd9Sstevel@tonic-gate
5457c478bd9Sstevel@tonic-gate /*
5467c478bd9Sstevel@tonic-gate * pci_msi_clr_mask:
5477c478bd9Sstevel@tonic-gate *
5487c478bd9Sstevel@tonic-gate * Clear the mask bit in the MSI/X capability structure
5497c478bd9Sstevel@tonic-gate */
5507c478bd9Sstevel@tonic-gate /* ARGSUSED */
5517c478bd9Sstevel@tonic-gate int
pci_msi_clr_mask(dev_info_t * rdip,int type,int inum)5527c478bd9Sstevel@tonic-gate pci_msi_clr_mask(dev_info_t *rdip, int type, int inum)
5537c478bd9Sstevel@tonic-gate {
5547c478bd9Sstevel@tonic-gate ushort_t caps_ptr, msi_ctrl;
5557c478bd9Sstevel@tonic-gate ddi_acc_handle_t cfg_hdle;
5567c478bd9Sstevel@tonic-gate int offset;
5577c478bd9Sstevel@tonic-gate int ret = DDI_FAILURE;
5589c75c6bfSgovinda uint32_t mask_bits;
5597c478bd9Sstevel@tonic-gate
5607c478bd9Sstevel@tonic-gate DDI_INTR_NEXDBG((CE_CONT, "pci_msi_clr_mask: rdip = 0x%p, "
5617c478bd9Sstevel@tonic-gate "type = 0x%x\n", (void *)rdip, type));
5627c478bd9Sstevel@tonic-gate
5637c478bd9Sstevel@tonic-gate if (pci_get_msi_ctrl(rdip, type, &msi_ctrl,
5647c478bd9Sstevel@tonic-gate &caps_ptr, &cfg_hdle) != DDI_SUCCESS)
5657c478bd9Sstevel@tonic-gate return (DDI_FAILURE);
5667c478bd9Sstevel@tonic-gate
5677c478bd9Sstevel@tonic-gate if (type == DDI_INTR_TYPE_MSI) {
5687c478bd9Sstevel@tonic-gate if (!(msi_ctrl & PCI_MSI_PVM_MASK))
5697c478bd9Sstevel@tonic-gate goto done;
5707c478bd9Sstevel@tonic-gate
5717c478bd9Sstevel@tonic-gate offset = (msi_ctrl & PCI_MSI_64BIT_MASK) ?
5727c478bd9Sstevel@tonic-gate PCI_MSI_64BIT_MASKBITS : PCI_MSI_32BIT_MASK;
57327255037Spjha if ((mask_bits = PCI_CAP_GET32(cfg_hdle, NULL, caps_ptr,
574dc5d169bSpjha offset)) == PCI_CAP_EINVAL32)
57527255037Spjha goto done;
5767c478bd9Sstevel@tonic-gate
5777c478bd9Sstevel@tonic-gate mask_bits &= ~(1 << inum);
5787c478bd9Sstevel@tonic-gate
57927255037Spjha PCI_CAP_PUT32(cfg_hdle, NULL, caps_ptr, offset, mask_bits);
5807c478bd9Sstevel@tonic-gate
5817c478bd9Sstevel@tonic-gate } else if (type == DDI_INTR_TYPE_MSIX) {
582abdbd06dSagiri uintptr_t off;
5837c478bd9Sstevel@tonic-gate ddi_intr_msix_t *msix_p;
5847c478bd9Sstevel@tonic-gate
5857c478bd9Sstevel@tonic-gate if (msi_ctrl & PCI_MSIX_FUNCTION_MASK) {
5867c478bd9Sstevel@tonic-gate ret = DDI_SUCCESS;
5877c478bd9Sstevel@tonic-gate goto done;
5887c478bd9Sstevel@tonic-gate }
5897c478bd9Sstevel@tonic-gate
5907c478bd9Sstevel@tonic-gate msix_p = i_ddi_get_msix(rdip);
5917c478bd9Sstevel@tonic-gate
5927c478bd9Sstevel@tonic-gate /* Offset into the "inum"th entry in the MSI-X table */
5939c75c6bfSgovinda off = (uintptr_t)msix_p->msix_tbl_addr + (inum *
5947c478bd9Sstevel@tonic-gate PCI_MSIX_VECTOR_SIZE) + PCI_MSIX_VECTOR_CTRL_OFFSET;
5957c478bd9Sstevel@tonic-gate
5967c478bd9Sstevel@tonic-gate /* Clear the Mask bit */
5979c75c6bfSgovinda ddi_put32(msix_p->msix_tbl_hdl, (uint32_t *)off, 0x0);
5987c478bd9Sstevel@tonic-gate }
5997c478bd9Sstevel@tonic-gate
6007c478bd9Sstevel@tonic-gate ret = DDI_SUCCESS;
6017c478bd9Sstevel@tonic-gate done:
6027c478bd9Sstevel@tonic-gate pci_config_teardown(&cfg_hdle);
6037c478bd9Sstevel@tonic-gate return (ret);
6047c478bd9Sstevel@tonic-gate }
6057c478bd9Sstevel@tonic-gate
6067c478bd9Sstevel@tonic-gate
6077c478bd9Sstevel@tonic-gate /*
6087c478bd9Sstevel@tonic-gate * pci_msi_get_pending:
6097c478bd9Sstevel@tonic-gate *
6107c478bd9Sstevel@tonic-gate * Get the pending bit from the MSI/X capability structure
6117c478bd9Sstevel@tonic-gate */
6127c478bd9Sstevel@tonic-gate /* ARGSUSED */
6137c478bd9Sstevel@tonic-gate int
pci_msi_get_pending(dev_info_t * rdip,int type,int inum,int * pendingp)6147c478bd9Sstevel@tonic-gate pci_msi_get_pending(dev_info_t *rdip, int type, int inum, int *pendingp)
6157c478bd9Sstevel@tonic-gate {
6167c478bd9Sstevel@tonic-gate ushort_t caps_ptr, msi_ctrl;
6177c478bd9Sstevel@tonic-gate ddi_acc_handle_t cfg_hdle;
6187c478bd9Sstevel@tonic-gate int offset;
6197c478bd9Sstevel@tonic-gate int ret = DDI_FAILURE;
6207c478bd9Sstevel@tonic-gate
6217c478bd9Sstevel@tonic-gate DDI_INTR_NEXDBG((CE_CONT, "pci_msi_get_pending: rdip = 0x%p\n",
6227c478bd9Sstevel@tonic-gate (void *)rdip));
6237c478bd9Sstevel@tonic-gate
6247c478bd9Sstevel@tonic-gate if (pci_get_msi_ctrl(rdip, type, &msi_ctrl,
6257c478bd9Sstevel@tonic-gate &caps_ptr, &cfg_hdle) != DDI_SUCCESS)
6267c478bd9Sstevel@tonic-gate return (DDI_FAILURE);
6277c478bd9Sstevel@tonic-gate
6287c478bd9Sstevel@tonic-gate if (type == DDI_INTR_TYPE_MSI) {
6297c478bd9Sstevel@tonic-gate uint32_t pending_bits;
6307c478bd9Sstevel@tonic-gate
6317c478bd9Sstevel@tonic-gate if (!(msi_ctrl & PCI_MSI_PVM_MASK)) {
6327c478bd9Sstevel@tonic-gate DDI_INTR_NEXDBG((CE_CONT, "pci_msi_get_pending: "
6337c478bd9Sstevel@tonic-gate "PVM is not supported\n"));
6347c478bd9Sstevel@tonic-gate goto done;
6357c478bd9Sstevel@tonic-gate }
6367c478bd9Sstevel@tonic-gate
6377c478bd9Sstevel@tonic-gate offset = (msi_ctrl & PCI_MSI_64BIT_MASK) ?
6387c478bd9Sstevel@tonic-gate PCI_MSI_64BIT_PENDING : PCI_MSI_32BIT_PENDING;
6397c478bd9Sstevel@tonic-gate
64027255037Spjha if ((pending_bits = PCI_CAP_GET32(cfg_hdle, NULL, caps_ptr,
641dc5d169bSpjha offset)) == PCI_CAP_EINVAL32)
64227255037Spjha goto done;
6437c478bd9Sstevel@tonic-gate
6447c478bd9Sstevel@tonic-gate *pendingp = pending_bits & ~(1 >> inum);
6457c478bd9Sstevel@tonic-gate
6467c478bd9Sstevel@tonic-gate } else if (type == DDI_INTR_TYPE_MSIX) {
647abdbd06dSagiri uintptr_t off;
6487c478bd9Sstevel@tonic-gate uint64_t pending_bits;
6497c478bd9Sstevel@tonic-gate ddi_intr_msix_t *msix_p = i_ddi_get_msix(rdip);
6507c478bd9Sstevel@tonic-gate
6517c478bd9Sstevel@tonic-gate /* Offset into the PBA array which has entry for "inum" */
6529c75c6bfSgovinda off = (uintptr_t)msix_p->msix_pba_addr + (inum / 64);
6537c478bd9Sstevel@tonic-gate
6547c478bd9Sstevel@tonic-gate /* Read the PBA array */
6559c75c6bfSgovinda pending_bits = ddi_get64(msix_p->msix_pba_hdl, (uint64_t *)off);
6567c478bd9Sstevel@tonic-gate
6577c478bd9Sstevel@tonic-gate *pendingp = pending_bits & ~(1 >> inum);
6587c478bd9Sstevel@tonic-gate }
6597c478bd9Sstevel@tonic-gate
6607c478bd9Sstevel@tonic-gate ret = DDI_SUCCESS;
6617c478bd9Sstevel@tonic-gate done:
6627c478bd9Sstevel@tonic-gate pci_config_teardown(&cfg_hdle);
6637c478bd9Sstevel@tonic-gate return (ret);
6647c478bd9Sstevel@tonic-gate }
6657c478bd9Sstevel@tonic-gate
6667c478bd9Sstevel@tonic-gate
6677c478bd9Sstevel@tonic-gate /*
6687c478bd9Sstevel@tonic-gate * pci_msi_get_nintrs:
6697c478bd9Sstevel@tonic-gate *
6707c478bd9Sstevel@tonic-gate * For a given type (MSI/X) returns the number of interrupts supported
6717c478bd9Sstevel@tonic-gate */
6727c478bd9Sstevel@tonic-gate int
pci_msi_get_nintrs(dev_info_t * rdip,int type,int * nintrs)6737c478bd9Sstevel@tonic-gate pci_msi_get_nintrs(dev_info_t *rdip, int type, int *nintrs)
6747c478bd9Sstevel@tonic-gate {
6757c478bd9Sstevel@tonic-gate ushort_t caps_ptr, msi_ctrl;
6767c478bd9Sstevel@tonic-gate ddi_acc_handle_t cfg_hdle;
6777c478bd9Sstevel@tonic-gate
6787c478bd9Sstevel@tonic-gate DDI_INTR_NEXDBG((CE_CONT, "pci_msi_get_nintrs: rdip = 0x%p\n",
6797c478bd9Sstevel@tonic-gate (void *)rdip));
6807c478bd9Sstevel@tonic-gate
6817c478bd9Sstevel@tonic-gate if (pci_get_msi_ctrl(rdip, type, &msi_ctrl,
6827c478bd9Sstevel@tonic-gate &caps_ptr, &cfg_hdle) != DDI_SUCCESS)
6837c478bd9Sstevel@tonic-gate return (DDI_FAILURE);
6847c478bd9Sstevel@tonic-gate
6857c478bd9Sstevel@tonic-gate if (type == DDI_INTR_TYPE_MSI) {
6867c478bd9Sstevel@tonic-gate *nintrs = 1 << ((msi_ctrl & PCI_MSI_MMC_MASK) >>
6877c478bd9Sstevel@tonic-gate PCI_MSI_MMC_SHIFT);
6887c478bd9Sstevel@tonic-gate } else if (type == DDI_INTR_TYPE_MSIX) {
6897c478bd9Sstevel@tonic-gate if (msi_ctrl & PCI_MSIX_TBL_SIZE_MASK)
6907c478bd9Sstevel@tonic-gate *nintrs = (msi_ctrl & PCI_MSIX_TBL_SIZE_MASK) + 1;
6917c478bd9Sstevel@tonic-gate }
6927c478bd9Sstevel@tonic-gate
6937c478bd9Sstevel@tonic-gate DDI_INTR_NEXDBG((CE_CONT, "pci_msi_get_nintrs: "
6947c478bd9Sstevel@tonic-gate "nintr = 0x%x\n", *nintrs));
6957c478bd9Sstevel@tonic-gate
6967c478bd9Sstevel@tonic-gate pci_config_teardown(&cfg_hdle);
6977c478bd9Sstevel@tonic-gate return (DDI_SUCCESS);
6987c478bd9Sstevel@tonic-gate }
6997c478bd9Sstevel@tonic-gate
7007c478bd9Sstevel@tonic-gate
7017c478bd9Sstevel@tonic-gate /*
7027c478bd9Sstevel@tonic-gate * pci_msi_set_nintrs:
7037c478bd9Sstevel@tonic-gate *
7047c478bd9Sstevel@tonic-gate * For a given type (MSI/X) sets the number of interrupts supported
7057c478bd9Sstevel@tonic-gate * by the system.
7067c478bd9Sstevel@tonic-gate * For MSI: Return an error if this func is called for navail > 32
7077c478bd9Sstevel@tonic-gate * For MSI-X: Return an error if this func is called for navail > 2048
7087c478bd9Sstevel@tonic-gate */
7097c478bd9Sstevel@tonic-gate int
pci_msi_set_nintrs(dev_info_t * rdip,int type,int navail)7107c478bd9Sstevel@tonic-gate pci_msi_set_nintrs(dev_info_t *rdip, int type, int navail)
7117c478bd9Sstevel@tonic-gate {
7127c478bd9Sstevel@tonic-gate ushort_t caps_ptr, msi_ctrl;
7137c478bd9Sstevel@tonic-gate ddi_acc_handle_t cfg_hdle;
7147c478bd9Sstevel@tonic-gate
7157c478bd9Sstevel@tonic-gate DDI_INTR_NEXDBG((CE_CONT, "pci_msi_set_nintrs: rdip = 0x%p, "
7167c478bd9Sstevel@tonic-gate "navail = 0x%x\n", (void *)rdip, navail));
7177c478bd9Sstevel@tonic-gate
7187c478bd9Sstevel@tonic-gate /* Check for valid input argument */
7197c478bd9Sstevel@tonic-gate if (((type == DDI_INTR_TYPE_MSI) && (navail > PCI_MSI_MAX_INTRS)) ||
7207c478bd9Sstevel@tonic-gate ((type == DDI_INTR_TYPE_MSIX) && (navail > PCI_MSIX_MAX_INTRS)))
7217c478bd9Sstevel@tonic-gate return (DDI_EINVAL);
7227c478bd9Sstevel@tonic-gate
7237c478bd9Sstevel@tonic-gate if (pci_get_msi_ctrl(rdip, type, &msi_ctrl,
7247c478bd9Sstevel@tonic-gate &caps_ptr, &cfg_hdle) != DDI_SUCCESS)
7257c478bd9Sstevel@tonic-gate return (DDI_FAILURE);
7267c478bd9Sstevel@tonic-gate
7277c478bd9Sstevel@tonic-gate if (type == DDI_INTR_TYPE_MSI) {
7287c478bd9Sstevel@tonic-gate msi_ctrl |= ((highbit(navail) -1) << PCI_MSI_MME_SHIFT);
7297c478bd9Sstevel@tonic-gate
73027255037Spjha PCI_CAP_PUT16(cfg_hdle, NULL, caps_ptr, PCI_MSI_CTRL, msi_ctrl);
7317c478bd9Sstevel@tonic-gate } else if (type == DDI_INTR_TYPE_MSIX) {
7327c478bd9Sstevel@tonic-gate DDI_INTR_NEXDBG((CE_CONT, "pci_msi_set_nintrs: unsupported\n"));
7337c478bd9Sstevel@tonic-gate }
7347c478bd9Sstevel@tonic-gate
7357c478bd9Sstevel@tonic-gate pci_config_teardown(&cfg_hdle);
7367c478bd9Sstevel@tonic-gate return (DDI_SUCCESS);
7377c478bd9Sstevel@tonic-gate }
7387c478bd9Sstevel@tonic-gate
7397c478bd9Sstevel@tonic-gate
7407c478bd9Sstevel@tonic-gate /*
7417c478bd9Sstevel@tonic-gate * pci_msi_get_supported_type:
7427c478bd9Sstevel@tonic-gate *
7437c478bd9Sstevel@tonic-gate * Returns DDI_INTR_TYPE_MSI and/or DDI_INTR_TYPE_MSIX as supported
7447c478bd9Sstevel@tonic-gate * types if device supports them. A DDI_FAILURE is returned otherwise.
7457c478bd9Sstevel@tonic-gate */
7467c478bd9Sstevel@tonic-gate int
pci_msi_get_supported_type(dev_info_t * rdip,int * typesp)7477c478bd9Sstevel@tonic-gate pci_msi_get_supported_type(dev_info_t *rdip, int *typesp)
7487c478bd9Sstevel@tonic-gate {
7497c478bd9Sstevel@tonic-gate ushort_t caps_ptr, msi_ctrl;
7507c478bd9Sstevel@tonic-gate ddi_acc_handle_t cfg_hdle;
7517c478bd9Sstevel@tonic-gate
7527c478bd9Sstevel@tonic-gate DDI_INTR_NEXDBG((CE_CONT, "pci_msi_get_supported_type: "
7537c478bd9Sstevel@tonic-gate "rdip = 0x%p\n", (void *)rdip));
7547c478bd9Sstevel@tonic-gate
7557c478bd9Sstevel@tonic-gate *typesp = 0;
7567c478bd9Sstevel@tonic-gate
7577c478bd9Sstevel@tonic-gate if (pci_get_msi_ctrl(rdip, DDI_INTR_TYPE_MSI, &msi_ctrl,
7587c478bd9Sstevel@tonic-gate &caps_ptr, &cfg_hdle) == DDI_SUCCESS) {
7597c478bd9Sstevel@tonic-gate *typesp |= DDI_INTR_TYPE_MSI;
7607c478bd9Sstevel@tonic-gate pci_config_teardown(&cfg_hdle);
7617c478bd9Sstevel@tonic-gate }
7627c478bd9Sstevel@tonic-gate
7637c478bd9Sstevel@tonic-gate if (pci_get_msi_ctrl(rdip, DDI_INTR_TYPE_MSIX, &msi_ctrl,
7647c478bd9Sstevel@tonic-gate &caps_ptr, &cfg_hdle) == DDI_SUCCESS) {
7657c478bd9Sstevel@tonic-gate *typesp |= DDI_INTR_TYPE_MSIX;
7667c478bd9Sstevel@tonic-gate pci_config_teardown(&cfg_hdle);
7677c478bd9Sstevel@tonic-gate }
7687c478bd9Sstevel@tonic-gate
7697c478bd9Sstevel@tonic-gate DDI_INTR_NEXDBG((CE_CONT, "pci_msi_get_supported_type: "
7707c478bd9Sstevel@tonic-gate "rdip = 0x%p types 0x%x\n", (void *)rdip, *typesp));
7717c478bd9Sstevel@tonic-gate
7727c478bd9Sstevel@tonic-gate return (*typesp == 0 ? DDI_FAILURE : DDI_SUCCESS);
7737c478bd9Sstevel@tonic-gate }
7747c478bd9Sstevel@tonic-gate
7757c478bd9Sstevel@tonic-gate
7767c478bd9Sstevel@tonic-gate /*
7777c478bd9Sstevel@tonic-gate * pci_msix_init:
7787c478bd9Sstevel@tonic-gate * This function initializes the various handles/addrs etc.
7797c478bd9Sstevel@tonic-gate * needed for MSI-X support. It also allocates a private
7807c478bd9Sstevel@tonic-gate * structure to keep track of these.
7817c478bd9Sstevel@tonic-gate */
7827c478bd9Sstevel@tonic-gate ddi_intr_msix_t *
pci_msix_init(dev_info_t * rdip)7837c478bd9Sstevel@tonic-gate pci_msix_init(dev_info_t *rdip)
7847c478bd9Sstevel@tonic-gate {
7859c75c6bfSgovinda uint_t rnumber, breg, nregs;
7867c478bd9Sstevel@tonic-gate size_t msix_tbl_size;
7877c478bd9Sstevel@tonic-gate size_t pba_tbl_size;
7889c75c6bfSgovinda ushort_t caps_ptr, msix_ctrl;
7897c478bd9Sstevel@tonic-gate ddi_intr_msix_t *msix_p;
7907c478bd9Sstevel@tonic-gate ddi_acc_handle_t cfg_hdle;
7919c75c6bfSgovinda pci_regspec_t *rp;
7929c75c6bfSgovinda int reg_size, addr_space, offset, *regs_list;
7939c75c6bfSgovinda int i, ret;
7947c478bd9Sstevel@tonic-gate
7957c478bd9Sstevel@tonic-gate DDI_INTR_NEXDBG((CE_CONT, "pci_msix_init: rdip = %p\n", (void *)rdip));
7967c478bd9Sstevel@tonic-gate
7979c75c6bfSgovinda if (pci_get_msi_ctrl(rdip, DDI_INTR_TYPE_MSIX, &msix_ctrl,
7987c478bd9Sstevel@tonic-gate &caps_ptr, &cfg_hdle) != DDI_SUCCESS)
7997c478bd9Sstevel@tonic-gate return (NULL);
8007c478bd9Sstevel@tonic-gate
8017c478bd9Sstevel@tonic-gate msix_p = kmem_zalloc(sizeof (ddi_intr_msix_t), KM_SLEEP);
8027c478bd9Sstevel@tonic-gate
8037c478bd9Sstevel@tonic-gate /*
8047c478bd9Sstevel@tonic-gate * Initialize the devacc structure
8057c478bd9Sstevel@tonic-gate */
8067c478bd9Sstevel@tonic-gate msix_p->msix_dev_attr.devacc_attr_version = DDI_DEVICE_ATTR_V0;
8077c478bd9Sstevel@tonic-gate msix_p->msix_dev_attr.devacc_attr_endian_flags =
8087c478bd9Sstevel@tonic-gate DDI_STRUCTURE_LE_ACC;
8097c478bd9Sstevel@tonic-gate msix_p->msix_dev_attr.devacc_attr_dataorder = DDI_STRICTORDER_ACC;
8107c478bd9Sstevel@tonic-gate
8119c75c6bfSgovinda /* Map the entire MSI-X vector table */
81227255037Spjha msix_p->msix_tbl_offset = PCI_CAP_GET32(cfg_hdle, NULL, caps_ptr,
81327255037Spjha PCI_MSIX_TBL_OFFSET);
8147c478bd9Sstevel@tonic-gate
8159c75c6bfSgovinda if ((breg = pci_msix_bir_index[msix_p->msix_tbl_offset &
8169c75c6bfSgovinda PCI_MSIX_TBL_BIR_MASK]) == 0xff)
8179c75c6bfSgovinda goto fail1;
8187c478bd9Sstevel@tonic-gate
8199c75c6bfSgovinda msix_p->msix_tbl_offset = msix_p->msix_tbl_offset &
8209c75c6bfSgovinda ~PCI_MSIX_TBL_BIR_MASK;
8219c75c6bfSgovinda msix_tbl_size = ((msix_ctrl & PCI_MSIX_TBL_SIZE_MASK) + 1) *
8229c75c6bfSgovinda PCI_MSIX_VECTOR_SIZE;
8239c75c6bfSgovinda
8249c75c6bfSgovinda DDI_INTR_NEXDBG((CE_CONT, "pci_msix_init: MSI-X table offset 0x%x "
8259c75c6bfSgovinda "breg 0x%x size 0x%lx\n", msix_p->msix_tbl_offset, breg,
8269c75c6bfSgovinda msix_tbl_size));
8279c75c6bfSgovinda
8289c75c6bfSgovinda if ((ret = ddi_prop_lookup_int_array(DDI_DEV_T_ANY, rdip,
8299c75c6bfSgovinda DDI_PROP_DONTPASS, "reg", (int **)®s_list, &nregs))
8309c75c6bfSgovinda != DDI_PROP_SUCCESS) {
8319c75c6bfSgovinda DDI_INTR_NEXDBG((CE_CONT, "pci_msix_init: "
8329c75c6bfSgovinda "ddi_prop_lookup_int_array failed %d\n", ret));
8339c75c6bfSgovinda
8349c75c6bfSgovinda goto fail1;
8359c75c6bfSgovinda }
8369c75c6bfSgovinda
8379c75c6bfSgovinda reg_size = sizeof (pci_regspec_t) / sizeof (int);
8389c75c6bfSgovinda
8399c75c6bfSgovinda for (i = 1, rnumber = 0; i < nregs/reg_size; i++) {
8409c75c6bfSgovinda rp = (pci_regspec_t *)®s_list[i * reg_size];
8419c75c6bfSgovinda addr_space = rp->pci_phys_hi & PCI_ADDR_MASK;
8429c75c6bfSgovinda offset = PCI_REG_REG_G(rp->pci_phys_hi);
8439c75c6bfSgovinda
8449c75c6bfSgovinda if ((offset == breg) && ((addr_space == PCI_ADDR_MEM32) ||
8459c75c6bfSgovinda (addr_space == PCI_ADDR_MEM64))) {
8469c75c6bfSgovinda rnumber = i;
8479c75c6bfSgovinda break;
8489c75c6bfSgovinda }
8499c75c6bfSgovinda }
8509c75c6bfSgovinda
8519c75c6bfSgovinda DDI_INTR_NEXDBG((CE_CONT, "pci_msix_init: MSI-X rnum = %d\n", rnumber));
8529c75c6bfSgovinda
8539c75c6bfSgovinda if (rnumber == 0) {
8549c75c6bfSgovinda DDI_INTR_NEXDBG((CE_CONT, "pci_msix_init: "
8559c75c6bfSgovinda "no mtaching reg number for offset 0x%x\n", breg));
8569c75c6bfSgovinda
8579c75c6bfSgovinda goto fail2;
8589c75c6bfSgovinda }
8599c75c6bfSgovinda
8609c75c6bfSgovinda if ((ret = ddi_regs_map_setup(rdip, rnumber,
8619c75c6bfSgovinda (caddr_t *)&msix_p->msix_tbl_addr, msix_p->msix_tbl_offset,
8629c75c6bfSgovinda msix_tbl_size, &msix_p->msix_dev_attr,
8639c75c6bfSgovinda &msix_p->msix_tbl_hdl)) != DDI_SUCCESS) {
8649c75c6bfSgovinda DDI_INTR_NEXDBG((CE_CONT, "pci_msix_init: MSI-X Table "
8659c75c6bfSgovinda "ddi_regs_map_setup failed %d\n", ret));
8669c75c6bfSgovinda
8679c75c6bfSgovinda goto fail2;
8687c478bd9Sstevel@tonic-gate }
8697c478bd9Sstevel@tonic-gate
8707c478bd9Sstevel@tonic-gate /*
8717c478bd9Sstevel@tonic-gate * Map in the MSI-X Pending Bit Array
8727c478bd9Sstevel@tonic-gate */
87327255037Spjha msix_p->msix_pba_offset = PCI_CAP_GET32(cfg_hdle, NULL, caps_ptr,
87427255037Spjha PCI_MSIX_PBA_OFFSET);
8757c478bd9Sstevel@tonic-gate
8769c75c6bfSgovinda if ((breg = pci_msix_bir_index[msix_p->msix_pba_offset &
8779c75c6bfSgovinda PCI_MSIX_PBA_BIR_MASK]) == 0xff)
8789c75c6bfSgovinda goto fail3;
8799c75c6bfSgovinda
8809c75c6bfSgovinda msix_p->msix_pba_offset = msix_p->msix_pba_offset &
8819c75c6bfSgovinda ~PCI_MSIX_PBA_BIR_MASK;
8829c75c6bfSgovinda pba_tbl_size = ((msix_ctrl & PCI_MSIX_TBL_SIZE_MASK) + 1)/8;
8839c75c6bfSgovinda
8849c75c6bfSgovinda DDI_INTR_NEXDBG((CE_CONT, "pci_msix_init: PBA table offset 0x%x "
8859c75c6bfSgovinda "breg 0x%x size 0x%lx\n", msix_p->msix_pba_offset, breg,
8869c75c6bfSgovinda pba_tbl_size));
8879c75c6bfSgovinda
8889c75c6bfSgovinda for (i = 1, rnumber = 0; i < nregs/reg_size; i++) {
8899c75c6bfSgovinda rp = (pci_regspec_t *)®s_list[i * reg_size];
8909c75c6bfSgovinda addr_space = rp->pci_phys_hi & PCI_ADDR_MASK;
8919c75c6bfSgovinda offset = PCI_REG_REG_G(rp->pci_phys_hi);
8929c75c6bfSgovinda
8939c75c6bfSgovinda if ((offset == breg) && ((addr_space == PCI_ADDR_MEM32) ||
8949c75c6bfSgovinda (addr_space == PCI_ADDR_MEM64))) {
8959c75c6bfSgovinda rnumber = i;
8969c75c6bfSgovinda break;
8979c75c6bfSgovinda }
8989c75c6bfSgovinda }
8999c75c6bfSgovinda
9009c75c6bfSgovinda DDI_INTR_NEXDBG((CE_CONT, "pci_msix_init: PBA rnum = %d\n", rnumber));
9019c75c6bfSgovinda
9029c75c6bfSgovinda if (rnumber == 0) {
9039c75c6bfSgovinda DDI_INTR_NEXDBG((CE_CONT, "pci_msix_init: "
90420036fe5Segillett "no matching reg number for offset 0x%x\n", breg));
9059c75c6bfSgovinda
9069c75c6bfSgovinda goto fail3;
9079c75c6bfSgovinda }
9089c75c6bfSgovinda
9099c75c6bfSgovinda if ((ret = ddi_regs_map_setup(rdip, rnumber,
9109c75c6bfSgovinda (caddr_t *)&msix_p->msix_pba_addr, msix_p->msix_pba_offset,
9119c75c6bfSgovinda pba_tbl_size, &msix_p->msix_dev_attr,
9129c75c6bfSgovinda &msix_p->msix_pba_hdl)) != DDI_SUCCESS) {
9139c75c6bfSgovinda DDI_INTR_NEXDBG((CE_CONT, "pci_msix_init: PBA "
9149c75c6bfSgovinda "ddi_regs_map_setup failed %d\n", ret));
9159c75c6bfSgovinda
9169c75c6bfSgovinda goto fail3;
9177c478bd9Sstevel@tonic-gate }
9187c478bd9Sstevel@tonic-gate
9197c478bd9Sstevel@tonic-gate DDI_INTR_NEXDBG((CE_CONT, "pci_msix_init: msix_p = 0x%p DONE!!\n",
9207c478bd9Sstevel@tonic-gate (void *)msix_p));
9217c478bd9Sstevel@tonic-gate
922a7639048Sjohnny ddi_prop_free(regs_list);
9239c75c6bfSgovinda goto done;
9249c75c6bfSgovinda
9259c75c6bfSgovinda fail3:
9269c75c6bfSgovinda ddi_regs_map_free(&msix_p->msix_tbl_hdl);
9279c75c6bfSgovinda fail2:
9289c75c6bfSgovinda ddi_prop_free(regs_list);
9299c75c6bfSgovinda fail1:
9309c75c6bfSgovinda kmem_free(msix_p, sizeof (ddi_intr_msix_t));
9319c75c6bfSgovinda msix_p = NULL;
9329c75c6bfSgovinda done:
9337c478bd9Sstevel@tonic-gate pci_config_teardown(&cfg_hdle);
9347c478bd9Sstevel@tonic-gate return (msix_p);
9357c478bd9Sstevel@tonic-gate }
9367c478bd9Sstevel@tonic-gate
9377c478bd9Sstevel@tonic-gate
9387c478bd9Sstevel@tonic-gate /*
9397c478bd9Sstevel@tonic-gate * pci_msix_fini:
9407c478bd9Sstevel@tonic-gate * This function cleans up previously allocated handles/addrs etc.
9417c478bd9Sstevel@tonic-gate * It is only called if no more MSI-X interrupts are being used.
9427c478bd9Sstevel@tonic-gate */
9437c478bd9Sstevel@tonic-gate void
pci_msix_fini(ddi_intr_msix_t * msix_p)9447c478bd9Sstevel@tonic-gate pci_msix_fini(ddi_intr_msix_t *msix_p)
9457c478bd9Sstevel@tonic-gate {
9467c478bd9Sstevel@tonic-gate DDI_INTR_NEXDBG((CE_CONT, "pci_msix_fini: msix_p = 0x%p\n",
9477c478bd9Sstevel@tonic-gate (void *)msix_p));
9487c478bd9Sstevel@tonic-gate
9497c478bd9Sstevel@tonic-gate ddi_regs_map_free(&msix_p->msix_pba_hdl);
9507c478bd9Sstevel@tonic-gate ddi_regs_map_free(&msix_p->msix_tbl_hdl);
9517c478bd9Sstevel@tonic-gate kmem_free(msix_p, sizeof (ddi_intr_msix_t));
9527c478bd9Sstevel@tonic-gate }
9537c478bd9Sstevel@tonic-gate
9547c478bd9Sstevel@tonic-gate
95520036fe5Segillett /*
95620036fe5Segillett * pci_msix_dup:
95720036fe5Segillett * This function duplicates the address and data pair of one msi-x
95820036fe5Segillett * vector to another msi-x vector.
95920036fe5Segillett */
96020036fe5Segillett int
pci_msix_dup(dev_info_t * rdip,int org_inum,int dup_inum)96120036fe5Segillett pci_msix_dup(dev_info_t *rdip, int org_inum, int dup_inum)
96220036fe5Segillett {
96320036fe5Segillett ddi_intr_msix_t *msix_p = i_ddi_get_msix(rdip);
96420036fe5Segillett uint64_t addr;
96520036fe5Segillett uint64_t data;
96620036fe5Segillett uintptr_t off;
96720036fe5Segillett
96820036fe5Segillett DDI_INTR_NEXDBG((CE_CONT, "pci_msix_dup: dip = %p, inum = 0x%x, "
96920036fe5Segillett "to_vector = 0x%x\n", (void *)rdip, org_inum, dup_inum));
97020036fe5Segillett
97120036fe5Segillett /* Offset into the original inum's entry in the MSI-X table */
97220036fe5Segillett off = (uintptr_t)msix_p->msix_tbl_addr +
97320036fe5Segillett (org_inum * PCI_MSIX_VECTOR_SIZE);
97420036fe5Segillett
975*f8c90ccaSGarrett D'Amore /*
976*f8c90ccaSGarrett D'Amore * For the MSI-X number passed in, get the "data" and "addr" fields.
977*f8c90ccaSGarrett D'Amore *
978*f8c90ccaSGarrett D'Amore * Note that the spec only requires 32-bit accesses to be supported.
979*f8c90ccaSGarrett D'Amore * Apparently some chipsets don't support 64-bit accesses.
980*f8c90ccaSGarrett D'Amore */
981*f8c90ccaSGarrett D'Amore addr = ddi_get32(msix_p->msix_tbl_hdl,
982*f8c90ccaSGarrett D'Amore (uint32_t *)(off + PCI_MSIX_UPPER_ADDR_OFFSET));
983*f8c90ccaSGarrett D'Amore addr = (addr << 32) | ddi_get32(msix_p->msix_tbl_hdl,
984*f8c90ccaSGarrett D'Amore (uint32_t *)(off + PCI_MSIX_LOWER_ADDR_OFFSET));
98520036fe5Segillett
98620036fe5Segillett data = ddi_get32(msix_p->msix_tbl_hdl,
98720036fe5Segillett (uint32_t *)(off + PCI_MSIX_DATA_OFFSET));
98820036fe5Segillett
98920036fe5Segillett /* Program new vector with these existing values */
99020036fe5Segillett return (pci_msi_configure(rdip, DDI_INTR_TYPE_MSIX, 1, dup_inum, addr,
99120036fe5Segillett data));
99220036fe5Segillett }
99320036fe5Segillett
9947c478bd9Sstevel@tonic-gate
9957c478bd9Sstevel@tonic-gate /*
9967c478bd9Sstevel@tonic-gate * Next set of routines are for INTx (legacy) PCI interrupt
9977c478bd9Sstevel@tonic-gate * support only.
9987c478bd9Sstevel@tonic-gate */
9997c478bd9Sstevel@tonic-gate
10007c478bd9Sstevel@tonic-gate /*
10017c478bd9Sstevel@tonic-gate * pci_intx_get_cap:
10027c478bd9Sstevel@tonic-gate * For non-MSI devices that comply to PCI v2.3 or greater;
10037c478bd9Sstevel@tonic-gate * read the command register. Bit 10 implies interrupt disable.
10047c478bd9Sstevel@tonic-gate * Set this bit and then read the status register bit 3.
10057c478bd9Sstevel@tonic-gate * Bit 3 of status register is Interrupt state.
10067c478bd9Sstevel@tonic-gate * If it is set; then the device supports 'Masking'
10077c478bd9Sstevel@tonic-gate *
10087c478bd9Sstevel@tonic-gate * Reset the device back to the original state.
10097c478bd9Sstevel@tonic-gate */
10107c478bd9Sstevel@tonic-gate int
pci_intx_get_cap(dev_info_t * dip,int * flagsp)10117c478bd9Sstevel@tonic-gate pci_intx_get_cap(dev_info_t *dip, int *flagsp)
10127c478bd9Sstevel@tonic-gate {
10137c478bd9Sstevel@tonic-gate uint16_t cmdreg, savereg;
10147c478bd9Sstevel@tonic-gate ddi_acc_handle_t cfg_hdl;
10157c478bd9Sstevel@tonic-gate #ifdef DEBUG
10167c478bd9Sstevel@tonic-gate uint16_t statreg;
10177c478bd9Sstevel@tonic-gate #endif /* DEBUG */
10187c478bd9Sstevel@tonic-gate
10197c478bd9Sstevel@tonic-gate *flagsp = 0;
10207c478bd9Sstevel@tonic-gate DDI_INTR_NEXDBG((CE_CONT, "pci_intx_get_cap: %s%d: called\n",
10217c478bd9Sstevel@tonic-gate ddi_driver_name(dip), ddi_get_instance(dip)));
10227c478bd9Sstevel@tonic-gate
10237c478bd9Sstevel@tonic-gate if (pci_config_setup(dip, &cfg_hdl) != DDI_SUCCESS) {
10247c478bd9Sstevel@tonic-gate DDI_INTR_NEXDBG((CE_CONT, "pci_intx_get_cap: can't get "
10257c478bd9Sstevel@tonic-gate "config handle\n"));
10267c478bd9Sstevel@tonic-gate return (DDI_FAILURE);
10277c478bd9Sstevel@tonic-gate }
10287c478bd9Sstevel@tonic-gate
10297c478bd9Sstevel@tonic-gate savereg = pci_config_get16(cfg_hdl, PCI_CONF_COMM);
10307c478bd9Sstevel@tonic-gate DDI_INTR_NEXDBG((CE_CONT, "pci_intx_get_cap: "
10317c478bd9Sstevel@tonic-gate "command register was 0x%x\n", savereg));
10327c478bd9Sstevel@tonic-gate
10337c478bd9Sstevel@tonic-gate /* Disable the interrupts */
10347c478bd9Sstevel@tonic-gate cmdreg = savereg | PCI_COMM_INTX_DISABLE;
10357c478bd9Sstevel@tonic-gate pci_config_put16(cfg_hdl, PCI_CONF_COMM, cmdreg);
10367c478bd9Sstevel@tonic-gate
10377c478bd9Sstevel@tonic-gate #ifdef DEBUG
10387c478bd9Sstevel@tonic-gate statreg = pci_config_get16(cfg_hdl, PCI_CONF_STAT);
10397c478bd9Sstevel@tonic-gate DDI_INTR_NEXDBG((CE_CONT, "pci_intx_get_cap: "
10407c478bd9Sstevel@tonic-gate "status register is 0x%x\n", statreg));
10417c478bd9Sstevel@tonic-gate #endif /* DEBUG */
10427c478bd9Sstevel@tonic-gate
10437c478bd9Sstevel@tonic-gate /* Read the bit back */
10447c478bd9Sstevel@tonic-gate cmdreg = pci_config_get16(cfg_hdl, PCI_CONF_COMM);
10457c478bd9Sstevel@tonic-gate DDI_INTR_NEXDBG((CE_CONT, "pci_intx_get_cap: "
10467c478bd9Sstevel@tonic-gate "command register is now 0x%x\n", cmdreg));
10477c478bd9Sstevel@tonic-gate
1048a195726fSgovinda *flagsp = DDI_INTR_FLAG_LEVEL;
1049a195726fSgovinda
10507c478bd9Sstevel@tonic-gate if (cmdreg & PCI_COMM_INTX_DISABLE) {
10517c478bd9Sstevel@tonic-gate DDI_INTR_NEXDBG((CE_CONT, "pci_intx_get_cap: "
10527c478bd9Sstevel@tonic-gate "masking supported\n"));
1053a195726fSgovinda *flagsp |= (DDI_INTR_FLAG_MASKABLE |
1054a195726fSgovinda DDI_INTR_FLAG_PENDING);
10557c478bd9Sstevel@tonic-gate }
10567c478bd9Sstevel@tonic-gate
10577c478bd9Sstevel@tonic-gate /* Restore the device back to the original state and return */
10587c478bd9Sstevel@tonic-gate pci_config_put16(cfg_hdl, PCI_CONF_COMM, savereg);
10597c478bd9Sstevel@tonic-gate
10607c478bd9Sstevel@tonic-gate pci_config_teardown(&cfg_hdl);
10617c478bd9Sstevel@tonic-gate return (DDI_SUCCESS);
10627c478bd9Sstevel@tonic-gate }
10637c478bd9Sstevel@tonic-gate
10647c478bd9Sstevel@tonic-gate
10657c478bd9Sstevel@tonic-gate /*
10667c478bd9Sstevel@tonic-gate * pci_intx_clr_mask:
10677c478bd9Sstevel@tonic-gate * For non-MSI devices that comply to PCI v2.3 or greater;
10687c478bd9Sstevel@tonic-gate * clear the bit10 in the command register.
10697c478bd9Sstevel@tonic-gate */
10707c478bd9Sstevel@tonic-gate int
pci_intx_clr_mask(dev_info_t * dip)10717c478bd9Sstevel@tonic-gate pci_intx_clr_mask(dev_info_t *dip)
10727c478bd9Sstevel@tonic-gate {
10737c478bd9Sstevel@tonic-gate uint16_t cmdreg;
10747c478bd9Sstevel@tonic-gate ddi_acc_handle_t cfg_hdl;
10757c478bd9Sstevel@tonic-gate
10767c478bd9Sstevel@tonic-gate DDI_INTR_NEXDBG((CE_CONT, "pci_intx_clr_mask: %s%d: called\n",
10777c478bd9Sstevel@tonic-gate ddi_driver_name(dip), ddi_get_instance(dip)));
10787c478bd9Sstevel@tonic-gate
10797c478bd9Sstevel@tonic-gate if (pci_config_setup(dip, &cfg_hdl) != DDI_SUCCESS) {
10807c478bd9Sstevel@tonic-gate DDI_INTR_NEXDBG((CE_CONT, "pci_intx_clr_mask: can't get "
10817c478bd9Sstevel@tonic-gate "config handle\n"));
10827c478bd9Sstevel@tonic-gate return (DDI_FAILURE);
10837c478bd9Sstevel@tonic-gate }
10847c478bd9Sstevel@tonic-gate
10857c478bd9Sstevel@tonic-gate cmdreg = pci_config_get16(cfg_hdl, PCI_CONF_COMM);
10867c478bd9Sstevel@tonic-gate DDI_INTR_NEXDBG((CE_CONT, "pci_intx_clr_mask: "
10877c478bd9Sstevel@tonic-gate "command register was 0x%x\n", cmdreg));
10887c478bd9Sstevel@tonic-gate
10897c478bd9Sstevel@tonic-gate /* Enable the interrupts */
10907c478bd9Sstevel@tonic-gate cmdreg &= ~PCI_COMM_INTX_DISABLE;
10917c478bd9Sstevel@tonic-gate pci_config_put16(cfg_hdl, PCI_CONF_COMM, cmdreg);
10927c478bd9Sstevel@tonic-gate pci_config_teardown(&cfg_hdl);
10937c478bd9Sstevel@tonic-gate return (DDI_SUCCESS);
10947c478bd9Sstevel@tonic-gate }
10957c478bd9Sstevel@tonic-gate
10967c478bd9Sstevel@tonic-gate
10977c478bd9Sstevel@tonic-gate /*
10987c478bd9Sstevel@tonic-gate * pci_intx_set_mask:
10997c478bd9Sstevel@tonic-gate * For non-MSI devices that comply to PCI v2.3 or greater;
11007c478bd9Sstevel@tonic-gate * set the bit10 in the command register.
11017c478bd9Sstevel@tonic-gate */
11027c478bd9Sstevel@tonic-gate int
pci_intx_set_mask(dev_info_t * dip)11037c478bd9Sstevel@tonic-gate pci_intx_set_mask(dev_info_t *dip)
11047c478bd9Sstevel@tonic-gate {
11057c478bd9Sstevel@tonic-gate uint16_t cmdreg;
11067c478bd9Sstevel@tonic-gate ddi_acc_handle_t cfg_hdl;
11077c478bd9Sstevel@tonic-gate
11087c478bd9Sstevel@tonic-gate DDI_INTR_NEXDBG((CE_CONT, "pci_intx_set_mask: %s%d: called\n",
11097c478bd9Sstevel@tonic-gate ddi_driver_name(dip), ddi_get_instance(dip)));
11107c478bd9Sstevel@tonic-gate
11117c478bd9Sstevel@tonic-gate if (pci_config_setup(dip, &cfg_hdl) != DDI_SUCCESS) {
11127c478bd9Sstevel@tonic-gate DDI_INTR_NEXDBG((CE_CONT, "pci_intx_set_mask: can't get "
11137c478bd9Sstevel@tonic-gate "config handle\n"));
11147c478bd9Sstevel@tonic-gate return (DDI_FAILURE);
11157c478bd9Sstevel@tonic-gate }
11167c478bd9Sstevel@tonic-gate
11177c478bd9Sstevel@tonic-gate cmdreg = pci_config_get16(cfg_hdl, PCI_CONF_COMM);
11187c478bd9Sstevel@tonic-gate DDI_INTR_NEXDBG((CE_CONT, "pci_intx_set_mask: "
11197c478bd9Sstevel@tonic-gate "command register was 0x%x\n", cmdreg));
11207c478bd9Sstevel@tonic-gate
11217c478bd9Sstevel@tonic-gate /* Disable the interrupts */
11227c478bd9Sstevel@tonic-gate cmdreg |= PCI_COMM_INTX_DISABLE;
11237c478bd9Sstevel@tonic-gate pci_config_put16(cfg_hdl, PCI_CONF_COMM, cmdreg);
11247c478bd9Sstevel@tonic-gate pci_config_teardown(&cfg_hdl);
11257c478bd9Sstevel@tonic-gate return (DDI_SUCCESS);
11267c478bd9Sstevel@tonic-gate }
11277c478bd9Sstevel@tonic-gate
11287c478bd9Sstevel@tonic-gate /*
11297c478bd9Sstevel@tonic-gate * pci_intx_get_pending:
11307c478bd9Sstevel@tonic-gate * For non-MSI devices that comply to PCI v2.3 or greater;
11317c478bd9Sstevel@tonic-gate * read the status register. Bit 3 of status register is
11327c478bd9Sstevel@tonic-gate * Interrupt state. If it is set; then the interrupt is
11337c478bd9Sstevel@tonic-gate * 'Pending'.
11347c478bd9Sstevel@tonic-gate */
11357c478bd9Sstevel@tonic-gate int
pci_intx_get_pending(dev_info_t * dip,int * pendingp)11367c478bd9Sstevel@tonic-gate pci_intx_get_pending(dev_info_t *dip, int *pendingp)
11377c478bd9Sstevel@tonic-gate {
11387c478bd9Sstevel@tonic-gate uint16_t statreg;
11397c478bd9Sstevel@tonic-gate ddi_acc_handle_t cfg_hdl;
11407c478bd9Sstevel@tonic-gate
11417c478bd9Sstevel@tonic-gate *pendingp = 0;
11427c478bd9Sstevel@tonic-gate DDI_INTR_NEXDBG((CE_CONT, "pci_intx_get_pending: %s%d: called\n",
11437c478bd9Sstevel@tonic-gate ddi_driver_name(dip), ddi_get_instance(dip)));
11447c478bd9Sstevel@tonic-gate
11457c478bd9Sstevel@tonic-gate if (pci_config_setup(dip, &cfg_hdl) != DDI_SUCCESS) {
11467c478bd9Sstevel@tonic-gate DDI_INTR_NEXDBG((CE_CONT, "pci_intx_get_pending: can't get "
11477c478bd9Sstevel@tonic-gate "config handle\n"));
11487c478bd9Sstevel@tonic-gate return (DDI_FAILURE);
11497c478bd9Sstevel@tonic-gate }
11507c478bd9Sstevel@tonic-gate
11517c478bd9Sstevel@tonic-gate statreg = pci_config_get16(cfg_hdl, PCI_CONF_STAT);
11527c478bd9Sstevel@tonic-gate
11537c478bd9Sstevel@tonic-gate if (statreg & PCI_STAT_INTR) {
11547c478bd9Sstevel@tonic-gate DDI_INTR_NEXDBG((CE_CONT, "pci_intx_get_pending: "
11557c478bd9Sstevel@tonic-gate "interrupt is pending\n"));
11567c478bd9Sstevel@tonic-gate *pendingp = 1;
11577c478bd9Sstevel@tonic-gate }
11587c478bd9Sstevel@tonic-gate
11597c478bd9Sstevel@tonic-gate pci_config_teardown(&cfg_hdl);
11607c478bd9Sstevel@tonic-gate return (DDI_SUCCESS);
11617c478bd9Sstevel@tonic-gate }
11627c478bd9Sstevel@tonic-gate
11637c478bd9Sstevel@tonic-gate
11647c478bd9Sstevel@tonic-gate /*
11657c478bd9Sstevel@tonic-gate * pci_intx_get_ispec:
11667c478bd9Sstevel@tonic-gate * Get intrspec for PCI devices (legacy support)
11677c478bd9Sstevel@tonic-gate * NOTE: This is moved here from x86 pci.c and is
11687c478bd9Sstevel@tonic-gate * needed here as pci-ide.c uses it as well
11697c478bd9Sstevel@tonic-gate */
11707c478bd9Sstevel@tonic-gate /*ARGSUSED*/
11717c478bd9Sstevel@tonic-gate ddi_intrspec_t
pci_intx_get_ispec(dev_info_t * dip,dev_info_t * rdip,int inum)11727c478bd9Sstevel@tonic-gate pci_intx_get_ispec(dev_info_t *dip, dev_info_t *rdip, int inum)
11737c478bd9Sstevel@tonic-gate {
1174614edcaeSEvan Yan int *intpriorities;
11757c478bd9Sstevel@tonic-gate uint_t num_intpriorities;
11767c478bd9Sstevel@tonic-gate struct intrspec *ispec;
11777c478bd9Sstevel@tonic-gate ddi_acc_handle_t cfg_hdl;
11787c478bd9Sstevel@tonic-gate struct ddi_parent_private_data *pdptr;
11797c478bd9Sstevel@tonic-gate
11807c478bd9Sstevel@tonic-gate if ((pdptr = ddi_get_parent_data(rdip)) == NULL)
11817c478bd9Sstevel@tonic-gate return (NULL);
11827c478bd9Sstevel@tonic-gate
11837c478bd9Sstevel@tonic-gate ispec = pdptr->par_intr;
11847c478bd9Sstevel@tonic-gate ASSERT(ispec);
11857c478bd9Sstevel@tonic-gate
11867c478bd9Sstevel@tonic-gate /* check if the intrspec_pri has been initialized */
11877c478bd9Sstevel@tonic-gate if (!ispec->intrspec_pri) {
11887c478bd9Sstevel@tonic-gate if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, rdip,
11897c478bd9Sstevel@tonic-gate DDI_PROP_DONTPASS, "interrupt-priorities",
11907c478bd9Sstevel@tonic-gate &intpriorities, &num_intpriorities) == DDI_PROP_SUCCESS) {
11917c478bd9Sstevel@tonic-gate if (inum < num_intpriorities)
11927c478bd9Sstevel@tonic-gate ispec->intrspec_pri = intpriorities[inum];
11937c478bd9Sstevel@tonic-gate ddi_prop_free(intpriorities);
11947c478bd9Sstevel@tonic-gate }
11957c478bd9Sstevel@tonic-gate
11967c478bd9Sstevel@tonic-gate /* If still no priority, guess based on the class code */
1197614edcaeSEvan Yan if (ispec->intrspec_pri == 0)
1198614edcaeSEvan Yan ispec->intrspec_pri = pci_class_to_pil(rdip);
11997c478bd9Sstevel@tonic-gate }
12007c478bd9Sstevel@tonic-gate
12017c478bd9Sstevel@tonic-gate /* Get interrupt line value */
12027c478bd9Sstevel@tonic-gate if (!ispec->intrspec_vec) {
12037c478bd9Sstevel@tonic-gate if (pci_config_setup(rdip, &cfg_hdl) != DDI_SUCCESS) {
12047c478bd9Sstevel@tonic-gate DDI_INTR_NEXDBG((CE_CONT, "pci_intx_get_iline: "
12057c478bd9Sstevel@tonic-gate "can't get config handle\n"));
12068d9a0e26Sanish return ((ddi_intrspec_t)ispec);
12077c478bd9Sstevel@tonic-gate }
12087c478bd9Sstevel@tonic-gate
12097c478bd9Sstevel@tonic-gate ispec->intrspec_vec = pci_config_get8(cfg_hdl, PCI_CONF_ILINE);
12107c478bd9Sstevel@tonic-gate pci_config_teardown(&cfg_hdl);
12117c478bd9Sstevel@tonic-gate }
12127c478bd9Sstevel@tonic-gate
12137c478bd9Sstevel@tonic-gate return ((ddi_intrspec_t)ispec);
12147c478bd9Sstevel@tonic-gate }
1215614edcaeSEvan Yan
1216614edcaeSEvan Yan static uint32_t
pci_match_class_val(uint32_t key,pci_class_val_t * rec_p,int nrec,uint32_t default_val)1217614edcaeSEvan Yan pci_match_class_val(uint32_t key, pci_class_val_t *rec_p, int nrec,
1218614edcaeSEvan Yan uint32_t default_val)
1219614edcaeSEvan Yan {
1220614edcaeSEvan Yan int i;
1221614edcaeSEvan Yan
1222614edcaeSEvan Yan for (i = 0; i < nrec; rec_p++, i++) {
1223614edcaeSEvan Yan if ((rec_p->class_code & rec_p->class_mask) ==
1224614edcaeSEvan Yan (key & rec_p->class_mask))
1225614edcaeSEvan Yan return (rec_p->class_val);
1226614edcaeSEvan Yan }
1227614edcaeSEvan Yan
1228614edcaeSEvan Yan return (default_val);
1229614edcaeSEvan Yan }
1230614edcaeSEvan Yan
1231614edcaeSEvan Yan /*
1232614edcaeSEvan Yan * Return the configuration value, based on class code and sub class code,
1233614edcaeSEvan Yan * from the specified property based or default pci_class_val_t table.
1234614edcaeSEvan Yan */
1235614edcaeSEvan Yan uint32_t
pci_class_to_val(dev_info_t * rdip,char * property_name,pci_class_val_t * rec_p,int nrec,uint32_t default_val)1236614edcaeSEvan Yan pci_class_to_val(dev_info_t *rdip, char *property_name, pci_class_val_t *rec_p,
1237614edcaeSEvan Yan int nrec, uint32_t default_val)
1238614edcaeSEvan Yan {
1239614edcaeSEvan Yan int property_len;
1240614edcaeSEvan Yan uint32_t class_code;
1241614edcaeSEvan Yan pci_class_val_t *conf;
1242614edcaeSEvan Yan uint32_t val = default_val;
1243614edcaeSEvan Yan
1244614edcaeSEvan Yan /*
1245614edcaeSEvan Yan * Use the "class-code" property to get the base and sub class
1246614edcaeSEvan Yan * codes for the requesting device.
1247614edcaeSEvan Yan */
1248614edcaeSEvan Yan class_code = (uint32_t)ddi_prop_get_int(DDI_DEV_T_ANY, rdip,
1249614edcaeSEvan Yan DDI_PROP_DONTPASS, "class-code", -1);
1250614edcaeSEvan Yan
1251614edcaeSEvan Yan if (class_code == -1)
1252614edcaeSEvan Yan return (val);
1253614edcaeSEvan Yan
1254614edcaeSEvan Yan /* look up the val from the default table */
1255614edcaeSEvan Yan val = pci_match_class_val(class_code, rec_p, nrec, val);
1256614edcaeSEvan Yan
1257614edcaeSEvan Yan
1258614edcaeSEvan Yan /* see if there is a more specific property specified value */
1259614edcaeSEvan Yan if (ddi_getlongprop(DDI_DEV_T_ANY, rdip, DDI_PROP_NOTPROM,
1260614edcaeSEvan Yan property_name, (caddr_t)&conf, &property_len))
1261614edcaeSEvan Yan return (val);
1262614edcaeSEvan Yan
1263614edcaeSEvan Yan if ((property_len % sizeof (pci_class_val_t)) == 0)
1264614edcaeSEvan Yan val = pci_match_class_val(class_code, conf,
1265614edcaeSEvan Yan property_len / sizeof (pci_class_val_t), val);
1266614edcaeSEvan Yan kmem_free(conf, property_len);
1267614edcaeSEvan Yan return (val);
1268614edcaeSEvan Yan }
1269614edcaeSEvan Yan
1270614edcaeSEvan Yan /*
1271614edcaeSEvan Yan * pci_class_to_pil:
1272614edcaeSEvan Yan *
1273614edcaeSEvan Yan * Return the pil for a given PCI device.
1274614edcaeSEvan Yan */
1275614edcaeSEvan Yan uint32_t
pci_class_to_pil(dev_info_t * rdip)1276614edcaeSEvan Yan pci_class_to_pil(dev_info_t *rdip)
1277614edcaeSEvan Yan {
1278614edcaeSEvan Yan uint32_t pil;
1279614edcaeSEvan Yan
1280614edcaeSEvan Yan /* Default pil is 1 */
1281614edcaeSEvan Yan pil = pci_class_to_val(rdip,
1282614edcaeSEvan Yan "pci-class-priorities", pci_default_pil,
1283614edcaeSEvan Yan sizeof (pci_default_pil) / sizeof (pci_class_val_t), 1);
1284614edcaeSEvan Yan
1285614edcaeSEvan Yan /* Range check the result */
1286614edcaeSEvan Yan if (pil >= 0xf)
1287614edcaeSEvan Yan pil = 1;
1288614edcaeSEvan Yan
1289614edcaeSEvan Yan return (pil);
1290614edcaeSEvan Yan }
1291614edcaeSEvan Yan
1292614edcaeSEvan Yan /*
1293614edcaeSEvan Yan * pci_class_to_intr_weight:
1294614edcaeSEvan Yan *
1295614edcaeSEvan Yan * Return the intr_weight for a given PCI device.
1296614edcaeSEvan Yan */
1297614edcaeSEvan Yan int32_t
pci_class_to_intr_weight(dev_info_t * rdip)1298614edcaeSEvan Yan pci_class_to_intr_weight(dev_info_t *rdip)
1299614edcaeSEvan Yan {
1300614edcaeSEvan Yan int32_t intr_weight;
1301614edcaeSEvan Yan
1302614edcaeSEvan Yan /* default weight is 0% */
1303614edcaeSEvan Yan intr_weight = pci_class_to_val(rdip,
1304614edcaeSEvan Yan "pci-class-intr-weights", pci_default_intr_weight,
1305614edcaeSEvan Yan sizeof (pci_default_intr_weight) / sizeof (pci_class_val_t), 0);
1306614edcaeSEvan Yan
1307614edcaeSEvan Yan /* range check the result */
1308614edcaeSEvan Yan if (intr_weight < 0)
1309614edcaeSEvan Yan intr_weight = 0;
1310614edcaeSEvan Yan if (intr_weight > 1000)
1311614edcaeSEvan Yan intr_weight = 1000;
1312614edcaeSEvan Yan
1313614edcaeSEvan Yan return (intr_weight);
1314614edcaeSEvan Yan }
1315