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