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 /* 223869Srm78299 * Copyright 2007 Sun Microsystems, Inc. All rights reserved. 230Sstevel@tonic-gate * Use is subject to license terms. 240Sstevel@tonic-gate */ 250Sstevel@tonic-gate 260Sstevel@tonic-gate #pragma ident "%Z%%M% %I% %E% SMI" 270Sstevel@tonic-gate 280Sstevel@tonic-gate /* 290Sstevel@tonic-gate * Support for MSI, MSIX and INTx 300Sstevel@tonic-gate */ 310Sstevel@tonic-gate 320Sstevel@tonic-gate #include <sys/conf.h> 330Sstevel@tonic-gate #include <sys/debug.h> 340Sstevel@tonic-gate #include <sys/pci.h> 351624Spjha #include <sys/pci_cap.h> 360Sstevel@tonic-gate #include <sys/sunddi.h> 370Sstevel@tonic-gate #include <sys/bitmap.h> 380Sstevel@tonic-gate 390Sstevel@tonic-gate /* 40965Sgovinda * MSI-X BIR Index Table: 41965Sgovinda * 42965Sgovinda * BAR indicator register (BIR) to Base Address register. 43965Sgovinda */ 44965Sgovinda static uchar_t pci_msix_bir_index[8] = {0x10, 0x14, 0x18, 0x1c, 45965Sgovinda 0x20, 0x24, 0xff, 0xff}; 46965Sgovinda 47965Sgovinda /* 480Sstevel@tonic-gate * Library utility functions 490Sstevel@tonic-gate */ 500Sstevel@tonic-gate 510Sstevel@tonic-gate /* 520Sstevel@tonic-gate * pci_get_msi_ctrl: 530Sstevel@tonic-gate * 540Sstevel@tonic-gate * Helper function that returns with 'cfg_hdl', MSI/X ctrl pointer, 550Sstevel@tonic-gate * and caps_ptr for MSI/X if these are found. 560Sstevel@tonic-gate */ 570Sstevel@tonic-gate static int 580Sstevel@tonic-gate pci_get_msi_ctrl(dev_info_t *dip, int type, ushort_t *msi_ctrl, 591624Spjha ushort_t *caps_ptr, ddi_acc_handle_t *h) 600Sstevel@tonic-gate { 610Sstevel@tonic-gate *msi_ctrl = *caps_ptr = 0; 620Sstevel@tonic-gate 631624Spjha if (pci_config_setup(dip, h) != DDI_SUCCESS) { 640Sstevel@tonic-gate DDI_INTR_NEXDBG((CE_CONT, "pci_get_msi_ctrl: " 651624Spjha "%s%d can't get config handle", 660Sstevel@tonic-gate ddi_driver_name(dip), ddi_get_instance(dip))); 670Sstevel@tonic-gate 680Sstevel@tonic-gate return (DDI_FAILURE); 690Sstevel@tonic-gate } 700Sstevel@tonic-gate 711624Spjha if ((PCI_CAP_LOCATE(*h, PCI_CAP_ID_MSI, caps_ptr) == DDI_SUCCESS) && 72*4937Sjohnny (type == DDI_INTR_TYPE_MSI)) { 731624Spjha if ((*msi_ctrl = PCI_CAP_GET16(*h, NULL, *caps_ptr, 74*4937Sjohnny PCI_MSI_CTRL)) == PCI_CAP_EINVAL16) 751624Spjha goto done; 760Sstevel@tonic-gate 771624Spjha DDI_INTR_NEXDBG((CE_CONT, "pci_get_msi_ctrl: MSI " 781624Spjha "caps_ptr=%x msi_ctrl=%x\n", *caps_ptr, *msi_ctrl)); 791624Spjha 801624Spjha return (DDI_SUCCESS); 810Sstevel@tonic-gate } 820Sstevel@tonic-gate 831624Spjha if ((PCI_CAP_LOCATE(*h, PCI_CAP_ID_MSI_X, caps_ptr) == DDI_SUCCESS) && 84*4937Sjohnny (type == DDI_INTR_TYPE_MSIX)) { 851624Spjha if ((*msi_ctrl = PCI_CAP_GET16(*h, NULL, *caps_ptr, 86*4937Sjohnny PCI_MSIX_CTRL)) == PCI_CAP_EINVAL16) 871624Spjha goto done; 880Sstevel@tonic-gate 891624Spjha DDI_INTR_NEXDBG((CE_CONT, "pci_get_msi_ctrl: MSI-X " 901624Spjha "caps_ptr=%x msi_ctrl=%x\n", *caps_ptr, *msi_ctrl)); 910Sstevel@tonic-gate 921624Spjha return (DDI_SUCCESS); 930Sstevel@tonic-gate } 940Sstevel@tonic-gate 951624Spjha done: 961624Spjha pci_config_teardown(h); 970Sstevel@tonic-gate return (DDI_FAILURE); 980Sstevel@tonic-gate } 990Sstevel@tonic-gate 1000Sstevel@tonic-gate 1010Sstevel@tonic-gate /* 1020Sstevel@tonic-gate * pci_msi_get_cap: 1030Sstevel@tonic-gate * 1040Sstevel@tonic-gate * Get the capabilities of the MSI/X interrupt 1050Sstevel@tonic-gate */ 1060Sstevel@tonic-gate int 1070Sstevel@tonic-gate pci_msi_get_cap(dev_info_t *rdip, int type, int *flagsp) 1080Sstevel@tonic-gate { 1090Sstevel@tonic-gate ushort_t caps_ptr, msi_ctrl; 1100Sstevel@tonic-gate ddi_acc_handle_t cfg_hdle; 1110Sstevel@tonic-gate 1120Sstevel@tonic-gate DDI_INTR_NEXDBG((CE_CONT, "pci_msi_get_cap: rdip = 0x%p\n", 1130Sstevel@tonic-gate (void *)rdip)); 1140Sstevel@tonic-gate 1150Sstevel@tonic-gate *flagsp = 0; 1160Sstevel@tonic-gate 1170Sstevel@tonic-gate if (pci_get_msi_ctrl(rdip, type, &msi_ctrl, 1180Sstevel@tonic-gate &caps_ptr, &cfg_hdle) != DDI_SUCCESS) 1190Sstevel@tonic-gate return (DDI_FAILURE); 1200Sstevel@tonic-gate 1210Sstevel@tonic-gate if (type == DDI_INTR_TYPE_MSI) { 1220Sstevel@tonic-gate if (msi_ctrl & PCI_MSI_64BIT_MASK) 1230Sstevel@tonic-gate *flagsp |= DDI_INTR_FLAG_MSI64; 1240Sstevel@tonic-gate if (msi_ctrl & PCI_MSI_PVM_MASK) 1250Sstevel@tonic-gate *flagsp |= (DDI_INTR_FLAG_MASKABLE | 1260Sstevel@tonic-gate DDI_INTR_FLAG_PENDING); 1270Sstevel@tonic-gate else 1280Sstevel@tonic-gate *flagsp |= DDI_INTR_FLAG_BLOCK; 1290Sstevel@tonic-gate } else if (type == DDI_INTR_TYPE_MSIX) { 1300Sstevel@tonic-gate /* MSI-X supports PVM, 64bit by default */ 1310Sstevel@tonic-gate *flagsp |= (DDI_INTR_FLAG_MASKABLE | DDI_INTR_FLAG_MSI64 | 1320Sstevel@tonic-gate DDI_INTR_FLAG_PENDING); 1330Sstevel@tonic-gate } 1340Sstevel@tonic-gate 1350Sstevel@tonic-gate *flagsp |= DDI_INTR_FLAG_EDGE; 1360Sstevel@tonic-gate 1370Sstevel@tonic-gate DDI_INTR_NEXDBG((CE_CONT, "pci_msi_get_cap: flags = 0x%x\n", *flagsp)); 1380Sstevel@tonic-gate 1390Sstevel@tonic-gate pci_config_teardown(&cfg_hdle); 1400Sstevel@tonic-gate return (DDI_SUCCESS); 1410Sstevel@tonic-gate } 1420Sstevel@tonic-gate 1430Sstevel@tonic-gate 1440Sstevel@tonic-gate /* 1450Sstevel@tonic-gate * pci_msi_configure: 1460Sstevel@tonic-gate * 1470Sstevel@tonic-gate * Configure address/data and number MSI/Xs fields in the MSI/X 1480Sstevel@tonic-gate * capability structure. 1490Sstevel@tonic-gate */ 1500Sstevel@tonic-gate /* ARGSUSED */ 1510Sstevel@tonic-gate int 1520Sstevel@tonic-gate pci_msi_configure(dev_info_t *rdip, int type, int count, int inum, 1530Sstevel@tonic-gate uint64_t addr, uint64_t data) 1540Sstevel@tonic-gate { 1550Sstevel@tonic-gate ushort_t caps_ptr, msi_ctrl; 1561624Spjha ddi_acc_handle_t h; 1570Sstevel@tonic-gate 158965Sgovinda DDI_INTR_NEXDBG((CE_CONT, "pci_msi_configure: rdip = 0x%p type 0x%x " 159965Sgovinda "count 0x%x inum 0x%x addr 0x%" PRIx64 " data 0x%" PRIx64 "\n", 160965Sgovinda (void *)rdip, type, count, inum, addr, data)); 1610Sstevel@tonic-gate 1620Sstevel@tonic-gate if (pci_get_msi_ctrl(rdip, type, &msi_ctrl, 1631624Spjha &caps_ptr, &h) != DDI_SUCCESS) 1640Sstevel@tonic-gate return (DDI_FAILURE); 1650Sstevel@tonic-gate 1660Sstevel@tonic-gate if (type == DDI_INTR_TYPE_MSI) { 1670Sstevel@tonic-gate /* Set the bits to inform how many MSIs are enabled */ 1680Sstevel@tonic-gate msi_ctrl |= ((highbit(count) -1) << PCI_MSI_MME_SHIFT); 1691624Spjha PCI_CAP_PUT16(h, NULL, caps_ptr, PCI_MSI_CTRL, msi_ctrl); 1700Sstevel@tonic-gate 171965Sgovinda DDI_INTR_NEXDBG((CE_CONT, "pci_msi_configure: msi_ctrl = %x\n", 1721624Spjha PCI_CAP_GET16(h, NULL, caps_ptr, PCI_MSI_CTRL))); 173965Sgovinda 1740Sstevel@tonic-gate /* Set the "data" and "addr" bits */ 1751624Spjha PCI_CAP_PUT32(h, NULL, caps_ptr, PCI_MSI_ADDR_OFFSET, addr); 1760Sstevel@tonic-gate 177965Sgovinda DDI_INTR_NEXDBG((CE_CONT, "pci_msi_configure: msi_addr = %x\n", 178*4937Sjohnny PCI_CAP_GET32(h, NULL, caps_ptr, PCI_MSI_ADDR_OFFSET))); 179965Sgovinda 1800Sstevel@tonic-gate if (msi_ctrl & PCI_MSI_64BIT_MASK) { 1811624Spjha PCI_CAP_PUT32(h, NULL, caps_ptr, PCI_MSI_ADDR_OFFSET 182*4937Sjohnny + 4, addr >> 32); 183965Sgovinda 1841624Spjha DDI_INTR_NEXDBG((CE_CONT, "pci_msi_configure: upper " 185*4937Sjohnny "32bit msi_addr = %x\n", PCI_CAP_GET32(h, NULL, 186*4937Sjohnny caps_ptr, PCI_MSI_ADDR_OFFSET + 4))); 187965Sgovinda 1881624Spjha PCI_CAP_PUT16(h, NULL, caps_ptr, PCI_MSI_64BIT_DATA, 189*4937Sjohnny data); 190965Sgovinda 1911624Spjha DDI_INTR_NEXDBG((CE_CONT, "pci_msi_configure: msi_data " 192*4937Sjohnny "= %x\n", PCI_CAP_GET16(h, NULL, caps_ptr, 193*4937Sjohnny PCI_MSI_64BIT_DATA))); 1940Sstevel@tonic-gate } else { 1951624Spjha PCI_CAP_PUT16(h, NULL, caps_ptr, PCI_MSI_32BIT_DATA, 196*4937Sjohnny data); 197965Sgovinda 1981624Spjha DDI_INTR_NEXDBG((CE_CONT, "pci_msi_configure: msi_data " 199*4937Sjohnny "= %x\n", PCI_CAP_GET16(h, NULL, caps_ptr, 200*4937Sjohnny PCI_MSI_32BIT_DATA))); 2010Sstevel@tonic-gate } 2020Sstevel@tonic-gate } else if (type == DDI_INTR_TYPE_MSIX) { 20342Sagiri uintptr_t off; 2040Sstevel@tonic-gate ddi_intr_msix_t *msix_p = i_ddi_get_msix(rdip); 2050Sstevel@tonic-gate 2060Sstevel@tonic-gate /* Offset into the "inum"th entry in the MSI-X table */ 20742Sagiri off = (uintptr_t)msix_p->msix_tbl_addr + 208965Sgovinda (inum * PCI_MSIX_VECTOR_SIZE); 2090Sstevel@tonic-gate 2100Sstevel@tonic-gate /* Set the "data" and "addr" bits */ 2110Sstevel@tonic-gate ddi_put32(msix_p->msix_tbl_hdl, 212965Sgovinda (uint32_t *)(off + PCI_MSIX_DATA_OFFSET), data); 213965Sgovinda 214965Sgovinda ddi_put64(msix_p->msix_tbl_hdl, 215965Sgovinda (uint64_t *)(off + PCI_MSIX_LOWER_ADDR_OFFSET), addr); 2160Sstevel@tonic-gate 217965Sgovinda DDI_INTR_NEXDBG((CE_CONT, "pci_msi_configure: " 218965Sgovinda "msix_addr 0x%" PRIx64 " msix_data 0x%x\n", 219965Sgovinda ddi_get64(msix_p->msix_tbl_hdl, 220965Sgovinda (uint64_t *)(off + PCI_MSIX_LOWER_ADDR_OFFSET)), 221965Sgovinda ddi_get32(msix_p->msix_tbl_hdl, 222965Sgovinda (uint32_t *)(off + PCI_MSIX_DATA_OFFSET)))); 2230Sstevel@tonic-gate } 2240Sstevel@tonic-gate 2251624Spjha pci_config_teardown(&h); 2260Sstevel@tonic-gate return (DDI_SUCCESS); 2270Sstevel@tonic-gate } 2280Sstevel@tonic-gate 2290Sstevel@tonic-gate 2300Sstevel@tonic-gate /* 2310Sstevel@tonic-gate * pci_msi_unconfigure: 2320Sstevel@tonic-gate * 2330Sstevel@tonic-gate * Unconfigure address/data and number MSI/Xs fields in the MSI/X 2340Sstevel@tonic-gate * capability structure. 2350Sstevel@tonic-gate */ 2360Sstevel@tonic-gate /* ARGSUSED */ 2370Sstevel@tonic-gate int 2380Sstevel@tonic-gate pci_msi_unconfigure(dev_info_t *rdip, int type, int inum) 2390Sstevel@tonic-gate { 2400Sstevel@tonic-gate ushort_t msi_ctrl, caps_ptr; 2411624Spjha ddi_acc_handle_t h; 2420Sstevel@tonic-gate 2432755Segillett DDI_INTR_NEXDBG((CE_CONT, "pci_msi_unconfigure: rdip = 0x%p type 0x%x " 2442755Segillett "inum 0x%x\n", (void *)rdip, type, inum)); 2450Sstevel@tonic-gate 2461624Spjha if (pci_get_msi_ctrl(rdip, type, &msi_ctrl, &caps_ptr, &h) != 2472755Segillett DDI_SUCCESS) 2480Sstevel@tonic-gate return (DDI_FAILURE); 2490Sstevel@tonic-gate 2500Sstevel@tonic-gate if (type == DDI_INTR_TYPE_MSI) { 2510Sstevel@tonic-gate msi_ctrl &= (~PCI_MSI_MME_MASK); 2521624Spjha PCI_CAP_PUT16(h, NULL, caps_ptr, PCI_MSI_CTRL, msi_ctrl); 2530Sstevel@tonic-gate 2541624Spjha PCI_CAP_PUT32(h, NULL, caps_ptr, PCI_MSI_ADDR_OFFSET, 0); 2551624Spjha 2560Sstevel@tonic-gate if (msi_ctrl & PCI_MSI_64BIT_MASK) { 2571624Spjha PCI_CAP_PUT16(h, NULL, caps_ptr, PCI_MSI_64BIT_DATA, 258*4937Sjohnny 0); 2591624Spjha PCI_CAP_PUT32(h, NULL, caps_ptr, PCI_MSI_ADDR_OFFSET 260*4937Sjohnny + 4, 0); 2610Sstevel@tonic-gate } else { 2621624Spjha PCI_CAP_PUT16(h, NULL, caps_ptr, PCI_MSI_32BIT_DATA, 263*4937Sjohnny 0); 2640Sstevel@tonic-gate } 2650Sstevel@tonic-gate 2661624Spjha DDI_INTR_NEXDBG((CE_CONT, "pci_msi_unconfigure: msi_ctrl " 2671624Spjha "= %x\n", PCI_CAP_GET16(h, NULL, caps_ptr, PCI_MSI_CTRL))); 2680Sstevel@tonic-gate 2690Sstevel@tonic-gate } else if (type == DDI_INTR_TYPE_MSIX) { 27042Sagiri uintptr_t off; 2710Sstevel@tonic-gate ddi_intr_msix_t *msix_p = i_ddi_get_msix(rdip); 2720Sstevel@tonic-gate 2730Sstevel@tonic-gate /* Offset into the "inum"th entry in the MSI-X table */ 27442Sagiri off = (uintptr_t)msix_p->msix_tbl_addr + 275965Sgovinda (inum * PCI_MSIX_VECTOR_SIZE); 2760Sstevel@tonic-gate 2770Sstevel@tonic-gate /* Reset the "data" and "addr" bits */ 2780Sstevel@tonic-gate ddi_put32(msix_p->msix_tbl_hdl, 279965Sgovinda (uint32_t *)(off + PCI_MSIX_DATA_OFFSET), 0); 2800Sstevel@tonic-gate 2812755Segillett ddi_put64(msix_p->msix_tbl_hdl, 2822755Segillett (uint64_t *)(off + PCI_MSIX_LOWER_ADDR_OFFSET), 0); 2830Sstevel@tonic-gate } 2840Sstevel@tonic-gate 2851624Spjha pci_config_teardown(&h); 2860Sstevel@tonic-gate return (DDI_SUCCESS); 2870Sstevel@tonic-gate } 2880Sstevel@tonic-gate 2890Sstevel@tonic-gate 2900Sstevel@tonic-gate /* 2910Sstevel@tonic-gate * pci_is_msi_enabled: 2920Sstevel@tonic-gate * 2930Sstevel@tonic-gate * This function returns DDI_SUCCESS if MSI/X is already enabled, otherwise 2940Sstevel@tonic-gate * it returns DDI_FAILURE. 2950Sstevel@tonic-gate */ 2960Sstevel@tonic-gate int 2970Sstevel@tonic-gate pci_is_msi_enabled(dev_info_t *rdip, int type) 2980Sstevel@tonic-gate { 2990Sstevel@tonic-gate ushort_t caps_ptr, msi_ctrl; 3000Sstevel@tonic-gate ddi_acc_handle_t cfg_hdle; 3010Sstevel@tonic-gate int ret = DDI_FAILURE; 3020Sstevel@tonic-gate 3030Sstevel@tonic-gate DDI_INTR_NEXDBG((CE_CONT, "pci_is_msi_enabled: rdip = 0x%p, " 3040Sstevel@tonic-gate "type = 0x%x\n", (void *)rdip, type)); 3050Sstevel@tonic-gate 3060Sstevel@tonic-gate if (pci_get_msi_ctrl(rdip, type, &msi_ctrl, 3070Sstevel@tonic-gate &caps_ptr, &cfg_hdle) != DDI_SUCCESS) 3080Sstevel@tonic-gate return (DDI_FAILURE); 3090Sstevel@tonic-gate 3100Sstevel@tonic-gate if ((type == DDI_INTR_TYPE_MSI) && (msi_ctrl & PCI_MSI_ENABLE_BIT)) 3110Sstevel@tonic-gate ret = DDI_SUCCESS; 3120Sstevel@tonic-gate 3130Sstevel@tonic-gate if ((type == DDI_INTR_TYPE_MSIX) && (msi_ctrl & PCI_MSIX_ENABLE_BIT)) 3140Sstevel@tonic-gate ret = DDI_SUCCESS; 3150Sstevel@tonic-gate 3160Sstevel@tonic-gate pci_config_teardown(&cfg_hdle); 3170Sstevel@tonic-gate return (ret); 3180Sstevel@tonic-gate } 3190Sstevel@tonic-gate 3200Sstevel@tonic-gate 3210Sstevel@tonic-gate /* 3220Sstevel@tonic-gate * pci_msi_enable_mode: 3230Sstevel@tonic-gate * 3240Sstevel@tonic-gate * This function sets the MSI_ENABLE bit in the capability structure 3250Sstevel@tonic-gate * (for MSI) and MSIX_ENABLE bit in the MSI-X capability structure. 3262755Segillett * 3272755Segillett * NOTE: It is the nexus driver's responsibility to clear the MSI/X 3282755Segillett * interrupt's mask bit in the MSI/X capability structure before the 3292755Segillett * interrupt can be used. 3300Sstevel@tonic-gate */ 3310Sstevel@tonic-gate int 3322755Segillett pci_msi_enable_mode(dev_info_t *rdip, int type) 3330Sstevel@tonic-gate { 3340Sstevel@tonic-gate ushort_t caps_ptr, msi_ctrl; 3350Sstevel@tonic-gate ddi_acc_handle_t cfg_hdle; 3360Sstevel@tonic-gate 3372755Segillett DDI_INTR_NEXDBG((CE_CONT, "pci_msi_enable_mode: rdip = 0x%p\n", 3382755Segillett (void *)rdip)); 3390Sstevel@tonic-gate 3400Sstevel@tonic-gate if (pci_get_msi_ctrl(rdip, type, &msi_ctrl, 3410Sstevel@tonic-gate &caps_ptr, &cfg_hdle) != DDI_SUCCESS) 3420Sstevel@tonic-gate return (DDI_FAILURE); 3430Sstevel@tonic-gate 3440Sstevel@tonic-gate if (type == DDI_INTR_TYPE_MSI) { 3450Sstevel@tonic-gate if (msi_ctrl & PCI_MSI_ENABLE_BIT) 3460Sstevel@tonic-gate goto finished; 3470Sstevel@tonic-gate 3480Sstevel@tonic-gate msi_ctrl |= PCI_MSI_ENABLE_BIT; 3491624Spjha PCI_CAP_PUT16(cfg_hdle, NULL, caps_ptr, PCI_MSI_CTRL, msi_ctrl); 3500Sstevel@tonic-gate 3510Sstevel@tonic-gate } else if (type == DDI_INTR_TYPE_MSIX) { 3520Sstevel@tonic-gate if (msi_ctrl & PCI_MSIX_ENABLE_BIT) 3530Sstevel@tonic-gate goto finished; 3540Sstevel@tonic-gate 3550Sstevel@tonic-gate msi_ctrl |= PCI_MSIX_ENABLE_BIT; 3561624Spjha PCI_CAP_PUT16(cfg_hdle, NULL, caps_ptr, PCI_MSIX_CTRL, 3572755Segillett msi_ctrl); 3580Sstevel@tonic-gate } 3590Sstevel@tonic-gate 3600Sstevel@tonic-gate finished: 3610Sstevel@tonic-gate DDI_INTR_NEXDBG((CE_CONT, "pci_msi_enable_mode: msi_ctrl = %x\n", 3620Sstevel@tonic-gate msi_ctrl)); 3630Sstevel@tonic-gate 3640Sstevel@tonic-gate pci_config_teardown(&cfg_hdle); 3650Sstevel@tonic-gate return (DDI_SUCCESS); 3660Sstevel@tonic-gate } 3670Sstevel@tonic-gate 3680Sstevel@tonic-gate 3690Sstevel@tonic-gate /* 3700Sstevel@tonic-gate * pci_msi_disable_mode: 3710Sstevel@tonic-gate * 3720Sstevel@tonic-gate * This function resets the MSI_ENABLE bit in the capability structure 3730Sstevel@tonic-gate * (for MSI) and MSIX_ENABLE bit in the MSI-X capability structure. 3742755Segillett * 3752755Segillett * NOTE: It is the nexus driver's responsibility to set the MSI/X 3762755Segillett * interrupt's mask bit in the MSI/X capability structure before the 3772755Segillett * interrupt can be disabled. 3780Sstevel@tonic-gate */ 3790Sstevel@tonic-gate int 3802755Segillett pci_msi_disable_mode(dev_info_t *rdip, int type, uint_t flags) 3810Sstevel@tonic-gate { 3820Sstevel@tonic-gate ushort_t caps_ptr, msi_ctrl; 3830Sstevel@tonic-gate ddi_acc_handle_t cfg_hdle; 3840Sstevel@tonic-gate 3850Sstevel@tonic-gate DDI_INTR_NEXDBG((CE_CONT, "pci_msi_disable_mode: rdip = 0x%p " 3862755Segillett "flags = 0x%x\n", (void *)rdip, flags)); 3872755Segillett 3882755Segillett /* 3892755Segillett * Do not turn off the master enable bit if other interrupts are 3902755Segillett * still active. 3912755Segillett */ 3922755Segillett if ((flags != DDI_INTR_FLAG_BLOCK) && 3932755Segillett ((i_ddi_intr_get_current_nintrs(rdip) - 1) > 0)) 3942755Segillett return (DDI_SUCCESS); 3950Sstevel@tonic-gate 3960Sstevel@tonic-gate if (pci_get_msi_ctrl(rdip, type, &msi_ctrl, 3970Sstevel@tonic-gate &caps_ptr, &cfg_hdle) != DDI_SUCCESS) 3980Sstevel@tonic-gate return (DDI_FAILURE); 3990Sstevel@tonic-gate 4000Sstevel@tonic-gate /* Reset the "enable" bit */ 4010Sstevel@tonic-gate if (type == DDI_INTR_TYPE_MSI) { 4020Sstevel@tonic-gate if (!(msi_ctrl & PCI_MSI_ENABLE_BIT)) 4030Sstevel@tonic-gate goto finished; 4040Sstevel@tonic-gate msi_ctrl &= ~PCI_MSI_ENABLE_BIT; 4051624Spjha PCI_CAP_PUT16(cfg_hdle, NULL, caps_ptr, PCI_MSI_CTRL, msi_ctrl); 4060Sstevel@tonic-gate } else if (type == DDI_INTR_TYPE_MSIX) { 4070Sstevel@tonic-gate if (!(msi_ctrl & PCI_MSIX_ENABLE_BIT)) 4080Sstevel@tonic-gate goto finished; 4090Sstevel@tonic-gate 4102755Segillett msi_ctrl &= ~PCI_MSIX_ENABLE_BIT; 4112755Segillett PCI_CAP_PUT16(cfg_hdle, NULL, caps_ptr, PCI_MSIX_CTRL, 4122755Segillett msi_ctrl); 4130Sstevel@tonic-gate } 4140Sstevel@tonic-gate 4150Sstevel@tonic-gate finished: 4160Sstevel@tonic-gate DDI_INTR_NEXDBG((CE_CONT, "pci_msi_disable_mode: msi_ctrl = %x\n", 4170Sstevel@tonic-gate msi_ctrl)); 4180Sstevel@tonic-gate 4190Sstevel@tonic-gate pci_config_teardown(&cfg_hdle); 4200Sstevel@tonic-gate return (DDI_SUCCESS); 4210Sstevel@tonic-gate } 4220Sstevel@tonic-gate 4230Sstevel@tonic-gate 4240Sstevel@tonic-gate /* 4250Sstevel@tonic-gate * pci_msi_set_mask: 4260Sstevel@tonic-gate * 4270Sstevel@tonic-gate * Set the mask bit in the MSI/X capability structure 4280Sstevel@tonic-gate */ 4290Sstevel@tonic-gate /* ARGSUSED */ 4300Sstevel@tonic-gate int 4310Sstevel@tonic-gate pci_msi_set_mask(dev_info_t *rdip, int type, int inum) 4320Sstevel@tonic-gate { 4330Sstevel@tonic-gate int offset; 4340Sstevel@tonic-gate int ret = DDI_FAILURE; 4350Sstevel@tonic-gate ushort_t caps_ptr, msi_ctrl; 4360Sstevel@tonic-gate ddi_acc_handle_t cfg_hdle; 437965Sgovinda uint32_t mask_bits; 4380Sstevel@tonic-gate 4390Sstevel@tonic-gate DDI_INTR_NEXDBG((CE_CONT, "pci_msi_set_mask: rdip = 0x%p, " 4400Sstevel@tonic-gate "type = 0x%x\n", (void *)rdip, type)); 4410Sstevel@tonic-gate 4420Sstevel@tonic-gate if (pci_get_msi_ctrl(rdip, type, &msi_ctrl, 4430Sstevel@tonic-gate &caps_ptr, &cfg_hdle) != DDI_SUCCESS) 4440Sstevel@tonic-gate return (DDI_FAILURE); 4450Sstevel@tonic-gate 4460Sstevel@tonic-gate if (type == DDI_INTR_TYPE_MSI) { 4470Sstevel@tonic-gate if (!(msi_ctrl & PCI_MSI_PVM_MASK)) 4480Sstevel@tonic-gate goto done; 4490Sstevel@tonic-gate 4500Sstevel@tonic-gate offset = (msi_ctrl & PCI_MSI_64BIT_MASK) ? 4510Sstevel@tonic-gate PCI_MSI_64BIT_MASKBITS : PCI_MSI_32BIT_MASK; 4520Sstevel@tonic-gate 4531624Spjha if ((mask_bits = PCI_CAP_GET32(cfg_hdle, NULL, caps_ptr, 454*4937Sjohnny offset)) == PCI_CAP_EINVAL32) 4551624Spjha goto done; 4560Sstevel@tonic-gate 4570Sstevel@tonic-gate mask_bits |= (1 << inum); 4580Sstevel@tonic-gate 4591624Spjha PCI_CAP_PUT32(cfg_hdle, NULL, caps_ptr, offset, mask_bits); 4600Sstevel@tonic-gate 4610Sstevel@tonic-gate } else if (type == DDI_INTR_TYPE_MSIX) { 46242Sagiri uintptr_t off; 4630Sstevel@tonic-gate ddi_intr_msix_t *msix_p; 4640Sstevel@tonic-gate 4650Sstevel@tonic-gate /* Set function mask */ 4660Sstevel@tonic-gate if (msi_ctrl & PCI_MSIX_FUNCTION_MASK) { 4670Sstevel@tonic-gate ret = DDI_SUCCESS; 4680Sstevel@tonic-gate goto done; 4690Sstevel@tonic-gate } 4700Sstevel@tonic-gate 4710Sstevel@tonic-gate msix_p = i_ddi_get_msix(rdip); 4720Sstevel@tonic-gate 4730Sstevel@tonic-gate /* Offset into the "inum"th entry in the MSI-X table */ 474965Sgovinda off = (uintptr_t)msix_p->msix_tbl_addr + (inum * 4750Sstevel@tonic-gate PCI_MSIX_VECTOR_SIZE) + PCI_MSIX_VECTOR_CTRL_OFFSET; 4760Sstevel@tonic-gate 4770Sstevel@tonic-gate /* Set the Mask bit */ 478965Sgovinda ddi_put32(msix_p->msix_tbl_hdl, (uint32_t *)off, 0x1); 4790Sstevel@tonic-gate } 4800Sstevel@tonic-gate 4810Sstevel@tonic-gate ret = DDI_SUCCESS; 4820Sstevel@tonic-gate done: 4830Sstevel@tonic-gate pci_config_teardown(&cfg_hdle); 4840Sstevel@tonic-gate return (ret); 4850Sstevel@tonic-gate } 4860Sstevel@tonic-gate 4870Sstevel@tonic-gate 4880Sstevel@tonic-gate /* 4890Sstevel@tonic-gate * pci_msi_clr_mask: 4900Sstevel@tonic-gate * 4910Sstevel@tonic-gate * Clear the mask bit in the MSI/X capability structure 4920Sstevel@tonic-gate */ 4930Sstevel@tonic-gate /* ARGSUSED */ 4940Sstevel@tonic-gate int 4950Sstevel@tonic-gate pci_msi_clr_mask(dev_info_t *rdip, int type, int inum) 4960Sstevel@tonic-gate { 4970Sstevel@tonic-gate ushort_t caps_ptr, msi_ctrl; 4980Sstevel@tonic-gate ddi_acc_handle_t cfg_hdle; 4990Sstevel@tonic-gate int offset; 5000Sstevel@tonic-gate int ret = DDI_FAILURE; 501965Sgovinda uint32_t mask_bits; 5020Sstevel@tonic-gate 5030Sstevel@tonic-gate DDI_INTR_NEXDBG((CE_CONT, "pci_msi_clr_mask: rdip = 0x%p, " 5040Sstevel@tonic-gate "type = 0x%x\n", (void *)rdip, type)); 5050Sstevel@tonic-gate 5060Sstevel@tonic-gate if (pci_get_msi_ctrl(rdip, type, &msi_ctrl, 5070Sstevel@tonic-gate &caps_ptr, &cfg_hdle) != DDI_SUCCESS) 5080Sstevel@tonic-gate return (DDI_FAILURE); 5090Sstevel@tonic-gate 5100Sstevel@tonic-gate if (type == DDI_INTR_TYPE_MSI) { 5110Sstevel@tonic-gate if (!(msi_ctrl & PCI_MSI_PVM_MASK)) 5120Sstevel@tonic-gate goto done; 5130Sstevel@tonic-gate 5140Sstevel@tonic-gate offset = (msi_ctrl & PCI_MSI_64BIT_MASK) ? 5150Sstevel@tonic-gate PCI_MSI_64BIT_MASKBITS : PCI_MSI_32BIT_MASK; 5161624Spjha if ((mask_bits = PCI_CAP_GET32(cfg_hdle, NULL, caps_ptr, 517*4937Sjohnny offset)) == PCI_CAP_EINVAL32) 5181624Spjha goto done; 5190Sstevel@tonic-gate 5200Sstevel@tonic-gate mask_bits &= ~(1 << inum); 5210Sstevel@tonic-gate 5221624Spjha PCI_CAP_PUT32(cfg_hdle, NULL, caps_ptr, offset, mask_bits); 5230Sstevel@tonic-gate 5240Sstevel@tonic-gate } else if (type == DDI_INTR_TYPE_MSIX) { 52542Sagiri uintptr_t off; 5260Sstevel@tonic-gate ddi_intr_msix_t *msix_p; 5270Sstevel@tonic-gate 5280Sstevel@tonic-gate if (msi_ctrl & PCI_MSIX_FUNCTION_MASK) { 5290Sstevel@tonic-gate ret = DDI_SUCCESS; 5300Sstevel@tonic-gate goto done; 5310Sstevel@tonic-gate } 5320Sstevel@tonic-gate 5330Sstevel@tonic-gate msix_p = i_ddi_get_msix(rdip); 5340Sstevel@tonic-gate 5350Sstevel@tonic-gate /* Offset into the "inum"th entry in the MSI-X table */ 536965Sgovinda off = (uintptr_t)msix_p->msix_tbl_addr + (inum * 5370Sstevel@tonic-gate PCI_MSIX_VECTOR_SIZE) + PCI_MSIX_VECTOR_CTRL_OFFSET; 5380Sstevel@tonic-gate 5390Sstevel@tonic-gate /* Clear the Mask bit */ 540965Sgovinda ddi_put32(msix_p->msix_tbl_hdl, (uint32_t *)off, 0x0); 5410Sstevel@tonic-gate } 5420Sstevel@tonic-gate 5430Sstevel@tonic-gate ret = DDI_SUCCESS; 5440Sstevel@tonic-gate done: 5450Sstevel@tonic-gate pci_config_teardown(&cfg_hdle); 5460Sstevel@tonic-gate return (ret); 5470Sstevel@tonic-gate } 5480Sstevel@tonic-gate 5490Sstevel@tonic-gate 5500Sstevel@tonic-gate /* 5510Sstevel@tonic-gate * pci_msi_get_pending: 5520Sstevel@tonic-gate * 5530Sstevel@tonic-gate * Get the pending bit from the MSI/X capability structure 5540Sstevel@tonic-gate */ 5550Sstevel@tonic-gate /* ARGSUSED */ 5560Sstevel@tonic-gate int 5570Sstevel@tonic-gate pci_msi_get_pending(dev_info_t *rdip, int type, int inum, int *pendingp) 5580Sstevel@tonic-gate { 5590Sstevel@tonic-gate ushort_t caps_ptr, msi_ctrl; 5600Sstevel@tonic-gate ddi_acc_handle_t cfg_hdle; 5610Sstevel@tonic-gate int offset; 5620Sstevel@tonic-gate int ret = DDI_FAILURE; 5630Sstevel@tonic-gate 5640Sstevel@tonic-gate DDI_INTR_NEXDBG((CE_CONT, "pci_msi_get_pending: rdip = 0x%p\n", 5650Sstevel@tonic-gate (void *)rdip)); 5660Sstevel@tonic-gate 5670Sstevel@tonic-gate if (pci_get_msi_ctrl(rdip, type, &msi_ctrl, 5680Sstevel@tonic-gate &caps_ptr, &cfg_hdle) != DDI_SUCCESS) 5690Sstevel@tonic-gate return (DDI_FAILURE); 5700Sstevel@tonic-gate 5710Sstevel@tonic-gate if (type == DDI_INTR_TYPE_MSI) { 5720Sstevel@tonic-gate uint32_t pending_bits; 5730Sstevel@tonic-gate 5740Sstevel@tonic-gate if (!(msi_ctrl & PCI_MSI_PVM_MASK)) { 5750Sstevel@tonic-gate DDI_INTR_NEXDBG((CE_CONT, "pci_msi_get_pending: " 5760Sstevel@tonic-gate "PVM is not supported\n")); 5770Sstevel@tonic-gate goto done; 5780Sstevel@tonic-gate } 5790Sstevel@tonic-gate 5800Sstevel@tonic-gate offset = (msi_ctrl & PCI_MSI_64BIT_MASK) ? 5810Sstevel@tonic-gate PCI_MSI_64BIT_PENDING : PCI_MSI_32BIT_PENDING; 5820Sstevel@tonic-gate 5831624Spjha if ((pending_bits = PCI_CAP_GET32(cfg_hdle, NULL, caps_ptr, 584*4937Sjohnny offset)) == PCI_CAP_EINVAL32) 5851624Spjha goto done; 5860Sstevel@tonic-gate 5870Sstevel@tonic-gate *pendingp = pending_bits & ~(1 >> inum); 5880Sstevel@tonic-gate 5890Sstevel@tonic-gate } else if (type == DDI_INTR_TYPE_MSIX) { 59042Sagiri uintptr_t off; 5910Sstevel@tonic-gate uint64_t pending_bits; 5920Sstevel@tonic-gate ddi_intr_msix_t *msix_p = i_ddi_get_msix(rdip); 5930Sstevel@tonic-gate 5940Sstevel@tonic-gate /* Offset into the PBA array which has entry for "inum" */ 595965Sgovinda off = (uintptr_t)msix_p->msix_pba_addr + (inum / 64); 5960Sstevel@tonic-gate 5970Sstevel@tonic-gate /* Read the PBA array */ 598965Sgovinda pending_bits = ddi_get64(msix_p->msix_pba_hdl, (uint64_t *)off); 5990Sstevel@tonic-gate 6000Sstevel@tonic-gate *pendingp = pending_bits & ~(1 >> inum); 6010Sstevel@tonic-gate } 6020Sstevel@tonic-gate 6030Sstevel@tonic-gate ret = DDI_SUCCESS; 6040Sstevel@tonic-gate done: 6050Sstevel@tonic-gate pci_config_teardown(&cfg_hdle); 6060Sstevel@tonic-gate return (ret); 6070Sstevel@tonic-gate } 6080Sstevel@tonic-gate 6090Sstevel@tonic-gate 6100Sstevel@tonic-gate /* 6110Sstevel@tonic-gate * pci_msi_get_nintrs: 6120Sstevel@tonic-gate * 6130Sstevel@tonic-gate * For a given type (MSI/X) returns the number of interrupts supported 6140Sstevel@tonic-gate */ 6150Sstevel@tonic-gate int 6160Sstevel@tonic-gate pci_msi_get_nintrs(dev_info_t *rdip, int type, int *nintrs) 6170Sstevel@tonic-gate { 6180Sstevel@tonic-gate ushort_t caps_ptr, msi_ctrl; 6190Sstevel@tonic-gate ddi_acc_handle_t cfg_hdle; 6200Sstevel@tonic-gate 6210Sstevel@tonic-gate DDI_INTR_NEXDBG((CE_CONT, "pci_msi_get_nintrs: rdip = 0x%p\n", 6220Sstevel@tonic-gate (void *)rdip)); 6230Sstevel@tonic-gate 6240Sstevel@tonic-gate if (pci_get_msi_ctrl(rdip, type, &msi_ctrl, 6250Sstevel@tonic-gate &caps_ptr, &cfg_hdle) != DDI_SUCCESS) 6260Sstevel@tonic-gate return (DDI_FAILURE); 6270Sstevel@tonic-gate 6280Sstevel@tonic-gate if (type == DDI_INTR_TYPE_MSI) { 6290Sstevel@tonic-gate *nintrs = 1 << ((msi_ctrl & PCI_MSI_MMC_MASK) >> 6300Sstevel@tonic-gate PCI_MSI_MMC_SHIFT); 6310Sstevel@tonic-gate } else if (type == DDI_INTR_TYPE_MSIX) { 6320Sstevel@tonic-gate if (msi_ctrl & PCI_MSIX_TBL_SIZE_MASK) 6330Sstevel@tonic-gate *nintrs = (msi_ctrl & PCI_MSIX_TBL_SIZE_MASK) + 1; 6340Sstevel@tonic-gate } 6350Sstevel@tonic-gate 6360Sstevel@tonic-gate DDI_INTR_NEXDBG((CE_CONT, "pci_msi_get_nintrs: " 6370Sstevel@tonic-gate "nintr = 0x%x\n", *nintrs)); 6380Sstevel@tonic-gate 6390Sstevel@tonic-gate pci_config_teardown(&cfg_hdle); 6400Sstevel@tonic-gate return (DDI_SUCCESS); 6410Sstevel@tonic-gate } 6420Sstevel@tonic-gate 6430Sstevel@tonic-gate 6440Sstevel@tonic-gate /* 6450Sstevel@tonic-gate * pci_msi_set_nintrs: 6460Sstevel@tonic-gate * 6470Sstevel@tonic-gate * For a given type (MSI/X) sets the number of interrupts supported 6480Sstevel@tonic-gate * by the system. 6490Sstevel@tonic-gate * For MSI: Return an error if this func is called for navail > 32 6500Sstevel@tonic-gate * For MSI-X: Return an error if this func is called for navail > 2048 6510Sstevel@tonic-gate */ 6520Sstevel@tonic-gate int 6530Sstevel@tonic-gate pci_msi_set_nintrs(dev_info_t *rdip, int type, int navail) 6540Sstevel@tonic-gate { 6550Sstevel@tonic-gate ushort_t caps_ptr, msi_ctrl; 6560Sstevel@tonic-gate ddi_acc_handle_t cfg_hdle; 6570Sstevel@tonic-gate 6580Sstevel@tonic-gate DDI_INTR_NEXDBG((CE_CONT, "pci_msi_set_nintrs: rdip = 0x%p, " 6590Sstevel@tonic-gate "navail = 0x%x\n", (void *)rdip, navail)); 6600Sstevel@tonic-gate 6610Sstevel@tonic-gate /* Check for valid input argument */ 6620Sstevel@tonic-gate if (((type == DDI_INTR_TYPE_MSI) && (navail > PCI_MSI_MAX_INTRS)) || 6630Sstevel@tonic-gate ((type == DDI_INTR_TYPE_MSIX) && (navail > PCI_MSIX_MAX_INTRS))) 6640Sstevel@tonic-gate return (DDI_EINVAL); 6650Sstevel@tonic-gate 6660Sstevel@tonic-gate if (pci_get_msi_ctrl(rdip, type, &msi_ctrl, 6670Sstevel@tonic-gate &caps_ptr, &cfg_hdle) != DDI_SUCCESS) 6680Sstevel@tonic-gate return (DDI_FAILURE); 6690Sstevel@tonic-gate 6700Sstevel@tonic-gate if (type == DDI_INTR_TYPE_MSI) { 6710Sstevel@tonic-gate msi_ctrl |= ((highbit(navail) -1) << PCI_MSI_MME_SHIFT); 6720Sstevel@tonic-gate 6731624Spjha PCI_CAP_PUT16(cfg_hdle, NULL, caps_ptr, PCI_MSI_CTRL, msi_ctrl); 6740Sstevel@tonic-gate } else if (type == DDI_INTR_TYPE_MSIX) { 6750Sstevel@tonic-gate DDI_INTR_NEXDBG((CE_CONT, "pci_msi_set_nintrs: unsupported\n")); 6760Sstevel@tonic-gate } 6770Sstevel@tonic-gate 6780Sstevel@tonic-gate pci_config_teardown(&cfg_hdle); 6790Sstevel@tonic-gate return (DDI_SUCCESS); 6800Sstevel@tonic-gate } 6810Sstevel@tonic-gate 6820Sstevel@tonic-gate 6830Sstevel@tonic-gate /* 6840Sstevel@tonic-gate * pci_msi_get_supported_type: 6850Sstevel@tonic-gate * 6860Sstevel@tonic-gate * Returns DDI_INTR_TYPE_MSI and/or DDI_INTR_TYPE_MSIX as supported 6870Sstevel@tonic-gate * types if device supports them. A DDI_FAILURE is returned otherwise. 6880Sstevel@tonic-gate */ 6890Sstevel@tonic-gate int 6900Sstevel@tonic-gate pci_msi_get_supported_type(dev_info_t *rdip, int *typesp) 6910Sstevel@tonic-gate { 6920Sstevel@tonic-gate ushort_t caps_ptr, msi_ctrl; 6930Sstevel@tonic-gate ddi_acc_handle_t cfg_hdle; 6940Sstevel@tonic-gate 6950Sstevel@tonic-gate DDI_INTR_NEXDBG((CE_CONT, "pci_msi_get_supported_type: " 6960Sstevel@tonic-gate "rdip = 0x%p\n", (void *)rdip)); 6970Sstevel@tonic-gate 6980Sstevel@tonic-gate *typesp = 0; 6990Sstevel@tonic-gate 7000Sstevel@tonic-gate if (pci_get_msi_ctrl(rdip, DDI_INTR_TYPE_MSI, &msi_ctrl, 7010Sstevel@tonic-gate &caps_ptr, &cfg_hdle) == DDI_SUCCESS) { 7020Sstevel@tonic-gate *typesp |= DDI_INTR_TYPE_MSI; 7030Sstevel@tonic-gate pci_config_teardown(&cfg_hdle); 7040Sstevel@tonic-gate } 7050Sstevel@tonic-gate 7060Sstevel@tonic-gate if (pci_get_msi_ctrl(rdip, DDI_INTR_TYPE_MSIX, &msi_ctrl, 7070Sstevel@tonic-gate &caps_ptr, &cfg_hdle) == DDI_SUCCESS) { 7080Sstevel@tonic-gate *typesp |= DDI_INTR_TYPE_MSIX; 7090Sstevel@tonic-gate pci_config_teardown(&cfg_hdle); 7100Sstevel@tonic-gate } 7110Sstevel@tonic-gate 7120Sstevel@tonic-gate DDI_INTR_NEXDBG((CE_CONT, "pci_msi_get_supported_type: " 7130Sstevel@tonic-gate "rdip = 0x%p types 0x%x\n", (void *)rdip, *typesp)); 7140Sstevel@tonic-gate 7150Sstevel@tonic-gate return (*typesp == 0 ? DDI_FAILURE : DDI_SUCCESS); 7160Sstevel@tonic-gate } 7170Sstevel@tonic-gate 7180Sstevel@tonic-gate 7190Sstevel@tonic-gate /* 7200Sstevel@tonic-gate * pci_msix_init: 7210Sstevel@tonic-gate * This function initializes the various handles/addrs etc. 7220Sstevel@tonic-gate * needed for MSI-X support. It also allocates a private 7230Sstevel@tonic-gate * structure to keep track of these. 7240Sstevel@tonic-gate */ 7250Sstevel@tonic-gate ddi_intr_msix_t * 7260Sstevel@tonic-gate pci_msix_init(dev_info_t *rdip) 7270Sstevel@tonic-gate { 728965Sgovinda uint_t rnumber, breg, nregs; 7290Sstevel@tonic-gate size_t msix_tbl_size; 7300Sstevel@tonic-gate size_t pba_tbl_size; 731965Sgovinda ushort_t caps_ptr, msix_ctrl; 7320Sstevel@tonic-gate ddi_intr_msix_t *msix_p; 7330Sstevel@tonic-gate ddi_acc_handle_t cfg_hdle; 734965Sgovinda pci_regspec_t *rp; 735965Sgovinda int reg_size, addr_space, offset, *regs_list; 736965Sgovinda int i, ret; 7370Sstevel@tonic-gate 7380Sstevel@tonic-gate DDI_INTR_NEXDBG((CE_CONT, "pci_msix_init: rdip = %p\n", (void *)rdip)); 7390Sstevel@tonic-gate 740965Sgovinda if (pci_get_msi_ctrl(rdip, DDI_INTR_TYPE_MSIX, &msix_ctrl, 7410Sstevel@tonic-gate &caps_ptr, &cfg_hdle) != DDI_SUCCESS) 7420Sstevel@tonic-gate return (NULL); 7430Sstevel@tonic-gate 7440Sstevel@tonic-gate msix_p = kmem_zalloc(sizeof (ddi_intr_msix_t), KM_SLEEP); 7450Sstevel@tonic-gate 7460Sstevel@tonic-gate /* 7470Sstevel@tonic-gate * Initialize the devacc structure 7480Sstevel@tonic-gate */ 7490Sstevel@tonic-gate msix_p->msix_dev_attr.devacc_attr_version = DDI_DEVICE_ATTR_V0; 7500Sstevel@tonic-gate msix_p->msix_dev_attr.devacc_attr_endian_flags = 7510Sstevel@tonic-gate DDI_STRUCTURE_LE_ACC; 7520Sstevel@tonic-gate msix_p->msix_dev_attr.devacc_attr_dataorder = DDI_STRICTORDER_ACC; 7530Sstevel@tonic-gate 754965Sgovinda /* Map the entire MSI-X vector table */ 7551624Spjha msix_p->msix_tbl_offset = PCI_CAP_GET32(cfg_hdle, NULL, caps_ptr, 756*4937Sjohnny PCI_MSIX_TBL_OFFSET); 757965Sgovinda 758965Sgovinda if ((breg = pci_msix_bir_index[msix_p->msix_tbl_offset & 759965Sgovinda PCI_MSIX_TBL_BIR_MASK]) == 0xff) 760965Sgovinda goto fail1; 761965Sgovinda 762965Sgovinda msix_p->msix_tbl_offset = msix_p->msix_tbl_offset & 763965Sgovinda ~PCI_MSIX_TBL_BIR_MASK; 764965Sgovinda msix_tbl_size = ((msix_ctrl & PCI_MSIX_TBL_SIZE_MASK) + 1) * 765965Sgovinda PCI_MSIX_VECTOR_SIZE; 7660Sstevel@tonic-gate 767965Sgovinda DDI_INTR_NEXDBG((CE_CONT, "pci_msix_init: MSI-X table offset 0x%x " 768965Sgovinda "breg 0x%x size 0x%lx\n", msix_p->msix_tbl_offset, breg, 769965Sgovinda msix_tbl_size)); 770965Sgovinda 771965Sgovinda if ((ret = ddi_prop_lookup_int_array(DDI_DEV_T_ANY, rdip, 772965Sgovinda DDI_PROP_DONTPASS, "reg", (int **)®s_list, &nregs)) 773965Sgovinda != DDI_PROP_SUCCESS) { 774965Sgovinda DDI_INTR_NEXDBG((CE_CONT, "pci_msix_init: " 775965Sgovinda "ddi_prop_lookup_int_array failed %d\n", ret)); 776965Sgovinda 777965Sgovinda goto fail1; 778965Sgovinda } 779965Sgovinda 780965Sgovinda reg_size = sizeof (pci_regspec_t) / sizeof (int); 7810Sstevel@tonic-gate 782965Sgovinda for (i = 1, rnumber = 0; i < nregs/reg_size; i++) { 783965Sgovinda rp = (pci_regspec_t *)®s_list[i * reg_size]; 784965Sgovinda addr_space = rp->pci_phys_hi & PCI_ADDR_MASK; 785965Sgovinda offset = PCI_REG_REG_G(rp->pci_phys_hi); 786965Sgovinda 787965Sgovinda if ((offset == breg) && ((addr_space == PCI_ADDR_MEM32) || 788965Sgovinda (addr_space == PCI_ADDR_MEM64))) { 789965Sgovinda rnumber = i; 790965Sgovinda break; 791965Sgovinda } 792965Sgovinda } 793965Sgovinda 794965Sgovinda DDI_INTR_NEXDBG((CE_CONT, "pci_msix_init: MSI-X rnum = %d\n", rnumber)); 795965Sgovinda 796965Sgovinda if (rnumber == 0) { 797965Sgovinda DDI_INTR_NEXDBG((CE_CONT, "pci_msix_init: " 798965Sgovinda "no mtaching reg number for offset 0x%x\n", breg)); 799965Sgovinda 800965Sgovinda goto fail2; 801965Sgovinda } 802965Sgovinda 803965Sgovinda if ((ret = ddi_regs_map_setup(rdip, rnumber, 804965Sgovinda (caddr_t *)&msix_p->msix_tbl_addr, msix_p->msix_tbl_offset, 805965Sgovinda msix_tbl_size, &msix_p->msix_dev_attr, 806965Sgovinda &msix_p->msix_tbl_hdl)) != DDI_SUCCESS) { 807965Sgovinda DDI_INTR_NEXDBG((CE_CONT, "pci_msix_init: MSI-X Table " 808965Sgovinda "ddi_regs_map_setup failed %d\n", ret)); 809965Sgovinda 810965Sgovinda goto fail2; 8110Sstevel@tonic-gate } 8120Sstevel@tonic-gate 8130Sstevel@tonic-gate /* 8140Sstevel@tonic-gate * Map in the MSI-X Pending Bit Array 8150Sstevel@tonic-gate */ 8161624Spjha msix_p->msix_pba_offset = PCI_CAP_GET32(cfg_hdle, NULL, caps_ptr, 817*4937Sjohnny PCI_MSIX_PBA_OFFSET); 818965Sgovinda 819965Sgovinda if ((breg = pci_msix_bir_index[msix_p->msix_pba_offset & 820965Sgovinda PCI_MSIX_PBA_BIR_MASK]) == 0xff) 821965Sgovinda goto fail3; 822965Sgovinda 823965Sgovinda msix_p->msix_pba_offset = msix_p->msix_pba_offset & 824965Sgovinda ~PCI_MSIX_PBA_BIR_MASK; 825965Sgovinda pba_tbl_size = ((msix_ctrl & PCI_MSIX_TBL_SIZE_MASK) + 1)/8; 826965Sgovinda 827965Sgovinda DDI_INTR_NEXDBG((CE_CONT, "pci_msix_init: PBA table offset 0x%x " 828965Sgovinda "breg 0x%x size 0x%lx\n", msix_p->msix_pba_offset, breg, 829965Sgovinda pba_tbl_size)); 830965Sgovinda 831965Sgovinda for (i = 1, rnumber = 0; i < nregs/reg_size; i++) { 832965Sgovinda rp = (pci_regspec_t *)®s_list[i * reg_size]; 833965Sgovinda addr_space = rp->pci_phys_hi & PCI_ADDR_MASK; 834965Sgovinda offset = PCI_REG_REG_G(rp->pci_phys_hi); 8350Sstevel@tonic-gate 836965Sgovinda if ((offset == breg) && ((addr_space == PCI_ADDR_MEM32) || 837965Sgovinda (addr_space == PCI_ADDR_MEM64))) { 838965Sgovinda rnumber = i; 839965Sgovinda break; 840965Sgovinda } 841965Sgovinda } 842965Sgovinda 843965Sgovinda DDI_INTR_NEXDBG((CE_CONT, "pci_msix_init: PBA rnum = %d\n", rnumber)); 844965Sgovinda 845965Sgovinda if (rnumber == 0) { 846965Sgovinda DDI_INTR_NEXDBG((CE_CONT, "pci_msix_init: " 8471725Segillett "no matching reg number for offset 0x%x\n", breg)); 848965Sgovinda 849965Sgovinda goto fail3; 850965Sgovinda } 851965Sgovinda 852965Sgovinda if ((ret = ddi_regs_map_setup(rdip, rnumber, 853965Sgovinda (caddr_t *)&msix_p->msix_pba_addr, msix_p->msix_pba_offset, 854965Sgovinda pba_tbl_size, &msix_p->msix_dev_attr, 855965Sgovinda &msix_p->msix_pba_hdl)) != DDI_SUCCESS) { 856965Sgovinda DDI_INTR_NEXDBG((CE_CONT, "pci_msix_init: PBA " 857965Sgovinda "ddi_regs_map_setup failed %d\n", ret)); 858965Sgovinda 859965Sgovinda goto fail3; 8600Sstevel@tonic-gate } 8610Sstevel@tonic-gate 8620Sstevel@tonic-gate DDI_INTR_NEXDBG((CE_CONT, "pci_msix_init: msix_p = 0x%p DONE!!\n", 8630Sstevel@tonic-gate (void *)msix_p)); 8640Sstevel@tonic-gate 865*4937Sjohnny ddi_prop_free(regs_list); 866965Sgovinda goto done; 867965Sgovinda 868965Sgovinda fail3: 869965Sgovinda ddi_regs_map_free(&msix_p->msix_tbl_hdl); 870965Sgovinda fail2: 871965Sgovinda ddi_prop_free(regs_list); 872965Sgovinda fail1: 873965Sgovinda kmem_free(msix_p, sizeof (ddi_intr_msix_t)); 874965Sgovinda msix_p = NULL; 875965Sgovinda done: 8760Sstevel@tonic-gate pci_config_teardown(&cfg_hdle); 8770Sstevel@tonic-gate return (msix_p); 8780Sstevel@tonic-gate } 8790Sstevel@tonic-gate 8800Sstevel@tonic-gate 8810Sstevel@tonic-gate /* 8820Sstevel@tonic-gate * pci_msix_fini: 8830Sstevel@tonic-gate * This function cleans up previously allocated handles/addrs etc. 8840Sstevel@tonic-gate * It is only called if no more MSI-X interrupts are being used. 8850Sstevel@tonic-gate */ 8860Sstevel@tonic-gate void 8870Sstevel@tonic-gate pci_msix_fini(ddi_intr_msix_t *msix_p) 8880Sstevel@tonic-gate { 8890Sstevel@tonic-gate DDI_INTR_NEXDBG((CE_CONT, "pci_msix_fini: msix_p = 0x%p\n", 8900Sstevel@tonic-gate (void *)msix_p)); 8910Sstevel@tonic-gate 8920Sstevel@tonic-gate ddi_regs_map_free(&msix_p->msix_pba_hdl); 8930Sstevel@tonic-gate ddi_regs_map_free(&msix_p->msix_tbl_hdl); 8940Sstevel@tonic-gate kmem_free(msix_p, sizeof (ddi_intr_msix_t)); 8950Sstevel@tonic-gate } 8960Sstevel@tonic-gate 8970Sstevel@tonic-gate 8981725Segillett /* 8991725Segillett * pci_msix_dup: 9001725Segillett * This function duplicates the address and data pair of one msi-x 9011725Segillett * vector to another msi-x vector. 9021725Segillett */ 9031725Segillett int 9041725Segillett pci_msix_dup(dev_info_t *rdip, int org_inum, int dup_inum) 9051725Segillett { 9061725Segillett ddi_intr_msix_t *msix_p = i_ddi_get_msix(rdip); 9071725Segillett uint64_t addr; 9081725Segillett uint64_t data; 9091725Segillett uintptr_t off; 9101725Segillett 9111725Segillett DDI_INTR_NEXDBG((CE_CONT, "pci_msix_dup: dip = %p, inum = 0x%x, " 9121725Segillett "to_vector = 0x%x\n", (void *)rdip, org_inum, dup_inum)); 9131725Segillett 9141725Segillett /* Offset into the original inum's entry in the MSI-X table */ 9151725Segillett off = (uintptr_t)msix_p->msix_tbl_addr + 9161725Segillett (org_inum * PCI_MSIX_VECTOR_SIZE); 9171725Segillett 9181725Segillett /* For the MSI-X number passed in, get the "data" and "addr" fields */ 9191725Segillett addr = ddi_get64(msix_p->msix_tbl_hdl, 9201725Segillett (uint64_t *)(off + PCI_MSIX_LOWER_ADDR_OFFSET)); 9211725Segillett 9221725Segillett data = ddi_get32(msix_p->msix_tbl_hdl, 9231725Segillett (uint32_t *)(off + PCI_MSIX_DATA_OFFSET)); 9241725Segillett 9251725Segillett /* Program new vector with these existing values */ 9261725Segillett return (pci_msi_configure(rdip, DDI_INTR_TYPE_MSIX, 1, dup_inum, addr, 9271725Segillett data)); 9281725Segillett } 9291725Segillett 9300Sstevel@tonic-gate 9310Sstevel@tonic-gate /* 9320Sstevel@tonic-gate * Next set of routines are for INTx (legacy) PCI interrupt 9330Sstevel@tonic-gate * support only. 9340Sstevel@tonic-gate */ 9350Sstevel@tonic-gate 9360Sstevel@tonic-gate /* 9370Sstevel@tonic-gate * pci_intx_get_cap: 9380Sstevel@tonic-gate * For non-MSI devices that comply to PCI v2.3 or greater; 9390Sstevel@tonic-gate * read the command register. Bit 10 implies interrupt disable. 9400Sstevel@tonic-gate * Set this bit and then read the status register bit 3. 9410Sstevel@tonic-gate * Bit 3 of status register is Interrupt state. 9420Sstevel@tonic-gate * If it is set; then the device supports 'Masking' 9430Sstevel@tonic-gate * 9440Sstevel@tonic-gate * Reset the device back to the original state. 9450Sstevel@tonic-gate */ 9460Sstevel@tonic-gate int 9470Sstevel@tonic-gate pci_intx_get_cap(dev_info_t *dip, int *flagsp) 9480Sstevel@tonic-gate { 9490Sstevel@tonic-gate uint16_t cmdreg, savereg; 9500Sstevel@tonic-gate ddi_acc_handle_t cfg_hdl; 9510Sstevel@tonic-gate #ifdef DEBUG 9520Sstevel@tonic-gate uint16_t statreg; 9530Sstevel@tonic-gate #endif /* DEBUG */ 9540Sstevel@tonic-gate 9550Sstevel@tonic-gate *flagsp = 0; 9560Sstevel@tonic-gate DDI_INTR_NEXDBG((CE_CONT, "pci_intx_get_cap: %s%d: called\n", 9570Sstevel@tonic-gate ddi_driver_name(dip), ddi_get_instance(dip))); 9580Sstevel@tonic-gate 9590Sstevel@tonic-gate if (pci_config_setup(dip, &cfg_hdl) != DDI_SUCCESS) { 9600Sstevel@tonic-gate DDI_INTR_NEXDBG((CE_CONT, "pci_intx_get_cap: can't get " 9610Sstevel@tonic-gate "config handle\n")); 9620Sstevel@tonic-gate return (DDI_FAILURE); 9630Sstevel@tonic-gate } 9640Sstevel@tonic-gate 9650Sstevel@tonic-gate savereg = pci_config_get16(cfg_hdl, PCI_CONF_COMM); 9660Sstevel@tonic-gate DDI_INTR_NEXDBG((CE_CONT, "pci_intx_get_cap: " 9670Sstevel@tonic-gate "command register was 0x%x\n", savereg)); 9680Sstevel@tonic-gate 9690Sstevel@tonic-gate /* Disable the interrupts */ 9700Sstevel@tonic-gate cmdreg = savereg | PCI_COMM_INTX_DISABLE; 9710Sstevel@tonic-gate pci_config_put16(cfg_hdl, PCI_CONF_COMM, cmdreg); 9720Sstevel@tonic-gate 9730Sstevel@tonic-gate #ifdef DEBUG 9740Sstevel@tonic-gate statreg = pci_config_get16(cfg_hdl, PCI_CONF_STAT); 9750Sstevel@tonic-gate DDI_INTR_NEXDBG((CE_CONT, "pci_intx_get_cap: " 9760Sstevel@tonic-gate "status register is 0x%x\n", statreg)); 9770Sstevel@tonic-gate #endif /* DEBUG */ 9780Sstevel@tonic-gate 9790Sstevel@tonic-gate /* Read the bit back */ 9800Sstevel@tonic-gate cmdreg = pci_config_get16(cfg_hdl, PCI_CONF_COMM); 9810Sstevel@tonic-gate DDI_INTR_NEXDBG((CE_CONT, "pci_intx_get_cap: " 9820Sstevel@tonic-gate "command register is now 0x%x\n", cmdreg)); 9830Sstevel@tonic-gate 984693Sgovinda *flagsp = DDI_INTR_FLAG_LEVEL; 985693Sgovinda 9860Sstevel@tonic-gate if (cmdreg & PCI_COMM_INTX_DISABLE) { 9870Sstevel@tonic-gate DDI_INTR_NEXDBG((CE_CONT, "pci_intx_get_cap: " 9880Sstevel@tonic-gate "masking supported\n")); 989693Sgovinda *flagsp |= (DDI_INTR_FLAG_MASKABLE | 990693Sgovinda DDI_INTR_FLAG_PENDING); 9910Sstevel@tonic-gate } 9920Sstevel@tonic-gate 9930Sstevel@tonic-gate /* Restore the device back to the original state and return */ 9940Sstevel@tonic-gate pci_config_put16(cfg_hdl, PCI_CONF_COMM, savereg); 9950Sstevel@tonic-gate 9960Sstevel@tonic-gate pci_config_teardown(&cfg_hdl); 9970Sstevel@tonic-gate return (DDI_SUCCESS); 9980Sstevel@tonic-gate } 9990Sstevel@tonic-gate 10000Sstevel@tonic-gate 10010Sstevel@tonic-gate /* 10020Sstevel@tonic-gate * pci_intx_clr_mask: 10030Sstevel@tonic-gate * For non-MSI devices that comply to PCI v2.3 or greater; 10040Sstevel@tonic-gate * clear the bit10 in the command register. 10050Sstevel@tonic-gate */ 10060Sstevel@tonic-gate int 10070Sstevel@tonic-gate pci_intx_clr_mask(dev_info_t *dip) 10080Sstevel@tonic-gate { 10090Sstevel@tonic-gate uint16_t cmdreg; 10100Sstevel@tonic-gate ddi_acc_handle_t cfg_hdl; 10110Sstevel@tonic-gate 10120Sstevel@tonic-gate DDI_INTR_NEXDBG((CE_CONT, "pci_intx_clr_mask: %s%d: called\n", 10130Sstevel@tonic-gate ddi_driver_name(dip), ddi_get_instance(dip))); 10140Sstevel@tonic-gate 10150Sstevel@tonic-gate if (pci_config_setup(dip, &cfg_hdl) != DDI_SUCCESS) { 10160Sstevel@tonic-gate DDI_INTR_NEXDBG((CE_CONT, "pci_intx_clr_mask: can't get " 10170Sstevel@tonic-gate "config handle\n")); 10180Sstevel@tonic-gate return (DDI_FAILURE); 10190Sstevel@tonic-gate } 10200Sstevel@tonic-gate 10210Sstevel@tonic-gate cmdreg = pci_config_get16(cfg_hdl, PCI_CONF_COMM); 10220Sstevel@tonic-gate DDI_INTR_NEXDBG((CE_CONT, "pci_intx_clr_mask: " 10230Sstevel@tonic-gate "command register was 0x%x\n", cmdreg)); 10240Sstevel@tonic-gate 10250Sstevel@tonic-gate /* Enable the interrupts */ 10260Sstevel@tonic-gate cmdreg &= ~PCI_COMM_INTX_DISABLE; 10270Sstevel@tonic-gate pci_config_put16(cfg_hdl, PCI_CONF_COMM, cmdreg); 10280Sstevel@tonic-gate pci_config_teardown(&cfg_hdl); 10290Sstevel@tonic-gate return (DDI_SUCCESS); 10300Sstevel@tonic-gate } 10310Sstevel@tonic-gate 10320Sstevel@tonic-gate 10330Sstevel@tonic-gate /* 10340Sstevel@tonic-gate * pci_intx_set_mask: 10350Sstevel@tonic-gate * For non-MSI devices that comply to PCI v2.3 or greater; 10360Sstevel@tonic-gate * set the bit10 in the command register. 10370Sstevel@tonic-gate */ 10380Sstevel@tonic-gate int 10390Sstevel@tonic-gate pci_intx_set_mask(dev_info_t *dip) 10400Sstevel@tonic-gate { 10410Sstevel@tonic-gate uint16_t cmdreg; 10420Sstevel@tonic-gate ddi_acc_handle_t cfg_hdl; 10430Sstevel@tonic-gate 10440Sstevel@tonic-gate DDI_INTR_NEXDBG((CE_CONT, "pci_intx_set_mask: %s%d: called\n", 10450Sstevel@tonic-gate ddi_driver_name(dip), ddi_get_instance(dip))); 10460Sstevel@tonic-gate 10470Sstevel@tonic-gate if (pci_config_setup(dip, &cfg_hdl) != DDI_SUCCESS) { 10480Sstevel@tonic-gate DDI_INTR_NEXDBG((CE_CONT, "pci_intx_set_mask: can't get " 10490Sstevel@tonic-gate "config handle\n")); 10500Sstevel@tonic-gate return (DDI_FAILURE); 10510Sstevel@tonic-gate } 10520Sstevel@tonic-gate 10530Sstevel@tonic-gate cmdreg = pci_config_get16(cfg_hdl, PCI_CONF_COMM); 10540Sstevel@tonic-gate DDI_INTR_NEXDBG((CE_CONT, "pci_intx_set_mask: " 10550Sstevel@tonic-gate "command register was 0x%x\n", cmdreg)); 10560Sstevel@tonic-gate 10570Sstevel@tonic-gate /* Disable the interrupts */ 10580Sstevel@tonic-gate cmdreg |= PCI_COMM_INTX_DISABLE; 10590Sstevel@tonic-gate pci_config_put16(cfg_hdl, PCI_CONF_COMM, cmdreg); 10600Sstevel@tonic-gate pci_config_teardown(&cfg_hdl); 10610Sstevel@tonic-gate return (DDI_SUCCESS); 10620Sstevel@tonic-gate } 10630Sstevel@tonic-gate 10640Sstevel@tonic-gate /* 10650Sstevel@tonic-gate * pci_intx_get_pending: 10660Sstevel@tonic-gate * For non-MSI devices that comply to PCI v2.3 or greater; 10670Sstevel@tonic-gate * read the status register. Bit 3 of status register is 10680Sstevel@tonic-gate * Interrupt state. If it is set; then the interrupt is 10690Sstevel@tonic-gate * 'Pending'. 10700Sstevel@tonic-gate */ 10710Sstevel@tonic-gate int 10720Sstevel@tonic-gate pci_intx_get_pending(dev_info_t *dip, int *pendingp) 10730Sstevel@tonic-gate { 10740Sstevel@tonic-gate uint16_t statreg; 10750Sstevel@tonic-gate ddi_acc_handle_t cfg_hdl; 10760Sstevel@tonic-gate 10770Sstevel@tonic-gate *pendingp = 0; 10780Sstevel@tonic-gate DDI_INTR_NEXDBG((CE_CONT, "pci_intx_get_pending: %s%d: called\n", 10790Sstevel@tonic-gate ddi_driver_name(dip), ddi_get_instance(dip))); 10800Sstevel@tonic-gate 10810Sstevel@tonic-gate if (pci_config_setup(dip, &cfg_hdl) != DDI_SUCCESS) { 10820Sstevel@tonic-gate DDI_INTR_NEXDBG((CE_CONT, "pci_intx_get_pending: can't get " 10830Sstevel@tonic-gate "config handle\n")); 10840Sstevel@tonic-gate return (DDI_FAILURE); 10850Sstevel@tonic-gate } 10860Sstevel@tonic-gate 10870Sstevel@tonic-gate statreg = pci_config_get16(cfg_hdl, PCI_CONF_STAT); 10880Sstevel@tonic-gate 10890Sstevel@tonic-gate if (statreg & PCI_STAT_INTR) { 10900Sstevel@tonic-gate DDI_INTR_NEXDBG((CE_CONT, "pci_intx_get_pending: " 10910Sstevel@tonic-gate "interrupt is pending\n")); 10920Sstevel@tonic-gate *pendingp = 1; 10930Sstevel@tonic-gate } 10940Sstevel@tonic-gate 10950Sstevel@tonic-gate pci_config_teardown(&cfg_hdl); 10960Sstevel@tonic-gate return (DDI_SUCCESS); 10970Sstevel@tonic-gate } 10980Sstevel@tonic-gate 10990Sstevel@tonic-gate 11000Sstevel@tonic-gate /* 11010Sstevel@tonic-gate * pci_devclass_to_ipl: 11020Sstevel@tonic-gate * translate from device class to ipl 11032755Segillett * NOTE: This function is added here as pci_intx_get_ispec() 11040Sstevel@tonic-gate * calls this to figure out the priority. 11050Sstevel@tonic-gate * It is moved over from x86 pci.c 11060Sstevel@tonic-gate */ 11070Sstevel@tonic-gate int 11080Sstevel@tonic-gate pci_devclass_to_ipl(int class) 11090Sstevel@tonic-gate { 11100Sstevel@tonic-gate int ipl; 11113869Srm78299 int base_class = (class & 0xff0000) >> 16; 11123869Srm78299 int sub_class = (class & 0xff00) >> 8; 11130Sstevel@tonic-gate 11140Sstevel@tonic-gate /* 11150Sstevel@tonic-gate * Use the class code values to construct an ipl for the device. 11160Sstevel@tonic-gate */ 11173869Srm78299 11183869Srm78299 switch (base_class) { 11190Sstevel@tonic-gate default: 11200Sstevel@tonic-gate case PCI_CLASS_NONE: 11210Sstevel@tonic-gate ipl = 1; 11220Sstevel@tonic-gate break; 11230Sstevel@tonic-gate case PCI_CLASS_MASS: 11240Sstevel@tonic-gate ipl = 0x5; 11250Sstevel@tonic-gate break; 11260Sstevel@tonic-gate case PCI_CLASS_NET: 11270Sstevel@tonic-gate ipl = 0x6; 11280Sstevel@tonic-gate break; 11290Sstevel@tonic-gate case PCI_CLASS_DISPLAY: 11300Sstevel@tonic-gate ipl = 0x9; 11310Sstevel@tonic-gate break; 11323869Srm78299 case PCI_CLASS_SERIALBUS: 11333869Srm78299 ipl = (sub_class == PCI_SERIAL_IB) ? 6 : 1; 11343869Srm78299 break; 11350Sstevel@tonic-gate /* 11360Sstevel@tonic-gate * for high priority interrupt handlers, use level 12 11370Sstevel@tonic-gate * as the highest for device drivers 11380Sstevel@tonic-gate */ 11390Sstevel@tonic-gate case PCI_CLASS_MM: 11400Sstevel@tonic-gate ipl = 0xc; 11410Sstevel@tonic-gate break; 11420Sstevel@tonic-gate case PCI_CLASS_MEM: 11430Sstevel@tonic-gate ipl = 0xc; 11440Sstevel@tonic-gate break; 11450Sstevel@tonic-gate case PCI_CLASS_BRIDGE: 11460Sstevel@tonic-gate ipl = 0xc; 11470Sstevel@tonic-gate break; 11480Sstevel@tonic-gate } 11493869Srm78299 11500Sstevel@tonic-gate return (ipl); 11510Sstevel@tonic-gate } 11520Sstevel@tonic-gate 11530Sstevel@tonic-gate 11540Sstevel@tonic-gate /* 11550Sstevel@tonic-gate * pci_intx_get_ispec: 11560Sstevel@tonic-gate * Get intrspec for PCI devices (legacy support) 11570Sstevel@tonic-gate * NOTE: This is moved here from x86 pci.c and is 11580Sstevel@tonic-gate * needed here as pci-ide.c uses it as well 11590Sstevel@tonic-gate */ 11600Sstevel@tonic-gate /*ARGSUSED*/ 11610Sstevel@tonic-gate ddi_intrspec_t 11620Sstevel@tonic-gate pci_intx_get_ispec(dev_info_t *dip, dev_info_t *rdip, int inum) 11630Sstevel@tonic-gate { 11640Sstevel@tonic-gate int class, *intpriorities; 11650Sstevel@tonic-gate uint_t num_intpriorities; 11660Sstevel@tonic-gate struct intrspec *ispec; 11670Sstevel@tonic-gate ddi_acc_handle_t cfg_hdl; 11680Sstevel@tonic-gate struct ddi_parent_private_data *pdptr; 11690Sstevel@tonic-gate 11700Sstevel@tonic-gate if ((pdptr = ddi_get_parent_data(rdip)) == NULL) 11710Sstevel@tonic-gate return (NULL); 11720Sstevel@tonic-gate 11730Sstevel@tonic-gate ispec = pdptr->par_intr; 11740Sstevel@tonic-gate ASSERT(ispec); 11750Sstevel@tonic-gate 11760Sstevel@tonic-gate /* check if the intrspec_pri has been initialized */ 11770Sstevel@tonic-gate if (!ispec->intrspec_pri) { 11780Sstevel@tonic-gate if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, rdip, 11790Sstevel@tonic-gate DDI_PROP_DONTPASS, "interrupt-priorities", 11800Sstevel@tonic-gate &intpriorities, &num_intpriorities) == DDI_PROP_SUCCESS) { 11810Sstevel@tonic-gate if (inum < num_intpriorities) 11820Sstevel@tonic-gate ispec->intrspec_pri = intpriorities[inum]; 11830Sstevel@tonic-gate ddi_prop_free(intpriorities); 11840Sstevel@tonic-gate } 11850Sstevel@tonic-gate 11860Sstevel@tonic-gate /* If still no priority, guess based on the class code */ 11870Sstevel@tonic-gate if (ispec->intrspec_pri == 0) { 11880Sstevel@tonic-gate /* get 'class' property to derive the intr priority */ 11890Sstevel@tonic-gate class = ddi_prop_get_int(DDI_DEV_T_ANY, rdip, 11900Sstevel@tonic-gate DDI_PROP_DONTPASS, "class-code", -1); 11910Sstevel@tonic-gate ispec->intrspec_pri = (class == -1) ? 1 : 11920Sstevel@tonic-gate pci_devclass_to_ipl(class); 11930Sstevel@tonic-gate } 11940Sstevel@tonic-gate } 11950Sstevel@tonic-gate 11960Sstevel@tonic-gate /* Get interrupt line value */ 11970Sstevel@tonic-gate if (!ispec->intrspec_vec) { 11980Sstevel@tonic-gate if (pci_config_setup(rdip, &cfg_hdl) != DDI_SUCCESS) { 11990Sstevel@tonic-gate DDI_INTR_NEXDBG((CE_CONT, "pci_intx_get_iline: " 12000Sstevel@tonic-gate "can't get config handle\n")); 12012288Sanish return ((ddi_intrspec_t)ispec); 12020Sstevel@tonic-gate } 12030Sstevel@tonic-gate 12040Sstevel@tonic-gate ispec->intrspec_vec = pci_config_get8(cfg_hdl, PCI_CONF_ILINE); 12050Sstevel@tonic-gate pci_config_teardown(&cfg_hdl); 12060Sstevel@tonic-gate } 12070Sstevel@tonic-gate 12080Sstevel@tonic-gate return ((ddi_intrspec_t)ispec); 12090Sstevel@tonic-gate } 1210