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