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*8181b438SGarrett 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 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 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 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*8181b438SGarrett D'Amore /* 263*8181b438SGarrett D'Amore * Note that the spec only requires 32-bit accesses 264*8181b438SGarrett D'Amore * to be supported. Apparently some chipsets don't 265*8181b438SGarrett D'Amore * support 64-bit accesses. 266*8181b438SGarrett D'Amore */ 267*8181b438SGarrett D'Amore ddi_put32(msix_p->msix_tbl_hdl, 268*8181b438SGarrett D'Amore (uint32_t *)(off + PCI_MSIX_LOWER_ADDR_OFFSET), addr); 269*8181b438SGarrett D'Amore ddi_put32(msix_p->msix_tbl_hdl, 270*8181b438SGarrett D'Amore (uint32_t *)(off + PCI_MSIX_UPPER_ADDR_OFFSET), 271*8181b438SGarrett D'Amore addr >> 32); 2729c75c6bfSgovinda 2739c75c6bfSgovinda DDI_INTR_NEXDBG((CE_CONT, "pci_msi_configure: " 274*8181b438SGarrett D'Amore "msix_addr 0x%x.%x msix_data 0x%x\n", 275*8181b438SGarrett D'Amore ddi_get32(msix_p->msix_tbl_hdl, 276*8181b438SGarrett D'Amore (uint32_t *)(off + PCI_MSIX_UPPER_ADDR_OFFSET)), 277*8181b438SGarrett D'Amore ddi_get32(msix_p->msix_tbl_hdl, 278*8181b438SGarrett 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 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*8181b438SGarrett D'Amore /* 340*8181b438SGarrett D'Amore * Note that the spec only requires 32-bit accesses 341*8181b438SGarrett D'Amore * to be supported. Apparently some chipsets don't 342*8181b438SGarrett D'Amore * support 64-bit accesses. 343*8181b438SGarrett D'Amore */ 344*8181b438SGarrett D'Amore ddi_put32(msix_p->msix_tbl_hdl, 345*8181b438SGarrett D'Amore (uint32_t *)(off + PCI_MSIX_LOWER_ADDR_OFFSET), 0); 346*8181b438SGarrett D'Amore ddi_put32(msix_p->msix_tbl_hdl, 347*8181b438SGarrett 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 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 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 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 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 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 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 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 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 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 * 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 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 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*8181b438SGarrett D'Amore /* 976*8181b438SGarrett D'Amore * For the MSI-X number passed in, get the "data" and "addr" fields. 977*8181b438SGarrett D'Amore * 978*8181b438SGarrett D'Amore * Note that the spec only requires 32-bit accesses to be supported. 979*8181b438SGarrett D'Amore * Apparently some chipsets don't support 64-bit accesses. 980*8181b438SGarrett D'Amore */ 981*8181b438SGarrett D'Amore addr = ddi_get32(msix_p->msix_tbl_hdl, 982*8181b438SGarrett D'Amore (uint32_t *)(off + PCI_MSIX_UPPER_ADDR_OFFSET)); 983*8181b438SGarrett D'Amore addr = (addr << 32) | ddi_get32(msix_p->msix_tbl_hdl, 984*8181b438SGarrett 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 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 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 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 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 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 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 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 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 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