xref: /onnv-gate/usr/src/uts/common/io/pci_intr_lib.c (revision 693:1c08294a694e)
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 /*
400Sstevel@tonic-gate  * Library utility functions
410Sstevel@tonic-gate  */
420Sstevel@tonic-gate 
430Sstevel@tonic-gate 
440Sstevel@tonic-gate /*
450Sstevel@tonic-gate  * pci_check_pciex:
460Sstevel@tonic-gate  *
470Sstevel@tonic-gate  * check whether the device has PCI-E capability
480Sstevel@tonic-gate  */
490Sstevel@tonic-gate int
500Sstevel@tonic-gate pci_check_pciex(dev_info_t *dip)
510Sstevel@tonic-gate {
520Sstevel@tonic-gate 	ddi_acc_handle_t cfg_handle;
530Sstevel@tonic-gate 	ushort_t status;
540Sstevel@tonic-gate 	ushort_t cap;
550Sstevel@tonic-gate 	ushort_t capsp;
560Sstevel@tonic-gate 	ushort_t cap_count = PCI_CAP_MAX_PTR;
570Sstevel@tonic-gate 
580Sstevel@tonic-gate 	DDI_INTR_NEXDBG((CE_CONT, "pci_check_pciex: dip: 0x%p, driver: %s, "
590Sstevel@tonic-gate 	    "binding: %s, nodename: %s\n", (void *)dip, ddi_driver_name(dip),
600Sstevel@tonic-gate 	    ddi_binding_name(dip), ddi_node_name(dip)));
610Sstevel@tonic-gate 
620Sstevel@tonic-gate 	if (pci_config_setup(dip, &cfg_handle) != DDI_SUCCESS) {
630Sstevel@tonic-gate 		DDI_INTR_NEXDBG((CE_CONT, "pci_check_pciex: "
640Sstevel@tonic-gate 		    "pci_config_setup() failed\n"));
650Sstevel@tonic-gate 		return (DDI_FAILURE);
660Sstevel@tonic-gate 	}
670Sstevel@tonic-gate 	status = pci_config_get16(cfg_handle, PCI_CONF_STAT);
680Sstevel@tonic-gate 	if (!(status & PCI_STAT_CAP))
690Sstevel@tonic-gate 		goto notpciex;
700Sstevel@tonic-gate 
710Sstevel@tonic-gate 	capsp = pci_config_get8(cfg_handle, PCI_CONF_CAP_PTR);
720Sstevel@tonic-gate 	while (cap_count-- && capsp >= PCI_CAP_PTR_OFF) {
730Sstevel@tonic-gate 		capsp &= PCI_CAP_PTR_MASK;
740Sstevel@tonic-gate 		cap = pci_config_get8(cfg_handle, capsp);
750Sstevel@tonic-gate 		DDI_INTR_NEXDBG((CE_CONT, "pci_check_pciex: capid=0x%x\n",
760Sstevel@tonic-gate 		    cap));
770Sstevel@tonic-gate 		if (cap == PCI_CAP_ID_PCI_E) {
780Sstevel@tonic-gate 			DDI_INTR_NEXDBG((CE_CONT, "pci_check_pciex: "
790Sstevel@tonic-gate 			    "PCI-Express capability found\n"));
800Sstevel@tonic-gate 			pci_config_teardown(&cfg_handle);
810Sstevel@tonic-gate 			return (DDI_SUCCESS);
820Sstevel@tonic-gate 		}
830Sstevel@tonic-gate 		capsp = pci_config_get8(cfg_handle, capsp + PCI_CAP_NEXT_PTR);
840Sstevel@tonic-gate 	}
850Sstevel@tonic-gate notpciex:
860Sstevel@tonic-gate 	pci_config_teardown(&cfg_handle);
870Sstevel@tonic-gate 	return (DDI_FAILURE);
880Sstevel@tonic-gate }
890Sstevel@tonic-gate 
900Sstevel@tonic-gate 
910Sstevel@tonic-gate /*
920Sstevel@tonic-gate  * pci_get_msi_ctrl:
930Sstevel@tonic-gate  *
940Sstevel@tonic-gate  *	Helper function that returns with 'cfg_hdl', MSI/X ctrl pointer,
950Sstevel@tonic-gate  *	and caps_ptr for MSI/X if these are found.
960Sstevel@tonic-gate  */
970Sstevel@tonic-gate static int
980Sstevel@tonic-gate pci_get_msi_ctrl(dev_info_t *dip, int type, ushort_t *msi_ctrl,
990Sstevel@tonic-gate     ushort_t *caps_ptr, ddi_acc_handle_t *cfg_hdle)
1000Sstevel@tonic-gate {
1010Sstevel@tonic-gate 	ushort_t	cap, cap_count;
1020Sstevel@tonic-gate 
1030Sstevel@tonic-gate 	*msi_ctrl = *caps_ptr = 0;
1040Sstevel@tonic-gate 
1050Sstevel@tonic-gate 	if (pci_config_setup(dip, cfg_hdle) != DDI_SUCCESS) {
1060Sstevel@tonic-gate 		DDI_INTR_NEXDBG((CE_CONT, "pci_get_msi_ctrl: "
1070Sstevel@tonic-gate 		    "%s%d can't get config handle",
1080Sstevel@tonic-gate 		    ddi_driver_name(dip), ddi_get_instance(dip)));
1090Sstevel@tonic-gate 
1100Sstevel@tonic-gate 		return (DDI_FAILURE);
1110Sstevel@tonic-gate 	}
1120Sstevel@tonic-gate 
1130Sstevel@tonic-gate 	/* Are capabilities supported? */
1140Sstevel@tonic-gate 	if (!(pci_config_get16(*cfg_hdle, PCI_CONF_STAT) & PCI_STAT_CAP)) {
1150Sstevel@tonic-gate 		DDI_INTR_NEXDBG((CE_CONT, "pci_get_msi_ctrl: "
1160Sstevel@tonic-gate 		    "%p doesn't support capabilities\n", (void *)dip));
1170Sstevel@tonic-gate 
1180Sstevel@tonic-gate 		pci_config_teardown(cfg_hdle);
1190Sstevel@tonic-gate 		return (DDI_FAILURE);
1200Sstevel@tonic-gate 	}
1210Sstevel@tonic-gate 
1220Sstevel@tonic-gate 	*caps_ptr = pci_config_get8(*cfg_hdle, PCI_CONF_CAP_PTR);
1230Sstevel@tonic-gate 	cap_count = PCI_CAP_MAX_PTR;
1240Sstevel@tonic-gate 
1250Sstevel@tonic-gate 	while ((cap_count--) && (*caps_ptr >= PCI_CAP_PTR_OFF)) {
1260Sstevel@tonic-gate 		*caps_ptr &= PCI_CAP_PTR_MASK;
1270Sstevel@tonic-gate 		cap = pci_config_get8(*cfg_hdle, *caps_ptr);
1280Sstevel@tonic-gate 
1290Sstevel@tonic-gate 		if ((cap == PCI_CAP_ID_MSI) && (type == DDI_INTR_TYPE_MSI)) {
1300Sstevel@tonic-gate 			*msi_ctrl = pci_config_get16(*cfg_hdle,
1310Sstevel@tonic-gate 			    *caps_ptr + PCI_MSI_CTRL);
1320Sstevel@tonic-gate 
1330Sstevel@tonic-gate 			DDI_INTR_NEXDBG((CE_CONT, "pci_get_msi_ctrl: MSI "
1340Sstevel@tonic-gate 			    "caps_ptr=%x msi_ctrl=%x\n", *caps_ptr, *msi_ctrl));
1350Sstevel@tonic-gate 
1360Sstevel@tonic-gate 			return (DDI_SUCCESS);
1370Sstevel@tonic-gate 		}
1380Sstevel@tonic-gate 
1390Sstevel@tonic-gate 		if ((cap == PCI_CAP_ID_MSI_X) && (type == DDI_INTR_TYPE_MSIX)) {
1400Sstevel@tonic-gate 			*msi_ctrl = pci_config_get16(*cfg_hdle,
1410Sstevel@tonic-gate 			    *caps_ptr + PCI_MSIX_CTRL);
1420Sstevel@tonic-gate 
1430Sstevel@tonic-gate 			DDI_INTR_NEXDBG((CE_CONT, "pci_get_msi_ctrl: MSI-X "
1440Sstevel@tonic-gate 			    "caps_ptr=%x msi_ctrl=%x\n", *caps_ptr, *msi_ctrl));
1450Sstevel@tonic-gate 
1460Sstevel@tonic-gate 			return (DDI_SUCCESS);
1470Sstevel@tonic-gate 		}
1480Sstevel@tonic-gate 
1490Sstevel@tonic-gate 		*caps_ptr = pci_config_get8(*cfg_hdle,
1500Sstevel@tonic-gate 		    *caps_ptr + PCI_CAP_NEXT_PTR);
1510Sstevel@tonic-gate 	}
1520Sstevel@tonic-gate 
1530Sstevel@tonic-gate 	pci_config_teardown(cfg_hdle);
1540Sstevel@tonic-gate 
1550Sstevel@tonic-gate 	return (DDI_FAILURE);
1560Sstevel@tonic-gate }
1570Sstevel@tonic-gate 
1580Sstevel@tonic-gate 
1590Sstevel@tonic-gate /*
1600Sstevel@tonic-gate  * pci_msi_get_cap:
1610Sstevel@tonic-gate  *
1620Sstevel@tonic-gate  * Get the capabilities of the MSI/X interrupt
1630Sstevel@tonic-gate  */
1640Sstevel@tonic-gate int
1650Sstevel@tonic-gate pci_msi_get_cap(dev_info_t *rdip, int type, int *flagsp)
1660Sstevel@tonic-gate {
1670Sstevel@tonic-gate 	ushort_t		caps_ptr, msi_ctrl;
1680Sstevel@tonic-gate 	ddi_acc_handle_t	cfg_hdle;
1690Sstevel@tonic-gate 
1700Sstevel@tonic-gate 	DDI_INTR_NEXDBG((CE_CONT, "pci_msi_get_cap: rdip = 0x%p\n",
1710Sstevel@tonic-gate 	    (void *)rdip));
1720Sstevel@tonic-gate 
1730Sstevel@tonic-gate 	*flagsp = 0;
1740Sstevel@tonic-gate 
1750Sstevel@tonic-gate 	if (pci_get_msi_ctrl(rdip, type, &msi_ctrl,
1760Sstevel@tonic-gate 	    &caps_ptr, &cfg_hdle) != DDI_SUCCESS)
1770Sstevel@tonic-gate 		return (DDI_FAILURE);
1780Sstevel@tonic-gate 
1790Sstevel@tonic-gate 	if (type == DDI_INTR_TYPE_MSI) {
1800Sstevel@tonic-gate 		if (msi_ctrl &  PCI_MSI_64BIT_MASK)
1810Sstevel@tonic-gate 			*flagsp |= DDI_INTR_FLAG_MSI64;
1820Sstevel@tonic-gate 		if (msi_ctrl & PCI_MSI_PVM_MASK)
1830Sstevel@tonic-gate 			*flagsp |= (DDI_INTR_FLAG_MASKABLE |
1840Sstevel@tonic-gate 			    DDI_INTR_FLAG_PENDING);
1850Sstevel@tonic-gate 		else
1860Sstevel@tonic-gate 			*flagsp |= DDI_INTR_FLAG_BLOCK;
1870Sstevel@tonic-gate 	} else if (type == DDI_INTR_TYPE_MSIX) {
1880Sstevel@tonic-gate 		/* MSI-X supports PVM, 64bit by default */
1890Sstevel@tonic-gate 		*flagsp |= (DDI_INTR_FLAG_MASKABLE | DDI_INTR_FLAG_MSI64 |
1900Sstevel@tonic-gate 		    DDI_INTR_FLAG_PENDING);
1910Sstevel@tonic-gate 	}
1920Sstevel@tonic-gate 
1930Sstevel@tonic-gate 	*flagsp |= DDI_INTR_FLAG_EDGE;
1940Sstevel@tonic-gate 
1950Sstevel@tonic-gate 	DDI_INTR_NEXDBG((CE_CONT, "pci_msi_get_cap: flags = 0x%x\n", *flagsp));
1960Sstevel@tonic-gate 
1970Sstevel@tonic-gate 	pci_config_teardown(&cfg_hdle);
1980Sstevel@tonic-gate 	return (DDI_SUCCESS);
1990Sstevel@tonic-gate }
2000Sstevel@tonic-gate 
2010Sstevel@tonic-gate 
2020Sstevel@tonic-gate /*
2030Sstevel@tonic-gate  * pci_msi_configure:
2040Sstevel@tonic-gate  *
2050Sstevel@tonic-gate  * Configure address/data and number MSI/Xs fields in the MSI/X
2060Sstevel@tonic-gate  * capability structure.
2070Sstevel@tonic-gate  */
2080Sstevel@tonic-gate /* ARGSUSED */
2090Sstevel@tonic-gate int
2100Sstevel@tonic-gate pci_msi_configure(dev_info_t *rdip, int type, int count, int inum,
2110Sstevel@tonic-gate     uint64_t addr, uint64_t data)
2120Sstevel@tonic-gate {
2130Sstevel@tonic-gate 	ushort_t		caps_ptr, msi_ctrl;
2140Sstevel@tonic-gate 	ddi_acc_handle_t	cfg_hdle;
2150Sstevel@tonic-gate 
2160Sstevel@tonic-gate 	DDI_INTR_NEXDBG((CE_CONT, "pci_msi_configure: rdip = 0x%p\n",
2170Sstevel@tonic-gate 	    (void *)rdip));
2180Sstevel@tonic-gate 
2190Sstevel@tonic-gate 	if (pci_get_msi_ctrl(rdip, type, &msi_ctrl,
2200Sstevel@tonic-gate 	    &caps_ptr, &cfg_hdle) != DDI_SUCCESS)
2210Sstevel@tonic-gate 		return (DDI_FAILURE);
2220Sstevel@tonic-gate 
2230Sstevel@tonic-gate 	if (type == DDI_INTR_TYPE_MSI) {
2240Sstevel@tonic-gate 		/* Set the bits to inform how many MSIs are enabled */
2250Sstevel@tonic-gate 		msi_ctrl |= ((highbit(count) -1) << PCI_MSI_MME_SHIFT);
2260Sstevel@tonic-gate 		pci_config_put16(cfg_hdle,
2270Sstevel@tonic-gate 		    caps_ptr + PCI_MSI_CTRL, msi_ctrl);
2280Sstevel@tonic-gate 
2290Sstevel@tonic-gate 		/* Set the "data" and "addr" bits */
2300Sstevel@tonic-gate 		pci_config_put32(cfg_hdle,
2310Sstevel@tonic-gate 		    caps_ptr + PCI_MSI_ADDR_OFFSET, addr);
2320Sstevel@tonic-gate 
2330Sstevel@tonic-gate 		if (msi_ctrl &  PCI_MSI_64BIT_MASK) {
2340Sstevel@tonic-gate 			pci_config_put32(cfg_hdle,
2350Sstevel@tonic-gate 			    caps_ptr + PCI_MSI_ADDR_OFFSET + 4, 0);
2360Sstevel@tonic-gate 			pci_config_put16(cfg_hdle,
2370Sstevel@tonic-gate 			    caps_ptr + PCI_MSI_64BIT_DATA, data);
2380Sstevel@tonic-gate 		} else {
2390Sstevel@tonic-gate 			pci_config_put16(cfg_hdle,
2400Sstevel@tonic-gate 			    caps_ptr + PCI_MSI_32BIT_DATA, data);
2410Sstevel@tonic-gate 		}
2420Sstevel@tonic-gate 
2430Sstevel@tonic-gate 		DDI_INTR_NEXDBG((CE_CONT, "pci_msi_configure: msi_ctrl = %x\n",
2440Sstevel@tonic-gate 		    pci_config_get16(cfg_hdle, caps_ptr + PCI_MSI_CTRL)));
2450Sstevel@tonic-gate 
2460Sstevel@tonic-gate 	} else if (type == DDI_INTR_TYPE_MSIX) {
24742Sagiri 		uintptr_t	off;
2480Sstevel@tonic-gate 		ddi_intr_msix_t	*msix_p = i_ddi_get_msix(rdip);
2490Sstevel@tonic-gate 
2500Sstevel@tonic-gate 		/* Offset into the "inum"th entry in the MSI-X table */
25142Sagiri 		off = (uintptr_t)msix_p->msix_tbl_addr +
2520Sstevel@tonic-gate 		    ((inum - 1) * PCI_MSIX_VECTOR_SIZE);
2530Sstevel@tonic-gate 
2540Sstevel@tonic-gate 		/* Set the "data" and "addr" bits */
2550Sstevel@tonic-gate 		ddi_put32(msix_p->msix_tbl_hdl,
2560Sstevel@tonic-gate 		    (uint32_t *)((uchar_t *)off + PCI_MSIX_DATA_OFFSET), data);
2570Sstevel@tonic-gate 
2580Sstevel@tonic-gate 		ddi_put64(msix_p->msix_tbl_hdl, (uint64_t *)off, addr);
2590Sstevel@tonic-gate 	}
2600Sstevel@tonic-gate 
2610Sstevel@tonic-gate 	pci_config_teardown(&cfg_hdle);
2620Sstevel@tonic-gate 	return (DDI_SUCCESS);
2630Sstevel@tonic-gate }
2640Sstevel@tonic-gate 
2650Sstevel@tonic-gate 
2660Sstevel@tonic-gate /*
2670Sstevel@tonic-gate  * pci_msi_unconfigure:
2680Sstevel@tonic-gate  *
2690Sstevel@tonic-gate  * Unconfigure address/data and number MSI/Xs fields in the MSI/X
2700Sstevel@tonic-gate  * capability structure.
2710Sstevel@tonic-gate  */
2720Sstevel@tonic-gate /* ARGSUSED */
2730Sstevel@tonic-gate int
2740Sstevel@tonic-gate pci_msi_unconfigure(dev_info_t *rdip, int type, int inum)
2750Sstevel@tonic-gate {
2760Sstevel@tonic-gate 	ushort_t		msi_ctrl, caps_ptr;
2770Sstevel@tonic-gate 	ddi_acc_handle_t	cfg_hdle;
2780Sstevel@tonic-gate 
2790Sstevel@tonic-gate 	DDI_INTR_NEXDBG((CE_CONT, "pci_msi_unconfigure: rdip = 0x%p\n",
2800Sstevel@tonic-gate 	    (void *)rdip));
2810Sstevel@tonic-gate 
2820Sstevel@tonic-gate 	if (pci_get_msi_ctrl(rdip, type, &msi_ctrl,
2830Sstevel@tonic-gate 	    &caps_ptr, &cfg_hdle) != DDI_SUCCESS)
2840Sstevel@tonic-gate 		return (DDI_FAILURE);
2850Sstevel@tonic-gate 
2860Sstevel@tonic-gate 	if (type == DDI_INTR_TYPE_MSI) {
2870Sstevel@tonic-gate 		msi_ctrl &= (~PCI_MSI_MME_MASK);
2880Sstevel@tonic-gate 		pci_config_put16(cfg_hdle, caps_ptr + PCI_MSI_CTRL, msi_ctrl);
2890Sstevel@tonic-gate 
2900Sstevel@tonic-gate 		pci_config_put32(cfg_hdle,
2910Sstevel@tonic-gate 		    caps_ptr + PCI_MSI_ADDR_OFFSET, 0);
2920Sstevel@tonic-gate 		if (msi_ctrl &  PCI_MSI_64BIT_MASK) {
2930Sstevel@tonic-gate 			pci_config_put16(cfg_hdle,
2940Sstevel@tonic-gate 			    caps_ptr + PCI_MSI_64BIT_DATA, 0);
2950Sstevel@tonic-gate 			pci_config_put32(cfg_hdle,
2960Sstevel@tonic-gate 			    caps_ptr + PCI_MSI_ADDR_OFFSET + 4, 0);
2970Sstevel@tonic-gate 		} else {
2980Sstevel@tonic-gate 			pci_config_put16(cfg_hdle,
2990Sstevel@tonic-gate 			    caps_ptr + PCI_MSI_32BIT_DATA, 0);
3000Sstevel@tonic-gate 		}
3010Sstevel@tonic-gate 
3020Sstevel@tonic-gate 		DDI_INTR_NEXDBG((CE_CONT, "pci_msi_unconfigure: "
3030Sstevel@tonic-gate 		    "msi_ctrl = %x\n",
3040Sstevel@tonic-gate 		    pci_config_get16(cfg_hdle, caps_ptr + PCI_MSI_CTRL)));
3050Sstevel@tonic-gate 
3060Sstevel@tonic-gate 	} else if (type == DDI_INTR_TYPE_MSIX) {
30742Sagiri 		uintptr_t	off;
3080Sstevel@tonic-gate 		ddi_intr_msix_t	*msix_p = i_ddi_get_msix(rdip);
3090Sstevel@tonic-gate 
3100Sstevel@tonic-gate 		/* Offset into the "inum"th entry in the MSI-X table */
31142Sagiri 		off = (uintptr_t)msix_p->msix_tbl_addr +
3120Sstevel@tonic-gate 		    ((inum - 1) * PCI_MSIX_VECTOR_SIZE);
3130Sstevel@tonic-gate 
3140Sstevel@tonic-gate 		/* Reset the "data" and "addr" bits */
3150Sstevel@tonic-gate 		ddi_put32(msix_p->msix_tbl_hdl,
3160Sstevel@tonic-gate 		    (uint32_t *)((uchar_t *)off + PCI_MSIX_DATA_OFFSET), 0);
3170Sstevel@tonic-gate 
3180Sstevel@tonic-gate 		ddi_put64(msix_p->msix_tbl_hdl, (uint64_t *)off, 0);
3190Sstevel@tonic-gate 	}
3200Sstevel@tonic-gate 
3210Sstevel@tonic-gate 	pci_config_teardown(&cfg_hdle);
3220Sstevel@tonic-gate 	return (DDI_SUCCESS);
3230Sstevel@tonic-gate }
3240Sstevel@tonic-gate 
3250Sstevel@tonic-gate 
3260Sstevel@tonic-gate /*
3270Sstevel@tonic-gate  * pci_is_msi_enabled:
3280Sstevel@tonic-gate  *
3290Sstevel@tonic-gate  * This function returns DDI_SUCCESS if MSI/X is already enabled, otherwise
3300Sstevel@tonic-gate  * it returns DDI_FAILURE.
3310Sstevel@tonic-gate  */
3320Sstevel@tonic-gate int
3330Sstevel@tonic-gate pci_is_msi_enabled(dev_info_t *rdip, int type)
3340Sstevel@tonic-gate {
3350Sstevel@tonic-gate 	ushort_t		caps_ptr, msi_ctrl;
3360Sstevel@tonic-gate 	ddi_acc_handle_t	cfg_hdle;
3370Sstevel@tonic-gate 	int			ret = DDI_FAILURE;
3380Sstevel@tonic-gate 
3390Sstevel@tonic-gate 	DDI_INTR_NEXDBG((CE_CONT, "pci_is_msi_enabled: rdip = 0x%p, "
3400Sstevel@tonic-gate 	    "type  = 0x%x\n", (void *)rdip, type));
3410Sstevel@tonic-gate 
3420Sstevel@tonic-gate 	if (pci_get_msi_ctrl(rdip, type, &msi_ctrl,
3430Sstevel@tonic-gate 	    &caps_ptr, &cfg_hdle) != DDI_SUCCESS)
3440Sstevel@tonic-gate 		return (DDI_FAILURE);
3450Sstevel@tonic-gate 
3460Sstevel@tonic-gate 	if ((type == DDI_INTR_TYPE_MSI) && (msi_ctrl & PCI_MSI_ENABLE_BIT))
3470Sstevel@tonic-gate 		ret = DDI_SUCCESS;
3480Sstevel@tonic-gate 
3490Sstevel@tonic-gate 	if ((type == DDI_INTR_TYPE_MSIX) && (msi_ctrl & PCI_MSIX_ENABLE_BIT))
3500Sstevel@tonic-gate 		ret = DDI_SUCCESS;
3510Sstevel@tonic-gate 
3520Sstevel@tonic-gate 	pci_config_teardown(&cfg_hdle);
3530Sstevel@tonic-gate 	return (ret);
3540Sstevel@tonic-gate }
3550Sstevel@tonic-gate 
3560Sstevel@tonic-gate 
3570Sstevel@tonic-gate /*
3580Sstevel@tonic-gate  * pci_msi_enable_mode:
3590Sstevel@tonic-gate  *
3600Sstevel@tonic-gate  * This function sets the MSI_ENABLE bit in the capability structure
3610Sstevel@tonic-gate  * (for MSI) and MSIX_ENABLE bit in the MSI-X capability structure.
3620Sstevel@tonic-gate  */
3630Sstevel@tonic-gate int
3640Sstevel@tonic-gate pci_msi_enable_mode(dev_info_t *rdip, int type, int inum)
3650Sstevel@tonic-gate {
3660Sstevel@tonic-gate 	ushort_t		caps_ptr, msi_ctrl;
3670Sstevel@tonic-gate 	uint16_t		cmd_reg;
3680Sstevel@tonic-gate 	ddi_acc_handle_t	cfg_hdle;
3690Sstevel@tonic-gate 
3700Sstevel@tonic-gate 	DDI_INTR_NEXDBG((CE_CONT, "pci_msi_enable_mode: rdip = 0x%p, "
3710Sstevel@tonic-gate 	    "inum  = 0x%x\n", (void *)rdip, inum));
3720Sstevel@tonic-gate 
3730Sstevel@tonic-gate 	if (pci_get_msi_ctrl(rdip, type, &msi_ctrl,
3740Sstevel@tonic-gate 	    &caps_ptr, &cfg_hdle) != DDI_SUCCESS)
3750Sstevel@tonic-gate 		return (DDI_FAILURE);
3760Sstevel@tonic-gate 
3770Sstevel@tonic-gate 	/* Disable INTx simulation, if applicable */
3780Sstevel@tonic-gate 	cmd_reg = pci_config_get16(cfg_hdle, PCI_CONF_COMM);
3790Sstevel@tonic-gate 
3800Sstevel@tonic-gate 	/* This write succeeds only for devices > PCI2.3 */
3810Sstevel@tonic-gate 	pci_config_put16(cfg_hdle, PCI_CONF_COMM,
3820Sstevel@tonic-gate 	    cmd_reg | PCI_COMM_INTX_DISABLE);
3830Sstevel@tonic-gate 
3840Sstevel@tonic-gate 	DDI_INTR_NEXDBG((CE_CONT, "pci_msi_enable_mode: "
3850Sstevel@tonic-gate 	    "Before CmdReg = 0x%x, After CmdReg = 0x%x\n",
3860Sstevel@tonic-gate 	    cmd_reg, pci_config_get16(cfg_hdle, PCI_CONF_COMM)));
3870Sstevel@tonic-gate 
3880Sstevel@tonic-gate 	if (type == DDI_INTR_TYPE_MSI) {
3890Sstevel@tonic-gate 		if (msi_ctrl & PCI_MSI_ENABLE_BIT)
3900Sstevel@tonic-gate 			goto finished;
3910Sstevel@tonic-gate 
3920Sstevel@tonic-gate 		msi_ctrl |= PCI_MSI_ENABLE_BIT;
3930Sstevel@tonic-gate 		pci_config_put16(cfg_hdle,
3940Sstevel@tonic-gate 		    caps_ptr + PCI_MSI_CTRL, msi_ctrl);
3950Sstevel@tonic-gate 
3960Sstevel@tonic-gate 	} else if (type == DDI_INTR_TYPE_MSIX) {
39742Sagiri 		uintptr_t	off;
3980Sstevel@tonic-gate 		uint32_t	mask_bits;
3990Sstevel@tonic-gate 		ddi_intr_msix_t	*msix_p;
4000Sstevel@tonic-gate 
4010Sstevel@tonic-gate 		if (msi_ctrl & PCI_MSIX_ENABLE_BIT)
4020Sstevel@tonic-gate 			goto finished;
4030Sstevel@tonic-gate 
4040Sstevel@tonic-gate 		msi_ctrl |= PCI_MSIX_ENABLE_BIT;
4050Sstevel@tonic-gate 		pci_config_put16(cfg_hdle,
4060Sstevel@tonic-gate 		    caps_ptr + PCI_MSIX_CTRL, msi_ctrl);
4070Sstevel@tonic-gate 
4080Sstevel@tonic-gate 		msix_p = i_ddi_get_msix(rdip);
4090Sstevel@tonic-gate 
4100Sstevel@tonic-gate 		/* Offset into the "inum"th entry in the MSI-X table */
41142Sagiri 		off = (uintptr_t)msix_p->msix_tbl_addr + ((inum - 1) *
4120Sstevel@tonic-gate 		    PCI_MSIX_VECTOR_SIZE) + PCI_MSIX_VECTOR_CTRL_OFFSET;
4130Sstevel@tonic-gate 
4140Sstevel@tonic-gate 		/* Clear the Mask bit */
4150Sstevel@tonic-gate 		mask_bits = ddi_get32(msix_p->msix_tbl_hdl,
4160Sstevel@tonic-gate 		    (uint32_t *)((uchar_t *)off));
4170Sstevel@tonic-gate 
4180Sstevel@tonic-gate 		mask_bits &= ~0;
4190Sstevel@tonic-gate 
4200Sstevel@tonic-gate 		ddi_put32(msix_p->msix_tbl_hdl,
4210Sstevel@tonic-gate 		    (uint32_t *)((uchar_t *)off), mask_bits);
4220Sstevel@tonic-gate 	}
4230Sstevel@tonic-gate 
4240Sstevel@tonic-gate finished:
4250Sstevel@tonic-gate 	DDI_INTR_NEXDBG((CE_CONT, "pci_msi_enable_mode: msi_ctrl = %x\n",
4260Sstevel@tonic-gate 	    msi_ctrl));
4270Sstevel@tonic-gate 
4280Sstevel@tonic-gate 	pci_config_teardown(&cfg_hdle);
4290Sstevel@tonic-gate 	return (DDI_SUCCESS);
4300Sstevel@tonic-gate }
4310Sstevel@tonic-gate 
4320Sstevel@tonic-gate 
4330Sstevel@tonic-gate /*
4340Sstevel@tonic-gate  * pci_msi_disable_mode:
4350Sstevel@tonic-gate  *
4360Sstevel@tonic-gate  * This function resets the MSI_ENABLE bit in the capability structure
4370Sstevel@tonic-gate  * (for MSI) and MSIX_ENABLE bit in the MSI-X capability structure.
4380Sstevel@tonic-gate  */
4390Sstevel@tonic-gate int
4400Sstevel@tonic-gate pci_msi_disable_mode(dev_info_t *rdip, int type, int inum)
4410Sstevel@tonic-gate {
4420Sstevel@tonic-gate 	ushort_t		caps_ptr, msi_ctrl;
4430Sstevel@tonic-gate 	ddi_acc_handle_t	cfg_hdle;
4440Sstevel@tonic-gate 
4450Sstevel@tonic-gate 	DDI_INTR_NEXDBG((CE_CONT, "pci_msi_disable_mode: rdip = 0x%p "
4460Sstevel@tonic-gate 	    "inum = 0x%x\n", (void *)rdip, inum));
4470Sstevel@tonic-gate 
4480Sstevel@tonic-gate 	if (pci_get_msi_ctrl(rdip, type, &msi_ctrl,
4490Sstevel@tonic-gate 	    &caps_ptr, &cfg_hdle) != DDI_SUCCESS)
4500Sstevel@tonic-gate 		return (DDI_FAILURE);
4510Sstevel@tonic-gate 
4520Sstevel@tonic-gate 	/* Reset the "enable" bit */
4530Sstevel@tonic-gate 	if (type == DDI_INTR_TYPE_MSI) {
4540Sstevel@tonic-gate 		if (!(msi_ctrl & PCI_MSI_ENABLE_BIT))
4550Sstevel@tonic-gate 			goto finished;
4560Sstevel@tonic-gate 		msi_ctrl &= ~PCI_MSI_ENABLE_BIT;
4570Sstevel@tonic-gate 		pci_config_put16(cfg_hdle,
4580Sstevel@tonic-gate 		    caps_ptr + PCI_MSI_CTRL, msi_ctrl);
4590Sstevel@tonic-gate 	} else if (type == DDI_INTR_TYPE_MSIX) {
46042Sagiri 		uintptr_t		off;
4610Sstevel@tonic-gate 		ddi_intr_msix_t		*msix_p;
4620Sstevel@tonic-gate 
4630Sstevel@tonic-gate 		if (!(msi_ctrl & PCI_MSIX_ENABLE_BIT))
4640Sstevel@tonic-gate 			goto finished;
4650Sstevel@tonic-gate 
4660Sstevel@tonic-gate 		msix_p = i_ddi_get_msix(rdip);
4670Sstevel@tonic-gate 
4680Sstevel@tonic-gate 		/* Offset into the "inum"th entry in the MSI-X table */
46942Sagiri 		off = (uintptr_t)msix_p->msix_tbl_addr + ((inum - 1) *
4700Sstevel@tonic-gate 		    PCI_MSIX_VECTOR_SIZE) + PCI_MSIX_VECTOR_CTRL_OFFSET;
4710Sstevel@tonic-gate 
4720Sstevel@tonic-gate 		/* Set the Mask bit */
4730Sstevel@tonic-gate 		ddi_put32(msix_p->msix_tbl_hdl,
4740Sstevel@tonic-gate 		    (uint32_t *)((uchar_t *)off), 0x1);
4750Sstevel@tonic-gate 	}
4760Sstevel@tonic-gate 
4770Sstevel@tonic-gate finished:
4780Sstevel@tonic-gate 	DDI_INTR_NEXDBG((CE_CONT, "pci_msi_disable_mode: msi_ctrl = %x\n",
4790Sstevel@tonic-gate 	    msi_ctrl));
4800Sstevel@tonic-gate 
4810Sstevel@tonic-gate 	pci_config_teardown(&cfg_hdle);
4820Sstevel@tonic-gate 	return (DDI_SUCCESS);
4830Sstevel@tonic-gate }
4840Sstevel@tonic-gate 
4850Sstevel@tonic-gate 
4860Sstevel@tonic-gate /*
4870Sstevel@tonic-gate  * pci_msi_set_mask:
4880Sstevel@tonic-gate  *
4890Sstevel@tonic-gate  * Set the mask bit in the MSI/X capability structure
4900Sstevel@tonic-gate  */
4910Sstevel@tonic-gate /* ARGSUSED */
4920Sstevel@tonic-gate int
4930Sstevel@tonic-gate pci_msi_set_mask(dev_info_t *rdip, int type, int inum)
4940Sstevel@tonic-gate {
4950Sstevel@tonic-gate 	int			offset;
4960Sstevel@tonic-gate 	int			ret = DDI_FAILURE;
4970Sstevel@tonic-gate 	ushort_t		caps_ptr, msi_ctrl;
4980Sstevel@tonic-gate 	ddi_acc_handle_t	cfg_hdle;
4990Sstevel@tonic-gate 	uint_t			mask_bits;
5000Sstevel@tonic-gate 
5010Sstevel@tonic-gate 	DDI_INTR_NEXDBG((CE_CONT, "pci_msi_set_mask: rdip = 0x%p, "
5020Sstevel@tonic-gate 	    "type = 0x%x\n", (void *)rdip, type));
5030Sstevel@tonic-gate 
5040Sstevel@tonic-gate 	if (pci_get_msi_ctrl(rdip, type, &msi_ctrl,
5050Sstevel@tonic-gate 	    &caps_ptr, &cfg_hdle) != DDI_SUCCESS)
5060Sstevel@tonic-gate 		return (DDI_FAILURE);
5070Sstevel@tonic-gate 
5080Sstevel@tonic-gate 	if (type == DDI_INTR_TYPE_MSI) {
5090Sstevel@tonic-gate 		if (!(msi_ctrl &  PCI_MSI_PVM_MASK))
5100Sstevel@tonic-gate 			goto done;
5110Sstevel@tonic-gate 
5120Sstevel@tonic-gate 		offset = (msi_ctrl &  PCI_MSI_64BIT_MASK) ?
5130Sstevel@tonic-gate 		    PCI_MSI_64BIT_MASKBITS : PCI_MSI_32BIT_MASK;
5140Sstevel@tonic-gate 
5150Sstevel@tonic-gate 		mask_bits = pci_config_get32(cfg_hdle,
5160Sstevel@tonic-gate 		    caps_ptr + offset);
5170Sstevel@tonic-gate 
5180Sstevel@tonic-gate 		mask_bits |= (1 << inum);
5190Sstevel@tonic-gate 
5200Sstevel@tonic-gate 		pci_config_put32(cfg_hdle,
5210Sstevel@tonic-gate 		    caps_ptr + offset, mask_bits);
5220Sstevel@tonic-gate 
5230Sstevel@tonic-gate 	} else if (type == DDI_INTR_TYPE_MSIX) {
52442Sagiri 		uintptr_t		off;
5250Sstevel@tonic-gate 		ddi_intr_msix_t		*msix_p;
5260Sstevel@tonic-gate 
5270Sstevel@tonic-gate 		/* Set function mask */
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 */
53642Sagiri 		off = (uintptr_t)msix_p->msix_tbl_addr + ((inum - 1) *
5370Sstevel@tonic-gate 		    PCI_MSIX_VECTOR_SIZE) + PCI_MSIX_VECTOR_CTRL_OFFSET;
5380Sstevel@tonic-gate 
5390Sstevel@tonic-gate 		/* Set the Mask bit */
5400Sstevel@tonic-gate 		ddi_put32(msix_p->msix_tbl_hdl,
5410Sstevel@tonic-gate 		    (uint32_t *)((uchar_t *)off), 0x1);
5420Sstevel@tonic-gate 	}
5430Sstevel@tonic-gate 
5440Sstevel@tonic-gate 	ret = DDI_SUCCESS;
5450Sstevel@tonic-gate done:
5460Sstevel@tonic-gate 	pci_config_teardown(&cfg_hdle);
5470Sstevel@tonic-gate 	return (ret);
5480Sstevel@tonic-gate }
5490Sstevel@tonic-gate 
5500Sstevel@tonic-gate 
5510Sstevel@tonic-gate /*
5520Sstevel@tonic-gate  * pci_msi_clr_mask:
5530Sstevel@tonic-gate  *
5540Sstevel@tonic-gate  * Clear the mask bit in the MSI/X capability structure
5550Sstevel@tonic-gate  */
5560Sstevel@tonic-gate /* ARGSUSED */
5570Sstevel@tonic-gate int
5580Sstevel@tonic-gate pci_msi_clr_mask(dev_info_t *rdip, int type, int inum)
5590Sstevel@tonic-gate {
5600Sstevel@tonic-gate 	ushort_t		caps_ptr, msi_ctrl;
5610Sstevel@tonic-gate 	ddi_acc_handle_t	cfg_hdle;
5620Sstevel@tonic-gate 	int			offset;
5630Sstevel@tonic-gate 	int			ret = DDI_FAILURE;
5640Sstevel@tonic-gate 	uint_t			mask_bits;
5650Sstevel@tonic-gate 
5660Sstevel@tonic-gate 	DDI_INTR_NEXDBG((CE_CONT, "pci_msi_clr_mask: rdip = 0x%p, "
5670Sstevel@tonic-gate 	    "type = 0x%x\n", (void *)rdip, type));
5680Sstevel@tonic-gate 
5690Sstevel@tonic-gate 	if (pci_get_msi_ctrl(rdip, type, &msi_ctrl,
5700Sstevel@tonic-gate 	    &caps_ptr, &cfg_hdle) != DDI_SUCCESS)
5710Sstevel@tonic-gate 		return (DDI_FAILURE);
5720Sstevel@tonic-gate 
5730Sstevel@tonic-gate 	if (type == DDI_INTR_TYPE_MSI) {
5740Sstevel@tonic-gate 		if (!(msi_ctrl &  PCI_MSI_PVM_MASK))
5750Sstevel@tonic-gate 			goto done;
5760Sstevel@tonic-gate 
5770Sstevel@tonic-gate 		offset = (msi_ctrl &  PCI_MSI_64BIT_MASK) ?
5780Sstevel@tonic-gate 		    PCI_MSI_64BIT_MASKBITS : PCI_MSI_32BIT_MASK;
5790Sstevel@tonic-gate 		mask_bits = pci_config_get32(cfg_hdle,
5800Sstevel@tonic-gate 		    caps_ptr + offset);
5810Sstevel@tonic-gate 
5820Sstevel@tonic-gate 		mask_bits &= ~(1 << inum);
5830Sstevel@tonic-gate 
5840Sstevel@tonic-gate 		pci_config_put32(cfg_hdle,
5850Sstevel@tonic-gate 		    caps_ptr + offset, mask_bits);
5860Sstevel@tonic-gate 
5870Sstevel@tonic-gate 	} else if (type == DDI_INTR_TYPE_MSIX) {
58842Sagiri 		uintptr_t		off;
5890Sstevel@tonic-gate 		ddi_intr_msix_t		*msix_p;
5900Sstevel@tonic-gate 
5910Sstevel@tonic-gate 		if (msi_ctrl & PCI_MSIX_FUNCTION_MASK) {
5920Sstevel@tonic-gate 			ret = DDI_SUCCESS;
5930Sstevel@tonic-gate 			goto done;
5940Sstevel@tonic-gate 		}
5950Sstevel@tonic-gate 
5960Sstevel@tonic-gate 		msix_p = i_ddi_get_msix(rdip);
5970Sstevel@tonic-gate 
5980Sstevel@tonic-gate 		/* Offset into the "inum"th entry in the MSI-X table */
59942Sagiri 		off = (uintptr_t)msix_p->msix_tbl_addr + ((inum - 1) *
6000Sstevel@tonic-gate 		    PCI_MSIX_VECTOR_SIZE) + PCI_MSIX_VECTOR_CTRL_OFFSET;
6010Sstevel@tonic-gate 
6020Sstevel@tonic-gate 		/* Clear the Mask bit */
6030Sstevel@tonic-gate 		mask_bits = ddi_get32(msix_p->msix_tbl_hdl,
6040Sstevel@tonic-gate 		    (uint32_t *)((uchar_t *)off));
6050Sstevel@tonic-gate 
6060Sstevel@tonic-gate 		mask_bits &= ~0;
6070Sstevel@tonic-gate 
6080Sstevel@tonic-gate 		ddi_put32(msix_p->msix_tbl_hdl,
6090Sstevel@tonic-gate 		    (uint32_t *)((uchar_t *)off), mask_bits);
6100Sstevel@tonic-gate 	}
6110Sstevel@tonic-gate 
6120Sstevel@tonic-gate 	ret = DDI_SUCCESS;
6130Sstevel@tonic-gate done:
6140Sstevel@tonic-gate 	pci_config_teardown(&cfg_hdle);
6150Sstevel@tonic-gate 	return (ret);
6160Sstevel@tonic-gate }
6170Sstevel@tonic-gate 
6180Sstevel@tonic-gate 
6190Sstevel@tonic-gate /*
6200Sstevel@tonic-gate  * pci_msi_get_pending:
6210Sstevel@tonic-gate  *
6220Sstevel@tonic-gate  * Get the pending bit from the MSI/X capability structure
6230Sstevel@tonic-gate  */
6240Sstevel@tonic-gate /* ARGSUSED */
6250Sstevel@tonic-gate int
6260Sstevel@tonic-gate pci_msi_get_pending(dev_info_t *rdip, int type, int inum, int *pendingp)
6270Sstevel@tonic-gate {
6280Sstevel@tonic-gate 	ushort_t		caps_ptr, msi_ctrl;
6290Sstevel@tonic-gate 	ddi_acc_handle_t	cfg_hdle;
6300Sstevel@tonic-gate 	int			offset;
6310Sstevel@tonic-gate 	int			ret = DDI_FAILURE;
6320Sstevel@tonic-gate 
6330Sstevel@tonic-gate 	DDI_INTR_NEXDBG((CE_CONT, "pci_msi_get_pending: rdip = 0x%p\n",
6340Sstevel@tonic-gate 	    (void *)rdip));
6350Sstevel@tonic-gate 
6360Sstevel@tonic-gate 	if (pci_get_msi_ctrl(rdip, type, &msi_ctrl,
6370Sstevel@tonic-gate 	    &caps_ptr, &cfg_hdle) != DDI_SUCCESS)
6380Sstevel@tonic-gate 		return (DDI_FAILURE);
6390Sstevel@tonic-gate 
6400Sstevel@tonic-gate 	if (type == DDI_INTR_TYPE_MSI) {
6410Sstevel@tonic-gate 		uint32_t	pending_bits;
6420Sstevel@tonic-gate 
6430Sstevel@tonic-gate 		if (!(msi_ctrl &  PCI_MSI_PVM_MASK)) {
6440Sstevel@tonic-gate 			DDI_INTR_NEXDBG((CE_CONT, "pci_msi_get_pending: "
6450Sstevel@tonic-gate 			    "PVM is not supported\n"));
6460Sstevel@tonic-gate 			goto done;
6470Sstevel@tonic-gate 		}
6480Sstevel@tonic-gate 
6490Sstevel@tonic-gate 		offset = (msi_ctrl &  PCI_MSI_64BIT_MASK) ?
6500Sstevel@tonic-gate 		    PCI_MSI_64BIT_PENDING : PCI_MSI_32BIT_PENDING;
6510Sstevel@tonic-gate 
6520Sstevel@tonic-gate 		pending_bits = pci_config_get32(cfg_hdle,
6530Sstevel@tonic-gate 		    caps_ptr + offset);
6540Sstevel@tonic-gate 
6550Sstevel@tonic-gate 		*pendingp = pending_bits & ~(1 >> inum);
6560Sstevel@tonic-gate 
6570Sstevel@tonic-gate 	} else if (type == DDI_INTR_TYPE_MSIX) {
65842Sagiri 		uintptr_t	off;
6590Sstevel@tonic-gate 		uint64_t	pending_bits;
6600Sstevel@tonic-gate 		ddi_intr_msix_t	*msix_p = i_ddi_get_msix(rdip);
6610Sstevel@tonic-gate 
6620Sstevel@tonic-gate 		/* Offset into the PBA array which has entry for "inum" */
66342Sagiri 		off = (uintptr_t)msix_p->msix_pba_addr + ((inum - 1) / 64);
6640Sstevel@tonic-gate 
6650Sstevel@tonic-gate 		/* Read the PBA array */
6660Sstevel@tonic-gate 		pending_bits = ddi_get64(msix_p->msix_pba_hdl,
6670Sstevel@tonic-gate 		    (uint64_t *)((uchar_t *)off));
6680Sstevel@tonic-gate 
6690Sstevel@tonic-gate 		*pendingp = pending_bits & ~(1 >> inum);
6700Sstevel@tonic-gate 	}
6710Sstevel@tonic-gate 
6720Sstevel@tonic-gate 	ret = DDI_SUCCESS;
6730Sstevel@tonic-gate done:
6740Sstevel@tonic-gate 	pci_config_teardown(&cfg_hdle);
6750Sstevel@tonic-gate 	return (ret);
6760Sstevel@tonic-gate }
6770Sstevel@tonic-gate 
6780Sstevel@tonic-gate 
6790Sstevel@tonic-gate /*
6800Sstevel@tonic-gate  * pci_msi_get_nintrs:
6810Sstevel@tonic-gate  *
6820Sstevel@tonic-gate  * For a given type (MSI/X) returns the number of interrupts supported
6830Sstevel@tonic-gate  */
6840Sstevel@tonic-gate int
6850Sstevel@tonic-gate pci_msi_get_nintrs(dev_info_t *rdip, int type, int *nintrs)
6860Sstevel@tonic-gate {
6870Sstevel@tonic-gate 	ushort_t		caps_ptr, msi_ctrl;
6880Sstevel@tonic-gate 	ddi_acc_handle_t	cfg_hdle;
6890Sstevel@tonic-gate 
6900Sstevel@tonic-gate 	DDI_INTR_NEXDBG((CE_CONT, "pci_msi_get_nintrs: rdip = 0x%p\n",
6910Sstevel@tonic-gate 	    (void *)rdip));
6920Sstevel@tonic-gate 
6930Sstevel@tonic-gate 	if (pci_get_msi_ctrl(rdip, type, &msi_ctrl,
6940Sstevel@tonic-gate 	    &caps_ptr, &cfg_hdle) != DDI_SUCCESS)
6950Sstevel@tonic-gate 		return (DDI_FAILURE);
6960Sstevel@tonic-gate 
6970Sstevel@tonic-gate 	if (type == DDI_INTR_TYPE_MSI) {
6980Sstevel@tonic-gate 		*nintrs = 1 << ((msi_ctrl & PCI_MSI_MMC_MASK) >>
6990Sstevel@tonic-gate 		    PCI_MSI_MMC_SHIFT);
7000Sstevel@tonic-gate 	} else if (type == DDI_INTR_TYPE_MSIX) {
7010Sstevel@tonic-gate 		if (msi_ctrl &  PCI_MSIX_TBL_SIZE_MASK)
7020Sstevel@tonic-gate 			*nintrs = (msi_ctrl & PCI_MSIX_TBL_SIZE_MASK) + 1;
7030Sstevel@tonic-gate 	}
7040Sstevel@tonic-gate 
7050Sstevel@tonic-gate 	DDI_INTR_NEXDBG((CE_CONT, "pci_msi_get_nintrs: "
7060Sstevel@tonic-gate 	    "nintr = 0x%x\n", *nintrs));
7070Sstevel@tonic-gate 
7080Sstevel@tonic-gate 	pci_config_teardown(&cfg_hdle);
7090Sstevel@tonic-gate 	return (DDI_SUCCESS);
7100Sstevel@tonic-gate }
7110Sstevel@tonic-gate 
7120Sstevel@tonic-gate 
7130Sstevel@tonic-gate /*
7140Sstevel@tonic-gate  * pci_msi_set_nintrs:
7150Sstevel@tonic-gate  *
7160Sstevel@tonic-gate  * For a given type (MSI/X) sets the number of interrupts supported
7170Sstevel@tonic-gate  * by the system.
7180Sstevel@tonic-gate  * For MSI: Return an error if this func is called for navail > 32
7190Sstevel@tonic-gate  * For MSI-X: Return an error if this func is called for navail > 2048
7200Sstevel@tonic-gate  */
7210Sstevel@tonic-gate int
7220Sstevel@tonic-gate pci_msi_set_nintrs(dev_info_t *rdip, int type, int navail)
7230Sstevel@tonic-gate {
7240Sstevel@tonic-gate 	ushort_t		caps_ptr, msi_ctrl;
7250Sstevel@tonic-gate 	ddi_acc_handle_t	cfg_hdle;
7260Sstevel@tonic-gate 
7270Sstevel@tonic-gate 	DDI_INTR_NEXDBG((CE_CONT, "pci_msi_set_nintrs: rdip = 0x%p, "
7280Sstevel@tonic-gate 	    "navail = 0x%x\n", (void *)rdip, navail));
7290Sstevel@tonic-gate 
7300Sstevel@tonic-gate 	/* Check for valid input argument */
7310Sstevel@tonic-gate 	if (((type == DDI_INTR_TYPE_MSI) && (navail > PCI_MSI_MAX_INTRS)) ||
7320Sstevel@tonic-gate 	    ((type == DDI_INTR_TYPE_MSIX) && (navail >  PCI_MSIX_MAX_INTRS)))
7330Sstevel@tonic-gate 		return (DDI_EINVAL);
7340Sstevel@tonic-gate 
7350Sstevel@tonic-gate 	if (pci_get_msi_ctrl(rdip, type, &msi_ctrl,
7360Sstevel@tonic-gate 	    &caps_ptr, &cfg_hdle) != DDI_SUCCESS)
7370Sstevel@tonic-gate 		return (DDI_FAILURE);
7380Sstevel@tonic-gate 
7390Sstevel@tonic-gate 	if (type == DDI_INTR_TYPE_MSI) {
7400Sstevel@tonic-gate 		msi_ctrl |= ((highbit(navail) -1) << PCI_MSI_MME_SHIFT);
7410Sstevel@tonic-gate 
7420Sstevel@tonic-gate 		pci_config_put16(cfg_hdle, caps_ptr + PCI_MSI_CTRL, msi_ctrl);
7430Sstevel@tonic-gate 	} else if (type == DDI_INTR_TYPE_MSIX) {
7440Sstevel@tonic-gate 		DDI_INTR_NEXDBG((CE_CONT, "pci_msi_set_nintrs: unsupported\n"));
7450Sstevel@tonic-gate 	}
7460Sstevel@tonic-gate 
7470Sstevel@tonic-gate 	pci_config_teardown(&cfg_hdle);
7480Sstevel@tonic-gate 	return (DDI_SUCCESS);
7490Sstevel@tonic-gate }
7500Sstevel@tonic-gate 
7510Sstevel@tonic-gate 
7520Sstevel@tonic-gate /*
7530Sstevel@tonic-gate  * pci_msi_get_supported_type:
7540Sstevel@tonic-gate  *
7550Sstevel@tonic-gate  * Returns DDI_INTR_TYPE_MSI and/or DDI_INTR_TYPE_MSIX as supported
7560Sstevel@tonic-gate  * types if device supports them. A DDI_FAILURE is returned otherwise.
7570Sstevel@tonic-gate  */
7580Sstevel@tonic-gate int
7590Sstevel@tonic-gate pci_msi_get_supported_type(dev_info_t *rdip, int *typesp)
7600Sstevel@tonic-gate {
7610Sstevel@tonic-gate 	ushort_t		caps_ptr, msi_ctrl;
7620Sstevel@tonic-gate 	ddi_acc_handle_t	cfg_hdle;
7630Sstevel@tonic-gate 
7640Sstevel@tonic-gate 	DDI_INTR_NEXDBG((CE_CONT, "pci_msi_get_supported_type: "
7650Sstevel@tonic-gate 	    "rdip = 0x%p\n", (void *)rdip));
7660Sstevel@tonic-gate 
7670Sstevel@tonic-gate 	*typesp = 0;
7680Sstevel@tonic-gate 
7690Sstevel@tonic-gate 	if (pci_get_msi_ctrl(rdip, DDI_INTR_TYPE_MSI, &msi_ctrl,
7700Sstevel@tonic-gate 	    &caps_ptr, &cfg_hdle) == DDI_SUCCESS) {
7710Sstevel@tonic-gate 		*typesp |= DDI_INTR_TYPE_MSI;
7720Sstevel@tonic-gate 		pci_config_teardown(&cfg_hdle);
7730Sstevel@tonic-gate 	}
7740Sstevel@tonic-gate 
7750Sstevel@tonic-gate 	if (pci_get_msi_ctrl(rdip, DDI_INTR_TYPE_MSIX, &msi_ctrl,
7760Sstevel@tonic-gate 	    &caps_ptr, &cfg_hdle) == DDI_SUCCESS) {
7770Sstevel@tonic-gate 		*typesp |= DDI_INTR_TYPE_MSIX;
7780Sstevel@tonic-gate 		pci_config_teardown(&cfg_hdle);
7790Sstevel@tonic-gate 	}
7800Sstevel@tonic-gate 
7810Sstevel@tonic-gate 	DDI_INTR_NEXDBG((CE_CONT, "pci_msi_get_supported_type: "
7820Sstevel@tonic-gate 	    "rdip = 0x%p types 0x%x\n", (void *)rdip, *typesp));
7830Sstevel@tonic-gate 
7840Sstevel@tonic-gate 	return (*typesp == 0 ? DDI_FAILURE : DDI_SUCCESS);
7850Sstevel@tonic-gate }
7860Sstevel@tonic-gate 
7870Sstevel@tonic-gate 
7880Sstevel@tonic-gate /*
7890Sstevel@tonic-gate  * pci_msix_init:
7900Sstevel@tonic-gate  *	This function initializes the various handles/addrs etc.
7910Sstevel@tonic-gate  *	needed for MSI-X support. It also allocates a private
7920Sstevel@tonic-gate  *	structure to keep track of these.
7930Sstevel@tonic-gate  */
7940Sstevel@tonic-gate ddi_intr_msix_t *
7950Sstevel@tonic-gate pci_msix_init(dev_info_t *rdip)
7960Sstevel@tonic-gate {
7970Sstevel@tonic-gate 	uint_t			rnumber;
7980Sstevel@tonic-gate 	size_t			msix_tbl_size;
7990Sstevel@tonic-gate 	size_t			pba_tbl_size;
8000Sstevel@tonic-gate 	ushort_t		caps_ptr, msi_ctrl;
8010Sstevel@tonic-gate 	ddi_intr_msix_t		*msix_p;
8020Sstevel@tonic-gate 	ddi_acc_handle_t	cfg_hdle;
8030Sstevel@tonic-gate 
8040Sstevel@tonic-gate 	DDI_INTR_NEXDBG((CE_CONT, "pci_msix_init: rdip = %p\n", (void *)rdip));
8050Sstevel@tonic-gate 
8060Sstevel@tonic-gate 	if (pci_get_msi_ctrl(rdip, DDI_INTR_TYPE_MSI, &msi_ctrl,
8070Sstevel@tonic-gate 	    &caps_ptr, &cfg_hdle) != DDI_SUCCESS)
8080Sstevel@tonic-gate 		return (NULL);
8090Sstevel@tonic-gate 
8100Sstevel@tonic-gate 	msix_p = kmem_zalloc(sizeof (ddi_intr_msix_t), KM_SLEEP);
8110Sstevel@tonic-gate 
8120Sstevel@tonic-gate 	/*
8130Sstevel@tonic-gate 	 * Initialize the devacc structure
8140Sstevel@tonic-gate 	 */
8150Sstevel@tonic-gate 	msix_p->msix_dev_attr.devacc_attr_version = DDI_DEVICE_ATTR_V0;
8160Sstevel@tonic-gate 	msix_p->msix_dev_attr.devacc_attr_endian_flags =
8170Sstevel@tonic-gate 	    DDI_STRUCTURE_LE_ACC;
8180Sstevel@tonic-gate 	msix_p->msix_dev_attr.devacc_attr_dataorder = DDI_STRICTORDER_ACC;
8190Sstevel@tonic-gate 
8200Sstevel@tonic-gate 	/*
8210Sstevel@tonic-gate 	 * Map the entire MSI-X vector table
8220Sstevel@tonic-gate 	 */
8230Sstevel@tonic-gate 	msix_p->msix_tbl_offset = pci_config_get32(cfg_hdle,
8240Sstevel@tonic-gate 	    caps_ptr + PCI_MSIX_TBL_OFFSET);
8250Sstevel@tonic-gate 	rnumber = msix_p->msix_tbl_offset & PCI_MSIX_TBL_BIR_MASK;
8260Sstevel@tonic-gate 	msix_p->msix_tbl_offset &= ~rnumber; /* Clear BIR from the offset */
8270Sstevel@tonic-gate 
8280Sstevel@tonic-gate 	msix_tbl_size = (msi_ctrl & PCI_MSIX_TBL_SIZE_MASK) + 1;
8290Sstevel@tonic-gate 
8300Sstevel@tonic-gate 	if (ddi_regs_map_setup(rdip,
8310Sstevel@tonic-gate 	    rnumber,
8320Sstevel@tonic-gate 	    &msix_p->msix_tbl_addr,
8330Sstevel@tonic-gate 	    msix_p->msix_tbl_offset,
8340Sstevel@tonic-gate 	    msix_tbl_size,
8350Sstevel@tonic-gate 	    &msix_p->msix_dev_attr,
8360Sstevel@tonic-gate 	    &msix_p->msix_tbl_hdl) !=
8370Sstevel@tonic-gate 	    DDI_SUCCESS) {
8380Sstevel@tonic-gate 		DDI_INTR_NEXDBG((CE_CONT, "pci_msix_initialize: MSI-X Table "
8390Sstevel@tonic-gate 		    "ddi_regs_map_setup failed\n"));
8400Sstevel@tonic-gate 		kmem_free(msix_p, sizeof (ddi_intr_msix_t));
8410Sstevel@tonic-gate 		pci_config_teardown(&cfg_hdle);
8420Sstevel@tonic-gate 		return (NULL);
8430Sstevel@tonic-gate 	}
8440Sstevel@tonic-gate 
8450Sstevel@tonic-gate 	/*
8460Sstevel@tonic-gate 	 * Map in the MSI-X Pending Bit Array
8470Sstevel@tonic-gate 	 */
8480Sstevel@tonic-gate 	if (msi_ctrl &  PCI_MSIX_TBL_SIZE_MASK)
8490Sstevel@tonic-gate 		pba_tbl_size = ((msi_ctrl & PCI_MSIX_TBL_SIZE_MASK) + 1)/64;
8500Sstevel@tonic-gate 
8510Sstevel@tonic-gate 	msix_p->msix_pba_offset = pci_config_get32(cfg_hdle,
8520Sstevel@tonic-gate 	    caps_ptr + PCI_MSIX_PBA_OFFSET);
8530Sstevel@tonic-gate 	rnumber = msix_p->msix_pba_offset & PCI_MSIX_PBA_BIR_MASK;
8540Sstevel@tonic-gate 	msix_p->msix_pba_offset &= ~rnumber; /* Clear offset from BIR */
8550Sstevel@tonic-gate 
8560Sstevel@tonic-gate 	if (ddi_regs_map_setup(rdip,
8570Sstevel@tonic-gate 	    rnumber,
8580Sstevel@tonic-gate 	    &msix_p->msix_pba_addr,
8590Sstevel@tonic-gate 	    msix_p->msix_pba_offset,
8600Sstevel@tonic-gate 	    pba_tbl_size,
8610Sstevel@tonic-gate 	    &msix_p->msix_dev_attr,
8620Sstevel@tonic-gate 	    &msix_p->msix_pba_hdl) != DDI_SUCCESS) {
8630Sstevel@tonic-gate 		DDI_INTR_NEXDBG((CE_CONT, "pci_msix_initialize: PBA "
8640Sstevel@tonic-gate 		    "ddi_regs_map_setup failed\n"));
8650Sstevel@tonic-gate 		ddi_regs_map_free(&msix_p->msix_tbl_hdl);
8660Sstevel@tonic-gate 		kmem_free(msix_p, sizeof (ddi_intr_msix_t));
8670Sstevel@tonic-gate 		pci_config_teardown(&cfg_hdle);
8680Sstevel@tonic-gate 		return (NULL);
8690Sstevel@tonic-gate 	}
8700Sstevel@tonic-gate 
8710Sstevel@tonic-gate 	DDI_INTR_NEXDBG((CE_CONT, "pci_msix_init: msix_p = 0x%p DONE!!\n",
8720Sstevel@tonic-gate 	    (void *)msix_p));
8730Sstevel@tonic-gate 
8740Sstevel@tonic-gate 	pci_config_teardown(&cfg_hdle);
8750Sstevel@tonic-gate 	return (msix_p);
8760Sstevel@tonic-gate }
8770Sstevel@tonic-gate 
8780Sstevel@tonic-gate 
8790Sstevel@tonic-gate /*
8800Sstevel@tonic-gate  * pci_msix_fini:
8810Sstevel@tonic-gate  *	This function cleans up previously allocated handles/addrs etc.
8820Sstevel@tonic-gate  *	It is only called if no more MSI-X interrupts are being used.
8830Sstevel@tonic-gate  */
8840Sstevel@tonic-gate void
8850Sstevel@tonic-gate pci_msix_fini(ddi_intr_msix_t *msix_p)
8860Sstevel@tonic-gate {
8870Sstevel@tonic-gate 	DDI_INTR_NEXDBG((CE_CONT, "pci_msix_fini: msix_p = 0x%p\n",
8880Sstevel@tonic-gate 	    (void *)msix_p));
8890Sstevel@tonic-gate 
8900Sstevel@tonic-gate 	ddi_regs_map_free(&msix_p->msix_pba_hdl);
8910Sstevel@tonic-gate 	ddi_regs_map_free(&msix_p->msix_tbl_hdl);
8920Sstevel@tonic-gate 	kmem_free(msix_p, sizeof (ddi_intr_msix_t));
8930Sstevel@tonic-gate }
8940Sstevel@tonic-gate 
8950Sstevel@tonic-gate 
8960Sstevel@tonic-gate 
8970Sstevel@tonic-gate /*
8980Sstevel@tonic-gate  * Next set of routines are for INTx (legacy) PCI interrupt
8990Sstevel@tonic-gate  * support only.
9000Sstevel@tonic-gate  */
9010Sstevel@tonic-gate 
9020Sstevel@tonic-gate /*
9030Sstevel@tonic-gate  * pci_intx_get_cap:
9040Sstevel@tonic-gate  *	For non-MSI devices that comply to PCI v2.3 or greater;
9050Sstevel@tonic-gate  *	read the command register. Bit 10 implies interrupt disable.
9060Sstevel@tonic-gate  *	Set this bit and then read the status register bit 3.
9070Sstevel@tonic-gate  *	Bit 3 of status register is Interrupt state.
9080Sstevel@tonic-gate  *	If it is set; then the device supports 'Masking'
9090Sstevel@tonic-gate  *
9100Sstevel@tonic-gate  *	Reset the device back to the original state.
9110Sstevel@tonic-gate  */
9120Sstevel@tonic-gate int
9130Sstevel@tonic-gate pci_intx_get_cap(dev_info_t *dip, int *flagsp)
9140Sstevel@tonic-gate {
9150Sstevel@tonic-gate 	uint16_t		cmdreg, savereg;
9160Sstevel@tonic-gate 	ddi_acc_handle_t	cfg_hdl;
9170Sstevel@tonic-gate #ifdef	DEBUG
9180Sstevel@tonic-gate 	uint16_t		statreg;
9190Sstevel@tonic-gate #endif /* DEBUG */
9200Sstevel@tonic-gate 
9210Sstevel@tonic-gate 	*flagsp = 0;
9220Sstevel@tonic-gate 	DDI_INTR_NEXDBG((CE_CONT, "pci_intx_get_cap: %s%d: called\n",
9230Sstevel@tonic-gate 	    ddi_driver_name(dip), ddi_get_instance(dip)));
9240Sstevel@tonic-gate 
9250Sstevel@tonic-gate 	if (pci_config_setup(dip, &cfg_hdl) != DDI_SUCCESS) {
9260Sstevel@tonic-gate 		DDI_INTR_NEXDBG((CE_CONT, "pci_intx_get_cap: can't get "
9270Sstevel@tonic-gate 		    "config handle\n"));
9280Sstevel@tonic-gate 		return (DDI_FAILURE);
9290Sstevel@tonic-gate 	}
9300Sstevel@tonic-gate 
9310Sstevel@tonic-gate 	savereg = pci_config_get16(cfg_hdl, PCI_CONF_COMM);
9320Sstevel@tonic-gate 	DDI_INTR_NEXDBG((CE_CONT, "pci_intx_get_cap: "
9330Sstevel@tonic-gate 	    "command register was 0x%x\n", savereg));
9340Sstevel@tonic-gate 
9350Sstevel@tonic-gate 	/* Disable the interrupts */
9360Sstevel@tonic-gate 	cmdreg = savereg | PCI_COMM_INTX_DISABLE;
9370Sstevel@tonic-gate 	pci_config_put16(cfg_hdl, PCI_CONF_COMM, cmdreg);
9380Sstevel@tonic-gate 
9390Sstevel@tonic-gate #ifdef	DEBUG
9400Sstevel@tonic-gate 	statreg = pci_config_get16(cfg_hdl, PCI_CONF_STAT);
9410Sstevel@tonic-gate 	DDI_INTR_NEXDBG((CE_CONT, "pci_intx_get_cap: "
9420Sstevel@tonic-gate 	    "status register is 0x%x\n", statreg));
9430Sstevel@tonic-gate #endif /* DEBUG */
9440Sstevel@tonic-gate 
9450Sstevel@tonic-gate 	/* Read the bit back */
9460Sstevel@tonic-gate 	cmdreg = pci_config_get16(cfg_hdl, PCI_CONF_COMM);
9470Sstevel@tonic-gate 	DDI_INTR_NEXDBG((CE_CONT, "pci_intx_get_cap: "
9480Sstevel@tonic-gate 	    "command register is now 0x%x\n", cmdreg));
9490Sstevel@tonic-gate 
950*693Sgovinda 	*flagsp = DDI_INTR_FLAG_LEVEL;
951*693Sgovinda 
9520Sstevel@tonic-gate 	if (cmdreg & PCI_COMM_INTX_DISABLE) {
9530Sstevel@tonic-gate 		DDI_INTR_NEXDBG((CE_CONT, "pci_intx_get_cap: "
9540Sstevel@tonic-gate 		    "masking supported\n"));
955*693Sgovinda 		*flagsp |= (DDI_INTR_FLAG_MASKABLE |
956*693Sgovinda 		    DDI_INTR_FLAG_PENDING);
9570Sstevel@tonic-gate 	}
9580Sstevel@tonic-gate 
9590Sstevel@tonic-gate 	/* Restore the device back to the original state and return */
9600Sstevel@tonic-gate 	pci_config_put16(cfg_hdl, PCI_CONF_COMM, savereg);
9610Sstevel@tonic-gate 
9620Sstevel@tonic-gate 	pci_config_teardown(&cfg_hdl);
9630Sstevel@tonic-gate 	return (DDI_SUCCESS);
9640Sstevel@tonic-gate }
9650Sstevel@tonic-gate 
9660Sstevel@tonic-gate 
9670Sstevel@tonic-gate /*
9680Sstevel@tonic-gate  * pci_intx_clr_mask:
9690Sstevel@tonic-gate  *	For non-MSI devices that comply to PCI v2.3 or greater;
9700Sstevel@tonic-gate  *	clear the bit10 in the command register.
9710Sstevel@tonic-gate  */
9720Sstevel@tonic-gate int
9730Sstevel@tonic-gate pci_intx_clr_mask(dev_info_t *dip)
9740Sstevel@tonic-gate {
9750Sstevel@tonic-gate 	uint16_t		cmdreg;
9760Sstevel@tonic-gate 	ddi_acc_handle_t	cfg_hdl;
9770Sstevel@tonic-gate 
9780Sstevel@tonic-gate 	DDI_INTR_NEXDBG((CE_CONT, "pci_intx_clr_mask: %s%d: called\n",
9790Sstevel@tonic-gate 	    ddi_driver_name(dip), ddi_get_instance(dip)));
9800Sstevel@tonic-gate 
9810Sstevel@tonic-gate 	if (pci_config_setup(dip, &cfg_hdl) != DDI_SUCCESS) {
9820Sstevel@tonic-gate 		DDI_INTR_NEXDBG((CE_CONT, "pci_intx_clr_mask: can't get "
9830Sstevel@tonic-gate 		    "config handle\n"));
9840Sstevel@tonic-gate 		return (DDI_FAILURE);
9850Sstevel@tonic-gate 	}
9860Sstevel@tonic-gate 
9870Sstevel@tonic-gate 	cmdreg = pci_config_get16(cfg_hdl, PCI_CONF_COMM);
9880Sstevel@tonic-gate 	DDI_INTR_NEXDBG((CE_CONT, "pci_intx_clr_mask: "
9890Sstevel@tonic-gate 	    "command register was 0x%x\n", cmdreg));
9900Sstevel@tonic-gate 
9910Sstevel@tonic-gate 	/* Enable the interrupts */
9920Sstevel@tonic-gate 	cmdreg &= ~PCI_COMM_INTX_DISABLE;
9930Sstevel@tonic-gate 	pci_config_put16(cfg_hdl, PCI_CONF_COMM, cmdreg);
9940Sstevel@tonic-gate 	pci_config_teardown(&cfg_hdl);
9950Sstevel@tonic-gate 	return (DDI_SUCCESS);
9960Sstevel@tonic-gate }
9970Sstevel@tonic-gate 
9980Sstevel@tonic-gate 
9990Sstevel@tonic-gate /*
10000Sstevel@tonic-gate  * pci_intx_set_mask:
10010Sstevel@tonic-gate  *	For non-MSI devices that comply to PCI v2.3 or greater;
10020Sstevel@tonic-gate  *	set the bit10 in the command register.
10030Sstevel@tonic-gate  */
10040Sstevel@tonic-gate int
10050Sstevel@tonic-gate pci_intx_set_mask(dev_info_t *dip)
10060Sstevel@tonic-gate {
10070Sstevel@tonic-gate 	uint16_t		cmdreg;
10080Sstevel@tonic-gate 	ddi_acc_handle_t	cfg_hdl;
10090Sstevel@tonic-gate 
10100Sstevel@tonic-gate 	DDI_INTR_NEXDBG((CE_CONT, "pci_intx_set_mask: %s%d: called\n",
10110Sstevel@tonic-gate 	    ddi_driver_name(dip), ddi_get_instance(dip)));
10120Sstevel@tonic-gate 
10130Sstevel@tonic-gate 	if (pci_config_setup(dip, &cfg_hdl) != DDI_SUCCESS) {
10140Sstevel@tonic-gate 		DDI_INTR_NEXDBG((CE_CONT, "pci_intx_set_mask: can't get "
10150Sstevel@tonic-gate 		    "config handle\n"));
10160Sstevel@tonic-gate 		return (DDI_FAILURE);
10170Sstevel@tonic-gate 	}
10180Sstevel@tonic-gate 
10190Sstevel@tonic-gate 	cmdreg = pci_config_get16(cfg_hdl, PCI_CONF_COMM);
10200Sstevel@tonic-gate 	DDI_INTR_NEXDBG((CE_CONT, "pci_intx_set_mask: "
10210Sstevel@tonic-gate 	    "command register was 0x%x\n", cmdreg));
10220Sstevel@tonic-gate 
10230Sstevel@tonic-gate 	/* Disable the interrupts */
10240Sstevel@tonic-gate 	cmdreg |= PCI_COMM_INTX_DISABLE;
10250Sstevel@tonic-gate 	pci_config_put16(cfg_hdl, PCI_CONF_COMM, cmdreg);
10260Sstevel@tonic-gate 	pci_config_teardown(&cfg_hdl);
10270Sstevel@tonic-gate 	return (DDI_SUCCESS);
10280Sstevel@tonic-gate }
10290Sstevel@tonic-gate 
10300Sstevel@tonic-gate /*
10310Sstevel@tonic-gate  * pci_intx_get_pending:
10320Sstevel@tonic-gate  *	For non-MSI devices that comply to PCI v2.3 or greater;
10330Sstevel@tonic-gate  *	read the status register. Bit 3 of status register is
10340Sstevel@tonic-gate  *	Interrupt state. If it is set; then the interrupt is
10350Sstevel@tonic-gate  *	'Pending'.
10360Sstevel@tonic-gate  */
10370Sstevel@tonic-gate int
10380Sstevel@tonic-gate pci_intx_get_pending(dev_info_t *dip, int *pendingp)
10390Sstevel@tonic-gate {
10400Sstevel@tonic-gate 	uint16_t		statreg;
10410Sstevel@tonic-gate 	ddi_acc_handle_t	cfg_hdl;
10420Sstevel@tonic-gate 
10430Sstevel@tonic-gate 	*pendingp = 0;
10440Sstevel@tonic-gate 	DDI_INTR_NEXDBG((CE_CONT, "pci_intx_get_pending: %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_get_pending: can't get "
10490Sstevel@tonic-gate 		    "config handle\n"));
10500Sstevel@tonic-gate 		return (DDI_FAILURE);
10510Sstevel@tonic-gate 	}
10520Sstevel@tonic-gate 
10530Sstevel@tonic-gate 	statreg = pci_config_get16(cfg_hdl, PCI_CONF_STAT);
10540Sstevel@tonic-gate 
10550Sstevel@tonic-gate 	if (statreg & PCI_STAT_INTR) {
10560Sstevel@tonic-gate 		DDI_INTR_NEXDBG((CE_CONT, "pci_intx_get_pending: "
10570Sstevel@tonic-gate 		    "interrupt is pending\n"));
10580Sstevel@tonic-gate 		*pendingp = 1;
10590Sstevel@tonic-gate 	}
10600Sstevel@tonic-gate 
10610Sstevel@tonic-gate 	pci_config_teardown(&cfg_hdl);
10620Sstevel@tonic-gate 	return (DDI_SUCCESS);
10630Sstevel@tonic-gate }
10640Sstevel@tonic-gate 
10650Sstevel@tonic-gate 
10660Sstevel@tonic-gate /*
10670Sstevel@tonic-gate  * pci_devclass_to_ipl:
10680Sstevel@tonic-gate  *	translate from device class to ipl
10690Sstevel@tonic-gate  *	NOTE: This function is  added here as pci_intx_get_ispec()
10700Sstevel@tonic-gate  *	calls this to figure out the priority.
10710Sstevel@tonic-gate  *	It is moved over from x86 pci.c
10720Sstevel@tonic-gate  */
10730Sstevel@tonic-gate int
10740Sstevel@tonic-gate pci_devclass_to_ipl(int class)
10750Sstevel@tonic-gate {
10760Sstevel@tonic-gate 	int	base_cl;
10770Sstevel@tonic-gate 	int	ipl;
10780Sstevel@tonic-gate 
10790Sstevel@tonic-gate 	base_cl = (class & 0xff0000) >> 16;
10800Sstevel@tonic-gate 
10810Sstevel@tonic-gate 	/*
10820Sstevel@tonic-gate 	 * Use the class code values to construct an ipl for the device.
10830Sstevel@tonic-gate 	 */
10840Sstevel@tonic-gate 	switch (base_cl) {
10850Sstevel@tonic-gate 	default:
10860Sstevel@tonic-gate 	case PCI_CLASS_NONE:
10870Sstevel@tonic-gate 		ipl = 1;
10880Sstevel@tonic-gate 		break;
10890Sstevel@tonic-gate 	case PCI_CLASS_MASS:
10900Sstevel@tonic-gate 		ipl = 0x5;
10910Sstevel@tonic-gate 		break;
10920Sstevel@tonic-gate 	case PCI_CLASS_NET:
10930Sstevel@tonic-gate 		ipl = 0x6;
10940Sstevel@tonic-gate 		break;
10950Sstevel@tonic-gate 	case PCI_CLASS_DISPLAY:
10960Sstevel@tonic-gate 		ipl = 0x9;
10970Sstevel@tonic-gate 		break;
10980Sstevel@tonic-gate 	/*
10990Sstevel@tonic-gate 	 * for high priority interrupt handlers, use level 12
11000Sstevel@tonic-gate 	 * as the highest for device drivers
11010Sstevel@tonic-gate 	 */
11020Sstevel@tonic-gate 	case PCI_CLASS_MM:
11030Sstevel@tonic-gate 		ipl = 0xc;
11040Sstevel@tonic-gate 		break;
11050Sstevel@tonic-gate 	case PCI_CLASS_MEM:
11060Sstevel@tonic-gate 		ipl = 0xc;
11070Sstevel@tonic-gate 		break;
11080Sstevel@tonic-gate 	case PCI_CLASS_BRIDGE:
11090Sstevel@tonic-gate 		ipl = 0xc;
11100Sstevel@tonic-gate 		break;
11110Sstevel@tonic-gate 	}
11120Sstevel@tonic-gate 	return (ipl);
11130Sstevel@tonic-gate }
11140Sstevel@tonic-gate 
11150Sstevel@tonic-gate 
11160Sstevel@tonic-gate /*
11170Sstevel@tonic-gate  * pci_intx_get_ispec:
11180Sstevel@tonic-gate  *	Get intrspec for PCI devices (legacy support)
11190Sstevel@tonic-gate  *	NOTE: This is moved here from x86 pci.c and is
11200Sstevel@tonic-gate  *	needed here as pci-ide.c uses it as well
11210Sstevel@tonic-gate  */
11220Sstevel@tonic-gate /*ARGSUSED*/
11230Sstevel@tonic-gate ddi_intrspec_t
11240Sstevel@tonic-gate pci_intx_get_ispec(dev_info_t *dip, dev_info_t *rdip, int inum)
11250Sstevel@tonic-gate {
11260Sstevel@tonic-gate 	int				class, *intpriorities;
11270Sstevel@tonic-gate 	uint_t				num_intpriorities;
11280Sstevel@tonic-gate 	struct intrspec			*ispec;
11290Sstevel@tonic-gate 	ddi_acc_handle_t		cfg_hdl;
11300Sstevel@tonic-gate 	struct ddi_parent_private_data	*pdptr;
11310Sstevel@tonic-gate 
11320Sstevel@tonic-gate 	if ((pdptr = ddi_get_parent_data(rdip)) == NULL)
11330Sstevel@tonic-gate 		return (NULL);
11340Sstevel@tonic-gate 
11350Sstevel@tonic-gate 	ispec = pdptr->par_intr;
11360Sstevel@tonic-gate 	ASSERT(ispec);
11370Sstevel@tonic-gate 
11380Sstevel@tonic-gate 	/* check if the intrspec_pri has been initialized */
11390Sstevel@tonic-gate 	if (!ispec->intrspec_pri) {
11400Sstevel@tonic-gate 		if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, rdip,
11410Sstevel@tonic-gate 		    DDI_PROP_DONTPASS, "interrupt-priorities",
11420Sstevel@tonic-gate 		    &intpriorities, &num_intpriorities) == DDI_PROP_SUCCESS) {
11430Sstevel@tonic-gate 			if (inum < num_intpriorities)
11440Sstevel@tonic-gate 				ispec->intrspec_pri = intpriorities[inum];
11450Sstevel@tonic-gate 			ddi_prop_free(intpriorities);
11460Sstevel@tonic-gate 		}
11470Sstevel@tonic-gate 
11480Sstevel@tonic-gate 		/* If still no priority, guess based on the class code */
11490Sstevel@tonic-gate 		if (ispec->intrspec_pri == 0) {
11500Sstevel@tonic-gate 			/* get 'class' property to derive the intr priority */
11510Sstevel@tonic-gate 			class = ddi_prop_get_int(DDI_DEV_T_ANY, rdip,
11520Sstevel@tonic-gate 			    DDI_PROP_DONTPASS, "class-code", -1);
11530Sstevel@tonic-gate 			ispec->intrspec_pri = (class == -1) ? 1 :
11540Sstevel@tonic-gate 			    pci_devclass_to_ipl(class);
11550Sstevel@tonic-gate 		}
11560Sstevel@tonic-gate 	}
11570Sstevel@tonic-gate 
11580Sstevel@tonic-gate 	/* Get interrupt line value */
11590Sstevel@tonic-gate 	if (!ispec->intrspec_vec) {
11600Sstevel@tonic-gate 		if (pci_config_setup(rdip, &cfg_hdl) != DDI_SUCCESS) {
11610Sstevel@tonic-gate 			DDI_INTR_NEXDBG((CE_CONT, "pci_intx_get_iline: "
11620Sstevel@tonic-gate 			    "can't get config handle\n"));
11630Sstevel@tonic-gate 			return (NULL);
11640Sstevel@tonic-gate 		}
11650Sstevel@tonic-gate 
11660Sstevel@tonic-gate 		ispec->intrspec_vec = pci_config_get8(cfg_hdl, PCI_CONF_ILINE);
11670Sstevel@tonic-gate 		pci_config_teardown(&cfg_hdl);
11680Sstevel@tonic-gate 	}
11690Sstevel@tonic-gate 
11700Sstevel@tonic-gate 	return ((ddi_intrspec_t)ispec);
11710Sstevel@tonic-gate }
1172