10Sstevel@tonic-gate /* 20Sstevel@tonic-gate * CDDL HEADER START 30Sstevel@tonic-gate * 40Sstevel@tonic-gate * The contents of this file are subject to the terms of the 51624Spjha * Common Development and Distribution License (the "License"). 61624Spjha * You may not use this file except in compliance with the License. 70Sstevel@tonic-gate * 80Sstevel@tonic-gate * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 90Sstevel@tonic-gate * or http://www.opensolaris.org/os/licensing. 100Sstevel@tonic-gate * See the License for the specific language governing permissions 110Sstevel@tonic-gate * and limitations under the License. 120Sstevel@tonic-gate * 130Sstevel@tonic-gate * When distributing Covered Code, include this CDDL HEADER in each 140Sstevel@tonic-gate * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 150Sstevel@tonic-gate * If applicable, add the following below this CDDL HEADER, with the 160Sstevel@tonic-gate * fields enclosed by brackets "[]" replaced with your own identifying 170Sstevel@tonic-gate * information: Portions Copyright [yyyy] [name of copyright owner] 180Sstevel@tonic-gate * 190Sstevel@tonic-gate * CDDL HEADER END 200Sstevel@tonic-gate */ 210Sstevel@tonic-gate /* 228535Sevan.yan@sun.com * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 230Sstevel@tonic-gate * Use is subject to license terms. 240Sstevel@tonic-gate */ 250Sstevel@tonic-gate 260Sstevel@tonic-gate /* 270Sstevel@tonic-gate * Support for MSI, MSIX and INTx 280Sstevel@tonic-gate */ 290Sstevel@tonic-gate 300Sstevel@tonic-gate #include <sys/conf.h> 310Sstevel@tonic-gate #include <sys/debug.h> 320Sstevel@tonic-gate #include <sys/pci.h> 331624Spjha #include <sys/pci_cap.h> 348535Sevan.yan@sun.com #include <sys/pci_intr_lib.h> 350Sstevel@tonic-gate #include <sys/sunddi.h> 360Sstevel@tonic-gate #include <sys/bitmap.h> 370Sstevel@tonic-gate 380Sstevel@tonic-gate /* 39965Sgovinda * MSI-X BIR Index Table: 40965Sgovinda * 41965Sgovinda * BAR indicator register (BIR) to Base Address register. 42965Sgovinda */ 43965Sgovinda static uchar_t pci_msix_bir_index[8] = {0x10, 0x14, 0x18, 0x1c, 44965Sgovinda 0x20, 0x24, 0xff, 0xff}; 45965Sgovinda 468535Sevan.yan@sun.com /* default class to pil value mapping */ 478535Sevan.yan@sun.com pci_class_val_t pci_default_pil [] = { 488535Sevan.yan@sun.com {0x000000, 0xff0000, 0x1}, /* Class code for pre-2.0 devices */ 498535Sevan.yan@sun.com {0x010000, 0xff0000, 0x5}, /* Mass Storage Controller */ 508535Sevan.yan@sun.com {0x020000, 0xff0000, 0x6}, /* Network Controller */ 518535Sevan.yan@sun.com {0x030000, 0xff0000, 0x9}, /* Display Controller */ 528535Sevan.yan@sun.com {0x040000, 0xff0000, 0x8}, /* Multimedia Controller */ 538535Sevan.yan@sun.com {0x050000, 0xff0000, 0x9}, /* Memory Controller */ 548535Sevan.yan@sun.com {0x060000, 0xff0000, 0x9}, /* Bridge Controller */ 558535Sevan.yan@sun.com {0x0c0000, 0xffff00, 0x9}, /* Serial Bus, FireWire (IEEE 1394) */ 568535Sevan.yan@sun.com {0x0c0100, 0xffff00, 0x4}, /* Serial Bus, ACCESS.bus */ 578535Sevan.yan@sun.com {0x0c0200, 0xffff00, 0x4}, /* Serial Bus, SSA */ 588535Sevan.yan@sun.com {0x0c0300, 0xffff00, 0x9}, /* Serial Bus Universal Serial Bus */ 59*9038SEvan.Yan@Sun.COM /* 60*9038SEvan.Yan@Sun.COM * XXX - This is a temporary workaround and it will be removed 61*9038SEvan.Yan@Sun.COM * after x86 interrupt scalability support. 62*9038SEvan.Yan@Sun.COM */ 63*9038SEvan.Yan@Sun.COM #if defined(__i386) || defined(__amd64) 64*9038SEvan.Yan@Sun.COM {0x0c0400, 0xffff00, 0x5}, /* Serial Bus, Fibre Channel */ 65*9038SEvan.Yan@Sun.COM #else 668535Sevan.yan@sun.com {0x0c0400, 0xffff00, 0x6}, /* Serial Bus, Fibre Channel */ 67*9038SEvan.Yan@Sun.COM #endif 688535Sevan.yan@sun.com {0x0c0600, 0xffff00, 0x6} /* Serial Bus, Infiniband */ 698535Sevan.yan@sun.com }; 708535Sevan.yan@sun.com 718535Sevan.yan@sun.com /* 728535Sevan.yan@sun.com * Default class to intr_weight value mapping (% of CPU). A driver.conf 738535Sevan.yan@sun.com * entry on or above the pci node like 748535Sevan.yan@sun.com * 758535Sevan.yan@sun.com * pci-class-intr-weights= 0x020000, 0xff0000, 30; 768535Sevan.yan@sun.com * 778535Sevan.yan@sun.com * can be used to augment or override entries in the default table below. 788535Sevan.yan@sun.com * 798535Sevan.yan@sun.com * NB: The values below give NICs preference on redistribution, and provide 808535Sevan.yan@sun.com * NICs some isolation from other interrupt sources. We need better interfaces 818535Sevan.yan@sun.com * that allow the NIC driver to identify a specific NIC instance as high 828535Sevan.yan@sun.com * bandwidth, and thus deserving of separation from other low bandwidth 838535Sevan.yan@sun.com * NICs additional isolation from other interrupt sources. 848535Sevan.yan@sun.com * 858535Sevan.yan@sun.com * NB: We treat Infiniband like a NIC. 868535Sevan.yan@sun.com */ 878535Sevan.yan@sun.com pci_class_val_t pci_default_intr_weight [] = { 888535Sevan.yan@sun.com {0x020000, 0xff0000, 35}, /* Network Controller */ 898535Sevan.yan@sun.com {0x010000, 0xff0000, 10}, /* Mass Storage Controller */ 908535Sevan.yan@sun.com {0x0c0400, 0xffff00, 10}, /* Serial Bus, Fibre Channel */ 918535Sevan.yan@sun.com {0x0c0600, 0xffff00, 50} /* Serial Bus, Infiniband */ 928535Sevan.yan@sun.com }; 938535Sevan.yan@sun.com 94965Sgovinda /* 950Sstevel@tonic-gate * Library utility functions 960Sstevel@tonic-gate */ 970Sstevel@tonic-gate 980Sstevel@tonic-gate /* 990Sstevel@tonic-gate * pci_get_msi_ctrl: 1000Sstevel@tonic-gate * 1010Sstevel@tonic-gate * Helper function that returns with 'cfg_hdl', MSI/X ctrl pointer, 1020Sstevel@tonic-gate * and caps_ptr for MSI/X if these are found. 1030Sstevel@tonic-gate */ 1040Sstevel@tonic-gate static int 1050Sstevel@tonic-gate pci_get_msi_ctrl(dev_info_t *dip, int type, ushort_t *msi_ctrl, 1061624Spjha ushort_t *caps_ptr, ddi_acc_handle_t *h) 1070Sstevel@tonic-gate { 1080Sstevel@tonic-gate *msi_ctrl = *caps_ptr = 0; 1090Sstevel@tonic-gate 1101624Spjha if (pci_config_setup(dip, h) != DDI_SUCCESS) { 1110Sstevel@tonic-gate DDI_INTR_NEXDBG((CE_CONT, "pci_get_msi_ctrl: " 1121624Spjha "%s%d can't get config handle", 1130Sstevel@tonic-gate ddi_driver_name(dip), ddi_get_instance(dip))); 1140Sstevel@tonic-gate 1150Sstevel@tonic-gate return (DDI_FAILURE); 1160Sstevel@tonic-gate } 1170Sstevel@tonic-gate 1181624Spjha if ((PCI_CAP_LOCATE(*h, PCI_CAP_ID_MSI, caps_ptr) == DDI_SUCCESS) && 1194937Sjohnny (type == DDI_INTR_TYPE_MSI)) { 1201624Spjha if ((*msi_ctrl = PCI_CAP_GET16(*h, NULL, *caps_ptr, 1214937Sjohnny PCI_MSI_CTRL)) == PCI_CAP_EINVAL16) 1221624Spjha goto done; 1230Sstevel@tonic-gate 1241624Spjha DDI_INTR_NEXDBG((CE_CONT, "pci_get_msi_ctrl: MSI " 1251624Spjha "caps_ptr=%x msi_ctrl=%x\n", *caps_ptr, *msi_ctrl)); 1261624Spjha 1271624Spjha return (DDI_SUCCESS); 1280Sstevel@tonic-gate } 1290Sstevel@tonic-gate 1301624Spjha if ((PCI_CAP_LOCATE(*h, PCI_CAP_ID_MSI_X, caps_ptr) == DDI_SUCCESS) && 1314937Sjohnny (type == DDI_INTR_TYPE_MSIX)) { 1321624Spjha if ((*msi_ctrl = PCI_CAP_GET16(*h, NULL, *caps_ptr, 1334937Sjohnny PCI_MSIX_CTRL)) == PCI_CAP_EINVAL16) 1341624Spjha goto done; 1350Sstevel@tonic-gate 1361624Spjha DDI_INTR_NEXDBG((CE_CONT, "pci_get_msi_ctrl: MSI-X " 1371624Spjha "caps_ptr=%x msi_ctrl=%x\n", *caps_ptr, *msi_ctrl)); 1380Sstevel@tonic-gate 1391624Spjha return (DDI_SUCCESS); 1400Sstevel@tonic-gate } 1410Sstevel@tonic-gate 1421624Spjha done: 1431624Spjha pci_config_teardown(h); 1440Sstevel@tonic-gate return (DDI_FAILURE); 1450Sstevel@tonic-gate } 1460Sstevel@tonic-gate 1470Sstevel@tonic-gate 1480Sstevel@tonic-gate /* 1490Sstevel@tonic-gate * pci_msi_get_cap: 1500Sstevel@tonic-gate * 1510Sstevel@tonic-gate * Get the capabilities of the MSI/X interrupt 1520Sstevel@tonic-gate */ 1530Sstevel@tonic-gate int 1540Sstevel@tonic-gate pci_msi_get_cap(dev_info_t *rdip, int type, int *flagsp) 1550Sstevel@tonic-gate { 1560Sstevel@tonic-gate ushort_t caps_ptr, msi_ctrl; 1570Sstevel@tonic-gate ddi_acc_handle_t cfg_hdle; 1580Sstevel@tonic-gate 1590Sstevel@tonic-gate DDI_INTR_NEXDBG((CE_CONT, "pci_msi_get_cap: rdip = 0x%p\n", 1600Sstevel@tonic-gate (void *)rdip)); 1610Sstevel@tonic-gate 1620Sstevel@tonic-gate *flagsp = 0; 1630Sstevel@tonic-gate 1640Sstevel@tonic-gate if (pci_get_msi_ctrl(rdip, type, &msi_ctrl, 1650Sstevel@tonic-gate &caps_ptr, &cfg_hdle) != DDI_SUCCESS) 1660Sstevel@tonic-gate return (DDI_FAILURE); 1670Sstevel@tonic-gate 1680Sstevel@tonic-gate if (type == DDI_INTR_TYPE_MSI) { 1690Sstevel@tonic-gate if (msi_ctrl & PCI_MSI_64BIT_MASK) 1700Sstevel@tonic-gate *flagsp |= DDI_INTR_FLAG_MSI64; 1710Sstevel@tonic-gate if (msi_ctrl & PCI_MSI_PVM_MASK) 1720Sstevel@tonic-gate *flagsp |= (DDI_INTR_FLAG_MASKABLE | 1730Sstevel@tonic-gate DDI_INTR_FLAG_PENDING); 1740Sstevel@tonic-gate else 1750Sstevel@tonic-gate *flagsp |= DDI_INTR_FLAG_BLOCK; 1760Sstevel@tonic-gate } else if (type == DDI_INTR_TYPE_MSIX) { 1770Sstevel@tonic-gate /* MSI-X supports PVM, 64bit by default */ 1780Sstevel@tonic-gate *flagsp |= (DDI_INTR_FLAG_MASKABLE | DDI_INTR_FLAG_MSI64 | 1790Sstevel@tonic-gate DDI_INTR_FLAG_PENDING); 1800Sstevel@tonic-gate } 1810Sstevel@tonic-gate 1820Sstevel@tonic-gate *flagsp |= DDI_INTR_FLAG_EDGE; 1830Sstevel@tonic-gate 1840Sstevel@tonic-gate DDI_INTR_NEXDBG((CE_CONT, "pci_msi_get_cap: flags = 0x%x\n", *flagsp)); 1850Sstevel@tonic-gate 1860Sstevel@tonic-gate pci_config_teardown(&cfg_hdle); 1870Sstevel@tonic-gate return (DDI_SUCCESS); 1880Sstevel@tonic-gate } 1890Sstevel@tonic-gate 1900Sstevel@tonic-gate 1910Sstevel@tonic-gate /* 1920Sstevel@tonic-gate * pci_msi_configure: 1930Sstevel@tonic-gate * 1940Sstevel@tonic-gate * Configure address/data and number MSI/Xs fields in the MSI/X 1950Sstevel@tonic-gate * capability structure. 1960Sstevel@tonic-gate */ 1970Sstevel@tonic-gate /* ARGSUSED */ 1980Sstevel@tonic-gate int 1990Sstevel@tonic-gate pci_msi_configure(dev_info_t *rdip, int type, int count, int inum, 2000Sstevel@tonic-gate uint64_t addr, uint64_t data) 2010Sstevel@tonic-gate { 2020Sstevel@tonic-gate ushort_t caps_ptr, msi_ctrl; 2031624Spjha ddi_acc_handle_t h; 2040Sstevel@tonic-gate 205965Sgovinda DDI_INTR_NEXDBG((CE_CONT, "pci_msi_configure: rdip = 0x%p type 0x%x " 206965Sgovinda "count 0x%x inum 0x%x addr 0x%" PRIx64 " data 0x%" PRIx64 "\n", 207965Sgovinda (void *)rdip, type, count, inum, addr, data)); 2080Sstevel@tonic-gate 2090Sstevel@tonic-gate if (pci_get_msi_ctrl(rdip, type, &msi_ctrl, 2101624Spjha &caps_ptr, &h) != DDI_SUCCESS) 2110Sstevel@tonic-gate return (DDI_FAILURE); 2120Sstevel@tonic-gate 2130Sstevel@tonic-gate if (type == DDI_INTR_TYPE_MSI) { 2140Sstevel@tonic-gate /* Set the bits to inform how many MSIs are enabled */ 2150Sstevel@tonic-gate msi_ctrl |= ((highbit(count) -1) << PCI_MSI_MME_SHIFT); 2161624Spjha PCI_CAP_PUT16(h, NULL, caps_ptr, PCI_MSI_CTRL, msi_ctrl); 2170Sstevel@tonic-gate 218965Sgovinda DDI_INTR_NEXDBG((CE_CONT, "pci_msi_configure: msi_ctrl = %x\n", 2191624Spjha PCI_CAP_GET16(h, NULL, caps_ptr, PCI_MSI_CTRL))); 220965Sgovinda 2210Sstevel@tonic-gate /* Set the "data" and "addr" bits */ 2221624Spjha PCI_CAP_PUT32(h, NULL, caps_ptr, PCI_MSI_ADDR_OFFSET, addr); 2230Sstevel@tonic-gate 224965Sgovinda DDI_INTR_NEXDBG((CE_CONT, "pci_msi_configure: msi_addr = %x\n", 2254937Sjohnny PCI_CAP_GET32(h, NULL, caps_ptr, PCI_MSI_ADDR_OFFSET))); 226965Sgovinda 2270Sstevel@tonic-gate if (msi_ctrl & PCI_MSI_64BIT_MASK) { 2281624Spjha PCI_CAP_PUT32(h, NULL, caps_ptr, PCI_MSI_ADDR_OFFSET 2294937Sjohnny + 4, addr >> 32); 230965Sgovinda 2311624Spjha DDI_INTR_NEXDBG((CE_CONT, "pci_msi_configure: upper " 2324937Sjohnny "32bit msi_addr = %x\n", PCI_CAP_GET32(h, NULL, 2334937Sjohnny caps_ptr, PCI_MSI_ADDR_OFFSET + 4))); 234965Sgovinda 2351624Spjha PCI_CAP_PUT16(h, NULL, caps_ptr, PCI_MSI_64BIT_DATA, 2364937Sjohnny data); 237965Sgovinda 2381624Spjha DDI_INTR_NEXDBG((CE_CONT, "pci_msi_configure: msi_data " 2394937Sjohnny "= %x\n", PCI_CAP_GET16(h, NULL, caps_ptr, 2404937Sjohnny PCI_MSI_64BIT_DATA))); 2410Sstevel@tonic-gate } else { 2421624Spjha PCI_CAP_PUT16(h, NULL, caps_ptr, PCI_MSI_32BIT_DATA, 2434937Sjohnny data); 244965Sgovinda 2451624Spjha DDI_INTR_NEXDBG((CE_CONT, "pci_msi_configure: msi_data " 2464937Sjohnny "= %x\n", PCI_CAP_GET16(h, NULL, caps_ptr, 2474937Sjohnny PCI_MSI_32BIT_DATA))); 2480Sstevel@tonic-gate } 2490Sstevel@tonic-gate } else if (type == DDI_INTR_TYPE_MSIX) { 25042Sagiri uintptr_t off; 2510Sstevel@tonic-gate ddi_intr_msix_t *msix_p = i_ddi_get_msix(rdip); 2520Sstevel@tonic-gate 2530Sstevel@tonic-gate /* Offset into the "inum"th entry in the MSI-X table */ 25442Sagiri off = (uintptr_t)msix_p->msix_tbl_addr + 255965Sgovinda (inum * PCI_MSIX_VECTOR_SIZE); 2560Sstevel@tonic-gate 2570Sstevel@tonic-gate /* Set the "data" and "addr" bits */ 2580Sstevel@tonic-gate ddi_put32(msix_p->msix_tbl_hdl, 259965Sgovinda (uint32_t *)(off + PCI_MSIX_DATA_OFFSET), data); 260965Sgovinda 261965Sgovinda ddi_put64(msix_p->msix_tbl_hdl, 262965Sgovinda (uint64_t *)(off + PCI_MSIX_LOWER_ADDR_OFFSET), addr); 2630Sstevel@tonic-gate 264965Sgovinda DDI_INTR_NEXDBG((CE_CONT, "pci_msi_configure: " 265965Sgovinda "msix_addr 0x%" PRIx64 " msix_data 0x%x\n", 266965Sgovinda ddi_get64(msix_p->msix_tbl_hdl, 267965Sgovinda (uint64_t *)(off + PCI_MSIX_LOWER_ADDR_OFFSET)), 268965Sgovinda ddi_get32(msix_p->msix_tbl_hdl, 269965Sgovinda (uint32_t *)(off + PCI_MSIX_DATA_OFFSET)))); 2700Sstevel@tonic-gate } 2710Sstevel@tonic-gate 2721624Spjha pci_config_teardown(&h); 2730Sstevel@tonic-gate return (DDI_SUCCESS); 2740Sstevel@tonic-gate } 2750Sstevel@tonic-gate 2760Sstevel@tonic-gate 2770Sstevel@tonic-gate /* 2780Sstevel@tonic-gate * pci_msi_unconfigure: 2790Sstevel@tonic-gate * 2800Sstevel@tonic-gate * Unconfigure address/data and number MSI/Xs fields in the MSI/X 2810Sstevel@tonic-gate * capability structure. 2820Sstevel@tonic-gate */ 2830Sstevel@tonic-gate /* ARGSUSED */ 2840Sstevel@tonic-gate int 2850Sstevel@tonic-gate pci_msi_unconfigure(dev_info_t *rdip, int type, int inum) 2860Sstevel@tonic-gate { 2870Sstevel@tonic-gate ushort_t msi_ctrl, caps_ptr; 2881624Spjha ddi_acc_handle_t h; 2890Sstevel@tonic-gate 2902755Segillett DDI_INTR_NEXDBG((CE_CONT, "pci_msi_unconfigure: rdip = 0x%p type 0x%x " 2912755Segillett "inum 0x%x\n", (void *)rdip, type, inum)); 2920Sstevel@tonic-gate 2931624Spjha if (pci_get_msi_ctrl(rdip, type, &msi_ctrl, &caps_ptr, &h) != 2942755Segillett DDI_SUCCESS) 2950Sstevel@tonic-gate return (DDI_FAILURE); 2960Sstevel@tonic-gate 2970Sstevel@tonic-gate if (type == DDI_INTR_TYPE_MSI) { 2980Sstevel@tonic-gate msi_ctrl &= (~PCI_MSI_MME_MASK); 2991624Spjha PCI_CAP_PUT16(h, NULL, caps_ptr, PCI_MSI_CTRL, msi_ctrl); 3000Sstevel@tonic-gate 3011624Spjha PCI_CAP_PUT32(h, NULL, caps_ptr, PCI_MSI_ADDR_OFFSET, 0); 3021624Spjha 3030Sstevel@tonic-gate if (msi_ctrl & PCI_MSI_64BIT_MASK) { 3041624Spjha PCI_CAP_PUT16(h, NULL, caps_ptr, PCI_MSI_64BIT_DATA, 3054937Sjohnny 0); 3061624Spjha PCI_CAP_PUT32(h, NULL, caps_ptr, PCI_MSI_ADDR_OFFSET 3074937Sjohnny + 4, 0); 3080Sstevel@tonic-gate } else { 3091624Spjha PCI_CAP_PUT16(h, NULL, caps_ptr, PCI_MSI_32BIT_DATA, 3104937Sjohnny 0); 3110Sstevel@tonic-gate } 3120Sstevel@tonic-gate 3131624Spjha DDI_INTR_NEXDBG((CE_CONT, "pci_msi_unconfigure: msi_ctrl " 3141624Spjha "= %x\n", PCI_CAP_GET16(h, NULL, caps_ptr, PCI_MSI_CTRL))); 3150Sstevel@tonic-gate 3160Sstevel@tonic-gate } else if (type == DDI_INTR_TYPE_MSIX) { 31742Sagiri uintptr_t off; 3180Sstevel@tonic-gate ddi_intr_msix_t *msix_p = i_ddi_get_msix(rdip); 3190Sstevel@tonic-gate 3200Sstevel@tonic-gate /* Offset into the "inum"th entry in the MSI-X table */ 32142Sagiri off = (uintptr_t)msix_p->msix_tbl_addr + 322965Sgovinda (inum * PCI_MSIX_VECTOR_SIZE); 3230Sstevel@tonic-gate 3240Sstevel@tonic-gate /* Reset the "data" and "addr" bits */ 3250Sstevel@tonic-gate ddi_put32(msix_p->msix_tbl_hdl, 326965Sgovinda (uint32_t *)(off + PCI_MSIX_DATA_OFFSET), 0); 3270Sstevel@tonic-gate 3282755Segillett ddi_put64(msix_p->msix_tbl_hdl, 3292755Segillett (uint64_t *)(off + PCI_MSIX_LOWER_ADDR_OFFSET), 0); 3300Sstevel@tonic-gate } 3310Sstevel@tonic-gate 3321624Spjha pci_config_teardown(&h); 3330Sstevel@tonic-gate return (DDI_SUCCESS); 3340Sstevel@tonic-gate } 3350Sstevel@tonic-gate 3360Sstevel@tonic-gate 3370Sstevel@tonic-gate /* 3380Sstevel@tonic-gate * pci_is_msi_enabled: 3390Sstevel@tonic-gate * 3400Sstevel@tonic-gate * This function returns DDI_SUCCESS if MSI/X is already enabled, otherwise 3410Sstevel@tonic-gate * it returns DDI_FAILURE. 3420Sstevel@tonic-gate */ 3430Sstevel@tonic-gate int 3440Sstevel@tonic-gate pci_is_msi_enabled(dev_info_t *rdip, int type) 3450Sstevel@tonic-gate { 3460Sstevel@tonic-gate ushort_t caps_ptr, msi_ctrl; 3470Sstevel@tonic-gate ddi_acc_handle_t cfg_hdle; 3480Sstevel@tonic-gate int ret = DDI_FAILURE; 3490Sstevel@tonic-gate 3500Sstevel@tonic-gate DDI_INTR_NEXDBG((CE_CONT, "pci_is_msi_enabled: rdip = 0x%p, " 3510Sstevel@tonic-gate "type = 0x%x\n", (void *)rdip, type)); 3520Sstevel@tonic-gate 3530Sstevel@tonic-gate if (pci_get_msi_ctrl(rdip, type, &msi_ctrl, 3540Sstevel@tonic-gate &caps_ptr, &cfg_hdle) != DDI_SUCCESS) 3550Sstevel@tonic-gate return (DDI_FAILURE); 3560Sstevel@tonic-gate 3570Sstevel@tonic-gate if ((type == DDI_INTR_TYPE_MSI) && (msi_ctrl & PCI_MSI_ENABLE_BIT)) 3580Sstevel@tonic-gate ret = DDI_SUCCESS; 3590Sstevel@tonic-gate 3600Sstevel@tonic-gate if ((type == DDI_INTR_TYPE_MSIX) && (msi_ctrl & PCI_MSIX_ENABLE_BIT)) 3610Sstevel@tonic-gate ret = DDI_SUCCESS; 3620Sstevel@tonic-gate 3630Sstevel@tonic-gate pci_config_teardown(&cfg_hdle); 3640Sstevel@tonic-gate return (ret); 3650Sstevel@tonic-gate } 3660Sstevel@tonic-gate 3670Sstevel@tonic-gate 3680Sstevel@tonic-gate /* 3690Sstevel@tonic-gate * pci_msi_enable_mode: 3700Sstevel@tonic-gate * 3710Sstevel@tonic-gate * This function sets the MSI_ENABLE bit in the capability structure 3720Sstevel@tonic-gate * (for MSI) and MSIX_ENABLE bit in the MSI-X capability structure. 3732755Segillett * 3742755Segillett * NOTE: It is the nexus driver's responsibility to clear the MSI/X 3752755Segillett * interrupt's mask bit in the MSI/X capability structure before the 3762755Segillett * interrupt can be used. 3770Sstevel@tonic-gate */ 3780Sstevel@tonic-gate int 3792755Segillett pci_msi_enable_mode(dev_info_t *rdip, int type) 3800Sstevel@tonic-gate { 3810Sstevel@tonic-gate ushort_t caps_ptr, msi_ctrl; 3820Sstevel@tonic-gate ddi_acc_handle_t cfg_hdle; 3830Sstevel@tonic-gate 3842755Segillett DDI_INTR_NEXDBG((CE_CONT, "pci_msi_enable_mode: rdip = 0x%p\n", 3852755Segillett (void *)rdip)); 3860Sstevel@tonic-gate 3870Sstevel@tonic-gate if (pci_get_msi_ctrl(rdip, type, &msi_ctrl, 3880Sstevel@tonic-gate &caps_ptr, &cfg_hdle) != DDI_SUCCESS) 3890Sstevel@tonic-gate return (DDI_FAILURE); 3900Sstevel@tonic-gate 3910Sstevel@tonic-gate if (type == DDI_INTR_TYPE_MSI) { 3920Sstevel@tonic-gate if (msi_ctrl & PCI_MSI_ENABLE_BIT) 3930Sstevel@tonic-gate goto finished; 3940Sstevel@tonic-gate 3950Sstevel@tonic-gate msi_ctrl |= PCI_MSI_ENABLE_BIT; 3961624Spjha PCI_CAP_PUT16(cfg_hdle, NULL, caps_ptr, PCI_MSI_CTRL, msi_ctrl); 3970Sstevel@tonic-gate 3980Sstevel@tonic-gate } else if (type == DDI_INTR_TYPE_MSIX) { 3990Sstevel@tonic-gate if (msi_ctrl & PCI_MSIX_ENABLE_BIT) 4000Sstevel@tonic-gate goto finished; 4010Sstevel@tonic-gate 4020Sstevel@tonic-gate msi_ctrl |= PCI_MSIX_ENABLE_BIT; 4031624Spjha PCI_CAP_PUT16(cfg_hdle, NULL, caps_ptr, PCI_MSIX_CTRL, 4042755Segillett msi_ctrl); 4050Sstevel@tonic-gate } 4060Sstevel@tonic-gate 4070Sstevel@tonic-gate finished: 4080Sstevel@tonic-gate DDI_INTR_NEXDBG((CE_CONT, "pci_msi_enable_mode: msi_ctrl = %x\n", 4090Sstevel@tonic-gate msi_ctrl)); 4100Sstevel@tonic-gate 4110Sstevel@tonic-gate pci_config_teardown(&cfg_hdle); 4120Sstevel@tonic-gate return (DDI_SUCCESS); 4130Sstevel@tonic-gate } 4140Sstevel@tonic-gate 4150Sstevel@tonic-gate 4160Sstevel@tonic-gate /* 4170Sstevel@tonic-gate * pci_msi_disable_mode: 4180Sstevel@tonic-gate * 4190Sstevel@tonic-gate * This function resets the MSI_ENABLE bit in the capability structure 4200Sstevel@tonic-gate * (for MSI) and MSIX_ENABLE bit in the MSI-X capability structure. 4212755Segillett * 4222755Segillett * NOTE: It is the nexus driver's responsibility to set the MSI/X 4232755Segillett * interrupt's mask bit in the MSI/X capability structure before the 4242755Segillett * interrupt can be disabled. 4250Sstevel@tonic-gate */ 4260Sstevel@tonic-gate int 4272755Segillett pci_msi_disable_mode(dev_info_t *rdip, int type, uint_t flags) 4280Sstevel@tonic-gate { 4290Sstevel@tonic-gate ushort_t caps_ptr, msi_ctrl; 4300Sstevel@tonic-gate ddi_acc_handle_t cfg_hdle; 4310Sstevel@tonic-gate 4320Sstevel@tonic-gate DDI_INTR_NEXDBG((CE_CONT, "pci_msi_disable_mode: rdip = 0x%p " 4332755Segillett "flags = 0x%x\n", (void *)rdip, flags)); 4342755Segillett 4352755Segillett /* 4362755Segillett * Do not turn off the master enable bit if other interrupts are 4372755Segillett * still active. 4382755Segillett */ 4392755Segillett if ((flags != DDI_INTR_FLAG_BLOCK) && 4408817SKerry.Shu@Sun.COM (i_ddi_intr_get_current_nenables(rdip) > 1)) 4412755Segillett return (DDI_SUCCESS); 4420Sstevel@tonic-gate 4430Sstevel@tonic-gate if (pci_get_msi_ctrl(rdip, type, &msi_ctrl, 4440Sstevel@tonic-gate &caps_ptr, &cfg_hdle) != DDI_SUCCESS) 4450Sstevel@tonic-gate return (DDI_FAILURE); 4460Sstevel@tonic-gate 4470Sstevel@tonic-gate /* Reset the "enable" bit */ 4480Sstevel@tonic-gate if (type == DDI_INTR_TYPE_MSI) { 4490Sstevel@tonic-gate if (!(msi_ctrl & PCI_MSI_ENABLE_BIT)) 4500Sstevel@tonic-gate goto finished; 4510Sstevel@tonic-gate msi_ctrl &= ~PCI_MSI_ENABLE_BIT; 4521624Spjha PCI_CAP_PUT16(cfg_hdle, NULL, caps_ptr, PCI_MSI_CTRL, msi_ctrl); 4530Sstevel@tonic-gate } else if (type == DDI_INTR_TYPE_MSIX) { 4540Sstevel@tonic-gate if (!(msi_ctrl & PCI_MSIX_ENABLE_BIT)) 4550Sstevel@tonic-gate goto finished; 4560Sstevel@tonic-gate 4572755Segillett msi_ctrl &= ~PCI_MSIX_ENABLE_BIT; 4582755Segillett PCI_CAP_PUT16(cfg_hdle, NULL, caps_ptr, PCI_MSIX_CTRL, 4592755Segillett msi_ctrl); 4600Sstevel@tonic-gate } 4610Sstevel@tonic-gate 4620Sstevel@tonic-gate finished: 4630Sstevel@tonic-gate DDI_INTR_NEXDBG((CE_CONT, "pci_msi_disable_mode: msi_ctrl = %x\n", 4640Sstevel@tonic-gate msi_ctrl)); 4650Sstevel@tonic-gate 4660Sstevel@tonic-gate pci_config_teardown(&cfg_hdle); 4670Sstevel@tonic-gate return (DDI_SUCCESS); 4680Sstevel@tonic-gate } 4690Sstevel@tonic-gate 4700Sstevel@tonic-gate 4710Sstevel@tonic-gate /* 4720Sstevel@tonic-gate * pci_msi_set_mask: 4730Sstevel@tonic-gate * 4740Sstevel@tonic-gate * Set the mask bit in the MSI/X capability structure 4750Sstevel@tonic-gate */ 4760Sstevel@tonic-gate /* ARGSUSED */ 4770Sstevel@tonic-gate int 4780Sstevel@tonic-gate pci_msi_set_mask(dev_info_t *rdip, int type, int inum) 4790Sstevel@tonic-gate { 4800Sstevel@tonic-gate int offset; 4810Sstevel@tonic-gate int ret = DDI_FAILURE; 4820Sstevel@tonic-gate ushort_t caps_ptr, msi_ctrl; 4830Sstevel@tonic-gate ddi_acc_handle_t cfg_hdle; 484965Sgovinda uint32_t mask_bits; 4850Sstevel@tonic-gate 4860Sstevel@tonic-gate DDI_INTR_NEXDBG((CE_CONT, "pci_msi_set_mask: rdip = 0x%p, " 4870Sstevel@tonic-gate "type = 0x%x\n", (void *)rdip, type)); 4880Sstevel@tonic-gate 4890Sstevel@tonic-gate if (pci_get_msi_ctrl(rdip, type, &msi_ctrl, 4900Sstevel@tonic-gate &caps_ptr, &cfg_hdle) != DDI_SUCCESS) 4910Sstevel@tonic-gate return (DDI_FAILURE); 4920Sstevel@tonic-gate 4930Sstevel@tonic-gate if (type == DDI_INTR_TYPE_MSI) { 4940Sstevel@tonic-gate if (!(msi_ctrl & PCI_MSI_PVM_MASK)) 4950Sstevel@tonic-gate goto done; 4960Sstevel@tonic-gate 4970Sstevel@tonic-gate offset = (msi_ctrl & PCI_MSI_64BIT_MASK) ? 4980Sstevel@tonic-gate PCI_MSI_64BIT_MASKBITS : PCI_MSI_32BIT_MASK; 4990Sstevel@tonic-gate 5001624Spjha if ((mask_bits = PCI_CAP_GET32(cfg_hdle, NULL, caps_ptr, 5014937Sjohnny offset)) == PCI_CAP_EINVAL32) 5021624Spjha goto done; 5030Sstevel@tonic-gate 5040Sstevel@tonic-gate mask_bits |= (1 << inum); 5050Sstevel@tonic-gate 5061624Spjha PCI_CAP_PUT32(cfg_hdle, NULL, caps_ptr, offset, mask_bits); 5070Sstevel@tonic-gate 5080Sstevel@tonic-gate } else if (type == DDI_INTR_TYPE_MSIX) { 50942Sagiri uintptr_t off; 5100Sstevel@tonic-gate ddi_intr_msix_t *msix_p; 5110Sstevel@tonic-gate 5120Sstevel@tonic-gate /* Set function mask */ 5130Sstevel@tonic-gate if (msi_ctrl & PCI_MSIX_FUNCTION_MASK) { 5140Sstevel@tonic-gate ret = DDI_SUCCESS; 5150Sstevel@tonic-gate goto done; 5160Sstevel@tonic-gate } 5170Sstevel@tonic-gate 5180Sstevel@tonic-gate msix_p = i_ddi_get_msix(rdip); 5190Sstevel@tonic-gate 5200Sstevel@tonic-gate /* Offset into the "inum"th entry in the MSI-X table */ 521965Sgovinda off = (uintptr_t)msix_p->msix_tbl_addr + (inum * 5220Sstevel@tonic-gate PCI_MSIX_VECTOR_SIZE) + PCI_MSIX_VECTOR_CTRL_OFFSET; 5230Sstevel@tonic-gate 5240Sstevel@tonic-gate /* Set the Mask bit */ 525965Sgovinda ddi_put32(msix_p->msix_tbl_hdl, (uint32_t *)off, 0x1); 5260Sstevel@tonic-gate } 5270Sstevel@tonic-gate 5280Sstevel@tonic-gate ret = DDI_SUCCESS; 5290Sstevel@tonic-gate done: 5300Sstevel@tonic-gate pci_config_teardown(&cfg_hdle); 5310Sstevel@tonic-gate return (ret); 5320Sstevel@tonic-gate } 5330Sstevel@tonic-gate 5340Sstevel@tonic-gate 5350Sstevel@tonic-gate /* 5360Sstevel@tonic-gate * pci_msi_clr_mask: 5370Sstevel@tonic-gate * 5380Sstevel@tonic-gate * Clear the mask bit in the MSI/X capability structure 5390Sstevel@tonic-gate */ 5400Sstevel@tonic-gate /* ARGSUSED */ 5410Sstevel@tonic-gate int 5420Sstevel@tonic-gate pci_msi_clr_mask(dev_info_t *rdip, int type, int inum) 5430Sstevel@tonic-gate { 5440Sstevel@tonic-gate ushort_t caps_ptr, msi_ctrl; 5450Sstevel@tonic-gate ddi_acc_handle_t cfg_hdle; 5460Sstevel@tonic-gate int offset; 5470Sstevel@tonic-gate int ret = DDI_FAILURE; 548965Sgovinda uint32_t mask_bits; 5490Sstevel@tonic-gate 5500Sstevel@tonic-gate DDI_INTR_NEXDBG((CE_CONT, "pci_msi_clr_mask: rdip = 0x%p, " 5510Sstevel@tonic-gate "type = 0x%x\n", (void *)rdip, type)); 5520Sstevel@tonic-gate 5530Sstevel@tonic-gate if (pci_get_msi_ctrl(rdip, type, &msi_ctrl, 5540Sstevel@tonic-gate &caps_ptr, &cfg_hdle) != DDI_SUCCESS) 5550Sstevel@tonic-gate return (DDI_FAILURE); 5560Sstevel@tonic-gate 5570Sstevel@tonic-gate if (type == DDI_INTR_TYPE_MSI) { 5580Sstevel@tonic-gate if (!(msi_ctrl & PCI_MSI_PVM_MASK)) 5590Sstevel@tonic-gate goto done; 5600Sstevel@tonic-gate 5610Sstevel@tonic-gate offset = (msi_ctrl & PCI_MSI_64BIT_MASK) ? 5620Sstevel@tonic-gate PCI_MSI_64BIT_MASKBITS : PCI_MSI_32BIT_MASK; 5631624Spjha if ((mask_bits = PCI_CAP_GET32(cfg_hdle, NULL, caps_ptr, 5644937Sjohnny offset)) == PCI_CAP_EINVAL32) 5651624Spjha goto done; 5660Sstevel@tonic-gate 5670Sstevel@tonic-gate mask_bits &= ~(1 << inum); 5680Sstevel@tonic-gate 5691624Spjha PCI_CAP_PUT32(cfg_hdle, NULL, caps_ptr, offset, mask_bits); 5700Sstevel@tonic-gate 5710Sstevel@tonic-gate } else if (type == DDI_INTR_TYPE_MSIX) { 57242Sagiri uintptr_t off; 5730Sstevel@tonic-gate ddi_intr_msix_t *msix_p; 5740Sstevel@tonic-gate 5750Sstevel@tonic-gate if (msi_ctrl & PCI_MSIX_FUNCTION_MASK) { 5760Sstevel@tonic-gate ret = DDI_SUCCESS; 5770Sstevel@tonic-gate goto done; 5780Sstevel@tonic-gate } 5790Sstevel@tonic-gate 5800Sstevel@tonic-gate msix_p = i_ddi_get_msix(rdip); 5810Sstevel@tonic-gate 5820Sstevel@tonic-gate /* Offset into the "inum"th entry in the MSI-X table */ 583965Sgovinda off = (uintptr_t)msix_p->msix_tbl_addr + (inum * 5840Sstevel@tonic-gate PCI_MSIX_VECTOR_SIZE) + PCI_MSIX_VECTOR_CTRL_OFFSET; 5850Sstevel@tonic-gate 5860Sstevel@tonic-gate /* Clear the Mask bit */ 587965Sgovinda ddi_put32(msix_p->msix_tbl_hdl, (uint32_t *)off, 0x0); 5880Sstevel@tonic-gate } 5890Sstevel@tonic-gate 5900Sstevel@tonic-gate ret = DDI_SUCCESS; 5910Sstevel@tonic-gate done: 5920Sstevel@tonic-gate pci_config_teardown(&cfg_hdle); 5930Sstevel@tonic-gate return (ret); 5940Sstevel@tonic-gate } 5950Sstevel@tonic-gate 5960Sstevel@tonic-gate 5970Sstevel@tonic-gate /* 5980Sstevel@tonic-gate * pci_msi_get_pending: 5990Sstevel@tonic-gate * 6000Sstevel@tonic-gate * Get the pending bit from the MSI/X capability structure 6010Sstevel@tonic-gate */ 6020Sstevel@tonic-gate /* ARGSUSED */ 6030Sstevel@tonic-gate int 6040Sstevel@tonic-gate pci_msi_get_pending(dev_info_t *rdip, int type, int inum, int *pendingp) 6050Sstevel@tonic-gate { 6060Sstevel@tonic-gate ushort_t caps_ptr, msi_ctrl; 6070Sstevel@tonic-gate ddi_acc_handle_t cfg_hdle; 6080Sstevel@tonic-gate int offset; 6090Sstevel@tonic-gate int ret = DDI_FAILURE; 6100Sstevel@tonic-gate 6110Sstevel@tonic-gate DDI_INTR_NEXDBG((CE_CONT, "pci_msi_get_pending: rdip = 0x%p\n", 6120Sstevel@tonic-gate (void *)rdip)); 6130Sstevel@tonic-gate 6140Sstevel@tonic-gate if (pci_get_msi_ctrl(rdip, type, &msi_ctrl, 6150Sstevel@tonic-gate &caps_ptr, &cfg_hdle) != DDI_SUCCESS) 6160Sstevel@tonic-gate return (DDI_FAILURE); 6170Sstevel@tonic-gate 6180Sstevel@tonic-gate if (type == DDI_INTR_TYPE_MSI) { 6190Sstevel@tonic-gate uint32_t pending_bits; 6200Sstevel@tonic-gate 6210Sstevel@tonic-gate if (!(msi_ctrl & PCI_MSI_PVM_MASK)) { 6220Sstevel@tonic-gate DDI_INTR_NEXDBG((CE_CONT, "pci_msi_get_pending: " 6230Sstevel@tonic-gate "PVM is not supported\n")); 6240Sstevel@tonic-gate goto done; 6250Sstevel@tonic-gate } 6260Sstevel@tonic-gate 6270Sstevel@tonic-gate offset = (msi_ctrl & PCI_MSI_64BIT_MASK) ? 6280Sstevel@tonic-gate PCI_MSI_64BIT_PENDING : PCI_MSI_32BIT_PENDING; 6290Sstevel@tonic-gate 6301624Spjha if ((pending_bits = PCI_CAP_GET32(cfg_hdle, NULL, caps_ptr, 6314937Sjohnny offset)) == PCI_CAP_EINVAL32) 6321624Spjha goto done; 6330Sstevel@tonic-gate 6340Sstevel@tonic-gate *pendingp = pending_bits & ~(1 >> inum); 6350Sstevel@tonic-gate 6360Sstevel@tonic-gate } else if (type == DDI_INTR_TYPE_MSIX) { 63742Sagiri uintptr_t off; 6380Sstevel@tonic-gate uint64_t pending_bits; 6390Sstevel@tonic-gate ddi_intr_msix_t *msix_p = i_ddi_get_msix(rdip); 6400Sstevel@tonic-gate 6410Sstevel@tonic-gate /* Offset into the PBA array which has entry for "inum" */ 642965Sgovinda off = (uintptr_t)msix_p->msix_pba_addr + (inum / 64); 6430Sstevel@tonic-gate 6440Sstevel@tonic-gate /* Read the PBA array */ 645965Sgovinda pending_bits = ddi_get64(msix_p->msix_pba_hdl, (uint64_t *)off); 6460Sstevel@tonic-gate 6470Sstevel@tonic-gate *pendingp = pending_bits & ~(1 >> inum); 6480Sstevel@tonic-gate } 6490Sstevel@tonic-gate 6500Sstevel@tonic-gate ret = DDI_SUCCESS; 6510Sstevel@tonic-gate done: 6520Sstevel@tonic-gate pci_config_teardown(&cfg_hdle); 6530Sstevel@tonic-gate return (ret); 6540Sstevel@tonic-gate } 6550Sstevel@tonic-gate 6560Sstevel@tonic-gate 6570Sstevel@tonic-gate /* 6580Sstevel@tonic-gate * pci_msi_get_nintrs: 6590Sstevel@tonic-gate * 6600Sstevel@tonic-gate * For a given type (MSI/X) returns the number of interrupts supported 6610Sstevel@tonic-gate */ 6620Sstevel@tonic-gate int 6630Sstevel@tonic-gate pci_msi_get_nintrs(dev_info_t *rdip, int type, int *nintrs) 6640Sstevel@tonic-gate { 6650Sstevel@tonic-gate ushort_t caps_ptr, msi_ctrl; 6660Sstevel@tonic-gate ddi_acc_handle_t cfg_hdle; 6670Sstevel@tonic-gate 6680Sstevel@tonic-gate DDI_INTR_NEXDBG((CE_CONT, "pci_msi_get_nintrs: rdip = 0x%p\n", 6690Sstevel@tonic-gate (void *)rdip)); 6700Sstevel@tonic-gate 6710Sstevel@tonic-gate if (pci_get_msi_ctrl(rdip, type, &msi_ctrl, 6720Sstevel@tonic-gate &caps_ptr, &cfg_hdle) != DDI_SUCCESS) 6730Sstevel@tonic-gate return (DDI_FAILURE); 6740Sstevel@tonic-gate 6750Sstevel@tonic-gate if (type == DDI_INTR_TYPE_MSI) { 6760Sstevel@tonic-gate *nintrs = 1 << ((msi_ctrl & PCI_MSI_MMC_MASK) >> 6770Sstevel@tonic-gate PCI_MSI_MMC_SHIFT); 6780Sstevel@tonic-gate } else if (type == DDI_INTR_TYPE_MSIX) { 6790Sstevel@tonic-gate if (msi_ctrl & PCI_MSIX_TBL_SIZE_MASK) 6800Sstevel@tonic-gate *nintrs = (msi_ctrl & PCI_MSIX_TBL_SIZE_MASK) + 1; 6810Sstevel@tonic-gate } 6820Sstevel@tonic-gate 6830Sstevel@tonic-gate DDI_INTR_NEXDBG((CE_CONT, "pci_msi_get_nintrs: " 6840Sstevel@tonic-gate "nintr = 0x%x\n", *nintrs)); 6850Sstevel@tonic-gate 6860Sstevel@tonic-gate pci_config_teardown(&cfg_hdle); 6870Sstevel@tonic-gate return (DDI_SUCCESS); 6880Sstevel@tonic-gate } 6890Sstevel@tonic-gate 6900Sstevel@tonic-gate 6910Sstevel@tonic-gate /* 6920Sstevel@tonic-gate * pci_msi_set_nintrs: 6930Sstevel@tonic-gate * 6940Sstevel@tonic-gate * For a given type (MSI/X) sets the number of interrupts supported 6950Sstevel@tonic-gate * by the system. 6960Sstevel@tonic-gate * For MSI: Return an error if this func is called for navail > 32 6970Sstevel@tonic-gate * For MSI-X: Return an error if this func is called for navail > 2048 6980Sstevel@tonic-gate */ 6990Sstevel@tonic-gate int 7000Sstevel@tonic-gate pci_msi_set_nintrs(dev_info_t *rdip, int type, int navail) 7010Sstevel@tonic-gate { 7020Sstevel@tonic-gate ushort_t caps_ptr, msi_ctrl; 7030Sstevel@tonic-gate ddi_acc_handle_t cfg_hdle; 7040Sstevel@tonic-gate 7050Sstevel@tonic-gate DDI_INTR_NEXDBG((CE_CONT, "pci_msi_set_nintrs: rdip = 0x%p, " 7060Sstevel@tonic-gate "navail = 0x%x\n", (void *)rdip, navail)); 7070Sstevel@tonic-gate 7080Sstevel@tonic-gate /* Check for valid input argument */ 7090Sstevel@tonic-gate if (((type == DDI_INTR_TYPE_MSI) && (navail > PCI_MSI_MAX_INTRS)) || 7100Sstevel@tonic-gate ((type == DDI_INTR_TYPE_MSIX) && (navail > PCI_MSIX_MAX_INTRS))) 7110Sstevel@tonic-gate return (DDI_EINVAL); 7120Sstevel@tonic-gate 7130Sstevel@tonic-gate if (pci_get_msi_ctrl(rdip, type, &msi_ctrl, 7140Sstevel@tonic-gate &caps_ptr, &cfg_hdle) != DDI_SUCCESS) 7150Sstevel@tonic-gate return (DDI_FAILURE); 7160Sstevel@tonic-gate 7170Sstevel@tonic-gate if (type == DDI_INTR_TYPE_MSI) { 7180Sstevel@tonic-gate msi_ctrl |= ((highbit(navail) -1) << PCI_MSI_MME_SHIFT); 7190Sstevel@tonic-gate 7201624Spjha PCI_CAP_PUT16(cfg_hdle, NULL, caps_ptr, PCI_MSI_CTRL, msi_ctrl); 7210Sstevel@tonic-gate } else if (type == DDI_INTR_TYPE_MSIX) { 7220Sstevel@tonic-gate DDI_INTR_NEXDBG((CE_CONT, "pci_msi_set_nintrs: unsupported\n")); 7230Sstevel@tonic-gate } 7240Sstevel@tonic-gate 7250Sstevel@tonic-gate pci_config_teardown(&cfg_hdle); 7260Sstevel@tonic-gate return (DDI_SUCCESS); 7270Sstevel@tonic-gate } 7280Sstevel@tonic-gate 7290Sstevel@tonic-gate 7300Sstevel@tonic-gate /* 7310Sstevel@tonic-gate * pci_msi_get_supported_type: 7320Sstevel@tonic-gate * 7330Sstevel@tonic-gate * Returns DDI_INTR_TYPE_MSI and/or DDI_INTR_TYPE_MSIX as supported 7340Sstevel@tonic-gate * types if device supports them. A DDI_FAILURE is returned otherwise. 7350Sstevel@tonic-gate */ 7360Sstevel@tonic-gate int 7370Sstevel@tonic-gate pci_msi_get_supported_type(dev_info_t *rdip, int *typesp) 7380Sstevel@tonic-gate { 7390Sstevel@tonic-gate ushort_t caps_ptr, msi_ctrl; 7400Sstevel@tonic-gate ddi_acc_handle_t cfg_hdle; 7410Sstevel@tonic-gate 7420Sstevel@tonic-gate DDI_INTR_NEXDBG((CE_CONT, "pci_msi_get_supported_type: " 7430Sstevel@tonic-gate "rdip = 0x%p\n", (void *)rdip)); 7440Sstevel@tonic-gate 7450Sstevel@tonic-gate *typesp = 0; 7460Sstevel@tonic-gate 7470Sstevel@tonic-gate if (pci_get_msi_ctrl(rdip, DDI_INTR_TYPE_MSI, &msi_ctrl, 7480Sstevel@tonic-gate &caps_ptr, &cfg_hdle) == DDI_SUCCESS) { 7490Sstevel@tonic-gate *typesp |= DDI_INTR_TYPE_MSI; 7500Sstevel@tonic-gate pci_config_teardown(&cfg_hdle); 7510Sstevel@tonic-gate } 7520Sstevel@tonic-gate 7530Sstevel@tonic-gate if (pci_get_msi_ctrl(rdip, DDI_INTR_TYPE_MSIX, &msi_ctrl, 7540Sstevel@tonic-gate &caps_ptr, &cfg_hdle) == DDI_SUCCESS) { 7550Sstevel@tonic-gate *typesp |= DDI_INTR_TYPE_MSIX; 7560Sstevel@tonic-gate pci_config_teardown(&cfg_hdle); 7570Sstevel@tonic-gate } 7580Sstevel@tonic-gate 7590Sstevel@tonic-gate DDI_INTR_NEXDBG((CE_CONT, "pci_msi_get_supported_type: " 7600Sstevel@tonic-gate "rdip = 0x%p types 0x%x\n", (void *)rdip, *typesp)); 7610Sstevel@tonic-gate 7620Sstevel@tonic-gate return (*typesp == 0 ? DDI_FAILURE : DDI_SUCCESS); 7630Sstevel@tonic-gate } 7640Sstevel@tonic-gate 7650Sstevel@tonic-gate 7660Sstevel@tonic-gate /* 7670Sstevel@tonic-gate * pci_msix_init: 7680Sstevel@tonic-gate * This function initializes the various handles/addrs etc. 7690Sstevel@tonic-gate * needed for MSI-X support. It also allocates a private 7700Sstevel@tonic-gate * structure to keep track of these. 7710Sstevel@tonic-gate */ 7720Sstevel@tonic-gate ddi_intr_msix_t * 7730Sstevel@tonic-gate pci_msix_init(dev_info_t *rdip) 7740Sstevel@tonic-gate { 775965Sgovinda uint_t rnumber, breg, nregs; 7760Sstevel@tonic-gate size_t msix_tbl_size; 7770Sstevel@tonic-gate size_t pba_tbl_size; 778965Sgovinda ushort_t caps_ptr, msix_ctrl; 7790Sstevel@tonic-gate ddi_intr_msix_t *msix_p; 7800Sstevel@tonic-gate ddi_acc_handle_t cfg_hdle; 781965Sgovinda pci_regspec_t *rp; 782965Sgovinda int reg_size, addr_space, offset, *regs_list; 783965Sgovinda int i, ret; 7840Sstevel@tonic-gate 7850Sstevel@tonic-gate DDI_INTR_NEXDBG((CE_CONT, "pci_msix_init: rdip = %p\n", (void *)rdip)); 7860Sstevel@tonic-gate 787965Sgovinda if (pci_get_msi_ctrl(rdip, DDI_INTR_TYPE_MSIX, &msix_ctrl, 7880Sstevel@tonic-gate &caps_ptr, &cfg_hdle) != DDI_SUCCESS) 7890Sstevel@tonic-gate return (NULL); 7900Sstevel@tonic-gate 7910Sstevel@tonic-gate msix_p = kmem_zalloc(sizeof (ddi_intr_msix_t), KM_SLEEP); 7920Sstevel@tonic-gate 7930Sstevel@tonic-gate /* 7940Sstevel@tonic-gate * Initialize the devacc structure 7950Sstevel@tonic-gate */ 7960Sstevel@tonic-gate msix_p->msix_dev_attr.devacc_attr_version = DDI_DEVICE_ATTR_V0; 7970Sstevel@tonic-gate msix_p->msix_dev_attr.devacc_attr_endian_flags = 7980Sstevel@tonic-gate DDI_STRUCTURE_LE_ACC; 7990Sstevel@tonic-gate msix_p->msix_dev_attr.devacc_attr_dataorder = DDI_STRICTORDER_ACC; 8000Sstevel@tonic-gate 801965Sgovinda /* Map the entire MSI-X vector table */ 8021624Spjha msix_p->msix_tbl_offset = PCI_CAP_GET32(cfg_hdle, NULL, caps_ptr, 8034937Sjohnny PCI_MSIX_TBL_OFFSET); 804965Sgovinda 805965Sgovinda if ((breg = pci_msix_bir_index[msix_p->msix_tbl_offset & 806965Sgovinda PCI_MSIX_TBL_BIR_MASK]) == 0xff) 807965Sgovinda goto fail1; 808965Sgovinda 809965Sgovinda msix_p->msix_tbl_offset = msix_p->msix_tbl_offset & 810965Sgovinda ~PCI_MSIX_TBL_BIR_MASK; 811965Sgovinda msix_tbl_size = ((msix_ctrl & PCI_MSIX_TBL_SIZE_MASK) + 1) * 812965Sgovinda PCI_MSIX_VECTOR_SIZE; 8130Sstevel@tonic-gate 814965Sgovinda DDI_INTR_NEXDBG((CE_CONT, "pci_msix_init: MSI-X table offset 0x%x " 815965Sgovinda "breg 0x%x size 0x%lx\n", msix_p->msix_tbl_offset, breg, 816965Sgovinda msix_tbl_size)); 817965Sgovinda 818965Sgovinda if ((ret = ddi_prop_lookup_int_array(DDI_DEV_T_ANY, rdip, 819965Sgovinda DDI_PROP_DONTPASS, "reg", (int **)®s_list, &nregs)) 820965Sgovinda != DDI_PROP_SUCCESS) { 821965Sgovinda DDI_INTR_NEXDBG((CE_CONT, "pci_msix_init: " 822965Sgovinda "ddi_prop_lookup_int_array failed %d\n", ret)); 823965Sgovinda 824965Sgovinda goto fail1; 825965Sgovinda } 826965Sgovinda 827965Sgovinda reg_size = sizeof (pci_regspec_t) / sizeof (int); 8280Sstevel@tonic-gate 829965Sgovinda for (i = 1, rnumber = 0; i < nregs/reg_size; i++) { 830965Sgovinda rp = (pci_regspec_t *)®s_list[i * reg_size]; 831965Sgovinda addr_space = rp->pci_phys_hi & PCI_ADDR_MASK; 832965Sgovinda offset = PCI_REG_REG_G(rp->pci_phys_hi); 833965Sgovinda 834965Sgovinda if ((offset == breg) && ((addr_space == PCI_ADDR_MEM32) || 835965Sgovinda (addr_space == PCI_ADDR_MEM64))) { 836965Sgovinda rnumber = i; 837965Sgovinda break; 838965Sgovinda } 839965Sgovinda } 840965Sgovinda 841965Sgovinda DDI_INTR_NEXDBG((CE_CONT, "pci_msix_init: MSI-X rnum = %d\n", rnumber)); 842965Sgovinda 843965Sgovinda if (rnumber == 0) { 844965Sgovinda DDI_INTR_NEXDBG((CE_CONT, "pci_msix_init: " 845965Sgovinda "no mtaching reg number for offset 0x%x\n", breg)); 846965Sgovinda 847965Sgovinda goto fail2; 848965Sgovinda } 849965Sgovinda 850965Sgovinda if ((ret = ddi_regs_map_setup(rdip, rnumber, 851965Sgovinda (caddr_t *)&msix_p->msix_tbl_addr, msix_p->msix_tbl_offset, 852965Sgovinda msix_tbl_size, &msix_p->msix_dev_attr, 853965Sgovinda &msix_p->msix_tbl_hdl)) != DDI_SUCCESS) { 854965Sgovinda DDI_INTR_NEXDBG((CE_CONT, "pci_msix_init: MSI-X Table " 855965Sgovinda "ddi_regs_map_setup failed %d\n", ret)); 856965Sgovinda 857965Sgovinda goto fail2; 8580Sstevel@tonic-gate } 8590Sstevel@tonic-gate 8600Sstevel@tonic-gate /* 8610Sstevel@tonic-gate * Map in the MSI-X Pending Bit Array 8620Sstevel@tonic-gate */ 8631624Spjha msix_p->msix_pba_offset = PCI_CAP_GET32(cfg_hdle, NULL, caps_ptr, 8644937Sjohnny PCI_MSIX_PBA_OFFSET); 865965Sgovinda 866965Sgovinda if ((breg = pci_msix_bir_index[msix_p->msix_pba_offset & 867965Sgovinda PCI_MSIX_PBA_BIR_MASK]) == 0xff) 868965Sgovinda goto fail3; 869965Sgovinda 870965Sgovinda msix_p->msix_pba_offset = msix_p->msix_pba_offset & 871965Sgovinda ~PCI_MSIX_PBA_BIR_MASK; 872965Sgovinda pba_tbl_size = ((msix_ctrl & PCI_MSIX_TBL_SIZE_MASK) + 1)/8; 873965Sgovinda 874965Sgovinda DDI_INTR_NEXDBG((CE_CONT, "pci_msix_init: PBA table offset 0x%x " 875965Sgovinda "breg 0x%x size 0x%lx\n", msix_p->msix_pba_offset, breg, 876965Sgovinda pba_tbl_size)); 877965Sgovinda 878965Sgovinda for (i = 1, rnumber = 0; i < nregs/reg_size; i++) { 879965Sgovinda rp = (pci_regspec_t *)®s_list[i * reg_size]; 880965Sgovinda addr_space = rp->pci_phys_hi & PCI_ADDR_MASK; 881965Sgovinda offset = PCI_REG_REG_G(rp->pci_phys_hi); 8820Sstevel@tonic-gate 883965Sgovinda if ((offset == breg) && ((addr_space == PCI_ADDR_MEM32) || 884965Sgovinda (addr_space == PCI_ADDR_MEM64))) { 885965Sgovinda rnumber = i; 886965Sgovinda break; 887965Sgovinda } 888965Sgovinda } 889965Sgovinda 890965Sgovinda DDI_INTR_NEXDBG((CE_CONT, "pci_msix_init: PBA rnum = %d\n", rnumber)); 891965Sgovinda 892965Sgovinda if (rnumber == 0) { 893965Sgovinda DDI_INTR_NEXDBG((CE_CONT, "pci_msix_init: " 8941725Segillett "no matching reg number for offset 0x%x\n", breg)); 895965Sgovinda 896965Sgovinda goto fail3; 897965Sgovinda } 898965Sgovinda 899965Sgovinda if ((ret = ddi_regs_map_setup(rdip, rnumber, 900965Sgovinda (caddr_t *)&msix_p->msix_pba_addr, msix_p->msix_pba_offset, 901965Sgovinda pba_tbl_size, &msix_p->msix_dev_attr, 902965Sgovinda &msix_p->msix_pba_hdl)) != DDI_SUCCESS) { 903965Sgovinda DDI_INTR_NEXDBG((CE_CONT, "pci_msix_init: PBA " 904965Sgovinda "ddi_regs_map_setup failed %d\n", ret)); 905965Sgovinda 906965Sgovinda goto fail3; 9070Sstevel@tonic-gate } 9080Sstevel@tonic-gate 9090Sstevel@tonic-gate DDI_INTR_NEXDBG((CE_CONT, "pci_msix_init: msix_p = 0x%p DONE!!\n", 9100Sstevel@tonic-gate (void *)msix_p)); 9110Sstevel@tonic-gate 9124937Sjohnny ddi_prop_free(regs_list); 913965Sgovinda goto done; 914965Sgovinda 915965Sgovinda fail3: 916965Sgovinda ddi_regs_map_free(&msix_p->msix_tbl_hdl); 917965Sgovinda fail2: 918965Sgovinda ddi_prop_free(regs_list); 919965Sgovinda fail1: 920965Sgovinda kmem_free(msix_p, sizeof (ddi_intr_msix_t)); 921965Sgovinda msix_p = NULL; 922965Sgovinda done: 9230Sstevel@tonic-gate pci_config_teardown(&cfg_hdle); 9240Sstevel@tonic-gate return (msix_p); 9250Sstevel@tonic-gate } 9260Sstevel@tonic-gate 9270Sstevel@tonic-gate 9280Sstevel@tonic-gate /* 9290Sstevel@tonic-gate * pci_msix_fini: 9300Sstevel@tonic-gate * This function cleans up previously allocated handles/addrs etc. 9310Sstevel@tonic-gate * It is only called if no more MSI-X interrupts are being used. 9320Sstevel@tonic-gate */ 9330Sstevel@tonic-gate void 9340Sstevel@tonic-gate pci_msix_fini(ddi_intr_msix_t *msix_p) 9350Sstevel@tonic-gate { 9360Sstevel@tonic-gate DDI_INTR_NEXDBG((CE_CONT, "pci_msix_fini: msix_p = 0x%p\n", 9370Sstevel@tonic-gate (void *)msix_p)); 9380Sstevel@tonic-gate 9390Sstevel@tonic-gate ddi_regs_map_free(&msix_p->msix_pba_hdl); 9400Sstevel@tonic-gate ddi_regs_map_free(&msix_p->msix_tbl_hdl); 9410Sstevel@tonic-gate kmem_free(msix_p, sizeof (ddi_intr_msix_t)); 9420Sstevel@tonic-gate } 9430Sstevel@tonic-gate 9440Sstevel@tonic-gate 9451725Segillett /* 9461725Segillett * pci_msix_dup: 9471725Segillett * This function duplicates the address and data pair of one msi-x 9481725Segillett * vector to another msi-x vector. 9491725Segillett */ 9501725Segillett int 9511725Segillett pci_msix_dup(dev_info_t *rdip, int org_inum, int dup_inum) 9521725Segillett { 9531725Segillett ddi_intr_msix_t *msix_p = i_ddi_get_msix(rdip); 9541725Segillett uint64_t addr; 9551725Segillett uint64_t data; 9561725Segillett uintptr_t off; 9571725Segillett 9581725Segillett DDI_INTR_NEXDBG((CE_CONT, "pci_msix_dup: dip = %p, inum = 0x%x, " 9591725Segillett "to_vector = 0x%x\n", (void *)rdip, org_inum, dup_inum)); 9601725Segillett 9611725Segillett /* Offset into the original inum's entry in the MSI-X table */ 9621725Segillett off = (uintptr_t)msix_p->msix_tbl_addr + 9631725Segillett (org_inum * PCI_MSIX_VECTOR_SIZE); 9641725Segillett 9651725Segillett /* For the MSI-X number passed in, get the "data" and "addr" fields */ 9661725Segillett addr = ddi_get64(msix_p->msix_tbl_hdl, 9671725Segillett (uint64_t *)(off + PCI_MSIX_LOWER_ADDR_OFFSET)); 9681725Segillett 9691725Segillett data = ddi_get32(msix_p->msix_tbl_hdl, 9701725Segillett (uint32_t *)(off + PCI_MSIX_DATA_OFFSET)); 9711725Segillett 9721725Segillett /* Program new vector with these existing values */ 9731725Segillett return (pci_msi_configure(rdip, DDI_INTR_TYPE_MSIX, 1, dup_inum, addr, 9741725Segillett data)); 9751725Segillett } 9761725Segillett 9770Sstevel@tonic-gate 9780Sstevel@tonic-gate /* 9790Sstevel@tonic-gate * Next set of routines are for INTx (legacy) PCI interrupt 9800Sstevel@tonic-gate * support only. 9810Sstevel@tonic-gate */ 9820Sstevel@tonic-gate 9830Sstevel@tonic-gate /* 9840Sstevel@tonic-gate * pci_intx_get_cap: 9850Sstevel@tonic-gate * For non-MSI devices that comply to PCI v2.3 or greater; 9860Sstevel@tonic-gate * read the command register. Bit 10 implies interrupt disable. 9870Sstevel@tonic-gate * Set this bit and then read the status register bit 3. 9880Sstevel@tonic-gate * Bit 3 of status register is Interrupt state. 9890Sstevel@tonic-gate * If it is set; then the device supports 'Masking' 9900Sstevel@tonic-gate * 9910Sstevel@tonic-gate * Reset the device back to the original state. 9920Sstevel@tonic-gate */ 9930Sstevel@tonic-gate int 9940Sstevel@tonic-gate pci_intx_get_cap(dev_info_t *dip, int *flagsp) 9950Sstevel@tonic-gate { 9960Sstevel@tonic-gate uint16_t cmdreg, savereg; 9970Sstevel@tonic-gate ddi_acc_handle_t cfg_hdl; 9980Sstevel@tonic-gate #ifdef DEBUG 9990Sstevel@tonic-gate uint16_t statreg; 10000Sstevel@tonic-gate #endif /* DEBUG */ 10010Sstevel@tonic-gate 10020Sstevel@tonic-gate *flagsp = 0; 10030Sstevel@tonic-gate DDI_INTR_NEXDBG((CE_CONT, "pci_intx_get_cap: %s%d: called\n", 10040Sstevel@tonic-gate ddi_driver_name(dip), ddi_get_instance(dip))); 10050Sstevel@tonic-gate 10060Sstevel@tonic-gate if (pci_config_setup(dip, &cfg_hdl) != DDI_SUCCESS) { 10070Sstevel@tonic-gate DDI_INTR_NEXDBG((CE_CONT, "pci_intx_get_cap: can't get " 10080Sstevel@tonic-gate "config handle\n")); 10090Sstevel@tonic-gate return (DDI_FAILURE); 10100Sstevel@tonic-gate } 10110Sstevel@tonic-gate 10120Sstevel@tonic-gate savereg = pci_config_get16(cfg_hdl, PCI_CONF_COMM); 10130Sstevel@tonic-gate DDI_INTR_NEXDBG((CE_CONT, "pci_intx_get_cap: " 10140Sstevel@tonic-gate "command register was 0x%x\n", savereg)); 10150Sstevel@tonic-gate 10160Sstevel@tonic-gate /* Disable the interrupts */ 10170Sstevel@tonic-gate cmdreg = savereg | PCI_COMM_INTX_DISABLE; 10180Sstevel@tonic-gate pci_config_put16(cfg_hdl, PCI_CONF_COMM, cmdreg); 10190Sstevel@tonic-gate 10200Sstevel@tonic-gate #ifdef DEBUG 10210Sstevel@tonic-gate statreg = pci_config_get16(cfg_hdl, PCI_CONF_STAT); 10220Sstevel@tonic-gate DDI_INTR_NEXDBG((CE_CONT, "pci_intx_get_cap: " 10230Sstevel@tonic-gate "status register is 0x%x\n", statreg)); 10240Sstevel@tonic-gate #endif /* DEBUG */ 10250Sstevel@tonic-gate 10260Sstevel@tonic-gate /* Read the bit back */ 10270Sstevel@tonic-gate cmdreg = pci_config_get16(cfg_hdl, PCI_CONF_COMM); 10280Sstevel@tonic-gate DDI_INTR_NEXDBG((CE_CONT, "pci_intx_get_cap: " 10290Sstevel@tonic-gate "command register is now 0x%x\n", cmdreg)); 10300Sstevel@tonic-gate 1031693Sgovinda *flagsp = DDI_INTR_FLAG_LEVEL; 1032693Sgovinda 10330Sstevel@tonic-gate if (cmdreg & PCI_COMM_INTX_DISABLE) { 10340Sstevel@tonic-gate DDI_INTR_NEXDBG((CE_CONT, "pci_intx_get_cap: " 10350Sstevel@tonic-gate "masking supported\n")); 1036693Sgovinda *flagsp |= (DDI_INTR_FLAG_MASKABLE | 1037693Sgovinda DDI_INTR_FLAG_PENDING); 10380Sstevel@tonic-gate } 10390Sstevel@tonic-gate 10400Sstevel@tonic-gate /* Restore the device back to the original state and return */ 10410Sstevel@tonic-gate pci_config_put16(cfg_hdl, PCI_CONF_COMM, savereg); 10420Sstevel@tonic-gate 10430Sstevel@tonic-gate pci_config_teardown(&cfg_hdl); 10440Sstevel@tonic-gate return (DDI_SUCCESS); 10450Sstevel@tonic-gate } 10460Sstevel@tonic-gate 10470Sstevel@tonic-gate 10480Sstevel@tonic-gate /* 10490Sstevel@tonic-gate * pci_intx_clr_mask: 10500Sstevel@tonic-gate * For non-MSI devices that comply to PCI v2.3 or greater; 10510Sstevel@tonic-gate * clear the bit10 in the command register. 10520Sstevel@tonic-gate */ 10530Sstevel@tonic-gate int 10540Sstevel@tonic-gate pci_intx_clr_mask(dev_info_t *dip) 10550Sstevel@tonic-gate { 10560Sstevel@tonic-gate uint16_t cmdreg; 10570Sstevel@tonic-gate ddi_acc_handle_t cfg_hdl; 10580Sstevel@tonic-gate 10590Sstevel@tonic-gate DDI_INTR_NEXDBG((CE_CONT, "pci_intx_clr_mask: %s%d: called\n", 10600Sstevel@tonic-gate ddi_driver_name(dip), ddi_get_instance(dip))); 10610Sstevel@tonic-gate 10620Sstevel@tonic-gate if (pci_config_setup(dip, &cfg_hdl) != DDI_SUCCESS) { 10630Sstevel@tonic-gate DDI_INTR_NEXDBG((CE_CONT, "pci_intx_clr_mask: can't get " 10640Sstevel@tonic-gate "config handle\n")); 10650Sstevel@tonic-gate return (DDI_FAILURE); 10660Sstevel@tonic-gate } 10670Sstevel@tonic-gate 10680Sstevel@tonic-gate cmdreg = pci_config_get16(cfg_hdl, PCI_CONF_COMM); 10690Sstevel@tonic-gate DDI_INTR_NEXDBG((CE_CONT, "pci_intx_clr_mask: " 10700Sstevel@tonic-gate "command register was 0x%x\n", cmdreg)); 10710Sstevel@tonic-gate 10720Sstevel@tonic-gate /* Enable the interrupts */ 10730Sstevel@tonic-gate cmdreg &= ~PCI_COMM_INTX_DISABLE; 10740Sstevel@tonic-gate pci_config_put16(cfg_hdl, PCI_CONF_COMM, cmdreg); 10750Sstevel@tonic-gate pci_config_teardown(&cfg_hdl); 10760Sstevel@tonic-gate return (DDI_SUCCESS); 10770Sstevel@tonic-gate } 10780Sstevel@tonic-gate 10790Sstevel@tonic-gate 10800Sstevel@tonic-gate /* 10810Sstevel@tonic-gate * pci_intx_set_mask: 10820Sstevel@tonic-gate * For non-MSI devices that comply to PCI v2.3 or greater; 10830Sstevel@tonic-gate * set the bit10 in the command register. 10840Sstevel@tonic-gate */ 10850Sstevel@tonic-gate int 10860Sstevel@tonic-gate pci_intx_set_mask(dev_info_t *dip) 10870Sstevel@tonic-gate { 10880Sstevel@tonic-gate uint16_t cmdreg; 10890Sstevel@tonic-gate ddi_acc_handle_t cfg_hdl; 10900Sstevel@tonic-gate 10910Sstevel@tonic-gate DDI_INTR_NEXDBG((CE_CONT, "pci_intx_set_mask: %s%d: called\n", 10920Sstevel@tonic-gate ddi_driver_name(dip), ddi_get_instance(dip))); 10930Sstevel@tonic-gate 10940Sstevel@tonic-gate if (pci_config_setup(dip, &cfg_hdl) != DDI_SUCCESS) { 10950Sstevel@tonic-gate DDI_INTR_NEXDBG((CE_CONT, "pci_intx_set_mask: can't get " 10960Sstevel@tonic-gate "config handle\n")); 10970Sstevel@tonic-gate return (DDI_FAILURE); 10980Sstevel@tonic-gate } 10990Sstevel@tonic-gate 11000Sstevel@tonic-gate cmdreg = pci_config_get16(cfg_hdl, PCI_CONF_COMM); 11010Sstevel@tonic-gate DDI_INTR_NEXDBG((CE_CONT, "pci_intx_set_mask: " 11020Sstevel@tonic-gate "command register was 0x%x\n", cmdreg)); 11030Sstevel@tonic-gate 11040Sstevel@tonic-gate /* Disable the interrupts */ 11050Sstevel@tonic-gate cmdreg |= PCI_COMM_INTX_DISABLE; 11060Sstevel@tonic-gate pci_config_put16(cfg_hdl, PCI_CONF_COMM, cmdreg); 11070Sstevel@tonic-gate pci_config_teardown(&cfg_hdl); 11080Sstevel@tonic-gate return (DDI_SUCCESS); 11090Sstevel@tonic-gate } 11100Sstevel@tonic-gate 11110Sstevel@tonic-gate /* 11120Sstevel@tonic-gate * pci_intx_get_pending: 11130Sstevel@tonic-gate * For non-MSI devices that comply to PCI v2.3 or greater; 11140Sstevel@tonic-gate * read the status register. Bit 3 of status register is 11150Sstevel@tonic-gate * Interrupt state. If it is set; then the interrupt is 11160Sstevel@tonic-gate * 'Pending'. 11170Sstevel@tonic-gate */ 11180Sstevel@tonic-gate int 11190Sstevel@tonic-gate pci_intx_get_pending(dev_info_t *dip, int *pendingp) 11200Sstevel@tonic-gate { 11210Sstevel@tonic-gate uint16_t statreg; 11220Sstevel@tonic-gate ddi_acc_handle_t cfg_hdl; 11230Sstevel@tonic-gate 11240Sstevel@tonic-gate *pendingp = 0; 11250Sstevel@tonic-gate DDI_INTR_NEXDBG((CE_CONT, "pci_intx_get_pending: %s%d: called\n", 11260Sstevel@tonic-gate ddi_driver_name(dip), ddi_get_instance(dip))); 11270Sstevel@tonic-gate 11280Sstevel@tonic-gate if (pci_config_setup(dip, &cfg_hdl) != DDI_SUCCESS) { 11290Sstevel@tonic-gate DDI_INTR_NEXDBG((CE_CONT, "pci_intx_get_pending: can't get " 11300Sstevel@tonic-gate "config handle\n")); 11310Sstevel@tonic-gate return (DDI_FAILURE); 11320Sstevel@tonic-gate } 11330Sstevel@tonic-gate 11340Sstevel@tonic-gate statreg = pci_config_get16(cfg_hdl, PCI_CONF_STAT); 11350Sstevel@tonic-gate 11360Sstevel@tonic-gate if (statreg & PCI_STAT_INTR) { 11370Sstevel@tonic-gate DDI_INTR_NEXDBG((CE_CONT, "pci_intx_get_pending: " 11380Sstevel@tonic-gate "interrupt is pending\n")); 11390Sstevel@tonic-gate *pendingp = 1; 11400Sstevel@tonic-gate } 11410Sstevel@tonic-gate 11420Sstevel@tonic-gate pci_config_teardown(&cfg_hdl); 11430Sstevel@tonic-gate return (DDI_SUCCESS); 11440Sstevel@tonic-gate } 11450Sstevel@tonic-gate 11460Sstevel@tonic-gate 11470Sstevel@tonic-gate /* 11480Sstevel@tonic-gate * pci_intx_get_ispec: 11490Sstevel@tonic-gate * Get intrspec for PCI devices (legacy support) 11500Sstevel@tonic-gate * NOTE: This is moved here from x86 pci.c and is 11510Sstevel@tonic-gate * needed here as pci-ide.c uses it as well 11520Sstevel@tonic-gate */ 11530Sstevel@tonic-gate /*ARGSUSED*/ 11540Sstevel@tonic-gate ddi_intrspec_t 11550Sstevel@tonic-gate pci_intx_get_ispec(dev_info_t *dip, dev_info_t *rdip, int inum) 11560Sstevel@tonic-gate { 11578535Sevan.yan@sun.com int *intpriorities; 11580Sstevel@tonic-gate uint_t num_intpriorities; 11590Sstevel@tonic-gate struct intrspec *ispec; 11600Sstevel@tonic-gate ddi_acc_handle_t cfg_hdl; 11610Sstevel@tonic-gate struct ddi_parent_private_data *pdptr; 11620Sstevel@tonic-gate 11630Sstevel@tonic-gate if ((pdptr = ddi_get_parent_data(rdip)) == NULL) 11640Sstevel@tonic-gate return (NULL); 11650Sstevel@tonic-gate 11660Sstevel@tonic-gate ispec = pdptr->par_intr; 11670Sstevel@tonic-gate ASSERT(ispec); 11680Sstevel@tonic-gate 11690Sstevel@tonic-gate /* check if the intrspec_pri has been initialized */ 11700Sstevel@tonic-gate if (!ispec->intrspec_pri) { 11710Sstevel@tonic-gate if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, rdip, 11720Sstevel@tonic-gate DDI_PROP_DONTPASS, "interrupt-priorities", 11730Sstevel@tonic-gate &intpriorities, &num_intpriorities) == DDI_PROP_SUCCESS) { 11740Sstevel@tonic-gate if (inum < num_intpriorities) 11750Sstevel@tonic-gate ispec->intrspec_pri = intpriorities[inum]; 11760Sstevel@tonic-gate ddi_prop_free(intpriorities); 11770Sstevel@tonic-gate } 11780Sstevel@tonic-gate 11790Sstevel@tonic-gate /* If still no priority, guess based on the class code */ 11808535Sevan.yan@sun.com if (ispec->intrspec_pri == 0) 11818535Sevan.yan@sun.com ispec->intrspec_pri = pci_class_to_pil(rdip); 11820Sstevel@tonic-gate } 11830Sstevel@tonic-gate 11840Sstevel@tonic-gate /* Get interrupt line value */ 11850Sstevel@tonic-gate if (!ispec->intrspec_vec) { 11860Sstevel@tonic-gate if (pci_config_setup(rdip, &cfg_hdl) != DDI_SUCCESS) { 11870Sstevel@tonic-gate DDI_INTR_NEXDBG((CE_CONT, "pci_intx_get_iline: " 11880Sstevel@tonic-gate "can't get config handle\n")); 11892288Sanish return ((ddi_intrspec_t)ispec); 11900Sstevel@tonic-gate } 11910Sstevel@tonic-gate 11920Sstevel@tonic-gate ispec->intrspec_vec = pci_config_get8(cfg_hdl, PCI_CONF_ILINE); 11930Sstevel@tonic-gate pci_config_teardown(&cfg_hdl); 11940Sstevel@tonic-gate } 11950Sstevel@tonic-gate 11960Sstevel@tonic-gate return ((ddi_intrspec_t)ispec); 11970Sstevel@tonic-gate } 11988535Sevan.yan@sun.com 11998535Sevan.yan@sun.com static uint32_t 12008535Sevan.yan@sun.com pci_match_class_val(uint32_t key, pci_class_val_t *rec_p, int nrec, 12018535Sevan.yan@sun.com uint32_t default_val) 12028535Sevan.yan@sun.com { 12038535Sevan.yan@sun.com int i; 12048535Sevan.yan@sun.com 12058535Sevan.yan@sun.com for (i = 0; i < nrec; rec_p++, i++) { 12068535Sevan.yan@sun.com if ((rec_p->class_code & rec_p->class_mask) == 12078535Sevan.yan@sun.com (key & rec_p->class_mask)) 12088535Sevan.yan@sun.com return (rec_p->class_val); 12098535Sevan.yan@sun.com } 12108535Sevan.yan@sun.com 12118535Sevan.yan@sun.com return (default_val); 12128535Sevan.yan@sun.com } 12138535Sevan.yan@sun.com 12148535Sevan.yan@sun.com /* 12158535Sevan.yan@sun.com * Return the configuration value, based on class code and sub class code, 12168535Sevan.yan@sun.com * from the specified property based or default pci_class_val_t table. 12178535Sevan.yan@sun.com */ 12188535Sevan.yan@sun.com uint32_t 12198535Sevan.yan@sun.com pci_class_to_val(dev_info_t *rdip, char *property_name, pci_class_val_t *rec_p, 12208535Sevan.yan@sun.com int nrec, uint32_t default_val) 12218535Sevan.yan@sun.com { 12228535Sevan.yan@sun.com int property_len; 12238535Sevan.yan@sun.com uint32_t class_code; 12248535Sevan.yan@sun.com pci_class_val_t *conf; 12258535Sevan.yan@sun.com uint32_t val = default_val; 12268535Sevan.yan@sun.com 12278535Sevan.yan@sun.com /* 12288535Sevan.yan@sun.com * Use the "class-code" property to get the base and sub class 12298535Sevan.yan@sun.com * codes for the requesting device. 12308535Sevan.yan@sun.com */ 12318535Sevan.yan@sun.com class_code = (uint32_t)ddi_prop_get_int(DDI_DEV_T_ANY, rdip, 12328535Sevan.yan@sun.com DDI_PROP_DONTPASS, "class-code", -1); 12338535Sevan.yan@sun.com 12348535Sevan.yan@sun.com if (class_code == -1) 12358535Sevan.yan@sun.com return (val); 12368535Sevan.yan@sun.com 12378535Sevan.yan@sun.com /* look up the val from the default table */ 12388535Sevan.yan@sun.com val = pci_match_class_val(class_code, rec_p, nrec, val); 12398535Sevan.yan@sun.com 12408535Sevan.yan@sun.com 12418535Sevan.yan@sun.com /* see if there is a more specific property specified value */ 12428535Sevan.yan@sun.com if (ddi_getlongprop(DDI_DEV_T_ANY, rdip, DDI_PROP_NOTPROM, 12438535Sevan.yan@sun.com property_name, (caddr_t)&conf, &property_len)) 12448535Sevan.yan@sun.com return (val); 12458535Sevan.yan@sun.com 12468535Sevan.yan@sun.com if ((property_len % sizeof (pci_class_val_t)) == 0) 12478535Sevan.yan@sun.com val = pci_match_class_val(class_code, conf, 12488535Sevan.yan@sun.com property_len / sizeof (pci_class_val_t), val); 12498535Sevan.yan@sun.com kmem_free(conf, property_len); 12508535Sevan.yan@sun.com return (val); 12518535Sevan.yan@sun.com } 12528535Sevan.yan@sun.com 12538535Sevan.yan@sun.com /* 12548535Sevan.yan@sun.com * pci_class_to_pil: 12558535Sevan.yan@sun.com * 12568535Sevan.yan@sun.com * Return the pil for a given PCI device. 12578535Sevan.yan@sun.com */ 12588535Sevan.yan@sun.com uint32_t 12598535Sevan.yan@sun.com pci_class_to_pil(dev_info_t *rdip) 12608535Sevan.yan@sun.com { 12618535Sevan.yan@sun.com uint32_t pil; 12628535Sevan.yan@sun.com 12638535Sevan.yan@sun.com /* Default pil is 1 */ 12648535Sevan.yan@sun.com pil = pci_class_to_val(rdip, 12658535Sevan.yan@sun.com "pci-class-priorities", pci_default_pil, 12668535Sevan.yan@sun.com sizeof (pci_default_pil) / sizeof (pci_class_val_t), 1); 12678535Sevan.yan@sun.com 12688535Sevan.yan@sun.com /* Range check the result */ 12698535Sevan.yan@sun.com if (pil >= 0xf) 12708535Sevan.yan@sun.com pil = 1; 12718535Sevan.yan@sun.com 12728535Sevan.yan@sun.com return (pil); 12738535Sevan.yan@sun.com } 12748535Sevan.yan@sun.com 12758535Sevan.yan@sun.com /* 12768535Sevan.yan@sun.com * pci_class_to_intr_weight: 12778535Sevan.yan@sun.com * 12788535Sevan.yan@sun.com * Return the intr_weight for a given PCI device. 12798535Sevan.yan@sun.com */ 12808535Sevan.yan@sun.com int32_t 12818535Sevan.yan@sun.com pci_class_to_intr_weight(dev_info_t *rdip) 12828535Sevan.yan@sun.com { 12838535Sevan.yan@sun.com int32_t intr_weight; 12848535Sevan.yan@sun.com 12858535Sevan.yan@sun.com /* default weight is 0% */ 12868535Sevan.yan@sun.com intr_weight = pci_class_to_val(rdip, 12878535Sevan.yan@sun.com "pci-class-intr-weights", pci_default_intr_weight, 12888535Sevan.yan@sun.com sizeof (pci_default_intr_weight) / sizeof (pci_class_val_t), 0); 12898535Sevan.yan@sun.com 12908535Sevan.yan@sun.com /* range check the result */ 12918535Sevan.yan@sun.com if (intr_weight < 0) 12928535Sevan.yan@sun.com intr_weight = 0; 12938535Sevan.yan@sun.com if (intr_weight > 1000) 12948535Sevan.yan@sun.com intr_weight = 1000; 12958535Sevan.yan@sun.com 12968535Sevan.yan@sun.com return (intr_weight); 12978535Sevan.yan@sun.com } 1298