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 50Sstevel@tonic-gate * Common Development and Distribution License, Version 1.0 only 60Sstevel@tonic-gate * (the "License"). You may not use this file except in compliance 70Sstevel@tonic-gate * with the License. 80Sstevel@tonic-gate * 90Sstevel@tonic-gate * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 100Sstevel@tonic-gate * or http://www.opensolaris.org/os/licensing. 110Sstevel@tonic-gate * See the License for the specific language governing permissions 120Sstevel@tonic-gate * and limitations under the License. 130Sstevel@tonic-gate * 140Sstevel@tonic-gate * When distributing Covered Code, include this CDDL HEADER in each 150Sstevel@tonic-gate * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 160Sstevel@tonic-gate * If applicable, add the following below this CDDL HEADER, with the 170Sstevel@tonic-gate * fields enclosed by brackets "[]" replaced with your own identifying 180Sstevel@tonic-gate * information: Portions Copyright [yyyy] [name of copyright owner] 190Sstevel@tonic-gate * 200Sstevel@tonic-gate * CDDL HEADER END 210Sstevel@tonic-gate */ 220Sstevel@tonic-gate /* 230Sstevel@tonic-gate * Copyright 2005 Sun Microsystems, Inc. All rights reserved. 240Sstevel@tonic-gate * Use is subject to license terms. 250Sstevel@tonic-gate */ 260Sstevel@tonic-gate 270Sstevel@tonic-gate #pragma ident "%Z%%M% %I% %E% SMI" 280Sstevel@tonic-gate 290Sstevel@tonic-gate /* 300Sstevel@tonic-gate * Support for MSI, MSIX and INTx 310Sstevel@tonic-gate */ 320Sstevel@tonic-gate 330Sstevel@tonic-gate #include <sys/conf.h> 340Sstevel@tonic-gate #include <sys/debug.h> 350Sstevel@tonic-gate #include <sys/pci.h> 360Sstevel@tonic-gate #include <sys/sunddi.h> 370Sstevel@tonic-gate #include <sys/bitmap.h> 380Sstevel@tonic-gate 390Sstevel@tonic-gate /* 40*965Sgovinda * MSI-X BIR Index Table: 41*965Sgovinda * 42*965Sgovinda * BAR indicator register (BIR) to Base Address register. 43*965Sgovinda */ 44*965Sgovinda static uchar_t pci_msix_bir_index[8] = {0x10, 0x14, 0x18, 0x1c, 45*965Sgovinda 0x20, 0x24, 0xff, 0xff}; 46*965Sgovinda 47*965Sgovinda /* 480Sstevel@tonic-gate * Library utility functions 490Sstevel@tonic-gate */ 500Sstevel@tonic-gate 510Sstevel@tonic-gate 520Sstevel@tonic-gate /* 530Sstevel@tonic-gate * pci_check_pciex: 540Sstevel@tonic-gate * 550Sstevel@tonic-gate * check whether the device has PCI-E capability 560Sstevel@tonic-gate */ 570Sstevel@tonic-gate int 580Sstevel@tonic-gate pci_check_pciex(dev_info_t *dip) 590Sstevel@tonic-gate { 600Sstevel@tonic-gate ddi_acc_handle_t cfg_handle; 610Sstevel@tonic-gate ushort_t status; 620Sstevel@tonic-gate ushort_t cap; 630Sstevel@tonic-gate ushort_t capsp; 640Sstevel@tonic-gate ushort_t cap_count = PCI_CAP_MAX_PTR; 650Sstevel@tonic-gate 660Sstevel@tonic-gate DDI_INTR_NEXDBG((CE_CONT, "pci_check_pciex: dip: 0x%p, driver: %s, " 670Sstevel@tonic-gate "binding: %s, nodename: %s\n", (void *)dip, ddi_driver_name(dip), 680Sstevel@tonic-gate ddi_binding_name(dip), ddi_node_name(dip))); 690Sstevel@tonic-gate 700Sstevel@tonic-gate if (pci_config_setup(dip, &cfg_handle) != DDI_SUCCESS) { 710Sstevel@tonic-gate DDI_INTR_NEXDBG((CE_CONT, "pci_check_pciex: " 720Sstevel@tonic-gate "pci_config_setup() failed\n")); 730Sstevel@tonic-gate return (DDI_FAILURE); 740Sstevel@tonic-gate } 750Sstevel@tonic-gate status = pci_config_get16(cfg_handle, PCI_CONF_STAT); 760Sstevel@tonic-gate if (!(status & PCI_STAT_CAP)) 770Sstevel@tonic-gate goto notpciex; 780Sstevel@tonic-gate 790Sstevel@tonic-gate capsp = pci_config_get8(cfg_handle, PCI_CONF_CAP_PTR); 800Sstevel@tonic-gate while (cap_count-- && capsp >= PCI_CAP_PTR_OFF) { 810Sstevel@tonic-gate capsp &= PCI_CAP_PTR_MASK; 820Sstevel@tonic-gate cap = pci_config_get8(cfg_handle, capsp); 830Sstevel@tonic-gate DDI_INTR_NEXDBG((CE_CONT, "pci_check_pciex: capid=0x%x\n", 840Sstevel@tonic-gate cap)); 850Sstevel@tonic-gate if (cap == PCI_CAP_ID_PCI_E) { 860Sstevel@tonic-gate DDI_INTR_NEXDBG((CE_CONT, "pci_check_pciex: " 870Sstevel@tonic-gate "PCI-Express capability found\n")); 880Sstevel@tonic-gate pci_config_teardown(&cfg_handle); 890Sstevel@tonic-gate return (DDI_SUCCESS); 900Sstevel@tonic-gate } 910Sstevel@tonic-gate capsp = pci_config_get8(cfg_handle, capsp + PCI_CAP_NEXT_PTR); 920Sstevel@tonic-gate } 930Sstevel@tonic-gate notpciex: 940Sstevel@tonic-gate pci_config_teardown(&cfg_handle); 950Sstevel@tonic-gate return (DDI_FAILURE); 960Sstevel@tonic-gate } 970Sstevel@tonic-gate 980Sstevel@tonic-gate 990Sstevel@tonic-gate /* 1000Sstevel@tonic-gate * pci_get_msi_ctrl: 1010Sstevel@tonic-gate * 1020Sstevel@tonic-gate * Helper function that returns with 'cfg_hdl', MSI/X ctrl pointer, 1030Sstevel@tonic-gate * and caps_ptr for MSI/X if these are found. 1040Sstevel@tonic-gate */ 1050Sstevel@tonic-gate static int 1060Sstevel@tonic-gate pci_get_msi_ctrl(dev_info_t *dip, int type, ushort_t *msi_ctrl, 1070Sstevel@tonic-gate ushort_t *caps_ptr, ddi_acc_handle_t *cfg_hdle) 1080Sstevel@tonic-gate { 109*965Sgovinda ushort_t cap; 1100Sstevel@tonic-gate 1110Sstevel@tonic-gate *msi_ctrl = *caps_ptr = 0; 1120Sstevel@tonic-gate 1130Sstevel@tonic-gate if (pci_config_setup(dip, cfg_hdle) != DDI_SUCCESS) { 1140Sstevel@tonic-gate DDI_INTR_NEXDBG((CE_CONT, "pci_get_msi_ctrl: " 115*965Sgovinda "%s%d can't get config handle\n", 1160Sstevel@tonic-gate ddi_driver_name(dip), ddi_get_instance(dip))); 1170Sstevel@tonic-gate 1180Sstevel@tonic-gate return (DDI_FAILURE); 1190Sstevel@tonic-gate } 1200Sstevel@tonic-gate 1210Sstevel@tonic-gate /* Are capabilities supported? */ 1220Sstevel@tonic-gate if (!(pci_config_get16(*cfg_hdle, PCI_CONF_STAT) & PCI_STAT_CAP)) { 1230Sstevel@tonic-gate DDI_INTR_NEXDBG((CE_CONT, "pci_get_msi_ctrl: " 1240Sstevel@tonic-gate "%p doesn't support capabilities\n", (void *)dip)); 1250Sstevel@tonic-gate 1260Sstevel@tonic-gate pci_config_teardown(cfg_hdle); 1270Sstevel@tonic-gate return (DDI_FAILURE); 1280Sstevel@tonic-gate } 1290Sstevel@tonic-gate 1300Sstevel@tonic-gate *caps_ptr = pci_config_get8(*cfg_hdle, PCI_CONF_CAP_PTR); 1310Sstevel@tonic-gate 132*965Sgovinda while (*caps_ptr && (*caps_ptr >= PCI_CAP_PTR_OFF)) { 1330Sstevel@tonic-gate *caps_ptr &= PCI_CAP_PTR_MASK; 1340Sstevel@tonic-gate cap = pci_config_get8(*cfg_hdle, *caps_ptr); 1350Sstevel@tonic-gate 1360Sstevel@tonic-gate if ((cap == PCI_CAP_ID_MSI) && (type == DDI_INTR_TYPE_MSI)) { 1370Sstevel@tonic-gate *msi_ctrl = pci_config_get16(*cfg_hdle, 1380Sstevel@tonic-gate *caps_ptr + PCI_MSI_CTRL); 1390Sstevel@tonic-gate 1400Sstevel@tonic-gate DDI_INTR_NEXDBG((CE_CONT, "pci_get_msi_ctrl: MSI " 1410Sstevel@tonic-gate "caps_ptr=%x msi_ctrl=%x\n", *caps_ptr, *msi_ctrl)); 1420Sstevel@tonic-gate 1430Sstevel@tonic-gate return (DDI_SUCCESS); 1440Sstevel@tonic-gate } 1450Sstevel@tonic-gate 1460Sstevel@tonic-gate if ((cap == PCI_CAP_ID_MSI_X) && (type == DDI_INTR_TYPE_MSIX)) { 1470Sstevel@tonic-gate *msi_ctrl = pci_config_get16(*cfg_hdle, 1480Sstevel@tonic-gate *caps_ptr + PCI_MSIX_CTRL); 1490Sstevel@tonic-gate 1500Sstevel@tonic-gate DDI_INTR_NEXDBG((CE_CONT, "pci_get_msi_ctrl: MSI-X " 1510Sstevel@tonic-gate "caps_ptr=%x msi_ctrl=%x\n", *caps_ptr, *msi_ctrl)); 1520Sstevel@tonic-gate 1530Sstevel@tonic-gate return (DDI_SUCCESS); 1540Sstevel@tonic-gate } 1550Sstevel@tonic-gate 1560Sstevel@tonic-gate *caps_ptr = pci_config_get8(*cfg_hdle, 1570Sstevel@tonic-gate *caps_ptr + PCI_CAP_NEXT_PTR); 1580Sstevel@tonic-gate } 1590Sstevel@tonic-gate 1600Sstevel@tonic-gate pci_config_teardown(cfg_hdle); 1610Sstevel@tonic-gate 1620Sstevel@tonic-gate return (DDI_FAILURE); 1630Sstevel@tonic-gate } 1640Sstevel@tonic-gate 1650Sstevel@tonic-gate 1660Sstevel@tonic-gate /* 1670Sstevel@tonic-gate * pci_msi_get_cap: 1680Sstevel@tonic-gate * 1690Sstevel@tonic-gate * Get the capabilities of the MSI/X interrupt 1700Sstevel@tonic-gate */ 1710Sstevel@tonic-gate int 1720Sstevel@tonic-gate pci_msi_get_cap(dev_info_t *rdip, int type, int *flagsp) 1730Sstevel@tonic-gate { 1740Sstevel@tonic-gate ushort_t caps_ptr, msi_ctrl; 1750Sstevel@tonic-gate ddi_acc_handle_t cfg_hdle; 1760Sstevel@tonic-gate 1770Sstevel@tonic-gate DDI_INTR_NEXDBG((CE_CONT, "pci_msi_get_cap: rdip = 0x%p\n", 1780Sstevel@tonic-gate (void *)rdip)); 1790Sstevel@tonic-gate 1800Sstevel@tonic-gate *flagsp = 0; 1810Sstevel@tonic-gate 1820Sstevel@tonic-gate if (pci_get_msi_ctrl(rdip, type, &msi_ctrl, 1830Sstevel@tonic-gate &caps_ptr, &cfg_hdle) != DDI_SUCCESS) 1840Sstevel@tonic-gate return (DDI_FAILURE); 1850Sstevel@tonic-gate 1860Sstevel@tonic-gate if (type == DDI_INTR_TYPE_MSI) { 1870Sstevel@tonic-gate if (msi_ctrl & PCI_MSI_64BIT_MASK) 1880Sstevel@tonic-gate *flagsp |= DDI_INTR_FLAG_MSI64; 1890Sstevel@tonic-gate if (msi_ctrl & PCI_MSI_PVM_MASK) 1900Sstevel@tonic-gate *flagsp |= (DDI_INTR_FLAG_MASKABLE | 1910Sstevel@tonic-gate DDI_INTR_FLAG_PENDING); 1920Sstevel@tonic-gate else 1930Sstevel@tonic-gate *flagsp |= DDI_INTR_FLAG_BLOCK; 1940Sstevel@tonic-gate } else if (type == DDI_INTR_TYPE_MSIX) { 1950Sstevel@tonic-gate /* MSI-X supports PVM, 64bit by default */ 1960Sstevel@tonic-gate *flagsp |= (DDI_INTR_FLAG_MASKABLE | DDI_INTR_FLAG_MSI64 | 1970Sstevel@tonic-gate DDI_INTR_FLAG_PENDING); 1980Sstevel@tonic-gate } 1990Sstevel@tonic-gate 2000Sstevel@tonic-gate *flagsp |= DDI_INTR_FLAG_EDGE; 2010Sstevel@tonic-gate 2020Sstevel@tonic-gate DDI_INTR_NEXDBG((CE_CONT, "pci_msi_get_cap: flags = 0x%x\n", *flagsp)); 2030Sstevel@tonic-gate 2040Sstevel@tonic-gate pci_config_teardown(&cfg_hdle); 2050Sstevel@tonic-gate return (DDI_SUCCESS); 2060Sstevel@tonic-gate } 2070Sstevel@tonic-gate 2080Sstevel@tonic-gate 2090Sstevel@tonic-gate /* 2100Sstevel@tonic-gate * pci_msi_configure: 2110Sstevel@tonic-gate * 2120Sstevel@tonic-gate * Configure address/data and number MSI/Xs fields in the MSI/X 2130Sstevel@tonic-gate * capability structure. 2140Sstevel@tonic-gate */ 2150Sstevel@tonic-gate /* ARGSUSED */ 2160Sstevel@tonic-gate int 2170Sstevel@tonic-gate pci_msi_configure(dev_info_t *rdip, int type, int count, int inum, 2180Sstevel@tonic-gate uint64_t addr, uint64_t data) 2190Sstevel@tonic-gate { 2200Sstevel@tonic-gate ushort_t caps_ptr, msi_ctrl; 2210Sstevel@tonic-gate ddi_acc_handle_t cfg_hdle; 2220Sstevel@tonic-gate 223*965Sgovinda DDI_INTR_NEXDBG((CE_CONT, "pci_msi_configure: rdip = 0x%p type 0x%x " 224*965Sgovinda "count 0x%x inum 0x%x addr 0x%" PRIx64 " data 0x%" PRIx64 "\n", 225*965Sgovinda (void *)rdip, type, count, inum, addr, data)); 2260Sstevel@tonic-gate 2270Sstevel@tonic-gate if (pci_get_msi_ctrl(rdip, type, &msi_ctrl, 2280Sstevel@tonic-gate &caps_ptr, &cfg_hdle) != DDI_SUCCESS) 2290Sstevel@tonic-gate return (DDI_FAILURE); 2300Sstevel@tonic-gate 2310Sstevel@tonic-gate if (type == DDI_INTR_TYPE_MSI) { 2320Sstevel@tonic-gate /* Set the bits to inform how many MSIs are enabled */ 2330Sstevel@tonic-gate msi_ctrl |= ((highbit(count) -1) << PCI_MSI_MME_SHIFT); 2340Sstevel@tonic-gate pci_config_put16(cfg_hdle, 2350Sstevel@tonic-gate caps_ptr + PCI_MSI_CTRL, msi_ctrl); 2360Sstevel@tonic-gate 237*965Sgovinda DDI_INTR_NEXDBG((CE_CONT, "pci_msi_configure: msi_ctrl = %x\n", 238*965Sgovinda pci_config_get16(cfg_hdle, caps_ptr + PCI_MSI_CTRL))); 239*965Sgovinda 2400Sstevel@tonic-gate /* Set the "data" and "addr" bits */ 2410Sstevel@tonic-gate pci_config_put32(cfg_hdle, 2420Sstevel@tonic-gate caps_ptr + PCI_MSI_ADDR_OFFSET, addr); 2430Sstevel@tonic-gate 244*965Sgovinda DDI_INTR_NEXDBG((CE_CONT, "pci_msi_configure: msi_addr = %x\n", 245*965Sgovinda pci_config_get32(cfg_hdle, caps_ptr + 246*965Sgovinda PCI_MSI_ADDR_OFFSET))); 247*965Sgovinda 2480Sstevel@tonic-gate if (msi_ctrl & PCI_MSI_64BIT_MASK) { 2490Sstevel@tonic-gate pci_config_put32(cfg_hdle, 250*965Sgovinda caps_ptr + PCI_MSI_ADDR_OFFSET + 4, addr >> 32); 251*965Sgovinda 252*965Sgovinda DDI_INTR_NEXDBG((CE_CONT, "pci_msi_configure: " 253*965Sgovinda "upper 32bit msi_addr = %x\n", pci_config_get32( 254*965Sgovinda cfg_hdle, caps_ptr + PCI_MSI_ADDR_OFFSET + 4))); 255*965Sgovinda 2560Sstevel@tonic-gate pci_config_put16(cfg_hdle, 2570Sstevel@tonic-gate caps_ptr + PCI_MSI_64BIT_DATA, data); 258*965Sgovinda 259*965Sgovinda DDI_INTR_NEXDBG((CE_CONT, "pci_msi_configure: " 260*965Sgovinda "msi_data = %x\n", pci_config_get16(cfg_hdle, 261*965Sgovinda caps_ptr + PCI_MSI_64BIT_DATA))); 2620Sstevel@tonic-gate } else { 2630Sstevel@tonic-gate pci_config_put16(cfg_hdle, 2640Sstevel@tonic-gate caps_ptr + PCI_MSI_32BIT_DATA, data); 265*965Sgovinda 266*965Sgovinda DDI_INTR_NEXDBG((CE_CONT, "pci_msi_configure: " 267*965Sgovinda "msi_data = %x\n", pci_config_get16(cfg_hdle, 268*965Sgovinda caps_ptr + PCI_MSI_32BIT_DATA))); 2690Sstevel@tonic-gate } 2700Sstevel@tonic-gate } else if (type == DDI_INTR_TYPE_MSIX) { 27142Sagiri uintptr_t off; 2720Sstevel@tonic-gate ddi_intr_msix_t *msix_p = i_ddi_get_msix(rdip); 2730Sstevel@tonic-gate 2740Sstevel@tonic-gate /* Offset into the "inum"th entry in the MSI-X table */ 27542Sagiri off = (uintptr_t)msix_p->msix_tbl_addr + 276*965Sgovinda (inum * PCI_MSIX_VECTOR_SIZE); 2770Sstevel@tonic-gate 2780Sstevel@tonic-gate /* Set the "data" and "addr" bits */ 2790Sstevel@tonic-gate ddi_put32(msix_p->msix_tbl_hdl, 280*965Sgovinda (uint32_t *)(off + PCI_MSIX_DATA_OFFSET), data); 281*965Sgovinda 282*965Sgovinda ddi_put64(msix_p->msix_tbl_hdl, 283*965Sgovinda (uint64_t *)(off + PCI_MSIX_LOWER_ADDR_OFFSET), addr); 2840Sstevel@tonic-gate 285*965Sgovinda DDI_INTR_NEXDBG((CE_CONT, "pci_msi_configure: " 286*965Sgovinda "msix_addr 0x%" PRIx64 " msix_data 0x%x\n", 287*965Sgovinda ddi_get64(msix_p->msix_tbl_hdl, 288*965Sgovinda (uint64_t *)(off + PCI_MSIX_LOWER_ADDR_OFFSET)), 289*965Sgovinda ddi_get32(msix_p->msix_tbl_hdl, 290*965Sgovinda (uint32_t *)(off + PCI_MSIX_DATA_OFFSET)))); 2910Sstevel@tonic-gate } 2920Sstevel@tonic-gate 2930Sstevel@tonic-gate pci_config_teardown(&cfg_hdle); 2940Sstevel@tonic-gate return (DDI_SUCCESS); 2950Sstevel@tonic-gate } 2960Sstevel@tonic-gate 2970Sstevel@tonic-gate 2980Sstevel@tonic-gate /* 2990Sstevel@tonic-gate * pci_msi_unconfigure: 3000Sstevel@tonic-gate * 3010Sstevel@tonic-gate * Unconfigure address/data and number MSI/Xs fields in the MSI/X 3020Sstevel@tonic-gate * capability structure. 3030Sstevel@tonic-gate */ 3040Sstevel@tonic-gate /* ARGSUSED */ 3050Sstevel@tonic-gate int 3060Sstevel@tonic-gate pci_msi_unconfigure(dev_info_t *rdip, int type, int inum) 3070Sstevel@tonic-gate { 3080Sstevel@tonic-gate ushort_t msi_ctrl, caps_ptr; 3090Sstevel@tonic-gate ddi_acc_handle_t cfg_hdle; 3100Sstevel@tonic-gate 3110Sstevel@tonic-gate DDI_INTR_NEXDBG((CE_CONT, "pci_msi_unconfigure: rdip = 0x%p\n", 3120Sstevel@tonic-gate (void *)rdip)); 3130Sstevel@tonic-gate 3140Sstevel@tonic-gate if (pci_get_msi_ctrl(rdip, type, &msi_ctrl, 3150Sstevel@tonic-gate &caps_ptr, &cfg_hdle) != DDI_SUCCESS) 3160Sstevel@tonic-gate return (DDI_FAILURE); 3170Sstevel@tonic-gate 3180Sstevel@tonic-gate if (type == DDI_INTR_TYPE_MSI) { 3190Sstevel@tonic-gate msi_ctrl &= (~PCI_MSI_MME_MASK); 3200Sstevel@tonic-gate pci_config_put16(cfg_hdle, caps_ptr + PCI_MSI_CTRL, msi_ctrl); 3210Sstevel@tonic-gate 3220Sstevel@tonic-gate pci_config_put32(cfg_hdle, 3230Sstevel@tonic-gate caps_ptr + PCI_MSI_ADDR_OFFSET, 0); 3240Sstevel@tonic-gate if (msi_ctrl & PCI_MSI_64BIT_MASK) { 3250Sstevel@tonic-gate pci_config_put16(cfg_hdle, 3260Sstevel@tonic-gate caps_ptr + PCI_MSI_64BIT_DATA, 0); 3270Sstevel@tonic-gate pci_config_put32(cfg_hdle, 3280Sstevel@tonic-gate caps_ptr + PCI_MSI_ADDR_OFFSET + 4, 0); 3290Sstevel@tonic-gate } else { 3300Sstevel@tonic-gate pci_config_put16(cfg_hdle, 3310Sstevel@tonic-gate caps_ptr + PCI_MSI_32BIT_DATA, 0); 3320Sstevel@tonic-gate } 3330Sstevel@tonic-gate 3340Sstevel@tonic-gate DDI_INTR_NEXDBG((CE_CONT, "pci_msi_unconfigure: " 3350Sstevel@tonic-gate "msi_ctrl = %x\n", 3360Sstevel@tonic-gate pci_config_get16(cfg_hdle, caps_ptr + PCI_MSI_CTRL))); 3370Sstevel@tonic-gate 3380Sstevel@tonic-gate } else if (type == DDI_INTR_TYPE_MSIX) { 33942Sagiri uintptr_t off; 3400Sstevel@tonic-gate ddi_intr_msix_t *msix_p = i_ddi_get_msix(rdip); 3410Sstevel@tonic-gate 3420Sstevel@tonic-gate /* Offset into the "inum"th entry in the MSI-X table */ 34342Sagiri off = (uintptr_t)msix_p->msix_tbl_addr + 344*965Sgovinda (inum * PCI_MSIX_VECTOR_SIZE); 3450Sstevel@tonic-gate 3460Sstevel@tonic-gate /* Reset the "data" and "addr" bits */ 3470Sstevel@tonic-gate ddi_put32(msix_p->msix_tbl_hdl, 348*965Sgovinda (uint32_t *)(off + PCI_MSIX_DATA_OFFSET), 0); 3490Sstevel@tonic-gate 3500Sstevel@tonic-gate ddi_put64(msix_p->msix_tbl_hdl, (uint64_t *)off, 0); 3510Sstevel@tonic-gate } 3520Sstevel@tonic-gate 3530Sstevel@tonic-gate pci_config_teardown(&cfg_hdle); 3540Sstevel@tonic-gate return (DDI_SUCCESS); 3550Sstevel@tonic-gate } 3560Sstevel@tonic-gate 3570Sstevel@tonic-gate 3580Sstevel@tonic-gate /* 3590Sstevel@tonic-gate * pci_is_msi_enabled: 3600Sstevel@tonic-gate * 3610Sstevel@tonic-gate * This function returns DDI_SUCCESS if MSI/X is already enabled, otherwise 3620Sstevel@tonic-gate * it returns DDI_FAILURE. 3630Sstevel@tonic-gate */ 3640Sstevel@tonic-gate int 3650Sstevel@tonic-gate pci_is_msi_enabled(dev_info_t *rdip, int type) 3660Sstevel@tonic-gate { 3670Sstevel@tonic-gate ushort_t caps_ptr, msi_ctrl; 3680Sstevel@tonic-gate ddi_acc_handle_t cfg_hdle; 3690Sstevel@tonic-gate int ret = DDI_FAILURE; 3700Sstevel@tonic-gate 3710Sstevel@tonic-gate DDI_INTR_NEXDBG((CE_CONT, "pci_is_msi_enabled: rdip = 0x%p, " 3720Sstevel@tonic-gate "type = 0x%x\n", (void *)rdip, type)); 3730Sstevel@tonic-gate 3740Sstevel@tonic-gate if (pci_get_msi_ctrl(rdip, type, &msi_ctrl, 3750Sstevel@tonic-gate &caps_ptr, &cfg_hdle) != DDI_SUCCESS) 3760Sstevel@tonic-gate return (DDI_FAILURE); 3770Sstevel@tonic-gate 3780Sstevel@tonic-gate if ((type == DDI_INTR_TYPE_MSI) && (msi_ctrl & PCI_MSI_ENABLE_BIT)) 3790Sstevel@tonic-gate ret = DDI_SUCCESS; 3800Sstevel@tonic-gate 3810Sstevel@tonic-gate if ((type == DDI_INTR_TYPE_MSIX) && (msi_ctrl & PCI_MSIX_ENABLE_BIT)) 3820Sstevel@tonic-gate ret = DDI_SUCCESS; 3830Sstevel@tonic-gate 3840Sstevel@tonic-gate pci_config_teardown(&cfg_hdle); 3850Sstevel@tonic-gate return (ret); 3860Sstevel@tonic-gate } 3870Sstevel@tonic-gate 3880Sstevel@tonic-gate 3890Sstevel@tonic-gate /* 3900Sstevel@tonic-gate * pci_msi_enable_mode: 3910Sstevel@tonic-gate * 3920Sstevel@tonic-gate * This function sets the MSI_ENABLE bit in the capability structure 3930Sstevel@tonic-gate * (for MSI) and MSIX_ENABLE bit in the MSI-X capability structure. 3940Sstevel@tonic-gate */ 3950Sstevel@tonic-gate int 3960Sstevel@tonic-gate pci_msi_enable_mode(dev_info_t *rdip, int type, int inum) 3970Sstevel@tonic-gate { 3980Sstevel@tonic-gate ushort_t caps_ptr, msi_ctrl; 3990Sstevel@tonic-gate ddi_acc_handle_t cfg_hdle; 4000Sstevel@tonic-gate 4010Sstevel@tonic-gate DDI_INTR_NEXDBG((CE_CONT, "pci_msi_enable_mode: rdip = 0x%p, " 4020Sstevel@tonic-gate "inum = 0x%x\n", (void *)rdip, inum)); 4030Sstevel@tonic-gate 4040Sstevel@tonic-gate if (pci_get_msi_ctrl(rdip, type, &msi_ctrl, 4050Sstevel@tonic-gate &caps_ptr, &cfg_hdle) != DDI_SUCCESS) 4060Sstevel@tonic-gate return (DDI_FAILURE); 4070Sstevel@tonic-gate 4080Sstevel@tonic-gate if (type == DDI_INTR_TYPE_MSI) { 4090Sstevel@tonic-gate if (msi_ctrl & PCI_MSI_ENABLE_BIT) 4100Sstevel@tonic-gate goto finished; 4110Sstevel@tonic-gate 4120Sstevel@tonic-gate msi_ctrl |= PCI_MSI_ENABLE_BIT; 4130Sstevel@tonic-gate pci_config_put16(cfg_hdle, 4140Sstevel@tonic-gate caps_ptr + PCI_MSI_CTRL, msi_ctrl); 4150Sstevel@tonic-gate 4160Sstevel@tonic-gate } else if (type == DDI_INTR_TYPE_MSIX) { 41742Sagiri uintptr_t off; 4180Sstevel@tonic-gate ddi_intr_msix_t *msix_p; 4190Sstevel@tonic-gate 4200Sstevel@tonic-gate if (msi_ctrl & PCI_MSIX_ENABLE_BIT) 4210Sstevel@tonic-gate goto finished; 4220Sstevel@tonic-gate 4230Sstevel@tonic-gate msi_ctrl |= PCI_MSIX_ENABLE_BIT; 4240Sstevel@tonic-gate pci_config_put16(cfg_hdle, 4250Sstevel@tonic-gate caps_ptr + PCI_MSIX_CTRL, msi_ctrl); 4260Sstevel@tonic-gate 4270Sstevel@tonic-gate msix_p = i_ddi_get_msix(rdip); 4280Sstevel@tonic-gate 4290Sstevel@tonic-gate /* Offset into the "inum"th entry in the MSI-X table */ 430*965Sgovinda off = (uintptr_t)msix_p->msix_tbl_addr + (inum * 4310Sstevel@tonic-gate PCI_MSIX_VECTOR_SIZE) + PCI_MSIX_VECTOR_CTRL_OFFSET; 4320Sstevel@tonic-gate 4330Sstevel@tonic-gate /* Clear the Mask bit */ 434*965Sgovinda ddi_put32(msix_p->msix_tbl_hdl, (uint32_t *)off, 0x0); 4350Sstevel@tonic-gate 436*965Sgovinda DDI_INTR_NEXDBG((CE_CONT, "pci_msi_enable: " 437*965Sgovinda "msix_vector_mask 0x%x\n", 438*965Sgovinda ddi_get32(msix_p->msix_tbl_hdl, (uint32_t *)off))); 4390Sstevel@tonic-gate } 4400Sstevel@tonic-gate 4410Sstevel@tonic-gate finished: 4420Sstevel@tonic-gate DDI_INTR_NEXDBG((CE_CONT, "pci_msi_enable_mode: msi_ctrl = %x\n", 4430Sstevel@tonic-gate msi_ctrl)); 4440Sstevel@tonic-gate 4450Sstevel@tonic-gate pci_config_teardown(&cfg_hdle); 4460Sstevel@tonic-gate return (DDI_SUCCESS); 4470Sstevel@tonic-gate } 4480Sstevel@tonic-gate 4490Sstevel@tonic-gate 4500Sstevel@tonic-gate /* 4510Sstevel@tonic-gate * pci_msi_disable_mode: 4520Sstevel@tonic-gate * 4530Sstevel@tonic-gate * This function resets the MSI_ENABLE bit in the capability structure 4540Sstevel@tonic-gate * (for MSI) and MSIX_ENABLE bit in the MSI-X capability structure. 4550Sstevel@tonic-gate */ 4560Sstevel@tonic-gate int 4570Sstevel@tonic-gate pci_msi_disable_mode(dev_info_t *rdip, int type, int inum) 4580Sstevel@tonic-gate { 4590Sstevel@tonic-gate ushort_t caps_ptr, msi_ctrl; 4600Sstevel@tonic-gate ddi_acc_handle_t cfg_hdle; 4610Sstevel@tonic-gate 4620Sstevel@tonic-gate DDI_INTR_NEXDBG((CE_CONT, "pci_msi_disable_mode: rdip = 0x%p " 4630Sstevel@tonic-gate "inum = 0x%x\n", (void *)rdip, inum)); 4640Sstevel@tonic-gate 4650Sstevel@tonic-gate if (pci_get_msi_ctrl(rdip, type, &msi_ctrl, 4660Sstevel@tonic-gate &caps_ptr, &cfg_hdle) != DDI_SUCCESS) 4670Sstevel@tonic-gate return (DDI_FAILURE); 4680Sstevel@tonic-gate 4690Sstevel@tonic-gate /* Reset the "enable" bit */ 4700Sstevel@tonic-gate if (type == DDI_INTR_TYPE_MSI) { 4710Sstevel@tonic-gate if (!(msi_ctrl & PCI_MSI_ENABLE_BIT)) 4720Sstevel@tonic-gate goto finished; 4730Sstevel@tonic-gate msi_ctrl &= ~PCI_MSI_ENABLE_BIT; 4740Sstevel@tonic-gate pci_config_put16(cfg_hdle, 4750Sstevel@tonic-gate caps_ptr + PCI_MSI_CTRL, msi_ctrl); 4760Sstevel@tonic-gate } else if (type == DDI_INTR_TYPE_MSIX) { 47742Sagiri uintptr_t off; 4780Sstevel@tonic-gate ddi_intr_msix_t *msix_p; 4790Sstevel@tonic-gate 4800Sstevel@tonic-gate if (!(msi_ctrl & PCI_MSIX_ENABLE_BIT)) 4810Sstevel@tonic-gate goto finished; 4820Sstevel@tonic-gate 4830Sstevel@tonic-gate msix_p = i_ddi_get_msix(rdip); 4840Sstevel@tonic-gate 4850Sstevel@tonic-gate /* Offset into the "inum"th entry in the MSI-X table */ 486*965Sgovinda off = (uintptr_t)msix_p->msix_tbl_addr + (inum * 4870Sstevel@tonic-gate PCI_MSIX_VECTOR_SIZE) + PCI_MSIX_VECTOR_CTRL_OFFSET; 4880Sstevel@tonic-gate 4890Sstevel@tonic-gate /* Set the Mask bit */ 490*965Sgovinda ddi_put32(msix_p->msix_tbl_hdl, (uint32_t *)off, 0x1); 4910Sstevel@tonic-gate } 4920Sstevel@tonic-gate 4930Sstevel@tonic-gate finished: 4940Sstevel@tonic-gate DDI_INTR_NEXDBG((CE_CONT, "pci_msi_disable_mode: msi_ctrl = %x\n", 4950Sstevel@tonic-gate msi_ctrl)); 4960Sstevel@tonic-gate 4970Sstevel@tonic-gate pci_config_teardown(&cfg_hdle); 4980Sstevel@tonic-gate return (DDI_SUCCESS); 4990Sstevel@tonic-gate } 5000Sstevel@tonic-gate 5010Sstevel@tonic-gate 5020Sstevel@tonic-gate /* 5030Sstevel@tonic-gate * pci_msi_set_mask: 5040Sstevel@tonic-gate * 5050Sstevel@tonic-gate * Set the mask bit in the MSI/X capability structure 5060Sstevel@tonic-gate */ 5070Sstevel@tonic-gate /* ARGSUSED */ 5080Sstevel@tonic-gate int 5090Sstevel@tonic-gate pci_msi_set_mask(dev_info_t *rdip, int type, int inum) 5100Sstevel@tonic-gate { 5110Sstevel@tonic-gate int offset; 5120Sstevel@tonic-gate int ret = DDI_FAILURE; 5130Sstevel@tonic-gate ushort_t caps_ptr, msi_ctrl; 5140Sstevel@tonic-gate ddi_acc_handle_t cfg_hdle; 515*965Sgovinda uint32_t mask_bits; 5160Sstevel@tonic-gate 5170Sstevel@tonic-gate DDI_INTR_NEXDBG((CE_CONT, "pci_msi_set_mask: rdip = 0x%p, " 5180Sstevel@tonic-gate "type = 0x%x\n", (void *)rdip, type)); 5190Sstevel@tonic-gate 5200Sstevel@tonic-gate if (pci_get_msi_ctrl(rdip, type, &msi_ctrl, 5210Sstevel@tonic-gate &caps_ptr, &cfg_hdle) != DDI_SUCCESS) 5220Sstevel@tonic-gate return (DDI_FAILURE); 5230Sstevel@tonic-gate 5240Sstevel@tonic-gate if (type == DDI_INTR_TYPE_MSI) { 5250Sstevel@tonic-gate if (!(msi_ctrl & PCI_MSI_PVM_MASK)) 5260Sstevel@tonic-gate goto done; 5270Sstevel@tonic-gate 5280Sstevel@tonic-gate offset = (msi_ctrl & PCI_MSI_64BIT_MASK) ? 5290Sstevel@tonic-gate PCI_MSI_64BIT_MASKBITS : PCI_MSI_32BIT_MASK; 5300Sstevel@tonic-gate 5310Sstevel@tonic-gate mask_bits = pci_config_get32(cfg_hdle, 5320Sstevel@tonic-gate caps_ptr + offset); 5330Sstevel@tonic-gate 5340Sstevel@tonic-gate mask_bits |= (1 << inum); 5350Sstevel@tonic-gate 5360Sstevel@tonic-gate pci_config_put32(cfg_hdle, 5370Sstevel@tonic-gate caps_ptr + offset, mask_bits); 5380Sstevel@tonic-gate 5390Sstevel@tonic-gate } else if (type == DDI_INTR_TYPE_MSIX) { 54042Sagiri uintptr_t off; 5410Sstevel@tonic-gate ddi_intr_msix_t *msix_p; 5420Sstevel@tonic-gate 5430Sstevel@tonic-gate /* Set function mask */ 5440Sstevel@tonic-gate if (msi_ctrl & PCI_MSIX_FUNCTION_MASK) { 5450Sstevel@tonic-gate ret = DDI_SUCCESS; 5460Sstevel@tonic-gate goto done; 5470Sstevel@tonic-gate } 5480Sstevel@tonic-gate 5490Sstevel@tonic-gate msix_p = i_ddi_get_msix(rdip); 5500Sstevel@tonic-gate 5510Sstevel@tonic-gate /* Offset into the "inum"th entry in the MSI-X table */ 552*965Sgovinda off = (uintptr_t)msix_p->msix_tbl_addr + (inum * 5530Sstevel@tonic-gate PCI_MSIX_VECTOR_SIZE) + PCI_MSIX_VECTOR_CTRL_OFFSET; 5540Sstevel@tonic-gate 5550Sstevel@tonic-gate /* Set the Mask bit */ 556*965Sgovinda ddi_put32(msix_p->msix_tbl_hdl, (uint32_t *)off, 0x1); 5570Sstevel@tonic-gate } 5580Sstevel@tonic-gate 5590Sstevel@tonic-gate ret = DDI_SUCCESS; 5600Sstevel@tonic-gate done: 5610Sstevel@tonic-gate pci_config_teardown(&cfg_hdle); 5620Sstevel@tonic-gate return (ret); 5630Sstevel@tonic-gate } 5640Sstevel@tonic-gate 5650Sstevel@tonic-gate 5660Sstevel@tonic-gate /* 5670Sstevel@tonic-gate * pci_msi_clr_mask: 5680Sstevel@tonic-gate * 5690Sstevel@tonic-gate * Clear the mask bit in the MSI/X capability structure 5700Sstevel@tonic-gate */ 5710Sstevel@tonic-gate /* ARGSUSED */ 5720Sstevel@tonic-gate int 5730Sstevel@tonic-gate pci_msi_clr_mask(dev_info_t *rdip, int type, int inum) 5740Sstevel@tonic-gate { 5750Sstevel@tonic-gate ushort_t caps_ptr, msi_ctrl; 5760Sstevel@tonic-gate ddi_acc_handle_t cfg_hdle; 5770Sstevel@tonic-gate int offset; 5780Sstevel@tonic-gate int ret = DDI_FAILURE; 579*965Sgovinda uint32_t mask_bits; 5800Sstevel@tonic-gate 5810Sstevel@tonic-gate DDI_INTR_NEXDBG((CE_CONT, "pci_msi_clr_mask: rdip = 0x%p, " 5820Sstevel@tonic-gate "type = 0x%x\n", (void *)rdip, type)); 5830Sstevel@tonic-gate 5840Sstevel@tonic-gate if (pci_get_msi_ctrl(rdip, type, &msi_ctrl, 5850Sstevel@tonic-gate &caps_ptr, &cfg_hdle) != DDI_SUCCESS) 5860Sstevel@tonic-gate return (DDI_FAILURE); 5870Sstevel@tonic-gate 5880Sstevel@tonic-gate if (type == DDI_INTR_TYPE_MSI) { 5890Sstevel@tonic-gate if (!(msi_ctrl & PCI_MSI_PVM_MASK)) 5900Sstevel@tonic-gate goto done; 5910Sstevel@tonic-gate 5920Sstevel@tonic-gate offset = (msi_ctrl & PCI_MSI_64BIT_MASK) ? 5930Sstevel@tonic-gate PCI_MSI_64BIT_MASKBITS : PCI_MSI_32BIT_MASK; 5940Sstevel@tonic-gate mask_bits = pci_config_get32(cfg_hdle, 5950Sstevel@tonic-gate caps_ptr + offset); 5960Sstevel@tonic-gate 5970Sstevel@tonic-gate mask_bits &= ~(1 << inum); 5980Sstevel@tonic-gate 5990Sstevel@tonic-gate pci_config_put32(cfg_hdle, 6000Sstevel@tonic-gate caps_ptr + offset, mask_bits); 6010Sstevel@tonic-gate 6020Sstevel@tonic-gate } else if (type == DDI_INTR_TYPE_MSIX) { 60342Sagiri uintptr_t off; 6040Sstevel@tonic-gate ddi_intr_msix_t *msix_p; 6050Sstevel@tonic-gate 6060Sstevel@tonic-gate if (msi_ctrl & PCI_MSIX_FUNCTION_MASK) { 6070Sstevel@tonic-gate ret = DDI_SUCCESS; 6080Sstevel@tonic-gate goto done; 6090Sstevel@tonic-gate } 6100Sstevel@tonic-gate 6110Sstevel@tonic-gate msix_p = i_ddi_get_msix(rdip); 6120Sstevel@tonic-gate 6130Sstevel@tonic-gate /* Offset into the "inum"th entry in the MSI-X table */ 614*965Sgovinda off = (uintptr_t)msix_p->msix_tbl_addr + (inum * 6150Sstevel@tonic-gate PCI_MSIX_VECTOR_SIZE) + PCI_MSIX_VECTOR_CTRL_OFFSET; 6160Sstevel@tonic-gate 6170Sstevel@tonic-gate /* Clear the Mask bit */ 618*965Sgovinda ddi_put32(msix_p->msix_tbl_hdl, (uint32_t *)off, 0x0); 6190Sstevel@tonic-gate } 6200Sstevel@tonic-gate 6210Sstevel@tonic-gate ret = DDI_SUCCESS; 6220Sstevel@tonic-gate done: 6230Sstevel@tonic-gate pci_config_teardown(&cfg_hdle); 6240Sstevel@tonic-gate return (ret); 6250Sstevel@tonic-gate } 6260Sstevel@tonic-gate 6270Sstevel@tonic-gate 6280Sstevel@tonic-gate /* 6290Sstevel@tonic-gate * pci_msi_get_pending: 6300Sstevel@tonic-gate * 6310Sstevel@tonic-gate * Get the pending bit from the MSI/X capability structure 6320Sstevel@tonic-gate */ 6330Sstevel@tonic-gate /* ARGSUSED */ 6340Sstevel@tonic-gate int 6350Sstevel@tonic-gate pci_msi_get_pending(dev_info_t *rdip, int type, int inum, int *pendingp) 6360Sstevel@tonic-gate { 6370Sstevel@tonic-gate ushort_t caps_ptr, msi_ctrl; 6380Sstevel@tonic-gate ddi_acc_handle_t cfg_hdle; 6390Sstevel@tonic-gate int offset; 6400Sstevel@tonic-gate int ret = DDI_FAILURE; 6410Sstevel@tonic-gate 6420Sstevel@tonic-gate DDI_INTR_NEXDBG((CE_CONT, "pci_msi_get_pending: rdip = 0x%p\n", 6430Sstevel@tonic-gate (void *)rdip)); 6440Sstevel@tonic-gate 6450Sstevel@tonic-gate if (pci_get_msi_ctrl(rdip, type, &msi_ctrl, 6460Sstevel@tonic-gate &caps_ptr, &cfg_hdle) != DDI_SUCCESS) 6470Sstevel@tonic-gate return (DDI_FAILURE); 6480Sstevel@tonic-gate 6490Sstevel@tonic-gate if (type == DDI_INTR_TYPE_MSI) { 6500Sstevel@tonic-gate uint32_t pending_bits; 6510Sstevel@tonic-gate 6520Sstevel@tonic-gate if (!(msi_ctrl & PCI_MSI_PVM_MASK)) { 6530Sstevel@tonic-gate DDI_INTR_NEXDBG((CE_CONT, "pci_msi_get_pending: " 6540Sstevel@tonic-gate "PVM is not supported\n")); 6550Sstevel@tonic-gate goto done; 6560Sstevel@tonic-gate } 6570Sstevel@tonic-gate 6580Sstevel@tonic-gate offset = (msi_ctrl & PCI_MSI_64BIT_MASK) ? 6590Sstevel@tonic-gate PCI_MSI_64BIT_PENDING : PCI_MSI_32BIT_PENDING; 6600Sstevel@tonic-gate 6610Sstevel@tonic-gate pending_bits = pci_config_get32(cfg_hdle, 6620Sstevel@tonic-gate caps_ptr + offset); 6630Sstevel@tonic-gate 6640Sstevel@tonic-gate *pendingp = pending_bits & ~(1 >> inum); 6650Sstevel@tonic-gate 6660Sstevel@tonic-gate } else if (type == DDI_INTR_TYPE_MSIX) { 66742Sagiri uintptr_t off; 6680Sstevel@tonic-gate uint64_t pending_bits; 6690Sstevel@tonic-gate ddi_intr_msix_t *msix_p = i_ddi_get_msix(rdip); 6700Sstevel@tonic-gate 6710Sstevel@tonic-gate /* Offset into the PBA array which has entry for "inum" */ 672*965Sgovinda off = (uintptr_t)msix_p->msix_pba_addr + (inum / 64); 6730Sstevel@tonic-gate 6740Sstevel@tonic-gate /* Read the PBA array */ 675*965Sgovinda pending_bits = ddi_get64(msix_p->msix_pba_hdl, (uint64_t *)off); 6760Sstevel@tonic-gate 6770Sstevel@tonic-gate *pendingp = pending_bits & ~(1 >> inum); 6780Sstevel@tonic-gate } 6790Sstevel@tonic-gate 6800Sstevel@tonic-gate ret = DDI_SUCCESS; 6810Sstevel@tonic-gate done: 6820Sstevel@tonic-gate pci_config_teardown(&cfg_hdle); 6830Sstevel@tonic-gate return (ret); 6840Sstevel@tonic-gate } 6850Sstevel@tonic-gate 6860Sstevel@tonic-gate 6870Sstevel@tonic-gate /* 6880Sstevel@tonic-gate * pci_msi_get_nintrs: 6890Sstevel@tonic-gate * 6900Sstevel@tonic-gate * For a given type (MSI/X) returns the number of interrupts supported 6910Sstevel@tonic-gate */ 6920Sstevel@tonic-gate int 6930Sstevel@tonic-gate pci_msi_get_nintrs(dev_info_t *rdip, int type, int *nintrs) 6940Sstevel@tonic-gate { 6950Sstevel@tonic-gate ushort_t caps_ptr, msi_ctrl; 6960Sstevel@tonic-gate ddi_acc_handle_t cfg_hdle; 6970Sstevel@tonic-gate 6980Sstevel@tonic-gate DDI_INTR_NEXDBG((CE_CONT, "pci_msi_get_nintrs: rdip = 0x%p\n", 6990Sstevel@tonic-gate (void *)rdip)); 7000Sstevel@tonic-gate 7010Sstevel@tonic-gate if (pci_get_msi_ctrl(rdip, type, &msi_ctrl, 7020Sstevel@tonic-gate &caps_ptr, &cfg_hdle) != DDI_SUCCESS) 7030Sstevel@tonic-gate return (DDI_FAILURE); 7040Sstevel@tonic-gate 7050Sstevel@tonic-gate if (type == DDI_INTR_TYPE_MSI) { 7060Sstevel@tonic-gate *nintrs = 1 << ((msi_ctrl & PCI_MSI_MMC_MASK) >> 7070Sstevel@tonic-gate PCI_MSI_MMC_SHIFT); 7080Sstevel@tonic-gate } else if (type == DDI_INTR_TYPE_MSIX) { 7090Sstevel@tonic-gate if (msi_ctrl & PCI_MSIX_TBL_SIZE_MASK) 7100Sstevel@tonic-gate *nintrs = (msi_ctrl & PCI_MSIX_TBL_SIZE_MASK) + 1; 7110Sstevel@tonic-gate } 7120Sstevel@tonic-gate 7130Sstevel@tonic-gate DDI_INTR_NEXDBG((CE_CONT, "pci_msi_get_nintrs: " 7140Sstevel@tonic-gate "nintr = 0x%x\n", *nintrs)); 7150Sstevel@tonic-gate 7160Sstevel@tonic-gate pci_config_teardown(&cfg_hdle); 7170Sstevel@tonic-gate return (DDI_SUCCESS); 7180Sstevel@tonic-gate } 7190Sstevel@tonic-gate 7200Sstevel@tonic-gate 7210Sstevel@tonic-gate /* 7220Sstevel@tonic-gate * pci_msi_set_nintrs: 7230Sstevel@tonic-gate * 7240Sstevel@tonic-gate * For a given type (MSI/X) sets the number of interrupts supported 7250Sstevel@tonic-gate * by the system. 7260Sstevel@tonic-gate * For MSI: Return an error if this func is called for navail > 32 7270Sstevel@tonic-gate * For MSI-X: Return an error if this func is called for navail > 2048 7280Sstevel@tonic-gate */ 7290Sstevel@tonic-gate int 7300Sstevel@tonic-gate pci_msi_set_nintrs(dev_info_t *rdip, int type, int navail) 7310Sstevel@tonic-gate { 7320Sstevel@tonic-gate ushort_t caps_ptr, msi_ctrl; 7330Sstevel@tonic-gate ddi_acc_handle_t cfg_hdle; 7340Sstevel@tonic-gate 7350Sstevel@tonic-gate DDI_INTR_NEXDBG((CE_CONT, "pci_msi_set_nintrs: rdip = 0x%p, " 7360Sstevel@tonic-gate "navail = 0x%x\n", (void *)rdip, navail)); 7370Sstevel@tonic-gate 7380Sstevel@tonic-gate /* Check for valid input argument */ 7390Sstevel@tonic-gate if (((type == DDI_INTR_TYPE_MSI) && (navail > PCI_MSI_MAX_INTRS)) || 7400Sstevel@tonic-gate ((type == DDI_INTR_TYPE_MSIX) && (navail > PCI_MSIX_MAX_INTRS))) 7410Sstevel@tonic-gate return (DDI_EINVAL); 7420Sstevel@tonic-gate 7430Sstevel@tonic-gate if (pci_get_msi_ctrl(rdip, type, &msi_ctrl, 7440Sstevel@tonic-gate &caps_ptr, &cfg_hdle) != DDI_SUCCESS) 7450Sstevel@tonic-gate return (DDI_FAILURE); 7460Sstevel@tonic-gate 7470Sstevel@tonic-gate if (type == DDI_INTR_TYPE_MSI) { 7480Sstevel@tonic-gate msi_ctrl |= ((highbit(navail) -1) << PCI_MSI_MME_SHIFT); 7490Sstevel@tonic-gate 7500Sstevel@tonic-gate pci_config_put16(cfg_hdle, caps_ptr + PCI_MSI_CTRL, msi_ctrl); 7510Sstevel@tonic-gate } else if (type == DDI_INTR_TYPE_MSIX) { 7520Sstevel@tonic-gate DDI_INTR_NEXDBG((CE_CONT, "pci_msi_set_nintrs: unsupported\n")); 7530Sstevel@tonic-gate } 7540Sstevel@tonic-gate 7550Sstevel@tonic-gate pci_config_teardown(&cfg_hdle); 7560Sstevel@tonic-gate return (DDI_SUCCESS); 7570Sstevel@tonic-gate } 7580Sstevel@tonic-gate 7590Sstevel@tonic-gate 7600Sstevel@tonic-gate /* 7610Sstevel@tonic-gate * pci_msi_get_supported_type: 7620Sstevel@tonic-gate * 7630Sstevel@tonic-gate * Returns DDI_INTR_TYPE_MSI and/or DDI_INTR_TYPE_MSIX as supported 7640Sstevel@tonic-gate * types if device supports them. A DDI_FAILURE is returned otherwise. 7650Sstevel@tonic-gate */ 7660Sstevel@tonic-gate int 7670Sstevel@tonic-gate pci_msi_get_supported_type(dev_info_t *rdip, int *typesp) 7680Sstevel@tonic-gate { 7690Sstevel@tonic-gate ushort_t caps_ptr, msi_ctrl; 7700Sstevel@tonic-gate ddi_acc_handle_t cfg_hdle; 7710Sstevel@tonic-gate 7720Sstevel@tonic-gate DDI_INTR_NEXDBG((CE_CONT, "pci_msi_get_supported_type: " 7730Sstevel@tonic-gate "rdip = 0x%p\n", (void *)rdip)); 7740Sstevel@tonic-gate 7750Sstevel@tonic-gate *typesp = 0; 7760Sstevel@tonic-gate 7770Sstevel@tonic-gate if (pci_get_msi_ctrl(rdip, DDI_INTR_TYPE_MSI, &msi_ctrl, 7780Sstevel@tonic-gate &caps_ptr, &cfg_hdle) == DDI_SUCCESS) { 7790Sstevel@tonic-gate *typesp |= DDI_INTR_TYPE_MSI; 7800Sstevel@tonic-gate pci_config_teardown(&cfg_hdle); 7810Sstevel@tonic-gate } 7820Sstevel@tonic-gate 7830Sstevel@tonic-gate if (pci_get_msi_ctrl(rdip, DDI_INTR_TYPE_MSIX, &msi_ctrl, 7840Sstevel@tonic-gate &caps_ptr, &cfg_hdle) == DDI_SUCCESS) { 7850Sstevel@tonic-gate *typesp |= DDI_INTR_TYPE_MSIX; 7860Sstevel@tonic-gate pci_config_teardown(&cfg_hdle); 7870Sstevel@tonic-gate } 7880Sstevel@tonic-gate 7890Sstevel@tonic-gate DDI_INTR_NEXDBG((CE_CONT, "pci_msi_get_supported_type: " 7900Sstevel@tonic-gate "rdip = 0x%p types 0x%x\n", (void *)rdip, *typesp)); 7910Sstevel@tonic-gate 7920Sstevel@tonic-gate return (*typesp == 0 ? DDI_FAILURE : DDI_SUCCESS); 7930Sstevel@tonic-gate } 7940Sstevel@tonic-gate 7950Sstevel@tonic-gate 7960Sstevel@tonic-gate /* 7970Sstevel@tonic-gate * pci_msix_init: 7980Sstevel@tonic-gate * This function initializes the various handles/addrs etc. 7990Sstevel@tonic-gate * needed for MSI-X support. It also allocates a private 8000Sstevel@tonic-gate * structure to keep track of these. 8010Sstevel@tonic-gate */ 8020Sstevel@tonic-gate ddi_intr_msix_t * 8030Sstevel@tonic-gate pci_msix_init(dev_info_t *rdip) 8040Sstevel@tonic-gate { 805*965Sgovinda uint_t rnumber, breg, nregs; 8060Sstevel@tonic-gate size_t msix_tbl_size; 8070Sstevel@tonic-gate size_t pba_tbl_size; 808*965Sgovinda ushort_t caps_ptr, msix_ctrl; 8090Sstevel@tonic-gate ddi_intr_msix_t *msix_p; 8100Sstevel@tonic-gate ddi_acc_handle_t cfg_hdle; 811*965Sgovinda pci_regspec_t *rp; 812*965Sgovinda int reg_size, addr_space, offset, *regs_list; 813*965Sgovinda int i, ret; 8140Sstevel@tonic-gate 8150Sstevel@tonic-gate DDI_INTR_NEXDBG((CE_CONT, "pci_msix_init: rdip = %p\n", (void *)rdip)); 8160Sstevel@tonic-gate 817*965Sgovinda if (pci_get_msi_ctrl(rdip, DDI_INTR_TYPE_MSIX, &msix_ctrl, 8180Sstevel@tonic-gate &caps_ptr, &cfg_hdle) != DDI_SUCCESS) 8190Sstevel@tonic-gate return (NULL); 8200Sstevel@tonic-gate 8210Sstevel@tonic-gate msix_p = kmem_zalloc(sizeof (ddi_intr_msix_t), KM_SLEEP); 8220Sstevel@tonic-gate 8230Sstevel@tonic-gate /* 8240Sstevel@tonic-gate * Initialize the devacc structure 8250Sstevel@tonic-gate */ 8260Sstevel@tonic-gate msix_p->msix_dev_attr.devacc_attr_version = DDI_DEVICE_ATTR_V0; 8270Sstevel@tonic-gate msix_p->msix_dev_attr.devacc_attr_endian_flags = 8280Sstevel@tonic-gate DDI_STRUCTURE_LE_ACC; 8290Sstevel@tonic-gate msix_p->msix_dev_attr.devacc_attr_dataorder = DDI_STRICTORDER_ACC; 8300Sstevel@tonic-gate 831*965Sgovinda /* Map the entire MSI-X vector table */ 8320Sstevel@tonic-gate msix_p->msix_tbl_offset = pci_config_get32(cfg_hdle, 8330Sstevel@tonic-gate caps_ptr + PCI_MSIX_TBL_OFFSET); 834*965Sgovinda 835*965Sgovinda if ((breg = pci_msix_bir_index[msix_p->msix_tbl_offset & 836*965Sgovinda PCI_MSIX_TBL_BIR_MASK]) == 0xff) 837*965Sgovinda goto fail1; 838*965Sgovinda 839*965Sgovinda msix_p->msix_tbl_offset = msix_p->msix_tbl_offset & 840*965Sgovinda ~PCI_MSIX_TBL_BIR_MASK; 841*965Sgovinda msix_tbl_size = ((msix_ctrl & PCI_MSIX_TBL_SIZE_MASK) + 1) * 842*965Sgovinda PCI_MSIX_VECTOR_SIZE; 8430Sstevel@tonic-gate 844*965Sgovinda DDI_INTR_NEXDBG((CE_CONT, "pci_msix_init: MSI-X table offset 0x%x " 845*965Sgovinda "breg 0x%x size 0x%lx\n", msix_p->msix_tbl_offset, breg, 846*965Sgovinda msix_tbl_size)); 847*965Sgovinda 848*965Sgovinda if ((ret = ddi_prop_lookup_int_array(DDI_DEV_T_ANY, rdip, 849*965Sgovinda DDI_PROP_DONTPASS, "reg", (int **)®s_list, &nregs)) 850*965Sgovinda != DDI_PROP_SUCCESS) { 851*965Sgovinda DDI_INTR_NEXDBG((CE_CONT, "pci_msix_init: " 852*965Sgovinda "ddi_prop_lookup_int_array failed %d\n", ret)); 853*965Sgovinda 854*965Sgovinda goto fail1; 855*965Sgovinda } 856*965Sgovinda 857*965Sgovinda reg_size = sizeof (pci_regspec_t) / sizeof (int); 8580Sstevel@tonic-gate 859*965Sgovinda for (i = 1, rnumber = 0; i < nregs/reg_size; i++) { 860*965Sgovinda rp = (pci_regspec_t *)®s_list[i * reg_size]; 861*965Sgovinda addr_space = rp->pci_phys_hi & PCI_ADDR_MASK; 862*965Sgovinda offset = PCI_REG_REG_G(rp->pci_phys_hi); 863*965Sgovinda 864*965Sgovinda if ((offset == breg) && ((addr_space == PCI_ADDR_MEM32) || 865*965Sgovinda (addr_space == PCI_ADDR_MEM64))) { 866*965Sgovinda rnumber = i; 867*965Sgovinda break; 868*965Sgovinda } 869*965Sgovinda } 870*965Sgovinda 871*965Sgovinda DDI_INTR_NEXDBG((CE_CONT, "pci_msix_init: MSI-X rnum = %d\n", rnumber)); 872*965Sgovinda 873*965Sgovinda if (rnumber == 0) { 874*965Sgovinda DDI_INTR_NEXDBG((CE_CONT, "pci_msix_init: " 875*965Sgovinda "no mtaching reg number for offset 0x%x\n", breg)); 876*965Sgovinda 877*965Sgovinda goto fail2; 878*965Sgovinda } 879*965Sgovinda 880*965Sgovinda if ((ret = ddi_regs_map_setup(rdip, rnumber, 881*965Sgovinda (caddr_t *)&msix_p->msix_tbl_addr, msix_p->msix_tbl_offset, 882*965Sgovinda msix_tbl_size, &msix_p->msix_dev_attr, 883*965Sgovinda &msix_p->msix_tbl_hdl)) != DDI_SUCCESS) { 884*965Sgovinda DDI_INTR_NEXDBG((CE_CONT, "pci_msix_init: MSI-X Table " 885*965Sgovinda "ddi_regs_map_setup failed %d\n", ret)); 886*965Sgovinda 887*965Sgovinda goto fail2; 8880Sstevel@tonic-gate } 8890Sstevel@tonic-gate 8900Sstevel@tonic-gate /* 8910Sstevel@tonic-gate * Map in the MSI-X Pending Bit Array 8920Sstevel@tonic-gate */ 8930Sstevel@tonic-gate msix_p->msix_pba_offset = pci_config_get32(cfg_hdle, 8940Sstevel@tonic-gate caps_ptr + PCI_MSIX_PBA_OFFSET); 895*965Sgovinda 896*965Sgovinda if ((breg = pci_msix_bir_index[msix_p->msix_pba_offset & 897*965Sgovinda PCI_MSIX_PBA_BIR_MASK]) == 0xff) 898*965Sgovinda goto fail3; 899*965Sgovinda 900*965Sgovinda msix_p->msix_pba_offset = msix_p->msix_pba_offset & 901*965Sgovinda ~PCI_MSIX_PBA_BIR_MASK; 902*965Sgovinda pba_tbl_size = ((msix_ctrl & PCI_MSIX_TBL_SIZE_MASK) + 1)/8; 903*965Sgovinda 904*965Sgovinda DDI_INTR_NEXDBG((CE_CONT, "pci_msix_init: PBA table offset 0x%x " 905*965Sgovinda "breg 0x%x size 0x%lx\n", msix_p->msix_pba_offset, breg, 906*965Sgovinda pba_tbl_size)); 907*965Sgovinda 908*965Sgovinda for (i = 1, rnumber = 0; i < nregs/reg_size; i++) { 909*965Sgovinda rp = (pci_regspec_t *)®s_list[i * reg_size]; 910*965Sgovinda addr_space = rp->pci_phys_hi & PCI_ADDR_MASK; 911*965Sgovinda offset = PCI_REG_REG_G(rp->pci_phys_hi); 9120Sstevel@tonic-gate 913*965Sgovinda if ((offset == breg) && ((addr_space == PCI_ADDR_MEM32) || 914*965Sgovinda (addr_space == PCI_ADDR_MEM64))) { 915*965Sgovinda ddi_prop_free(regs_list); 916*965Sgovinda rnumber = i; 917*965Sgovinda break; 918*965Sgovinda } 919*965Sgovinda } 920*965Sgovinda 921*965Sgovinda DDI_INTR_NEXDBG((CE_CONT, "pci_msix_init: PBA rnum = %d\n", rnumber)); 922*965Sgovinda 923*965Sgovinda if (rnumber == 0) { 924*965Sgovinda DDI_INTR_NEXDBG((CE_CONT, "pci_msix_init: " 925*965Sgovinda "no mtaching reg number for offset 0x%x\n", breg)); 926*965Sgovinda 927*965Sgovinda goto fail3; 928*965Sgovinda } 929*965Sgovinda 930*965Sgovinda if ((ret = ddi_regs_map_setup(rdip, rnumber, 931*965Sgovinda (caddr_t *)&msix_p->msix_pba_addr, msix_p->msix_pba_offset, 932*965Sgovinda pba_tbl_size, &msix_p->msix_dev_attr, 933*965Sgovinda &msix_p->msix_pba_hdl)) != DDI_SUCCESS) { 934*965Sgovinda DDI_INTR_NEXDBG((CE_CONT, "pci_msix_init: PBA " 935*965Sgovinda "ddi_regs_map_setup failed %d\n", ret)); 936*965Sgovinda 937*965Sgovinda goto fail3; 9380Sstevel@tonic-gate } 9390Sstevel@tonic-gate 9400Sstevel@tonic-gate DDI_INTR_NEXDBG((CE_CONT, "pci_msix_init: msix_p = 0x%p DONE!!\n", 9410Sstevel@tonic-gate (void *)msix_p)); 9420Sstevel@tonic-gate 943*965Sgovinda goto done; 944*965Sgovinda 945*965Sgovinda fail3: 946*965Sgovinda ddi_regs_map_free(&msix_p->msix_tbl_hdl); 947*965Sgovinda fail2: 948*965Sgovinda ddi_prop_free(regs_list); 949*965Sgovinda fail1: 950*965Sgovinda kmem_free(msix_p, sizeof (ddi_intr_msix_t)); 951*965Sgovinda msix_p = NULL; 952*965Sgovinda done: 9530Sstevel@tonic-gate pci_config_teardown(&cfg_hdle); 9540Sstevel@tonic-gate return (msix_p); 9550Sstevel@tonic-gate } 9560Sstevel@tonic-gate 9570Sstevel@tonic-gate 9580Sstevel@tonic-gate /* 9590Sstevel@tonic-gate * pci_msix_fini: 9600Sstevel@tonic-gate * This function cleans up previously allocated handles/addrs etc. 9610Sstevel@tonic-gate * It is only called if no more MSI-X interrupts are being used. 9620Sstevel@tonic-gate */ 9630Sstevel@tonic-gate void 9640Sstevel@tonic-gate pci_msix_fini(ddi_intr_msix_t *msix_p) 9650Sstevel@tonic-gate { 9660Sstevel@tonic-gate DDI_INTR_NEXDBG((CE_CONT, "pci_msix_fini: msix_p = 0x%p\n", 9670Sstevel@tonic-gate (void *)msix_p)); 9680Sstevel@tonic-gate 9690Sstevel@tonic-gate ddi_regs_map_free(&msix_p->msix_pba_hdl); 9700Sstevel@tonic-gate ddi_regs_map_free(&msix_p->msix_tbl_hdl); 9710Sstevel@tonic-gate kmem_free(msix_p, sizeof (ddi_intr_msix_t)); 9720Sstevel@tonic-gate } 9730Sstevel@tonic-gate 9740Sstevel@tonic-gate 9750Sstevel@tonic-gate 9760Sstevel@tonic-gate /* 9770Sstevel@tonic-gate * Next set of routines are for INTx (legacy) PCI interrupt 9780Sstevel@tonic-gate * support only. 9790Sstevel@tonic-gate */ 9800Sstevel@tonic-gate 9810Sstevel@tonic-gate /* 9820Sstevel@tonic-gate * pci_intx_get_cap: 9830Sstevel@tonic-gate * For non-MSI devices that comply to PCI v2.3 or greater; 9840Sstevel@tonic-gate * read the command register. Bit 10 implies interrupt disable. 9850Sstevel@tonic-gate * Set this bit and then read the status register bit 3. 9860Sstevel@tonic-gate * Bit 3 of status register is Interrupt state. 9870Sstevel@tonic-gate * If it is set; then the device supports 'Masking' 9880Sstevel@tonic-gate * 9890Sstevel@tonic-gate * Reset the device back to the original state. 9900Sstevel@tonic-gate */ 9910Sstevel@tonic-gate int 9920Sstevel@tonic-gate pci_intx_get_cap(dev_info_t *dip, int *flagsp) 9930Sstevel@tonic-gate { 9940Sstevel@tonic-gate uint16_t cmdreg, savereg; 9950Sstevel@tonic-gate ddi_acc_handle_t cfg_hdl; 9960Sstevel@tonic-gate #ifdef DEBUG 9970Sstevel@tonic-gate uint16_t statreg; 9980Sstevel@tonic-gate #endif /* DEBUG */ 9990Sstevel@tonic-gate 10000Sstevel@tonic-gate *flagsp = 0; 10010Sstevel@tonic-gate DDI_INTR_NEXDBG((CE_CONT, "pci_intx_get_cap: %s%d: called\n", 10020Sstevel@tonic-gate ddi_driver_name(dip), ddi_get_instance(dip))); 10030Sstevel@tonic-gate 10040Sstevel@tonic-gate if (pci_config_setup(dip, &cfg_hdl) != DDI_SUCCESS) { 10050Sstevel@tonic-gate DDI_INTR_NEXDBG((CE_CONT, "pci_intx_get_cap: can't get " 10060Sstevel@tonic-gate "config handle\n")); 10070Sstevel@tonic-gate return (DDI_FAILURE); 10080Sstevel@tonic-gate } 10090Sstevel@tonic-gate 10100Sstevel@tonic-gate savereg = pci_config_get16(cfg_hdl, PCI_CONF_COMM); 10110Sstevel@tonic-gate DDI_INTR_NEXDBG((CE_CONT, "pci_intx_get_cap: " 10120Sstevel@tonic-gate "command register was 0x%x\n", savereg)); 10130Sstevel@tonic-gate 10140Sstevel@tonic-gate /* Disable the interrupts */ 10150Sstevel@tonic-gate cmdreg = savereg | PCI_COMM_INTX_DISABLE; 10160Sstevel@tonic-gate pci_config_put16(cfg_hdl, PCI_CONF_COMM, cmdreg); 10170Sstevel@tonic-gate 10180Sstevel@tonic-gate #ifdef DEBUG 10190Sstevel@tonic-gate statreg = pci_config_get16(cfg_hdl, PCI_CONF_STAT); 10200Sstevel@tonic-gate DDI_INTR_NEXDBG((CE_CONT, "pci_intx_get_cap: " 10210Sstevel@tonic-gate "status register is 0x%x\n", statreg)); 10220Sstevel@tonic-gate #endif /* DEBUG */ 10230Sstevel@tonic-gate 10240Sstevel@tonic-gate /* Read the bit back */ 10250Sstevel@tonic-gate cmdreg = pci_config_get16(cfg_hdl, PCI_CONF_COMM); 10260Sstevel@tonic-gate DDI_INTR_NEXDBG((CE_CONT, "pci_intx_get_cap: " 10270Sstevel@tonic-gate "command register is now 0x%x\n", cmdreg)); 10280Sstevel@tonic-gate 1029693Sgovinda *flagsp = DDI_INTR_FLAG_LEVEL; 1030693Sgovinda 10310Sstevel@tonic-gate if (cmdreg & PCI_COMM_INTX_DISABLE) { 10320Sstevel@tonic-gate DDI_INTR_NEXDBG((CE_CONT, "pci_intx_get_cap: " 10330Sstevel@tonic-gate "masking supported\n")); 1034693Sgovinda *flagsp |= (DDI_INTR_FLAG_MASKABLE | 1035693Sgovinda DDI_INTR_FLAG_PENDING); 10360Sstevel@tonic-gate } 10370Sstevel@tonic-gate 10380Sstevel@tonic-gate /* Restore the device back to the original state and return */ 10390Sstevel@tonic-gate pci_config_put16(cfg_hdl, PCI_CONF_COMM, savereg); 10400Sstevel@tonic-gate 10410Sstevel@tonic-gate pci_config_teardown(&cfg_hdl); 10420Sstevel@tonic-gate return (DDI_SUCCESS); 10430Sstevel@tonic-gate } 10440Sstevel@tonic-gate 10450Sstevel@tonic-gate 10460Sstevel@tonic-gate /* 10470Sstevel@tonic-gate * pci_intx_clr_mask: 10480Sstevel@tonic-gate * For non-MSI devices that comply to PCI v2.3 or greater; 10490Sstevel@tonic-gate * clear the bit10 in the command register. 10500Sstevel@tonic-gate */ 10510Sstevel@tonic-gate int 10520Sstevel@tonic-gate pci_intx_clr_mask(dev_info_t *dip) 10530Sstevel@tonic-gate { 10540Sstevel@tonic-gate uint16_t cmdreg; 10550Sstevel@tonic-gate ddi_acc_handle_t cfg_hdl; 10560Sstevel@tonic-gate 10570Sstevel@tonic-gate DDI_INTR_NEXDBG((CE_CONT, "pci_intx_clr_mask: %s%d: called\n", 10580Sstevel@tonic-gate ddi_driver_name(dip), ddi_get_instance(dip))); 10590Sstevel@tonic-gate 10600Sstevel@tonic-gate if (pci_config_setup(dip, &cfg_hdl) != DDI_SUCCESS) { 10610Sstevel@tonic-gate DDI_INTR_NEXDBG((CE_CONT, "pci_intx_clr_mask: can't get " 10620Sstevel@tonic-gate "config handle\n")); 10630Sstevel@tonic-gate return (DDI_FAILURE); 10640Sstevel@tonic-gate } 10650Sstevel@tonic-gate 10660Sstevel@tonic-gate cmdreg = pci_config_get16(cfg_hdl, PCI_CONF_COMM); 10670Sstevel@tonic-gate DDI_INTR_NEXDBG((CE_CONT, "pci_intx_clr_mask: " 10680Sstevel@tonic-gate "command register was 0x%x\n", cmdreg)); 10690Sstevel@tonic-gate 10700Sstevel@tonic-gate /* Enable the interrupts */ 10710Sstevel@tonic-gate cmdreg &= ~PCI_COMM_INTX_DISABLE; 10720Sstevel@tonic-gate pci_config_put16(cfg_hdl, PCI_CONF_COMM, cmdreg); 10730Sstevel@tonic-gate pci_config_teardown(&cfg_hdl); 10740Sstevel@tonic-gate return (DDI_SUCCESS); 10750Sstevel@tonic-gate } 10760Sstevel@tonic-gate 10770Sstevel@tonic-gate 10780Sstevel@tonic-gate /* 10790Sstevel@tonic-gate * pci_intx_set_mask: 10800Sstevel@tonic-gate * For non-MSI devices that comply to PCI v2.3 or greater; 10810Sstevel@tonic-gate * set the bit10 in the command register. 10820Sstevel@tonic-gate */ 10830Sstevel@tonic-gate int 10840Sstevel@tonic-gate pci_intx_set_mask(dev_info_t *dip) 10850Sstevel@tonic-gate { 10860Sstevel@tonic-gate uint16_t cmdreg; 10870Sstevel@tonic-gate ddi_acc_handle_t cfg_hdl; 10880Sstevel@tonic-gate 10890Sstevel@tonic-gate DDI_INTR_NEXDBG((CE_CONT, "pci_intx_set_mask: %s%d: called\n", 10900Sstevel@tonic-gate ddi_driver_name(dip), ddi_get_instance(dip))); 10910Sstevel@tonic-gate 10920Sstevel@tonic-gate if (pci_config_setup(dip, &cfg_hdl) != DDI_SUCCESS) { 10930Sstevel@tonic-gate DDI_INTR_NEXDBG((CE_CONT, "pci_intx_set_mask: can't get " 10940Sstevel@tonic-gate "config handle\n")); 10950Sstevel@tonic-gate return (DDI_FAILURE); 10960Sstevel@tonic-gate } 10970Sstevel@tonic-gate 10980Sstevel@tonic-gate cmdreg = pci_config_get16(cfg_hdl, PCI_CONF_COMM); 10990Sstevel@tonic-gate DDI_INTR_NEXDBG((CE_CONT, "pci_intx_set_mask: " 11000Sstevel@tonic-gate "command register was 0x%x\n", cmdreg)); 11010Sstevel@tonic-gate 11020Sstevel@tonic-gate /* Disable the interrupts */ 11030Sstevel@tonic-gate cmdreg |= PCI_COMM_INTX_DISABLE; 11040Sstevel@tonic-gate pci_config_put16(cfg_hdl, PCI_CONF_COMM, cmdreg); 11050Sstevel@tonic-gate pci_config_teardown(&cfg_hdl); 11060Sstevel@tonic-gate return (DDI_SUCCESS); 11070Sstevel@tonic-gate } 11080Sstevel@tonic-gate 11090Sstevel@tonic-gate /* 11100Sstevel@tonic-gate * pci_intx_get_pending: 11110Sstevel@tonic-gate * For non-MSI devices that comply to PCI v2.3 or greater; 11120Sstevel@tonic-gate * read the status register. Bit 3 of status register is 11130Sstevel@tonic-gate * Interrupt state. If it is set; then the interrupt is 11140Sstevel@tonic-gate * 'Pending'. 11150Sstevel@tonic-gate */ 11160Sstevel@tonic-gate int 11170Sstevel@tonic-gate pci_intx_get_pending(dev_info_t *dip, int *pendingp) 11180Sstevel@tonic-gate { 11190Sstevel@tonic-gate uint16_t statreg; 11200Sstevel@tonic-gate ddi_acc_handle_t cfg_hdl; 11210Sstevel@tonic-gate 11220Sstevel@tonic-gate *pendingp = 0; 11230Sstevel@tonic-gate DDI_INTR_NEXDBG((CE_CONT, "pci_intx_get_pending: %s%d: called\n", 11240Sstevel@tonic-gate ddi_driver_name(dip), ddi_get_instance(dip))); 11250Sstevel@tonic-gate 11260Sstevel@tonic-gate if (pci_config_setup(dip, &cfg_hdl) != DDI_SUCCESS) { 11270Sstevel@tonic-gate DDI_INTR_NEXDBG((CE_CONT, "pci_intx_get_pending: can't get " 11280Sstevel@tonic-gate "config handle\n")); 11290Sstevel@tonic-gate return (DDI_FAILURE); 11300Sstevel@tonic-gate } 11310Sstevel@tonic-gate 11320Sstevel@tonic-gate statreg = pci_config_get16(cfg_hdl, PCI_CONF_STAT); 11330Sstevel@tonic-gate 11340Sstevel@tonic-gate if (statreg & PCI_STAT_INTR) { 11350Sstevel@tonic-gate DDI_INTR_NEXDBG((CE_CONT, "pci_intx_get_pending: " 11360Sstevel@tonic-gate "interrupt is pending\n")); 11370Sstevel@tonic-gate *pendingp = 1; 11380Sstevel@tonic-gate } 11390Sstevel@tonic-gate 11400Sstevel@tonic-gate pci_config_teardown(&cfg_hdl); 11410Sstevel@tonic-gate return (DDI_SUCCESS); 11420Sstevel@tonic-gate } 11430Sstevel@tonic-gate 11440Sstevel@tonic-gate 11450Sstevel@tonic-gate /* 11460Sstevel@tonic-gate * pci_devclass_to_ipl: 11470Sstevel@tonic-gate * translate from device class to ipl 11480Sstevel@tonic-gate * NOTE: This function is added here as pci_intx_get_ispec() 11490Sstevel@tonic-gate * calls this to figure out the priority. 11500Sstevel@tonic-gate * It is moved over from x86 pci.c 11510Sstevel@tonic-gate */ 11520Sstevel@tonic-gate int 11530Sstevel@tonic-gate pci_devclass_to_ipl(int class) 11540Sstevel@tonic-gate { 11550Sstevel@tonic-gate int base_cl; 11560Sstevel@tonic-gate int ipl; 11570Sstevel@tonic-gate 11580Sstevel@tonic-gate base_cl = (class & 0xff0000) >> 16; 11590Sstevel@tonic-gate 11600Sstevel@tonic-gate /* 11610Sstevel@tonic-gate * Use the class code values to construct an ipl for the device. 11620Sstevel@tonic-gate */ 11630Sstevel@tonic-gate switch (base_cl) { 11640Sstevel@tonic-gate default: 11650Sstevel@tonic-gate case PCI_CLASS_NONE: 11660Sstevel@tonic-gate ipl = 1; 11670Sstevel@tonic-gate break; 11680Sstevel@tonic-gate case PCI_CLASS_MASS: 11690Sstevel@tonic-gate ipl = 0x5; 11700Sstevel@tonic-gate break; 11710Sstevel@tonic-gate case PCI_CLASS_NET: 11720Sstevel@tonic-gate ipl = 0x6; 11730Sstevel@tonic-gate break; 11740Sstevel@tonic-gate case PCI_CLASS_DISPLAY: 11750Sstevel@tonic-gate ipl = 0x9; 11760Sstevel@tonic-gate break; 11770Sstevel@tonic-gate /* 11780Sstevel@tonic-gate * for high priority interrupt handlers, use level 12 11790Sstevel@tonic-gate * as the highest for device drivers 11800Sstevel@tonic-gate */ 11810Sstevel@tonic-gate case PCI_CLASS_MM: 11820Sstevel@tonic-gate ipl = 0xc; 11830Sstevel@tonic-gate break; 11840Sstevel@tonic-gate case PCI_CLASS_MEM: 11850Sstevel@tonic-gate ipl = 0xc; 11860Sstevel@tonic-gate break; 11870Sstevel@tonic-gate case PCI_CLASS_BRIDGE: 11880Sstevel@tonic-gate ipl = 0xc; 11890Sstevel@tonic-gate break; 11900Sstevel@tonic-gate } 11910Sstevel@tonic-gate return (ipl); 11920Sstevel@tonic-gate } 11930Sstevel@tonic-gate 11940Sstevel@tonic-gate 11950Sstevel@tonic-gate /* 11960Sstevel@tonic-gate * pci_intx_get_ispec: 11970Sstevel@tonic-gate * Get intrspec for PCI devices (legacy support) 11980Sstevel@tonic-gate * NOTE: This is moved here from x86 pci.c and is 11990Sstevel@tonic-gate * needed here as pci-ide.c uses it as well 12000Sstevel@tonic-gate */ 12010Sstevel@tonic-gate /*ARGSUSED*/ 12020Sstevel@tonic-gate ddi_intrspec_t 12030Sstevel@tonic-gate pci_intx_get_ispec(dev_info_t *dip, dev_info_t *rdip, int inum) 12040Sstevel@tonic-gate { 12050Sstevel@tonic-gate int class, *intpriorities; 12060Sstevel@tonic-gate uint_t num_intpriorities; 12070Sstevel@tonic-gate struct intrspec *ispec; 12080Sstevel@tonic-gate ddi_acc_handle_t cfg_hdl; 12090Sstevel@tonic-gate struct ddi_parent_private_data *pdptr; 12100Sstevel@tonic-gate 12110Sstevel@tonic-gate if ((pdptr = ddi_get_parent_data(rdip)) == NULL) 12120Sstevel@tonic-gate return (NULL); 12130Sstevel@tonic-gate 12140Sstevel@tonic-gate ispec = pdptr->par_intr; 12150Sstevel@tonic-gate ASSERT(ispec); 12160Sstevel@tonic-gate 12170Sstevel@tonic-gate /* check if the intrspec_pri has been initialized */ 12180Sstevel@tonic-gate if (!ispec->intrspec_pri) { 12190Sstevel@tonic-gate if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, rdip, 12200Sstevel@tonic-gate DDI_PROP_DONTPASS, "interrupt-priorities", 12210Sstevel@tonic-gate &intpriorities, &num_intpriorities) == DDI_PROP_SUCCESS) { 12220Sstevel@tonic-gate if (inum < num_intpriorities) 12230Sstevel@tonic-gate ispec->intrspec_pri = intpriorities[inum]; 12240Sstevel@tonic-gate ddi_prop_free(intpriorities); 12250Sstevel@tonic-gate } 12260Sstevel@tonic-gate 12270Sstevel@tonic-gate /* If still no priority, guess based on the class code */ 12280Sstevel@tonic-gate if (ispec->intrspec_pri == 0) { 12290Sstevel@tonic-gate /* get 'class' property to derive the intr priority */ 12300Sstevel@tonic-gate class = ddi_prop_get_int(DDI_DEV_T_ANY, rdip, 12310Sstevel@tonic-gate DDI_PROP_DONTPASS, "class-code", -1); 12320Sstevel@tonic-gate ispec->intrspec_pri = (class == -1) ? 1 : 12330Sstevel@tonic-gate pci_devclass_to_ipl(class); 12340Sstevel@tonic-gate } 12350Sstevel@tonic-gate } 12360Sstevel@tonic-gate 12370Sstevel@tonic-gate /* Get interrupt line value */ 12380Sstevel@tonic-gate if (!ispec->intrspec_vec) { 12390Sstevel@tonic-gate if (pci_config_setup(rdip, &cfg_hdl) != DDI_SUCCESS) { 12400Sstevel@tonic-gate DDI_INTR_NEXDBG((CE_CONT, "pci_intx_get_iline: " 12410Sstevel@tonic-gate "can't get config handle\n")); 12420Sstevel@tonic-gate return (NULL); 12430Sstevel@tonic-gate } 12440Sstevel@tonic-gate 12450Sstevel@tonic-gate ispec->intrspec_vec = pci_config_get8(cfg_hdl, PCI_CONF_ILINE); 12460Sstevel@tonic-gate pci_config_teardown(&cfg_hdl); 12470Sstevel@tonic-gate } 12480Sstevel@tonic-gate 12490Sstevel@tonic-gate return ((ddi_intrspec_t)ispec); 12500Sstevel@tonic-gate } 1251