17c478bd9Sstevel@tonic-gate /*
27c478bd9Sstevel@tonic-gate  * CDDL HEADER START
37c478bd9Sstevel@tonic-gate  *
47c478bd9Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
527255037Spjha  * Common Development and Distribution License (the "License").
627255037Spjha  * You may not use this file except in compliance with the License.
77c478bd9Sstevel@tonic-gate  *
87c478bd9Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
97c478bd9Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
107c478bd9Sstevel@tonic-gate  * See the License for the specific language governing permissions
117c478bd9Sstevel@tonic-gate  * and limitations under the License.
127c478bd9Sstevel@tonic-gate  *
137c478bd9Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
147c478bd9Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
157c478bd9Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
167c478bd9Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
177c478bd9Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
187c478bd9Sstevel@tonic-gate  *
197c478bd9Sstevel@tonic-gate  * CDDL HEADER END
207c478bd9Sstevel@tonic-gate  */
217c478bd9Sstevel@tonic-gate /*
22614edcaeSEvan Yan  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
237c478bd9Sstevel@tonic-gate  * Use is subject to license terms.
248181b438SGarrett D'Amore  * Copyright 2013 Pluribus Networks, Inc.
257c478bd9Sstevel@tonic-gate  */
267c478bd9Sstevel@tonic-gate 
277c478bd9Sstevel@tonic-gate /*
287c478bd9Sstevel@tonic-gate  * Support for MSI, MSIX and INTx
297c478bd9Sstevel@tonic-gate  */
307c478bd9Sstevel@tonic-gate 
317c478bd9Sstevel@tonic-gate #include <sys/conf.h>
327c478bd9Sstevel@tonic-gate #include <sys/debug.h>
337c478bd9Sstevel@tonic-gate #include <sys/pci.h>
3427255037Spjha #include <sys/pci_cap.h>
35614edcaeSEvan Yan #include <sys/pci_intr_lib.h>
367c478bd9Sstevel@tonic-gate #include <sys/sunddi.h>
377c478bd9Sstevel@tonic-gate #include <sys/bitmap.h>
387c478bd9Sstevel@tonic-gate 
399c75c6bfSgovinda /*
409c75c6bfSgovinda  * MSI-X BIR Index Table:
419c75c6bfSgovinda  *
429c75c6bfSgovinda  * BAR indicator register (BIR) to Base Address register.
439c75c6bfSgovinda  */
449c75c6bfSgovinda static	uchar_t pci_msix_bir_index[8] = {0x10, 0x14, 0x18, 0x1c,
459c75c6bfSgovinda 					0x20, 0x24, 0xff, 0xff};
469c75c6bfSgovinda 
47614edcaeSEvan Yan /* default class to pil value mapping */
48614edcaeSEvan Yan pci_class_val_t pci_default_pil [] = {
49614edcaeSEvan Yan 	{0x000000, 0xff0000, 0x1},	/* Class code for pre-2.0 devices */
50614edcaeSEvan Yan 	{0x010000, 0xff0000, 0x5},	/* Mass Storage Controller */
51614edcaeSEvan Yan 	{0x020000, 0xff0000, 0x6},	/* Network Controller */
52614edcaeSEvan Yan 	{0x030000, 0xff0000, 0x9},	/* Display Controller */
53614edcaeSEvan Yan 	{0x040000, 0xff0000, 0x8},	/* Multimedia Controller */
54614edcaeSEvan Yan 	{0x050000, 0xff0000, 0x9},	/* Memory Controller */
55614edcaeSEvan Yan 	{0x060000, 0xff0000, 0x9},	/* Bridge Controller */
56614edcaeSEvan Yan 	{0x0c0000, 0xffff00, 0x9},	/* Serial Bus, FireWire (IEEE 1394) */
57614edcaeSEvan Yan 	{0x0c0100, 0xffff00, 0x4},	/* Serial Bus, ACCESS.bus */
58614edcaeSEvan Yan 	{0x0c0200, 0xffff00, 0x4},	/* Serial Bus, SSA */
59614edcaeSEvan Yan 	{0x0c0300, 0xffff00, 0x9},	/* Serial Bus Universal Serial Bus */
604f3b09fdSEvan Yan /*
614f3b09fdSEvan Yan  * XXX - This is a temporary workaround and it will be removed
624f3b09fdSEvan Yan  *       after x86 interrupt scalability support.
634f3b09fdSEvan Yan  */
64*86ef0a63SRichard Lowe #if defined(__x86)
654f3b09fdSEvan Yan 	{0x0c0400, 0xffff00, 0x5},	/* Serial Bus, Fibre Channel */
664f3b09fdSEvan Yan #else
67614edcaeSEvan Yan 	{0x0c0400, 0xffff00, 0x6},	/* Serial Bus, Fibre Channel */
684f3b09fdSEvan Yan #endif
69614edcaeSEvan Yan 	{0x0c0600, 0xffff00, 0x6}	/* Serial Bus, Infiniband */
70614edcaeSEvan Yan };
71614edcaeSEvan Yan 
72614edcaeSEvan Yan /*
73614edcaeSEvan Yan  * Default class to intr_weight value mapping (% of CPU).  A driver.conf
74614edcaeSEvan Yan  * entry on or above the pci node like
75614edcaeSEvan Yan  *
76614edcaeSEvan Yan  *	pci-class-intr-weights= 0x020000, 0xff0000, 30;
77614edcaeSEvan Yan  *
78614edcaeSEvan Yan  * can be used to augment or override entries in the default table below.
79614edcaeSEvan Yan  *
80614edcaeSEvan Yan  * NB: The values below give NICs preference on redistribution, and provide
81614edcaeSEvan Yan  * NICs some isolation from other interrupt sources. We need better interfaces
82614edcaeSEvan Yan  * that allow the NIC driver to identify a specific NIC instance as high
83614edcaeSEvan Yan  * bandwidth, and thus deserving of separation from other low bandwidth
84614edcaeSEvan Yan  * NICs additional isolation from other interrupt sources.
85614edcaeSEvan Yan  *
86614edcaeSEvan Yan  * NB: We treat Infiniband like a NIC.
87614edcaeSEvan Yan  */
88614edcaeSEvan Yan pci_class_val_t pci_default_intr_weight [] = {
89614edcaeSEvan Yan 	{0x020000, 0xff0000, 35},	/* Network Controller */
90614edcaeSEvan Yan 	{0x010000, 0xff0000, 10},	/* Mass Storage Controller */
91614edcaeSEvan Yan 	{0x0c0400, 0xffff00, 10},	/* Serial Bus, Fibre Channel */
92614edcaeSEvan Yan 	{0x0c0600, 0xffff00, 50}	/* Serial Bus, Infiniband */
93614edcaeSEvan Yan };
94614edcaeSEvan Yan 
957c478bd9Sstevel@tonic-gate /*
967c478bd9Sstevel@tonic-gate  * Library utility functions
977c478bd9Sstevel@tonic-gate  */
987c478bd9Sstevel@tonic-gate 
997c478bd9Sstevel@tonic-gate /*
1007c478bd9Sstevel@tonic-gate  * pci_get_msi_ctrl:
1017c478bd9Sstevel@tonic-gate  *
1027c478bd9Sstevel@tonic-gate  *	Helper function that returns with 'cfg_hdl', MSI/X ctrl pointer,
1037c478bd9Sstevel@tonic-gate  *	and caps_ptr for MSI/X if these are found.
1047c478bd9Sstevel@tonic-gate  */
1057c478bd9Sstevel@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)1067c478bd9Sstevel@tonic-gate pci_get_msi_ctrl(dev_info_t *dip, int type, ushort_t *msi_ctrl,
10727255037Spjha     ushort_t *caps_ptr, ddi_acc_handle_t *h)
1087c478bd9Sstevel@tonic-gate {
1097c478bd9Sstevel@tonic-gate 	*msi_ctrl = *caps_ptr = 0;
1107c478bd9Sstevel@tonic-gate 
11127255037Spjha 	if (pci_config_setup(dip, h) != DDI_SUCCESS) {
1127c478bd9Sstevel@tonic-gate 		DDI_INTR_NEXDBG((CE_CONT, "pci_get_msi_ctrl: "
11327255037Spjha 		    "%s%d can't get config handle",
1147c478bd9Sstevel@tonic-gate 		    ddi_driver_name(dip), ddi_get_instance(dip)));
1157c478bd9Sstevel@tonic-gate 
1167c478bd9Sstevel@tonic-gate 		return (DDI_FAILURE);
1177c478bd9Sstevel@tonic-gate 	}
1187c478bd9Sstevel@tonic-gate 
11927255037Spjha 	if ((PCI_CAP_LOCATE(*h, PCI_CAP_ID_MSI, caps_ptr) == DDI_SUCCESS) &&
120a7639048Sjohnny 	    (type == DDI_INTR_TYPE_MSI)) {
1217e12ceb3SToomas Soome 		if ((*msi_ctrl = PCI_CAP_GET16(*h, 0, *caps_ptr,
122a7639048Sjohnny 		    PCI_MSI_CTRL)) == PCI_CAP_EINVAL16)
12327255037Spjha 			goto done;
1247c478bd9Sstevel@tonic-gate 
12527255037Spjha 		DDI_INTR_NEXDBG((CE_CONT, "pci_get_msi_ctrl: MSI "
12627255037Spjha 		    "caps_ptr=%x msi_ctrl=%x\n", *caps_ptr, *msi_ctrl));
1277c478bd9Sstevel@tonic-gate 
12827255037Spjha 		return (DDI_SUCCESS);
12927255037Spjha 	}
1307c478bd9Sstevel@tonic-gate 
13127255037Spjha 	if ((PCI_CAP_LOCATE(*h, PCI_CAP_ID_MSI_X, caps_ptr) == DDI_SUCCESS) &&
132a7639048Sjohnny 	    (type == DDI_INTR_TYPE_MSIX)) {
1337e12ceb3SToomas Soome 		if ((*msi_ctrl = PCI_CAP_GET16(*h, 0, *caps_ptr,
134a7639048Sjohnny 		    PCI_MSIX_CTRL)) == PCI_CAP_EINVAL16)
13527255037Spjha 			goto done;
1367c478bd9Sstevel@tonic-gate 
13727255037Spjha 		DDI_INTR_NEXDBG((CE_CONT, "pci_get_msi_ctrl: MSI-X "
13827255037Spjha 		    "caps_ptr=%x msi_ctrl=%x\n", *caps_ptr, *msi_ctrl));
1397c478bd9Sstevel@tonic-gate 
14027255037Spjha 		return (DDI_SUCCESS);
1417c478bd9Sstevel@tonic-gate 	}
1427c478bd9Sstevel@tonic-gate 
14327255037Spjha done:
14427255037Spjha 	pci_config_teardown(h);
1457c478bd9Sstevel@tonic-gate 	return (DDI_FAILURE);
1467c478bd9Sstevel@tonic-gate }
1477c478bd9Sstevel@tonic-gate 
1487c478bd9Sstevel@tonic-gate 
1497c478bd9Sstevel@tonic-gate /*
1507c478bd9Sstevel@tonic-gate  * pci_msi_get_cap:
1517c478bd9Sstevel@tonic-gate  *
1527c478bd9Sstevel@tonic-gate  * Get the capabilities of the MSI/X interrupt
1537c478bd9Sstevel@tonic-gate  */
1547c478bd9Sstevel@tonic-gate int
pci_msi_get_cap(dev_info_t * rdip,int type,int * flagsp)1557c478bd9Sstevel@tonic-gate pci_msi_get_cap(dev_info_t *rdip, int type, int *flagsp)
1567c478bd9Sstevel@tonic-gate {
1577c478bd9Sstevel@tonic-gate 	ushort_t		caps_ptr, msi_ctrl;
1587c478bd9Sstevel@tonic-gate 	ddi_acc_handle_t	cfg_hdle;
1597c478bd9Sstevel@tonic-gate 
1607c478bd9Sstevel@tonic-gate 	DDI_INTR_NEXDBG((CE_CONT, "pci_msi_get_cap: rdip = 0x%p\n",
1617c478bd9Sstevel@tonic-gate 	    (void *)rdip));
1627c478bd9Sstevel@tonic-gate 
1637c478bd9Sstevel@tonic-gate 	*flagsp = 0;
1647c478bd9Sstevel@tonic-gate 
1657c478bd9Sstevel@tonic-gate 	if (pci_get_msi_ctrl(rdip, type, &msi_ctrl,
1667c478bd9Sstevel@tonic-gate 	    &caps_ptr, &cfg_hdle) != DDI_SUCCESS)
1677c478bd9Sstevel@tonic-gate 		return (DDI_FAILURE);
1687c478bd9Sstevel@tonic-gate 
1697c478bd9Sstevel@tonic-gate 	if (type == DDI_INTR_TYPE_MSI) {
1707c478bd9Sstevel@tonic-gate 		if (msi_ctrl &  PCI_MSI_64BIT_MASK)
1717c478bd9Sstevel@tonic-gate 			*flagsp |= DDI_INTR_FLAG_MSI64;
1727c478bd9Sstevel@tonic-gate 		if (msi_ctrl & PCI_MSI_PVM_MASK)
1737c478bd9Sstevel@tonic-gate 			*flagsp |= (DDI_INTR_FLAG_MASKABLE |
1747c478bd9Sstevel@tonic-gate 			    DDI_INTR_FLAG_PENDING);
1757c478bd9Sstevel@tonic-gate 		else
1767c478bd9Sstevel@tonic-gate 			*flagsp |= DDI_INTR_FLAG_BLOCK;
1777c478bd9Sstevel@tonic-gate 	} else if (type == DDI_INTR_TYPE_MSIX) {
1787c478bd9Sstevel@tonic-gate 		/* MSI-X supports PVM, 64bit by default */
1797c478bd9Sstevel@tonic-gate 		*flagsp |= (DDI_INTR_FLAG_MASKABLE | DDI_INTR_FLAG_MSI64 |
1807c478bd9Sstevel@tonic-gate 		    DDI_INTR_FLAG_PENDING);
1817c478bd9Sstevel@tonic-gate 	}
1827c478bd9Sstevel@tonic-gate 
1837c478bd9Sstevel@tonic-gate 	*flagsp |= DDI_INTR_FLAG_EDGE;
1847c478bd9Sstevel@tonic-gate 
1857c478bd9Sstevel@tonic-gate 	DDI_INTR_NEXDBG((CE_CONT, "pci_msi_get_cap: flags = 0x%x\n", *flagsp));
1867c478bd9Sstevel@tonic-gate 
1877c478bd9Sstevel@tonic-gate 	pci_config_teardown(&cfg_hdle);
1887c478bd9Sstevel@tonic-gate 	return (DDI_SUCCESS);
1897c478bd9Sstevel@tonic-gate }
1907c478bd9Sstevel@tonic-gate 
1917c478bd9Sstevel@tonic-gate 
1927c478bd9Sstevel@tonic-gate /*
1937c478bd9Sstevel@tonic-gate  * pci_msi_configure:
1947c478bd9Sstevel@tonic-gate  *
1957c478bd9Sstevel@tonic-gate  * Configure address/data and number MSI/Xs fields in the MSI/X
1967c478bd9Sstevel@tonic-gate  * capability structure.
1977c478bd9Sstevel@tonic-gate  */
1987c478bd9Sstevel@tonic-gate /* ARGSUSED */
1997c478bd9Sstevel@tonic-gate int
pci_msi_configure(dev_info_t * rdip,int type,int count,int inum,uint64_t addr,uint64_t data)2007c478bd9Sstevel@tonic-gate pci_msi_configure(dev_info_t *rdip, int type, int count, int inum,
2017c478bd9Sstevel@tonic-gate     uint64_t addr, uint64_t data)
2027c478bd9Sstevel@tonic-gate {
2037c478bd9Sstevel@tonic-gate 	ushort_t		caps_ptr, msi_ctrl;
20427255037Spjha 	ddi_acc_handle_t	h;
2057c478bd9Sstevel@tonic-gate 
2069c75c6bfSgovinda 	DDI_INTR_NEXDBG((CE_CONT, "pci_msi_configure: rdip = 0x%p type 0x%x "
2079c75c6bfSgovinda 	    "count 0x%x inum 0x%x addr 0x%" PRIx64 " data 0x%" PRIx64 "\n",
2089c75c6bfSgovinda 	    (void *)rdip, type, count, inum, addr, data));
2097c478bd9Sstevel@tonic-gate 
2107c478bd9Sstevel@tonic-gate 	if (pci_get_msi_ctrl(rdip, type, &msi_ctrl,
21127255037Spjha 	    &caps_ptr, &h) != DDI_SUCCESS)
2127c478bd9Sstevel@tonic-gate 		return (DDI_FAILURE);
2137c478bd9Sstevel@tonic-gate 
2147c478bd9Sstevel@tonic-gate 	if (type == DDI_INTR_TYPE_MSI) {
2157c478bd9Sstevel@tonic-gate 		/* Set the bits to inform how many MSIs are enabled */
2167c478bd9Sstevel@tonic-gate 		msi_ctrl |= ((highbit(count) -1) << PCI_MSI_MME_SHIFT);
2177e12ceb3SToomas Soome 		PCI_CAP_PUT16(h, 0, caps_ptr, PCI_MSI_CTRL, msi_ctrl);
2187c478bd9Sstevel@tonic-gate 
2199c75c6bfSgovinda 		DDI_INTR_NEXDBG((CE_CONT, "pci_msi_configure: msi_ctrl = %x\n",
2207e12ceb3SToomas Soome 		    PCI_CAP_GET16(h, 0, caps_ptr, PCI_MSI_CTRL)));
2219c75c6bfSgovinda 
2227c478bd9Sstevel@tonic-gate 		/* Set the "data" and "addr" bits */
2237e12ceb3SToomas Soome 		PCI_CAP_PUT32(h, 0, caps_ptr, PCI_MSI_ADDR_OFFSET, addr);
2247c478bd9Sstevel@tonic-gate 
2259c75c6bfSgovinda 		DDI_INTR_NEXDBG((CE_CONT, "pci_msi_configure: msi_addr = %x\n",
2267e12ceb3SToomas Soome 		    PCI_CAP_GET32(h, 0, caps_ptr, PCI_MSI_ADDR_OFFSET)));
2279c75c6bfSgovinda 
2287c478bd9Sstevel@tonic-gate 		if (msi_ctrl &  PCI_MSI_64BIT_MASK) {
2297e12ceb3SToomas Soome 			PCI_CAP_PUT32(h, 0, caps_ptr, PCI_MSI_ADDR_OFFSET
230a7639048Sjohnny 			    + 4, addr >> 32);
2319c75c6bfSgovinda 
23227255037Spjha 			DDI_INTR_NEXDBG((CE_CONT, "pci_msi_configure: upper "
2337e12ceb3SToomas Soome 			    "32bit msi_addr = %x\n", PCI_CAP_GET32(h, 0,
234a7639048Sjohnny 			    caps_ptr, PCI_MSI_ADDR_OFFSET + 4)));
2359c75c6bfSgovinda 
2367e12ceb3SToomas Soome 			PCI_CAP_PUT16(h, 0, caps_ptr, PCI_MSI_64BIT_DATA, data);
2379c75c6bfSgovinda 
23827255037Spjha 			DDI_INTR_NEXDBG((CE_CONT, "pci_msi_configure: msi_data "
2397e12ceb3SToomas Soome 			    "= %x\n", PCI_CAP_GET16(h, 0, caps_ptr,
240a7639048Sjohnny 			    PCI_MSI_64BIT_DATA)));
2417c478bd9Sstevel@tonic-gate 		} else {
2427e12ceb3SToomas Soome 			PCI_CAP_PUT16(h, 0, caps_ptr, PCI_MSI_32BIT_DATA, data);
2437c478bd9Sstevel@tonic-gate 
24427255037Spjha 			DDI_INTR_NEXDBG((CE_CONT, "pci_msi_configure: msi_data "
2457e12ceb3SToomas Soome 			    "= %x\n", PCI_CAP_GET16(h, 0, caps_ptr,
246a7639048Sjohnny 			    PCI_MSI_32BIT_DATA)));
2479c75c6bfSgovinda 		}
2487c478bd9Sstevel@tonic-gate 	} else if (type == DDI_INTR_TYPE_MSIX) {
249abdbd06dSagiri 		uintptr_t	off;
2507c478bd9Sstevel@tonic-gate 		ddi_intr_msix_t	*msix_p = i_ddi_get_msix(rdip);
2517c478bd9Sstevel@tonic-gate 
2527c478bd9Sstevel@tonic-gate 		/* Offset into the "inum"th entry in the MSI-X table */
253abdbd06dSagiri 		off = (uintptr_t)msix_p->msix_tbl_addr +
2549c75c6bfSgovinda 		    (inum * PCI_MSIX_VECTOR_SIZE);
2557c478bd9Sstevel@tonic-gate 
2567c478bd9Sstevel@tonic-gate 		/* Set the "data" and "addr" bits */
2577c478bd9Sstevel@tonic-gate 		ddi_put32(msix_p->msix_tbl_hdl,
2589c75c6bfSgovinda 		    (uint32_t *)(off + PCI_MSIX_DATA_OFFSET), data);
2597c478bd9Sstevel@tonic-gate 
2608181b438SGarrett D'Amore 		/*
2618181b438SGarrett D'Amore 		 * Note that the spec only requires 32-bit accesses
2628181b438SGarrett D'Amore 		 * to be supported.  Apparently some chipsets don't
2638181b438SGarrett D'Amore 		 * support 64-bit accesses.
2648181b438SGarrett D'Amore 		 */
2658181b438SGarrett D'Amore 		ddi_put32(msix_p->msix_tbl_hdl,
2668181b438SGarrett D'Amore 		    (uint32_t *)(off + PCI_MSIX_LOWER_ADDR_OFFSET), addr);
2678181b438SGarrett D'Amore 		ddi_put32(msix_p->msix_tbl_hdl,
2688181b438SGarrett D'Amore 		    (uint32_t *)(off + PCI_MSIX_UPPER_ADDR_OFFSET),
2698181b438SGarrett D'Amore 		    addr >> 32);
2709c75c6bfSgovinda 
2719c75c6bfSgovinda 		DDI_INTR_NEXDBG((CE_CONT, "pci_msi_configure: "
2728181b438SGarrett D'Amore 		    "msix_addr 0x%x.%x msix_data 0x%x\n",
2738181b438SGarrett D'Amore 		    ddi_get32(msix_p->msix_tbl_hdl,
2748181b438SGarrett D'Amore 		    (uint32_t *)(off + PCI_MSIX_UPPER_ADDR_OFFSET)),
2758181b438SGarrett D'Amore 		    ddi_get32(msix_p->msix_tbl_hdl,
2768181b438SGarrett D'Amore 		    (uint32_t *)(off + PCI_MSIX_LOWER_ADDR_OFFSET)),
2779c75c6bfSgovinda 		    ddi_get32(msix_p->msix_tbl_hdl,
2789c75c6bfSgovinda 		    (uint32_t *)(off + PCI_MSIX_DATA_OFFSET))));
2797c478bd9Sstevel@tonic-gate 	}
2807c478bd9Sstevel@tonic-gate 
28127255037Spjha 	pci_config_teardown(&h);
2827c478bd9Sstevel@tonic-gate 	return (DDI_SUCCESS);
2837c478bd9Sstevel@tonic-gate }
2847c478bd9Sstevel@tonic-gate 
2857c478bd9Sstevel@tonic-gate 
2867c478bd9Sstevel@tonic-gate /*
2877c478bd9Sstevel@tonic-gate  * pci_msi_unconfigure:
2887c478bd9Sstevel@tonic-gate  *
2897c478bd9Sstevel@tonic-gate  * Unconfigure address/data and number MSI/Xs fields in the MSI/X
2907c478bd9Sstevel@tonic-gate  * capability structure.
2917c478bd9Sstevel@tonic-gate  */
2927c478bd9Sstevel@tonic-gate /* ARGSUSED */
2937c478bd9Sstevel@tonic-gate int
pci_msi_unconfigure(dev_info_t * rdip,int type,int inum)2947c478bd9Sstevel@tonic-gate pci_msi_unconfigure(dev_info_t *rdip, int type, int inum)
2957c478bd9Sstevel@tonic-gate {
2967c478bd9Sstevel@tonic-gate 	ushort_t		msi_ctrl, caps_ptr;
29727255037Spjha 	ddi_acc_handle_t	h;
2987c478bd9Sstevel@tonic-gate 
29995003185Segillett 	DDI_INTR_NEXDBG((CE_CONT, "pci_msi_unconfigure: rdip = 0x%p type 0x%x "
30095003185Segillett 	    "inum 0x%x\n", (void *)rdip, type, inum));
3017c478bd9Sstevel@tonic-gate 
30227255037Spjha 	if (pci_get_msi_ctrl(rdip, type, &msi_ctrl, &caps_ptr, &h) !=
30395003185Segillett 	    DDI_SUCCESS)
3047c478bd9Sstevel@tonic-gate 		return (DDI_FAILURE);
3057c478bd9Sstevel@tonic-gate 
3067c478bd9Sstevel@tonic-gate 	if (type == DDI_INTR_TYPE_MSI) {
3077c478bd9Sstevel@tonic-gate 		msi_ctrl &= (~PCI_MSI_MME_MASK);
3087e12ceb3SToomas Soome 		PCI_CAP_PUT16(h, 0, caps_ptr, PCI_MSI_CTRL, msi_ctrl);
30927255037Spjha 
3107e12ceb3SToomas Soome 		PCI_CAP_PUT32(h, 0, caps_ptr, PCI_MSI_ADDR_OFFSET, 0);
3117c478bd9Sstevel@tonic-gate 
3127c478bd9Sstevel@tonic-gate 		if (msi_ctrl &  PCI_MSI_64BIT_MASK) {
3137e12ceb3SToomas Soome 			PCI_CAP_PUT16(h, 0, caps_ptr, PCI_MSI_64BIT_DATA, 0);
3147e12ceb3SToomas Soome 			PCI_CAP_PUT32(h, 0, caps_ptr, PCI_MSI_ADDR_OFFSET
315a7639048Sjohnny 			    + 4, 0);
3167c478bd9Sstevel@tonic-gate 		} else {
3177e12ceb3SToomas Soome 			PCI_CAP_PUT16(h, 0, caps_ptr, PCI_MSI_32BIT_DATA, 0);
3187c478bd9Sstevel@tonic-gate 		}
3197c478bd9Sstevel@tonic-gate 
32027255037Spjha 		DDI_INTR_NEXDBG((CE_CONT, "pci_msi_unconfigure: msi_ctrl "
3217e12ceb3SToomas Soome 		    "= %x\n", PCI_CAP_GET16(h, 0, caps_ptr, PCI_MSI_CTRL)));
3227c478bd9Sstevel@tonic-gate 
3237c478bd9Sstevel@tonic-gate 	} else if (type == DDI_INTR_TYPE_MSIX) {
324abdbd06dSagiri 		uintptr_t	off;
3257c478bd9Sstevel@tonic-gate 		ddi_intr_msix_t	*msix_p = i_ddi_get_msix(rdip);
3267c478bd9Sstevel@tonic-gate 
3277c478bd9Sstevel@tonic-gate 		/* Offset into the "inum"th entry in the MSI-X table */
328abdbd06dSagiri 		off = (uintptr_t)msix_p->msix_tbl_addr +
3299c75c6bfSgovinda 		    (inum * PCI_MSIX_VECTOR_SIZE);
3307c478bd9Sstevel@tonic-gate 
3317c478bd9Sstevel@tonic-gate 		/* Reset the "data" and "addr" bits */
3327c478bd9Sstevel@tonic-gate 		ddi_put32(msix_p->msix_tbl_hdl,
3339c75c6bfSgovinda 		    (uint32_t *)(off + PCI_MSIX_DATA_OFFSET), 0);
3347c478bd9Sstevel@tonic-gate 
3358181b438SGarrett D'Amore 		/*
3368181b438SGarrett D'Amore 		 * Note that the spec only requires 32-bit accesses
3378181b438SGarrett D'Amore 		 * to be supported.  Apparently some chipsets don't
3388181b438SGarrett D'Amore 		 * support 64-bit accesses.
3398181b438SGarrett D'Amore 		 */
3408181b438SGarrett D'Amore 		ddi_put32(msix_p->msix_tbl_hdl,
3418181b438SGarrett D'Amore 		    (uint32_t *)(off + PCI_MSIX_LOWER_ADDR_OFFSET), 0);
3428181b438SGarrett D'Amore 		ddi_put32(msix_p->msix_tbl_hdl,
3438181b438SGarrett D'Amore 		    (uint32_t *)(off + PCI_MSIX_UPPER_ADDR_OFFSET), 0);
3447c478bd9Sstevel@tonic-gate 	}
3457c478bd9Sstevel@tonic-gate 
34627255037Spjha 	pci_config_teardown(&h);
3477c478bd9Sstevel@tonic-gate 	return (DDI_SUCCESS);
3487c478bd9Sstevel@tonic-gate }
3497c478bd9Sstevel@tonic-gate 
3507c478bd9Sstevel@tonic-gate 
3517c478bd9Sstevel@tonic-gate /*
3527c478bd9Sstevel@tonic-gate  * pci_is_msi_enabled:
3537c478bd9Sstevel@tonic-gate  *
3547c478bd9Sstevel@tonic-gate  * This function returns DDI_SUCCESS if MSI/X is already enabled, otherwise
3557c478bd9Sstevel@tonic-gate  * it returns DDI_FAILURE.
3567c478bd9Sstevel@tonic-gate  */
3577c478bd9Sstevel@tonic-gate int
pci_is_msi_enabled(dev_info_t * rdip,int type)3587c478bd9Sstevel@tonic-gate pci_is_msi_enabled(dev_info_t *rdip, int type)
3597c478bd9Sstevel@tonic-gate {
3607c478bd9Sstevel@tonic-gate 	ushort_t		caps_ptr, msi_ctrl;
3617c478bd9Sstevel@tonic-gate 	ddi_acc_handle_t	cfg_hdle;
3627c478bd9Sstevel@tonic-gate 	int			ret = DDI_FAILURE;
3637c478bd9Sstevel@tonic-gate 
3647c478bd9Sstevel@tonic-gate 	DDI_INTR_NEXDBG((CE_CONT, "pci_is_msi_enabled: rdip = 0x%p, "
3657c478bd9Sstevel@tonic-gate 	    "type  = 0x%x\n", (void *)rdip, type));
3667c478bd9Sstevel@tonic-gate 
3677c478bd9Sstevel@tonic-gate 	if (pci_get_msi_ctrl(rdip, type, &msi_ctrl,
3687c478bd9Sstevel@tonic-gate 	    &caps_ptr, &cfg_hdle) != DDI_SUCCESS)
3697c478bd9Sstevel@tonic-gate 		return (DDI_FAILURE);
3707c478bd9Sstevel@tonic-gate 
3717c478bd9Sstevel@tonic-gate 	if ((type == DDI_INTR_TYPE_MSI) && (msi_ctrl & PCI_MSI_ENABLE_BIT))
3727c478bd9Sstevel@tonic-gate 		ret = DDI_SUCCESS;
3737c478bd9Sstevel@tonic-gate 
3747c478bd9Sstevel@tonic-gate 	if ((type == DDI_INTR_TYPE_MSIX) && (msi_ctrl & PCI_MSIX_ENABLE_BIT))
3757c478bd9Sstevel@tonic-gate 		ret = DDI_SUCCESS;
3767c478bd9Sstevel@tonic-gate 
3777c478bd9Sstevel@tonic-gate 	pci_config_teardown(&cfg_hdle);
3787c478bd9Sstevel@tonic-gate 	return (ret);
3797c478bd9Sstevel@tonic-gate }
3807c478bd9Sstevel@tonic-gate 
3817c478bd9Sstevel@tonic-gate 
3827c478bd9Sstevel@tonic-gate /*
3837c478bd9Sstevel@tonic-gate  * pci_msi_enable_mode:
3847c478bd9Sstevel@tonic-gate  *
3857c478bd9Sstevel@tonic-gate  * This function sets the MSI_ENABLE bit in the capability structure
3867c478bd9Sstevel@tonic-gate  * (for MSI) and MSIX_ENABLE bit in the MSI-X capability structure.
38795003185Segillett  *
38895003185Segillett  * NOTE: It is the nexus driver's responsibility to clear the MSI/X
38995003185Segillett  * interrupt's mask bit in the MSI/X capability structure before the
39095003185Segillett  * interrupt can be used.
3917c478bd9Sstevel@tonic-gate  */
3927c478bd9Sstevel@tonic-gate int
pci_msi_enable_mode(dev_info_t * rdip,int type)39395003185Segillett pci_msi_enable_mode(dev_info_t *rdip, int type)
3947c478bd9Sstevel@tonic-gate {
3957c478bd9Sstevel@tonic-gate 	ushort_t		caps_ptr, msi_ctrl;
3967c478bd9Sstevel@tonic-gate 	ddi_acc_handle_t	cfg_hdle;
3977c478bd9Sstevel@tonic-gate 
39895003185Segillett 	DDI_INTR_NEXDBG((CE_CONT, "pci_msi_enable_mode: rdip = 0x%p\n",
39995003185Segillett 	    (void *)rdip));
4007c478bd9Sstevel@tonic-gate 
4017c478bd9Sstevel@tonic-gate 	if (pci_get_msi_ctrl(rdip, type, &msi_ctrl,
4027c478bd9Sstevel@tonic-gate 	    &caps_ptr, &cfg_hdle) != DDI_SUCCESS)
4037c478bd9Sstevel@tonic-gate 		return (DDI_FAILURE);
4047c478bd9Sstevel@tonic-gate 
4057c478bd9Sstevel@tonic-gate 	if (type == DDI_INTR_TYPE_MSI) {
4067c478bd9Sstevel@tonic-gate 		if (msi_ctrl & PCI_MSI_ENABLE_BIT)
4077c478bd9Sstevel@tonic-gate 			goto finished;
4087c478bd9Sstevel@tonic-gate 
4097c478bd9Sstevel@tonic-gate 		msi_ctrl |= PCI_MSI_ENABLE_BIT;
4107e12ceb3SToomas Soome 		PCI_CAP_PUT16(cfg_hdle, 0, caps_ptr, PCI_MSI_CTRL, msi_ctrl);
4117c478bd9Sstevel@tonic-gate 
4127c478bd9Sstevel@tonic-gate 	} else if (type == DDI_INTR_TYPE_MSIX) {
4137c478bd9Sstevel@tonic-gate 		if (msi_ctrl & PCI_MSIX_ENABLE_BIT)
4147c478bd9Sstevel@tonic-gate 			goto finished;
4157c478bd9Sstevel@tonic-gate 
4167c478bd9Sstevel@tonic-gate 		msi_ctrl |= PCI_MSIX_ENABLE_BIT;
4177e12ceb3SToomas Soome 		PCI_CAP_PUT16(cfg_hdle, 0, caps_ptr, PCI_MSIX_CTRL,
41895003185Segillett 		    msi_ctrl);
4197c478bd9Sstevel@tonic-gate 	}
4207c478bd9Sstevel@tonic-gate 
4217c478bd9Sstevel@tonic-gate finished:
4227c478bd9Sstevel@tonic-gate 	DDI_INTR_NEXDBG((CE_CONT, "pci_msi_enable_mode: msi_ctrl = %x\n",
4237c478bd9Sstevel@tonic-gate 	    msi_ctrl));
4247c478bd9Sstevel@tonic-gate 
4257c478bd9Sstevel@tonic-gate 	pci_config_teardown(&cfg_hdle);
4267c478bd9Sstevel@tonic-gate 	return (DDI_SUCCESS);
4277c478bd9Sstevel@tonic-gate }
4287c478bd9Sstevel@tonic-gate 
4297c478bd9Sstevel@tonic-gate 
4307c478bd9Sstevel@tonic-gate /*
4317c478bd9Sstevel@tonic-gate  * pci_msi_disable_mode:
4327c478bd9Sstevel@tonic-gate  *
4337c478bd9Sstevel@tonic-gate  * This function resets the MSI_ENABLE bit in the capability structure
4347c478bd9Sstevel@tonic-gate  * (for MSI) and MSIX_ENABLE bit in the MSI-X capability structure.
43595003185Segillett  *
43695003185Segillett  * NOTE: It is the nexus driver's responsibility to set the MSI/X
43795003185Segillett  * interrupt's mask bit in the MSI/X capability structure before the
43895003185Segillett  * interrupt can be disabled.
4397c478bd9Sstevel@tonic-gate  */
4407c478bd9Sstevel@tonic-gate int
pci_msi_disable_mode(dev_info_t * rdip,int type)44109b1eac2SEvan Yan pci_msi_disable_mode(dev_info_t *rdip, int type)
4427c478bd9Sstevel@tonic-gate {
4437c478bd9Sstevel@tonic-gate 	ushort_t		caps_ptr, msi_ctrl;
4447c478bd9Sstevel@tonic-gate 	ddi_acc_handle_t	cfg_hdle;
4457c478bd9Sstevel@tonic-gate 
44609b1eac2SEvan Yan 	DDI_INTR_NEXDBG((CE_CONT, "pci_msi_disable_mode: rdip = 0x%p\n",
44709b1eac2SEvan Yan 	    (void *)rdip));
4487c478bd9Sstevel@tonic-gate 
4497c478bd9Sstevel@tonic-gate 	if (pci_get_msi_ctrl(rdip, type, &msi_ctrl,
4507c478bd9Sstevel@tonic-gate 	    &caps_ptr, &cfg_hdle) != DDI_SUCCESS)
4517c478bd9Sstevel@tonic-gate 		return (DDI_FAILURE);
4527c478bd9Sstevel@tonic-gate 
4537c478bd9Sstevel@tonic-gate 	/* Reset the "enable" bit */
4547c478bd9Sstevel@tonic-gate 	if (type == DDI_INTR_TYPE_MSI) {
4557c478bd9Sstevel@tonic-gate 		if (!(msi_ctrl & PCI_MSI_ENABLE_BIT))
4567c478bd9Sstevel@tonic-gate 			goto finished;
4577c478bd9Sstevel@tonic-gate 		msi_ctrl &= ~PCI_MSI_ENABLE_BIT;
4587e12ceb3SToomas Soome 		PCI_CAP_PUT16(cfg_hdle, 0, caps_ptr, PCI_MSI_CTRL, msi_ctrl);
4597c478bd9Sstevel@tonic-gate 	} else if (type == DDI_INTR_TYPE_MSIX) {
4607c478bd9Sstevel@tonic-gate 		if (!(msi_ctrl & PCI_MSIX_ENABLE_BIT))
4617c478bd9Sstevel@tonic-gate 			goto finished;
4627c478bd9Sstevel@tonic-gate 
46395003185Segillett 		msi_ctrl &= ~PCI_MSIX_ENABLE_BIT;
4647e12ceb3SToomas Soome 		PCI_CAP_PUT16(cfg_hdle, 0, caps_ptr, PCI_MSIX_CTRL, msi_ctrl);
4657c478bd9Sstevel@tonic-gate 	}
4667c478bd9Sstevel@tonic-gate 
4677c478bd9Sstevel@tonic-gate finished:
4687c478bd9Sstevel@tonic-gate 	DDI_INTR_NEXDBG((CE_CONT, "pci_msi_disable_mode: msi_ctrl = %x\n",
4697c478bd9Sstevel@tonic-gate 	    msi_ctrl));
4707c478bd9Sstevel@tonic-gate 
4717c478bd9Sstevel@tonic-gate 	pci_config_teardown(&cfg_hdle);
4727c478bd9Sstevel@tonic-gate 	return (DDI_SUCCESS);
4737c478bd9Sstevel@tonic-gate }
4747c478bd9Sstevel@tonic-gate 
4757c478bd9Sstevel@tonic-gate 
4767c478bd9Sstevel@tonic-gate /*
4777c478bd9Sstevel@tonic-gate  * pci_msi_set_mask:
4787c478bd9Sstevel@tonic-gate  *
4797c478bd9Sstevel@tonic-gate  * Set the mask bit in the MSI/X capability structure
4807c478bd9Sstevel@tonic-gate  */
4817c478bd9Sstevel@tonic-gate /* ARGSUSED */
4827c478bd9Sstevel@tonic-gate int
pci_msi_set_mask(dev_info_t * rdip,int type,int inum)4837c478bd9Sstevel@tonic-gate pci_msi_set_mask(dev_info_t *rdip, int type, int inum)
4847c478bd9Sstevel@tonic-gate {
4857c478bd9Sstevel@tonic-gate 	int			offset;
4867c478bd9Sstevel@tonic-gate 	int			ret = DDI_FAILURE;
4877c478bd9Sstevel@tonic-gate 	ushort_t		caps_ptr, msi_ctrl;
4887c478bd9Sstevel@tonic-gate 	ddi_acc_handle_t	cfg_hdle;
4899c75c6bfSgovinda 	uint32_t		mask_bits;
4907c478bd9Sstevel@tonic-gate 
4917c478bd9Sstevel@tonic-gate 	DDI_INTR_NEXDBG((CE_CONT, "pci_msi_set_mask: rdip = 0x%p, "
4927c478bd9Sstevel@tonic-gate 	    "type = 0x%x\n", (void *)rdip, type));
4937c478bd9Sstevel@tonic-gate 
4947c478bd9Sstevel@tonic-gate 	if (pci_get_msi_ctrl(rdip, type, &msi_ctrl,
4957c478bd9Sstevel@tonic-gate 	    &caps_ptr, &cfg_hdle) != DDI_SUCCESS)
4967c478bd9Sstevel@tonic-gate 		return (DDI_FAILURE);
4977c478bd9Sstevel@tonic-gate 
4987c478bd9Sstevel@tonic-gate 	if (type == DDI_INTR_TYPE_MSI) {
4997c478bd9Sstevel@tonic-gate 		if (!(msi_ctrl &  PCI_MSI_PVM_MASK))
5007c478bd9Sstevel@tonic-gate 			goto done;
5017c478bd9Sstevel@tonic-gate 
5027c478bd9Sstevel@tonic-gate 		offset = (msi_ctrl &  PCI_MSI_64BIT_MASK) ?
5037c478bd9Sstevel@tonic-gate 		    PCI_MSI_64BIT_MASKBITS : PCI_MSI_32BIT_MASK;
5047c478bd9Sstevel@tonic-gate 
5057e12ceb3SToomas Soome 		if ((mask_bits = PCI_CAP_GET32(cfg_hdle, 0, caps_ptr,
506a7639048Sjohnny 		    offset)) == PCI_CAP_EINVAL32)
50727255037Spjha 			goto done;
5087c478bd9Sstevel@tonic-gate 
5097c478bd9Sstevel@tonic-gate 		mask_bits |= (1 << inum);
5107c478bd9Sstevel@tonic-gate 
5117e12ceb3SToomas Soome 		PCI_CAP_PUT32(cfg_hdle, 0, caps_ptr, offset, mask_bits);
5127c478bd9Sstevel@tonic-gate 
5137c478bd9Sstevel@tonic-gate 	} else if (type == DDI_INTR_TYPE_MSIX) {
514abdbd06dSagiri 		uintptr_t		off;
5157c478bd9Sstevel@tonic-gate 		ddi_intr_msix_t		*msix_p;
5167c478bd9Sstevel@tonic-gate 
5177c478bd9Sstevel@tonic-gate 		/* Set function mask */
5187c478bd9Sstevel@tonic-gate 		if (msi_ctrl & PCI_MSIX_FUNCTION_MASK) {
5197c478bd9Sstevel@tonic-gate 			ret = DDI_SUCCESS;
5207c478bd9Sstevel@tonic-gate 			goto done;
5217c478bd9Sstevel@tonic-gate 		}
5227c478bd9Sstevel@tonic-gate 
5237c478bd9Sstevel@tonic-gate 		msix_p = i_ddi_get_msix(rdip);
5247c478bd9Sstevel@tonic-gate 
5257c478bd9Sstevel@tonic-gate 		/* Offset into the "inum"th entry in the MSI-X table */
5269c75c6bfSgovinda 		off = (uintptr_t)msix_p->msix_tbl_addr + (inum *
5277c478bd9Sstevel@tonic-gate 		    PCI_MSIX_VECTOR_SIZE) + PCI_MSIX_VECTOR_CTRL_OFFSET;
5287c478bd9Sstevel@tonic-gate 
5297c478bd9Sstevel@tonic-gate 		/* Set the Mask bit */
5309c75c6bfSgovinda 		ddi_put32(msix_p->msix_tbl_hdl, (uint32_t *)off, 0x1);
5317c478bd9Sstevel@tonic-gate 	}
5327c478bd9Sstevel@tonic-gate 
5337c478bd9Sstevel@tonic-gate 	ret = DDI_SUCCESS;
5347c478bd9Sstevel@tonic-gate done:
5357c478bd9Sstevel@tonic-gate 	pci_config_teardown(&cfg_hdle);
5367c478bd9Sstevel@tonic-gate 	return (ret);
5377c478bd9Sstevel@tonic-gate }
5387c478bd9Sstevel@tonic-gate 
5397c478bd9Sstevel@tonic-gate 
5407c478bd9Sstevel@tonic-gate /*
5417c478bd9Sstevel@tonic-gate  * pci_msi_clr_mask:
5427c478bd9Sstevel@tonic-gate  *
5437c478bd9Sstevel@tonic-gate  * Clear the mask bit in the MSI/X capability structure
5447c478bd9Sstevel@tonic-gate  */
5457c478bd9Sstevel@tonic-gate /* ARGSUSED */
5467c478bd9Sstevel@tonic-gate int
pci_msi_clr_mask(dev_info_t * rdip,int type,int inum)5477c478bd9Sstevel@tonic-gate pci_msi_clr_mask(dev_info_t *rdip, int type, int inum)
5487c478bd9Sstevel@tonic-gate {
5497c478bd9Sstevel@tonic-gate 	ushort_t		caps_ptr, msi_ctrl;
5507c478bd9Sstevel@tonic-gate 	ddi_acc_handle_t	cfg_hdle;
5517c478bd9Sstevel@tonic-gate 	int			offset;
5527c478bd9Sstevel@tonic-gate 	int			ret = DDI_FAILURE;
5539c75c6bfSgovinda 	uint32_t		mask_bits;
5547c478bd9Sstevel@tonic-gate 
5557c478bd9Sstevel@tonic-gate 	DDI_INTR_NEXDBG((CE_CONT, "pci_msi_clr_mask: rdip = 0x%p, "
5567c478bd9Sstevel@tonic-gate 	    "type = 0x%x\n", (void *)rdip, type));
5577c478bd9Sstevel@tonic-gate 
5587c478bd9Sstevel@tonic-gate 	if (pci_get_msi_ctrl(rdip, type, &msi_ctrl,
5597c478bd9Sstevel@tonic-gate 	    &caps_ptr, &cfg_hdle) != DDI_SUCCESS)
5607c478bd9Sstevel@tonic-gate 		return (DDI_FAILURE);
5617c478bd9Sstevel@tonic-gate 
5627c478bd9Sstevel@tonic-gate 	if (type == DDI_INTR_TYPE_MSI) {
5637c478bd9Sstevel@tonic-gate 		if (!(msi_ctrl &  PCI_MSI_PVM_MASK))
5647c478bd9Sstevel@tonic-gate 			goto done;
5657c478bd9Sstevel@tonic-gate 
5667c478bd9Sstevel@tonic-gate 		offset = (msi_ctrl &  PCI_MSI_64BIT_MASK) ?
5677c478bd9Sstevel@tonic-gate 		    PCI_MSI_64BIT_MASKBITS : PCI_MSI_32BIT_MASK;
5687e12ceb3SToomas Soome 		if ((mask_bits = PCI_CAP_GET32(cfg_hdle, 0, caps_ptr,
569a7639048Sjohnny 		    offset)) == PCI_CAP_EINVAL32)
57027255037Spjha 			goto done;
5717c478bd9Sstevel@tonic-gate 
5727c478bd9Sstevel@tonic-gate 		mask_bits &= ~(1 << inum);
5737c478bd9Sstevel@tonic-gate 
5747e12ceb3SToomas Soome 		PCI_CAP_PUT32(cfg_hdle, 0, caps_ptr, offset, mask_bits);
5757c478bd9Sstevel@tonic-gate 
5767c478bd9Sstevel@tonic-gate 	} else if (type == DDI_INTR_TYPE_MSIX) {
577abdbd06dSagiri 		uintptr_t		off;
5787c478bd9Sstevel@tonic-gate 		ddi_intr_msix_t		*msix_p;
5797c478bd9Sstevel@tonic-gate 
5807c478bd9Sstevel@tonic-gate 		if (msi_ctrl & PCI_MSIX_FUNCTION_MASK) {
5817c478bd9Sstevel@tonic-gate 			ret = DDI_SUCCESS;
5827c478bd9Sstevel@tonic-gate 			goto done;
5837c478bd9Sstevel@tonic-gate 		}
5847c478bd9Sstevel@tonic-gate 
5857c478bd9Sstevel@tonic-gate 		msix_p = i_ddi_get_msix(rdip);
5867c478bd9Sstevel@tonic-gate 
5877c478bd9Sstevel@tonic-gate 		/* Offset into the "inum"th entry in the MSI-X table */
5889c75c6bfSgovinda 		off = (uintptr_t)msix_p->msix_tbl_addr + (inum *
5897c478bd9Sstevel@tonic-gate 		    PCI_MSIX_VECTOR_SIZE) + PCI_MSIX_VECTOR_CTRL_OFFSET;
5907c478bd9Sstevel@tonic-gate 
5917c478bd9Sstevel@tonic-gate 		/* Clear the Mask bit */
5929c75c6bfSgovinda 		ddi_put32(msix_p->msix_tbl_hdl, (uint32_t *)off, 0x0);
5937c478bd9Sstevel@tonic-gate 	}
5947c478bd9Sstevel@tonic-gate 
5957c478bd9Sstevel@tonic-gate 	ret = DDI_SUCCESS;
5967c478bd9Sstevel@tonic-gate done:
5977c478bd9Sstevel@tonic-gate 	pci_config_teardown(&cfg_hdle);
5987c478bd9Sstevel@tonic-gate 	return (ret);
5997c478bd9Sstevel@tonic-gate }
6007c478bd9Sstevel@tonic-gate 
6017c478bd9Sstevel@tonic-gate 
6027c478bd9Sstevel@tonic-gate /*
6037c478bd9Sstevel@tonic-gate  * pci_msi_get_pending:
6047c478bd9Sstevel@tonic-gate  *
6057c478bd9Sstevel@tonic-gate  * Get the pending bit from the MSI/X capability structure
6067c478bd9Sstevel@tonic-gate  */
6077c478bd9Sstevel@tonic-gate /* ARGSUSED */
6087c478bd9Sstevel@tonic-gate int
pci_msi_get_pending(dev_info_t * rdip,int type,int inum,int * pendingp)6097c478bd9Sstevel@tonic-gate pci_msi_get_pending(dev_info_t *rdip, int type, int inum, int *pendingp)
6107c478bd9Sstevel@tonic-gate {
6117c478bd9Sstevel@tonic-gate 	ushort_t		caps_ptr, msi_ctrl;
6127c478bd9Sstevel@tonic-gate 	ddi_acc_handle_t	cfg_hdle;
6137c478bd9Sstevel@tonic-gate 	int			offset;
6147c478bd9Sstevel@tonic-gate 	int			ret = DDI_FAILURE;
6157c478bd9Sstevel@tonic-gate 
6167c478bd9Sstevel@tonic-gate 	DDI_INTR_NEXDBG((CE_CONT, "pci_msi_get_pending: rdip = 0x%p\n",
6177c478bd9Sstevel@tonic-gate 	    (void *)rdip));
6187c478bd9Sstevel@tonic-gate 
6197c478bd9Sstevel@tonic-gate 	if (pci_get_msi_ctrl(rdip, type, &msi_ctrl,
6207c478bd9Sstevel@tonic-gate 	    &caps_ptr, &cfg_hdle) != DDI_SUCCESS)
6217c478bd9Sstevel@tonic-gate 		return (DDI_FAILURE);
6227c478bd9Sstevel@tonic-gate 
6237c478bd9Sstevel@tonic-gate 	if (type == DDI_INTR_TYPE_MSI) {
6247c478bd9Sstevel@tonic-gate 		uint32_t	pending_bits;
6257c478bd9Sstevel@tonic-gate 
6267c478bd9Sstevel@tonic-gate 		if (!(msi_ctrl &  PCI_MSI_PVM_MASK)) {
6277c478bd9Sstevel@tonic-gate 			DDI_INTR_NEXDBG((CE_CONT, "pci_msi_get_pending: "
6287c478bd9Sstevel@tonic-gate 			    "PVM is not supported\n"));
6297c478bd9Sstevel@tonic-gate 			goto done;
6307c478bd9Sstevel@tonic-gate 		}
6317c478bd9Sstevel@tonic-gate 
6327c478bd9Sstevel@tonic-gate 		offset = (msi_ctrl &  PCI_MSI_64BIT_MASK) ?
6337c478bd9Sstevel@tonic-gate 		    PCI_MSI_64BIT_PENDING : PCI_MSI_32BIT_PENDING;
6347c478bd9Sstevel@tonic-gate 
6357e12ceb3SToomas Soome 		if ((pending_bits = PCI_CAP_GET32(cfg_hdle, 0, caps_ptr,
636a7639048Sjohnny 		    offset)) == PCI_CAP_EINVAL32)
63727255037Spjha 			goto done;
6387c478bd9Sstevel@tonic-gate 
6397c478bd9Sstevel@tonic-gate 		*pendingp = pending_bits & ~(1 >> inum);
6407c478bd9Sstevel@tonic-gate 
6417c478bd9Sstevel@tonic-gate 	} else if (type == DDI_INTR_TYPE_MSIX) {
642abdbd06dSagiri 		uintptr_t	off;
6437c478bd9Sstevel@tonic-gate 		uint64_t	pending_bits;
6447c478bd9Sstevel@tonic-gate 		ddi_intr_msix_t	*msix_p = i_ddi_get_msix(rdip);
6457c478bd9Sstevel@tonic-gate 
6467c478bd9Sstevel@tonic-gate 		/* Offset into the PBA array which has entry for "inum" */
6479c75c6bfSgovinda 		off = (uintptr_t)msix_p->msix_pba_addr + (inum / 64);
6487c478bd9Sstevel@tonic-gate 
6497c478bd9Sstevel@tonic-gate 		/* Read the PBA array */
6509c75c6bfSgovinda 		pending_bits = ddi_get64(msix_p->msix_pba_hdl, (uint64_t *)off);
6517c478bd9Sstevel@tonic-gate 
6527c478bd9Sstevel@tonic-gate 		*pendingp = pending_bits & ~(1 >> inum);
6537c478bd9Sstevel@tonic-gate 	}
6547c478bd9Sstevel@tonic-gate 
6557c478bd9Sstevel@tonic-gate 	ret = DDI_SUCCESS;
6567c478bd9Sstevel@tonic-gate done:
6577c478bd9Sstevel@tonic-gate 	pci_config_teardown(&cfg_hdle);
6587c478bd9Sstevel@tonic-gate 	return (ret);
6597c478bd9Sstevel@tonic-gate }
6607c478bd9Sstevel@tonic-gate 
6617c478bd9Sstevel@tonic-gate 
6627c478bd9Sstevel@tonic-gate /*
6637c478bd9Sstevel@tonic-gate  * pci_msi_get_nintrs:
6647c478bd9Sstevel@tonic-gate  *
6657c478bd9Sstevel@tonic-gate  * For a given type (MSI/X) returns the number of interrupts supported
6667c478bd9Sstevel@tonic-gate  */
6677c478bd9Sstevel@tonic-gate int
pci_msi_get_nintrs(dev_info_t * rdip,int type,int * nintrs)6687c478bd9Sstevel@tonic-gate pci_msi_get_nintrs(dev_info_t *rdip, int type, int *nintrs)
6697c478bd9Sstevel@tonic-gate {
6707c478bd9Sstevel@tonic-gate 	ushort_t		caps_ptr, msi_ctrl;
6717c478bd9Sstevel@tonic-gate 	ddi_acc_handle_t	cfg_hdle;
6727c478bd9Sstevel@tonic-gate 
6737c478bd9Sstevel@tonic-gate 	DDI_INTR_NEXDBG((CE_CONT, "pci_msi_get_nintrs: rdip = 0x%p\n",
6747c478bd9Sstevel@tonic-gate 	    (void *)rdip));
6757c478bd9Sstevel@tonic-gate 
6767c478bd9Sstevel@tonic-gate 	if (pci_get_msi_ctrl(rdip, type, &msi_ctrl,
6777c478bd9Sstevel@tonic-gate 	    &caps_ptr, &cfg_hdle) != DDI_SUCCESS)
6787c478bd9Sstevel@tonic-gate 		return (DDI_FAILURE);
6797c478bd9Sstevel@tonic-gate 
6807c478bd9Sstevel@tonic-gate 	if (type == DDI_INTR_TYPE_MSI) {
6817c478bd9Sstevel@tonic-gate 		*nintrs = 1 << ((msi_ctrl & PCI_MSI_MMC_MASK) >>
6827c478bd9Sstevel@tonic-gate 		    PCI_MSI_MMC_SHIFT);
6837c478bd9Sstevel@tonic-gate 	} else if (type == DDI_INTR_TYPE_MSIX) {
6847c478bd9Sstevel@tonic-gate 		if (msi_ctrl &  PCI_MSIX_TBL_SIZE_MASK)
6857c478bd9Sstevel@tonic-gate 			*nintrs = (msi_ctrl & PCI_MSIX_TBL_SIZE_MASK) + 1;
6867c478bd9Sstevel@tonic-gate 	}
6877c478bd9Sstevel@tonic-gate 
6887c478bd9Sstevel@tonic-gate 	DDI_INTR_NEXDBG((CE_CONT, "pci_msi_get_nintrs: "
6897c478bd9Sstevel@tonic-gate 	    "nintr = 0x%x\n", *nintrs));
6907c478bd9Sstevel@tonic-gate 
6917c478bd9Sstevel@tonic-gate 	pci_config_teardown(&cfg_hdle);
6927c478bd9Sstevel@tonic-gate 	return (DDI_SUCCESS);
6937c478bd9Sstevel@tonic-gate }
6947c478bd9Sstevel@tonic-gate 
6957c478bd9Sstevel@tonic-gate 
6967c478bd9Sstevel@tonic-gate /*
6977c478bd9Sstevel@tonic-gate  * pci_msi_set_nintrs:
6987c478bd9Sstevel@tonic-gate  *
6997c478bd9Sstevel@tonic-gate  * For a given type (MSI/X) sets the number of interrupts supported
7007c478bd9Sstevel@tonic-gate  * by the system.
7017c478bd9Sstevel@tonic-gate  * For MSI: Return an error if this func is called for navail > 32
7027c478bd9Sstevel@tonic-gate  * For MSI-X: Return an error if this func is called for navail > 2048
7037c478bd9Sstevel@tonic-gate  */
7047c478bd9Sstevel@tonic-gate int
pci_msi_set_nintrs(dev_info_t * rdip,int type,int navail)7057c478bd9Sstevel@tonic-gate pci_msi_set_nintrs(dev_info_t *rdip, int type, int navail)
7067c478bd9Sstevel@tonic-gate {
7077c478bd9Sstevel@tonic-gate 	ushort_t		caps_ptr, msi_ctrl;
7087c478bd9Sstevel@tonic-gate 	ddi_acc_handle_t	cfg_hdle;
7097c478bd9Sstevel@tonic-gate 
7107c478bd9Sstevel@tonic-gate 	DDI_INTR_NEXDBG((CE_CONT, "pci_msi_set_nintrs: rdip = 0x%p, "
7117c478bd9Sstevel@tonic-gate 	    "navail = 0x%x\n", (void *)rdip, navail));
7127c478bd9Sstevel@tonic-gate 
7137c478bd9Sstevel@tonic-gate 	/* Check for valid input argument */
7147c478bd9Sstevel@tonic-gate 	if (((type == DDI_INTR_TYPE_MSI) && (navail > PCI_MSI_MAX_INTRS)) ||
7157c478bd9Sstevel@tonic-gate 	    ((type == DDI_INTR_TYPE_MSIX) && (navail >  PCI_MSIX_MAX_INTRS)))
7167c478bd9Sstevel@tonic-gate 		return (DDI_EINVAL);
7177c478bd9Sstevel@tonic-gate 
7187c478bd9Sstevel@tonic-gate 	if (pci_get_msi_ctrl(rdip, type, &msi_ctrl,
7197c478bd9Sstevel@tonic-gate 	    &caps_ptr, &cfg_hdle) != DDI_SUCCESS)
7207c478bd9Sstevel@tonic-gate 		return (DDI_FAILURE);
7217c478bd9Sstevel@tonic-gate 
7227c478bd9Sstevel@tonic-gate 	if (type == DDI_INTR_TYPE_MSI) {
7237c478bd9Sstevel@tonic-gate 		msi_ctrl |= ((highbit(navail) -1) << PCI_MSI_MME_SHIFT);
7247c478bd9Sstevel@tonic-gate 
7257e12ceb3SToomas Soome 		PCI_CAP_PUT16(cfg_hdle, 0, caps_ptr, PCI_MSI_CTRL, msi_ctrl);
7267c478bd9Sstevel@tonic-gate 	} else if (type == DDI_INTR_TYPE_MSIX) {
7277c478bd9Sstevel@tonic-gate 		DDI_INTR_NEXDBG((CE_CONT, "pci_msi_set_nintrs: unsupported\n"));
7287c478bd9Sstevel@tonic-gate 	}
7297c478bd9Sstevel@tonic-gate 
7307c478bd9Sstevel@tonic-gate 	pci_config_teardown(&cfg_hdle);
7317c478bd9Sstevel@tonic-gate 	return (DDI_SUCCESS);
7327c478bd9Sstevel@tonic-gate }
7337c478bd9Sstevel@tonic-gate 
7347c478bd9Sstevel@tonic-gate 
7357c478bd9Sstevel@tonic-gate /*
7367c478bd9Sstevel@tonic-gate  * pci_msi_get_supported_type:
7377c478bd9Sstevel@tonic-gate  *
7387c478bd9Sstevel@tonic-gate  * Returns DDI_INTR_TYPE_MSI and/or DDI_INTR_TYPE_MSIX as supported
7397c478bd9Sstevel@tonic-gate  * types if device supports them. A DDI_FAILURE is returned otherwise.
7407c478bd9Sstevel@tonic-gate  */
7417c478bd9Sstevel@tonic-gate int
pci_msi_get_supported_type(dev_info_t * rdip,int * typesp)7427c478bd9Sstevel@tonic-gate pci_msi_get_supported_type(dev_info_t *rdip, int *typesp)
7437c478bd9Sstevel@tonic-gate {
7447c478bd9Sstevel@tonic-gate 	ushort_t		caps_ptr, msi_ctrl;
7457c478bd9Sstevel@tonic-gate 	ddi_acc_handle_t	cfg_hdle;
7467c478bd9Sstevel@tonic-gate 
7477c478bd9Sstevel@tonic-gate 	DDI_INTR_NEXDBG((CE_CONT, "pci_msi_get_supported_type: "
7487c478bd9Sstevel@tonic-gate 	    "rdip = 0x%p\n", (void *)rdip));
7497c478bd9Sstevel@tonic-gate 
7507c478bd9Sstevel@tonic-gate 	*typesp = 0;
7517c478bd9Sstevel@tonic-gate 
7527c478bd9Sstevel@tonic-gate 	if (pci_get_msi_ctrl(rdip, DDI_INTR_TYPE_MSI, &msi_ctrl,
7537c478bd9Sstevel@tonic-gate 	    &caps_ptr, &cfg_hdle) == DDI_SUCCESS) {
7547c478bd9Sstevel@tonic-gate 		*typesp |= DDI_INTR_TYPE_MSI;
7557c478bd9Sstevel@tonic-gate 		pci_config_teardown(&cfg_hdle);
7567c478bd9Sstevel@tonic-gate 	}
7577c478bd9Sstevel@tonic-gate 
7587c478bd9Sstevel@tonic-gate 	if (pci_get_msi_ctrl(rdip, DDI_INTR_TYPE_MSIX, &msi_ctrl,
7597c478bd9Sstevel@tonic-gate 	    &caps_ptr, &cfg_hdle) == DDI_SUCCESS) {
7607c478bd9Sstevel@tonic-gate 		*typesp |= DDI_INTR_TYPE_MSIX;
7617c478bd9Sstevel@tonic-gate 		pci_config_teardown(&cfg_hdle);
7627c478bd9Sstevel@tonic-gate 	}
7637c478bd9Sstevel@tonic-gate 
7647c478bd9Sstevel@tonic-gate 	DDI_INTR_NEXDBG((CE_CONT, "pci_msi_get_supported_type: "
7657c478bd9Sstevel@tonic-gate 	    "rdip = 0x%p types 0x%x\n", (void *)rdip, *typesp));
7667c478bd9Sstevel@tonic-gate 
7677c478bd9Sstevel@tonic-gate 	return (*typesp == 0 ? DDI_FAILURE : DDI_SUCCESS);
7687c478bd9Sstevel@tonic-gate }
7697c478bd9Sstevel@tonic-gate 
7707c478bd9Sstevel@tonic-gate 
7717c478bd9Sstevel@tonic-gate /*
7727c478bd9Sstevel@tonic-gate  * pci_msix_init:
7737c478bd9Sstevel@tonic-gate  *	This function initializes the various handles/addrs etc.
7747c478bd9Sstevel@tonic-gate  *	needed for MSI-X support. It also allocates a private
7757c478bd9Sstevel@tonic-gate  *	structure to keep track of these.
7767c478bd9Sstevel@tonic-gate  */
7777c478bd9Sstevel@tonic-gate ddi_intr_msix_t *
pci_msix_init(dev_info_t * rdip)7787c478bd9Sstevel@tonic-gate pci_msix_init(dev_info_t *rdip)
7797c478bd9Sstevel@tonic-gate {
7809c75c6bfSgovinda 	uint_t			rnumber, breg, nregs;
7817c478bd9Sstevel@tonic-gate 	size_t			msix_tbl_size;
7827c478bd9Sstevel@tonic-gate 	size_t			pba_tbl_size;
7839c75c6bfSgovinda 	ushort_t		caps_ptr, msix_ctrl;
7847c478bd9Sstevel@tonic-gate 	ddi_intr_msix_t		*msix_p;
7857c478bd9Sstevel@tonic-gate 	ddi_acc_handle_t	cfg_hdle;
7869c75c6bfSgovinda 	pci_regspec_t		*rp;
7879c75c6bfSgovinda 	int			reg_size, addr_space, offset, *regs_list;
7889c75c6bfSgovinda 	int			i, ret;
7897c478bd9Sstevel@tonic-gate 
7907c478bd9Sstevel@tonic-gate 	DDI_INTR_NEXDBG((CE_CONT, "pci_msix_init: rdip = %p\n", (void *)rdip));
7917c478bd9Sstevel@tonic-gate 
7929c75c6bfSgovinda 	if (pci_get_msi_ctrl(rdip, DDI_INTR_TYPE_MSIX, &msix_ctrl,
7937c478bd9Sstevel@tonic-gate 	    &caps_ptr, &cfg_hdle) != DDI_SUCCESS)
7947c478bd9Sstevel@tonic-gate 		return (NULL);
7957c478bd9Sstevel@tonic-gate 
7967c478bd9Sstevel@tonic-gate 	msix_p = kmem_zalloc(sizeof (ddi_intr_msix_t), KM_SLEEP);
7977c478bd9Sstevel@tonic-gate 
7987c478bd9Sstevel@tonic-gate 	/*
7997c478bd9Sstevel@tonic-gate 	 * Initialize the devacc structure
8007c478bd9Sstevel@tonic-gate 	 */
8017c478bd9Sstevel@tonic-gate 	msix_p->msix_dev_attr.devacc_attr_version = DDI_DEVICE_ATTR_V0;
8027c478bd9Sstevel@tonic-gate 	msix_p->msix_dev_attr.devacc_attr_endian_flags =
8037c478bd9Sstevel@tonic-gate 	    DDI_STRUCTURE_LE_ACC;
8047c478bd9Sstevel@tonic-gate 	msix_p->msix_dev_attr.devacc_attr_dataorder = DDI_STRICTORDER_ACC;
8057c478bd9Sstevel@tonic-gate 
8069c75c6bfSgovinda 	/* Map the entire MSI-X vector table */
8077e12ceb3SToomas Soome 	msix_p->msix_tbl_offset = PCI_CAP_GET32(cfg_hdle, 0, caps_ptr,
808a7639048Sjohnny 	    PCI_MSIX_TBL_OFFSET);
8099c75c6bfSgovinda 
8109c75c6bfSgovinda 	if ((breg = pci_msix_bir_index[msix_p->msix_tbl_offset &
8119c75c6bfSgovinda 	    PCI_MSIX_TBL_BIR_MASK]) == 0xff)
8129c75c6bfSgovinda 		goto fail1;
8139c75c6bfSgovinda 
8149c75c6bfSgovinda 	msix_p->msix_tbl_offset = msix_p->msix_tbl_offset &
8159c75c6bfSgovinda 	    ~PCI_MSIX_TBL_BIR_MASK;
8169c75c6bfSgovinda 	msix_tbl_size = ((msix_ctrl & PCI_MSIX_TBL_SIZE_MASK) + 1) *
8179c75c6bfSgovinda 	    PCI_MSIX_VECTOR_SIZE;
8189c75c6bfSgovinda 
8199c75c6bfSgovinda 	DDI_INTR_NEXDBG((CE_CONT, "pci_msix_init: MSI-X table offset 0x%x "
8209c75c6bfSgovinda 	    "breg 0x%x size 0x%lx\n", msix_p->msix_tbl_offset, breg,
8219c75c6bfSgovinda 	    msix_tbl_size));
8229c75c6bfSgovinda 
8239c75c6bfSgovinda 	if ((ret = ddi_prop_lookup_int_array(DDI_DEV_T_ANY, rdip,
8249c75c6bfSgovinda 	    DDI_PROP_DONTPASS, "reg", (int **)&regs_list, &nregs))
8259c75c6bfSgovinda 	    != DDI_PROP_SUCCESS) {
8269c75c6bfSgovinda 		DDI_INTR_NEXDBG((CE_CONT, "pci_msix_init: "
8279c75c6bfSgovinda 		    "ddi_prop_lookup_int_array failed %d\n", ret));
8289c75c6bfSgovinda 
8299c75c6bfSgovinda 		goto fail1;
8309c75c6bfSgovinda 	}
8319c75c6bfSgovinda 
8329c75c6bfSgovinda 	reg_size = sizeof (pci_regspec_t) / sizeof (int);
8339c75c6bfSgovinda 
8349c75c6bfSgovinda 	for (i = 1, rnumber = 0; i < nregs/reg_size; i++) {
8359c75c6bfSgovinda 		rp = (pci_regspec_t *)&regs_list[i * reg_size];
8369c75c6bfSgovinda 		addr_space = rp->pci_phys_hi & PCI_ADDR_MASK;
8379c75c6bfSgovinda 		offset = PCI_REG_REG_G(rp->pci_phys_hi);
8389c75c6bfSgovinda 
8399c75c6bfSgovinda 		if ((offset == breg) && ((addr_space == PCI_ADDR_MEM32) ||
8409c75c6bfSgovinda 		    (addr_space == PCI_ADDR_MEM64))) {
8419c75c6bfSgovinda 			rnumber = i;
8429c75c6bfSgovinda 			break;
8439c75c6bfSgovinda 		}
8449c75c6bfSgovinda 	}
8459c75c6bfSgovinda 
8469c75c6bfSgovinda 	DDI_INTR_NEXDBG((CE_CONT, "pci_msix_init: MSI-X rnum = %d\n", rnumber));
8479c75c6bfSgovinda 
8489c75c6bfSgovinda 	if (rnumber == 0) {
8499c75c6bfSgovinda 		DDI_INTR_NEXDBG((CE_CONT, "pci_msix_init: "
8509c75c6bfSgovinda 		    "no mtaching reg number for offset 0x%x\n", breg));
8519c75c6bfSgovinda 
8529c75c6bfSgovinda 		goto fail2;
8539c75c6bfSgovinda 	}
8549c75c6bfSgovinda 
8559c75c6bfSgovinda 	if ((ret = ddi_regs_map_setup(rdip, rnumber,
8569c75c6bfSgovinda 	    (caddr_t *)&msix_p->msix_tbl_addr, msix_p->msix_tbl_offset,
8579c75c6bfSgovinda 	    msix_tbl_size, &msix_p->msix_dev_attr,
8589c75c6bfSgovinda 	    &msix_p->msix_tbl_hdl)) != DDI_SUCCESS) {
8599c75c6bfSgovinda 		DDI_INTR_NEXDBG((CE_CONT, "pci_msix_init: MSI-X Table "
8609c75c6bfSgovinda 		    "ddi_regs_map_setup failed %d\n", ret));
8619c75c6bfSgovinda 
8629c75c6bfSgovinda 		goto fail2;
8637c478bd9Sstevel@tonic-gate 	}
8647c478bd9Sstevel@tonic-gate 
8657c478bd9Sstevel@tonic-gate 	/*
8667c478bd9Sstevel@tonic-gate 	 * Map in the MSI-X Pending Bit Array
8677c478bd9Sstevel@tonic-gate 	 */
8687e12ceb3SToomas Soome 	msix_p->msix_pba_offset = PCI_CAP_GET32(cfg_hdle, 0, caps_ptr,
869a7639048Sjohnny 	    PCI_MSIX_PBA_OFFSET);
8709c75c6bfSgovinda 
8719c75c6bfSgovinda 	if ((breg = pci_msix_bir_index[msix_p->msix_pba_offset &
8729c75c6bfSgovinda 	    PCI_MSIX_PBA_BIR_MASK]) == 0xff)
8739c75c6bfSgovinda 		goto fail3;
8749c75c6bfSgovinda 
8759c75c6bfSgovinda 	msix_p->msix_pba_offset = msix_p->msix_pba_offset &
8769c75c6bfSgovinda 	    ~PCI_MSIX_PBA_BIR_MASK;
8779c75c6bfSgovinda 	pba_tbl_size = ((msix_ctrl & PCI_MSIX_TBL_SIZE_MASK) + 1)/8;
8789c75c6bfSgovinda 
8799c75c6bfSgovinda 	DDI_INTR_NEXDBG((CE_CONT, "pci_msix_init: PBA table offset 0x%x "
8809c75c6bfSgovinda 	    "breg 0x%x size 0x%lx\n", msix_p->msix_pba_offset, breg,
8819c75c6bfSgovinda 	    pba_tbl_size));
8829c75c6bfSgovinda 
8839c75c6bfSgovinda 	for (i = 1, rnumber = 0; i < nregs/reg_size; i++) {
8849c75c6bfSgovinda 		rp = (pci_regspec_t *)&regs_list[i * reg_size];
8859c75c6bfSgovinda 		addr_space = rp->pci_phys_hi & PCI_ADDR_MASK;
8869c75c6bfSgovinda 		offset = PCI_REG_REG_G(rp->pci_phys_hi);
8879c75c6bfSgovinda 
8889c75c6bfSgovinda 		if ((offset == breg) && ((addr_space == PCI_ADDR_MEM32) ||
8899c75c6bfSgovinda 		    (addr_space == PCI_ADDR_MEM64))) {
8909c75c6bfSgovinda 			rnumber = i;
8919c75c6bfSgovinda 			break;
8929c75c6bfSgovinda 		}
8939c75c6bfSgovinda 	}
8949c75c6bfSgovinda 
8959c75c6bfSgovinda 	DDI_INTR_NEXDBG((CE_CONT, "pci_msix_init: PBA rnum = %d\n", rnumber));
8969c75c6bfSgovinda 
8979c75c6bfSgovinda 	if (rnumber == 0) {
8989c75c6bfSgovinda 		DDI_INTR_NEXDBG((CE_CONT, "pci_msix_init: "
89920036fe5Segillett 		    "no matching reg number for offset 0x%x\n", breg));
9009c75c6bfSgovinda 
9019c75c6bfSgovinda 		goto fail3;
9029c75c6bfSgovinda 	}
9039c75c6bfSgovinda 
9049c75c6bfSgovinda 	if ((ret = ddi_regs_map_setup(rdip, rnumber,
9059c75c6bfSgovinda 	    (caddr_t *)&msix_p->msix_pba_addr, msix_p->msix_pba_offset,
9069c75c6bfSgovinda 	    pba_tbl_size, &msix_p->msix_dev_attr,
9079c75c6bfSgovinda 	    &msix_p->msix_pba_hdl)) != DDI_SUCCESS) {
9089c75c6bfSgovinda 		DDI_INTR_NEXDBG((CE_CONT, "pci_msix_init: PBA "
9099c75c6bfSgovinda 		    "ddi_regs_map_setup failed %d\n", ret));
9109c75c6bfSgovinda 
9119c75c6bfSgovinda 		goto fail3;
9127c478bd9Sstevel@tonic-gate 	}
9137c478bd9Sstevel@tonic-gate 
9147c478bd9Sstevel@tonic-gate 	DDI_INTR_NEXDBG((CE_CONT, "pci_msix_init: msix_p = 0x%p DONE!!\n",
9157c478bd9Sstevel@tonic-gate 	    (void *)msix_p));
9167c478bd9Sstevel@tonic-gate 
917a7639048Sjohnny 	ddi_prop_free(regs_list);
9189c75c6bfSgovinda 	goto done;
9199c75c6bfSgovinda 
9209c75c6bfSgovinda fail3:
9219c75c6bfSgovinda 	ddi_regs_map_free(&msix_p->msix_tbl_hdl);
9229c75c6bfSgovinda fail2:
9239c75c6bfSgovinda 	ddi_prop_free(regs_list);
9249c75c6bfSgovinda fail1:
9259c75c6bfSgovinda 	kmem_free(msix_p, sizeof (ddi_intr_msix_t));
9269c75c6bfSgovinda 	msix_p = NULL;
9279c75c6bfSgovinda done:
9287c478bd9Sstevel@tonic-gate 	pci_config_teardown(&cfg_hdle);
9297c478bd9Sstevel@tonic-gate 	return (msix_p);
9307c478bd9Sstevel@tonic-gate }
9317c478bd9Sstevel@tonic-gate 
9327c478bd9Sstevel@tonic-gate 
9337c478bd9Sstevel@tonic-gate /*
9347c478bd9Sstevel@tonic-gate  * pci_msix_fini:
9357c478bd9Sstevel@tonic-gate  *	This function cleans up previously allocated handles/addrs etc.
9367c478bd9Sstevel@tonic-gate  *	It is only called if no more MSI-X interrupts are being used.
9377c478bd9Sstevel@tonic-gate  */
9387c478bd9Sstevel@tonic-gate void
pci_msix_fini(ddi_intr_msix_t * msix_p)9397c478bd9Sstevel@tonic-gate pci_msix_fini(ddi_intr_msix_t *msix_p)
9407c478bd9Sstevel@tonic-gate {
9417c478bd9Sstevel@tonic-gate 	DDI_INTR_NEXDBG((CE_CONT, "pci_msix_fini: msix_p = 0x%p\n",
9427c478bd9Sstevel@tonic-gate 	    (void *)msix_p));
9437c478bd9Sstevel@tonic-gate 
9447c478bd9Sstevel@tonic-gate 	ddi_regs_map_free(&msix_p->msix_pba_hdl);
9457c478bd9Sstevel@tonic-gate 	ddi_regs_map_free(&msix_p->msix_tbl_hdl);
9467c478bd9Sstevel@tonic-gate 	kmem_free(msix_p, sizeof (ddi_intr_msix_t));
9477c478bd9Sstevel@tonic-gate }
9487c478bd9Sstevel@tonic-gate 
9497c478bd9Sstevel@tonic-gate 
95020036fe5Segillett /*
95120036fe5Segillett  * pci_msix_dup:
95220036fe5Segillett  *	This function duplicates the address and data pair of one msi-x
95320036fe5Segillett  *	vector to another msi-x vector.
95420036fe5Segillett  */
95520036fe5Segillett int
pci_msix_dup(dev_info_t * rdip,int org_inum,int dup_inum)95620036fe5Segillett pci_msix_dup(dev_info_t *rdip, int org_inum, int dup_inum)
95720036fe5Segillett {
95820036fe5Segillett 	ddi_intr_msix_t	*msix_p = i_ddi_get_msix(rdip);
95920036fe5Segillett 	uint64_t	addr;
96020036fe5Segillett 	uint64_t	data;
96120036fe5Segillett 	uintptr_t	off;
96220036fe5Segillett 
96320036fe5Segillett 	DDI_INTR_NEXDBG((CE_CONT, "pci_msix_dup: dip = %p, inum = 0x%x, "
96420036fe5Segillett 	    "to_vector = 0x%x\n", (void *)rdip, org_inum, dup_inum));
96520036fe5Segillett 
96620036fe5Segillett 	/* Offset into the original inum's entry in the MSI-X table */
96720036fe5Segillett 	off = (uintptr_t)msix_p->msix_tbl_addr +
96820036fe5Segillett 	    (org_inum * PCI_MSIX_VECTOR_SIZE);
96920036fe5Segillett 
9708181b438SGarrett D'Amore 	/*
9718181b438SGarrett D'Amore 	 * For the MSI-X number passed in, get the "data" and "addr" fields.
9728181b438SGarrett D'Amore 	 *
9738181b438SGarrett D'Amore 	 * Note that the spec only requires 32-bit accesses to be supported.
9748181b438SGarrett D'Amore 	 * Apparently some chipsets don't support 64-bit accesses.
9758181b438SGarrett D'Amore 	 */
9768181b438SGarrett D'Amore 	addr = ddi_get32(msix_p->msix_tbl_hdl,
9778181b438SGarrett D'Amore 	    (uint32_t *)(off + PCI_MSIX_UPPER_ADDR_OFFSET));
9788181b438SGarrett D'Amore 	addr = (addr << 32) | ddi_get32(msix_p->msix_tbl_hdl,
9798181b438SGarrett D'Amore 	    (uint32_t *)(off + PCI_MSIX_LOWER_ADDR_OFFSET));
98020036fe5Segillett 
98120036fe5Segillett 	data = ddi_get32(msix_p->msix_tbl_hdl,
98220036fe5Segillett 	    (uint32_t *)(off + PCI_MSIX_DATA_OFFSET));
98320036fe5Segillett 
98420036fe5Segillett 	/* Program new vector with these existing values */
98520036fe5Segillett 	return (pci_msi_configure(rdip, DDI_INTR_TYPE_MSIX, 1, dup_inum, addr,
98620036fe5Segillett 	    data));
98720036fe5Segillett }
98820036fe5Segillett 
9897c478bd9Sstevel@tonic-gate 
9907c478bd9Sstevel@tonic-gate /*
9917c478bd9Sstevel@tonic-gate  * Next set of routines are for INTx (legacy) PCI interrupt
9927c478bd9Sstevel@tonic-gate  * support only.
9937c478bd9Sstevel@tonic-gate  */
9947c478bd9Sstevel@tonic-gate 
9957c478bd9Sstevel@tonic-gate /*
9967c478bd9Sstevel@tonic-gate  * pci_intx_get_cap:
9977c478bd9Sstevel@tonic-gate  *	For non-MSI devices that comply to PCI v2.3 or greater;
9987c478bd9Sstevel@tonic-gate  *	read the command register. Bit 10 implies interrupt disable.
9997c478bd9Sstevel@tonic-gate  *	Set this bit and then read the status register bit 3.
10007c478bd9Sstevel@tonic-gate  *	Bit 3 of status register is Interrupt state.
10017c478bd9Sstevel@tonic-gate  *	If it is set; then the device supports 'Masking'
10027c478bd9Sstevel@tonic-gate  *
10037c478bd9Sstevel@tonic-gate  *	Reset the device back to the original state.
10047c478bd9Sstevel@tonic-gate  */
10057c478bd9Sstevel@tonic-gate int
pci_intx_get_cap(dev_info_t * dip,int * flagsp)10067c478bd9Sstevel@tonic-gate pci_intx_get_cap(dev_info_t *dip, int *flagsp)
10077c478bd9Sstevel@tonic-gate {
10087c478bd9Sstevel@tonic-gate 	uint16_t		cmdreg, savereg;
10097c478bd9Sstevel@tonic-gate 	ddi_acc_handle_t	cfg_hdl;
10107c478bd9Sstevel@tonic-gate #ifdef	DEBUG
10117c478bd9Sstevel@tonic-gate 	uint16_t		statreg;
10127c478bd9Sstevel@tonic-gate #endif /* DEBUG */
10137c478bd9Sstevel@tonic-gate 
10147c478bd9Sstevel@tonic-gate 	*flagsp = 0;
10157c478bd9Sstevel@tonic-gate 	DDI_INTR_NEXDBG((CE_CONT, "pci_intx_get_cap: %s%d: called\n",
10167c478bd9Sstevel@tonic-gate 	    ddi_driver_name(dip), ddi_get_instance(dip)));
10177c478bd9Sstevel@tonic-gate 
10187c478bd9Sstevel@tonic-gate 	if (pci_config_setup(dip, &cfg_hdl) != DDI_SUCCESS) {
10197c478bd9Sstevel@tonic-gate 		DDI_INTR_NEXDBG((CE_CONT, "pci_intx_get_cap: can't get "
10207c478bd9Sstevel@tonic-gate 		    "config handle\n"));
10217c478bd9Sstevel@tonic-gate 		return (DDI_FAILURE);
10227c478bd9Sstevel@tonic-gate 	}
10237c478bd9Sstevel@tonic-gate 
10247c478bd9Sstevel@tonic-gate 	savereg = pci_config_get16(cfg_hdl, PCI_CONF_COMM);
10257c478bd9Sstevel@tonic-gate 	DDI_INTR_NEXDBG((CE_CONT, "pci_intx_get_cap: "
10267c478bd9Sstevel@tonic-gate 	    "command register was 0x%x\n", savereg));
10277c478bd9Sstevel@tonic-gate 
10287c478bd9Sstevel@tonic-gate 	/* Disable the interrupts */
10297c478bd9Sstevel@tonic-gate 	cmdreg = savereg | PCI_COMM_INTX_DISABLE;
10307c478bd9Sstevel@tonic-gate 	pci_config_put16(cfg_hdl, PCI_CONF_COMM, cmdreg);
10317c478bd9Sstevel@tonic-gate 
10327c478bd9Sstevel@tonic-gate #ifdef	DEBUG
10337c478bd9Sstevel@tonic-gate 	statreg = pci_config_get16(cfg_hdl, PCI_CONF_STAT);
10347c478bd9Sstevel@tonic-gate 	DDI_INTR_NEXDBG((CE_CONT, "pci_intx_get_cap: "
10357c478bd9Sstevel@tonic-gate 	    "status register is 0x%x\n", statreg));
10367c478bd9Sstevel@tonic-gate #endif /* DEBUG */
10377c478bd9Sstevel@tonic-gate 
10387c478bd9Sstevel@tonic-gate 	/* Read the bit back */
10397c478bd9Sstevel@tonic-gate 	cmdreg = pci_config_get16(cfg_hdl, PCI_CONF_COMM);
10407c478bd9Sstevel@tonic-gate 	DDI_INTR_NEXDBG((CE_CONT, "pci_intx_get_cap: "
10417c478bd9Sstevel@tonic-gate 	    "command register is now 0x%x\n", cmdreg));
10427c478bd9Sstevel@tonic-gate 
1043a195726fSgovinda 	*flagsp = DDI_INTR_FLAG_LEVEL;
1044a195726fSgovinda 
10457c478bd9Sstevel@tonic-gate 	if (cmdreg & PCI_COMM_INTX_DISABLE) {
10467c478bd9Sstevel@tonic-gate 		DDI_INTR_NEXDBG((CE_CONT, "pci_intx_get_cap: "
10477c478bd9Sstevel@tonic-gate 		    "masking supported\n"));
1048a195726fSgovinda 		*flagsp |= (DDI_INTR_FLAG_MASKABLE |
1049a195726fSgovinda 		    DDI_INTR_FLAG_PENDING);
10507c478bd9Sstevel@tonic-gate 	}
10517c478bd9Sstevel@tonic-gate 
10527c478bd9Sstevel@tonic-gate 	/* Restore the device back to the original state and return */
10537c478bd9Sstevel@tonic-gate 	pci_config_put16(cfg_hdl, PCI_CONF_COMM, savereg);
10547c478bd9Sstevel@tonic-gate 
10557c478bd9Sstevel@tonic-gate 	pci_config_teardown(&cfg_hdl);
10567c478bd9Sstevel@tonic-gate 	return (DDI_SUCCESS);
10577c478bd9Sstevel@tonic-gate }
10587c478bd9Sstevel@tonic-gate 
10597c478bd9Sstevel@tonic-gate 
10607c478bd9Sstevel@tonic-gate /*
10617c478bd9Sstevel@tonic-gate  * pci_intx_clr_mask:
10627c478bd9Sstevel@tonic-gate  *	For non-MSI devices that comply to PCI v2.3 or greater;
10637c478bd9Sstevel@tonic-gate  *	clear the bit10 in the command register.
10647c478bd9Sstevel@tonic-gate  */
10657c478bd9Sstevel@tonic-gate int
pci_intx_clr_mask(dev_info_t * dip)10667c478bd9Sstevel@tonic-gate pci_intx_clr_mask(dev_info_t *dip)
10677c478bd9Sstevel@tonic-gate {
10687c478bd9Sstevel@tonic-gate 	uint16_t		cmdreg;
10697c478bd9Sstevel@tonic-gate 	ddi_acc_handle_t	cfg_hdl;
10707c478bd9Sstevel@tonic-gate 
10717c478bd9Sstevel@tonic-gate 	DDI_INTR_NEXDBG((CE_CONT, "pci_intx_clr_mask: %s%d: called\n",
10727c478bd9Sstevel@tonic-gate 	    ddi_driver_name(dip), ddi_get_instance(dip)));
10737c478bd9Sstevel@tonic-gate 
10747c478bd9Sstevel@tonic-gate 	if (pci_config_setup(dip, &cfg_hdl) != DDI_SUCCESS) {
10757c478bd9Sstevel@tonic-gate 		DDI_INTR_NEXDBG((CE_CONT, "pci_intx_clr_mask: can't get "
10767c478bd9Sstevel@tonic-gate 		    "config handle\n"));
10777c478bd9Sstevel@tonic-gate 		return (DDI_FAILURE);
10787c478bd9Sstevel@tonic-gate 	}
10797c478bd9Sstevel@tonic-gate 
10807c478bd9Sstevel@tonic-gate 	cmdreg = pci_config_get16(cfg_hdl, PCI_CONF_COMM);
10817c478bd9Sstevel@tonic-gate 	DDI_INTR_NEXDBG((CE_CONT, "pci_intx_clr_mask: "
10827c478bd9Sstevel@tonic-gate 	    "command register was 0x%x\n", cmdreg));
10837c478bd9Sstevel@tonic-gate 
10847c478bd9Sstevel@tonic-gate 	/* Enable the interrupts */
10857c478bd9Sstevel@tonic-gate 	cmdreg &= ~PCI_COMM_INTX_DISABLE;
10867c478bd9Sstevel@tonic-gate 	pci_config_put16(cfg_hdl, PCI_CONF_COMM, cmdreg);
10877c478bd9Sstevel@tonic-gate 	pci_config_teardown(&cfg_hdl);
10887c478bd9Sstevel@tonic-gate 	return (DDI_SUCCESS);
10897c478bd9Sstevel@tonic-gate }
10907c478bd9Sstevel@tonic-gate 
10917c478bd9Sstevel@tonic-gate 
10927c478bd9Sstevel@tonic-gate /*
10937c478bd9Sstevel@tonic-gate  * pci_intx_set_mask:
10947c478bd9Sstevel@tonic-gate  *	For non-MSI devices that comply to PCI v2.3 or greater;
10957c478bd9Sstevel@tonic-gate  *	set the bit10 in the command register.
10967c478bd9Sstevel@tonic-gate  */
10977c478bd9Sstevel@tonic-gate int
pci_intx_set_mask(dev_info_t * dip)10987c478bd9Sstevel@tonic-gate pci_intx_set_mask(dev_info_t *dip)
10997c478bd9Sstevel@tonic-gate {
11007c478bd9Sstevel@tonic-gate 	uint16_t		cmdreg;
11017c478bd9Sstevel@tonic-gate 	ddi_acc_handle_t	cfg_hdl;
11027c478bd9Sstevel@tonic-gate 
11037c478bd9Sstevel@tonic-gate 	DDI_INTR_NEXDBG((CE_CONT, "pci_intx_set_mask: %s%d: called\n",
11047c478bd9Sstevel@tonic-gate 	    ddi_driver_name(dip), ddi_get_instance(dip)));
11057c478bd9Sstevel@tonic-gate 
11067c478bd9Sstevel@tonic-gate 	if (pci_config_setup(dip, &cfg_hdl) != DDI_SUCCESS) {
11077c478bd9Sstevel@tonic-gate 		DDI_INTR_NEXDBG((CE_CONT, "pci_intx_set_mask: can't get "
11087c478bd9Sstevel@tonic-gate 		    "config handle\n"));
11097c478bd9Sstevel@tonic-gate 		return (DDI_FAILURE);
11107c478bd9Sstevel@tonic-gate 	}
11117c478bd9Sstevel@tonic-gate 
11127c478bd9Sstevel@tonic-gate 	cmdreg = pci_config_get16(cfg_hdl, PCI_CONF_COMM);
11137c478bd9Sstevel@tonic-gate 	DDI_INTR_NEXDBG((CE_CONT, "pci_intx_set_mask: "
11147c478bd9Sstevel@tonic-gate 	    "command register was 0x%x\n", cmdreg));
11157c478bd9Sstevel@tonic-gate 
11167c478bd9Sstevel@tonic-gate 	/* Disable the interrupts */
11177c478bd9Sstevel@tonic-gate 	cmdreg |= PCI_COMM_INTX_DISABLE;
11187c478bd9Sstevel@tonic-gate 	pci_config_put16(cfg_hdl, PCI_CONF_COMM, cmdreg);
11197c478bd9Sstevel@tonic-gate 	pci_config_teardown(&cfg_hdl);
11207c478bd9Sstevel@tonic-gate 	return (DDI_SUCCESS);
11217c478bd9Sstevel@tonic-gate }
11227c478bd9Sstevel@tonic-gate 
11237c478bd9Sstevel@tonic-gate /*
11247c478bd9Sstevel@tonic-gate  * pci_intx_get_pending:
11257c478bd9Sstevel@tonic-gate  *	For non-MSI devices that comply to PCI v2.3 or greater;
11267c478bd9Sstevel@tonic-gate  *	read the status register. Bit 3 of status register is
11277c478bd9Sstevel@tonic-gate  *	Interrupt state. If it is set; then the interrupt is
11287c478bd9Sstevel@tonic-gate  *	'Pending'.
11297c478bd9Sstevel@tonic-gate  */
11307c478bd9Sstevel@tonic-gate int
pci_intx_get_pending(dev_info_t * dip,int * pendingp)11317c478bd9Sstevel@tonic-gate pci_intx_get_pending(dev_info_t *dip, int *pendingp)
11327c478bd9Sstevel@tonic-gate {
11337c478bd9Sstevel@tonic-gate 	uint16_t		statreg;
11347c478bd9Sstevel@tonic-gate 	ddi_acc_handle_t	cfg_hdl;
11357c478bd9Sstevel@tonic-gate 
11367c478bd9Sstevel@tonic-gate 	*pendingp = 0;
11377c478bd9Sstevel@tonic-gate 	DDI_INTR_NEXDBG((CE_CONT, "pci_intx_get_pending: %s%d: called\n",
11387c478bd9Sstevel@tonic-gate 	    ddi_driver_name(dip), ddi_get_instance(dip)));
11397c478bd9Sstevel@tonic-gate 
11407c478bd9Sstevel@tonic-gate 	if (pci_config_setup(dip, &cfg_hdl) != DDI_SUCCESS) {
11417c478bd9Sstevel@tonic-gate 		DDI_INTR_NEXDBG((CE_CONT, "pci_intx_get_pending: can't get "
11427c478bd9Sstevel@tonic-gate 		    "config handle\n"));
11437c478bd9Sstevel@tonic-gate 		return (DDI_FAILURE);
11447c478bd9Sstevel@tonic-gate 	}
11457c478bd9Sstevel@tonic-gate 
11467c478bd9Sstevel@tonic-gate 	statreg = pci_config_get16(cfg_hdl, PCI_CONF_STAT);
11477c478bd9Sstevel@tonic-gate 
11487c478bd9Sstevel@tonic-gate 	if (statreg & PCI_STAT_INTR) {
11497c478bd9Sstevel@tonic-gate 		DDI_INTR_NEXDBG((CE_CONT, "pci_intx_get_pending: "
11507c478bd9Sstevel@tonic-gate 		    "interrupt is pending\n"));
11517c478bd9Sstevel@tonic-gate 		*pendingp = 1;
11527c478bd9Sstevel@tonic-gate 	}
11537c478bd9Sstevel@tonic-gate 
11547c478bd9Sstevel@tonic-gate 	pci_config_teardown(&cfg_hdl);
11557c478bd9Sstevel@tonic-gate 	return (DDI_SUCCESS);
11567c478bd9Sstevel@tonic-gate }
11577c478bd9Sstevel@tonic-gate 
11587c478bd9Sstevel@tonic-gate 
11597c478bd9Sstevel@tonic-gate /*
11607c478bd9Sstevel@tonic-gate  * pci_intx_get_ispec:
11617c478bd9Sstevel@tonic-gate  *	Get intrspec for PCI devices (legacy support)
11627c478bd9Sstevel@tonic-gate  *	NOTE: This is moved here from x86 pci.c and is
11637c478bd9Sstevel@tonic-gate  *	needed here as pci-ide.c uses it as well
11647c478bd9Sstevel@tonic-gate  */
11657c478bd9Sstevel@tonic-gate /*ARGSUSED*/
11667c478bd9Sstevel@tonic-gate ddi_intrspec_t
pci_intx_get_ispec(dev_info_t * dip,dev_info_t * rdip,int inum)11677c478bd9Sstevel@tonic-gate pci_intx_get_ispec(dev_info_t *dip, dev_info_t *rdip, int inum)
11687c478bd9Sstevel@tonic-gate {
1169614edcaeSEvan Yan 	int				*intpriorities;
11707c478bd9Sstevel@tonic-gate 	uint_t				num_intpriorities;
11717c478bd9Sstevel@tonic-gate 	struct intrspec			*ispec;
11727c478bd9Sstevel@tonic-gate 	ddi_acc_handle_t		cfg_hdl;
11737c478bd9Sstevel@tonic-gate 	struct ddi_parent_private_data	*pdptr;
11747c478bd9Sstevel@tonic-gate 
11757c478bd9Sstevel@tonic-gate 	if ((pdptr = ddi_get_parent_data(rdip)) == NULL)
11767c478bd9Sstevel@tonic-gate 		return (NULL);
11777c478bd9Sstevel@tonic-gate 
11787c478bd9Sstevel@tonic-gate 	ispec = pdptr->par_intr;
11797c478bd9Sstevel@tonic-gate 	ASSERT(ispec);
11807c478bd9Sstevel@tonic-gate 
11817c478bd9Sstevel@tonic-gate 	/* check if the intrspec_pri has been initialized */
11827c478bd9Sstevel@tonic-gate 	if (!ispec->intrspec_pri) {
11837c478bd9Sstevel@tonic-gate 		if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, rdip,
11847c478bd9Sstevel@tonic-gate 		    DDI_PROP_DONTPASS, "interrupt-priorities",
11857c478bd9Sstevel@tonic-gate 		    &intpriorities, &num_intpriorities) == DDI_PROP_SUCCESS) {
11867c478bd9Sstevel@tonic-gate 			if (inum < num_intpriorities)
11877c478bd9Sstevel@tonic-gate 				ispec->intrspec_pri = intpriorities[inum];
11887c478bd9Sstevel@tonic-gate 			ddi_prop_free(intpriorities);
11897c478bd9Sstevel@tonic-gate 		}
11907c478bd9Sstevel@tonic-gate 
11917c478bd9Sstevel@tonic-gate 		/* If still no priority, guess based on the class code */
1192614edcaeSEvan Yan 		if (ispec->intrspec_pri == 0)
1193614edcaeSEvan Yan 			ispec->intrspec_pri = pci_class_to_pil(rdip);
11947c478bd9Sstevel@tonic-gate 	}
11957c478bd9Sstevel@tonic-gate 
11967c478bd9Sstevel@tonic-gate 	/* Get interrupt line value */
11977c478bd9Sstevel@tonic-gate 	if (!ispec->intrspec_vec) {
11987c478bd9Sstevel@tonic-gate 		if (pci_config_setup(rdip, &cfg_hdl) != DDI_SUCCESS) {
11997c478bd9Sstevel@tonic-gate 			DDI_INTR_NEXDBG((CE_CONT, "pci_intx_get_iline: "
12007c478bd9Sstevel@tonic-gate 			    "can't get config handle\n"));
12018d9a0e26Sanish 			return ((ddi_intrspec_t)ispec);
12027c478bd9Sstevel@tonic-gate 		}
12037c478bd9Sstevel@tonic-gate 
12047c478bd9Sstevel@tonic-gate 		ispec->intrspec_vec = pci_config_get8(cfg_hdl, PCI_CONF_ILINE);
12057c478bd9Sstevel@tonic-gate 		pci_config_teardown(&cfg_hdl);
12067c478bd9Sstevel@tonic-gate 	}
12077c478bd9Sstevel@tonic-gate 
12087c478bd9Sstevel@tonic-gate 	return ((ddi_intrspec_t)ispec);
12097c478bd9Sstevel@tonic-gate }
1210614edcaeSEvan Yan 
1211614edcaeSEvan Yan static uint32_t
pci_match_class_val(uint32_t key,pci_class_val_t * rec_p,int nrec,uint32_t default_val)1212614edcaeSEvan Yan pci_match_class_val(uint32_t key, pci_class_val_t *rec_p, int nrec,
1213614edcaeSEvan Yan     uint32_t default_val)
1214614edcaeSEvan Yan {
1215614edcaeSEvan Yan 	int i;
1216614edcaeSEvan Yan 
1217614edcaeSEvan Yan 	for (i = 0; i < nrec; rec_p++, i++) {
1218614edcaeSEvan Yan 		if ((rec_p->class_code & rec_p->class_mask) ==
1219614edcaeSEvan Yan 		    (key & rec_p->class_mask))
1220614edcaeSEvan Yan 			return (rec_p->class_val);
1221614edcaeSEvan Yan 	}
1222614edcaeSEvan Yan 
1223614edcaeSEvan Yan 	return (default_val);
1224614edcaeSEvan Yan }
1225614edcaeSEvan Yan 
1226614edcaeSEvan Yan /*
1227614edcaeSEvan Yan  * Return the configuration value, based on class code and sub class code,
1228614edcaeSEvan Yan  * from the specified property based or default pci_class_val_t table.
1229614edcaeSEvan Yan  */
1230614edcaeSEvan Yan 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)1231614edcaeSEvan Yan pci_class_to_val(dev_info_t *rdip, char *property_name, pci_class_val_t *rec_p,
1232614edcaeSEvan Yan     int nrec, uint32_t default_val)
1233614edcaeSEvan Yan {
1234614edcaeSEvan Yan 	int property_len;
1235614edcaeSEvan Yan 	uint32_t class_code;
1236614edcaeSEvan Yan 	pci_class_val_t *conf;
1237614edcaeSEvan Yan 	uint32_t val = default_val;
1238614edcaeSEvan Yan 
1239614edcaeSEvan Yan 	/*
1240614edcaeSEvan Yan 	 * Use the "class-code" property to get the base and sub class
1241614edcaeSEvan Yan 	 * codes for the requesting device.
1242614edcaeSEvan Yan 	 */
1243614edcaeSEvan Yan 	class_code = (uint32_t)ddi_prop_get_int(DDI_DEV_T_ANY, rdip,
1244614edcaeSEvan Yan 	    DDI_PROP_DONTPASS, "class-code", -1);
1245614edcaeSEvan Yan 
1246614edcaeSEvan Yan 	if (class_code == -1)
1247614edcaeSEvan Yan 		return (val);
1248614edcaeSEvan Yan 
1249614edcaeSEvan Yan 	/* look up the val from the default table */
1250614edcaeSEvan Yan 	val = pci_match_class_val(class_code, rec_p, nrec, val);
1251614edcaeSEvan Yan 
1252614edcaeSEvan Yan 
1253614edcaeSEvan Yan 	/* see if there is a more specific property specified value */
1254614edcaeSEvan Yan 	if (ddi_getlongprop(DDI_DEV_T_ANY, rdip, DDI_PROP_NOTPROM,
1255614edcaeSEvan Yan 	    property_name, (caddr_t)&conf, &property_len))
1256614edcaeSEvan Yan 			return (val);
1257614edcaeSEvan Yan 
1258614edcaeSEvan Yan 	if ((property_len % sizeof (pci_class_val_t)) == 0)
1259614edcaeSEvan Yan 		val = pci_match_class_val(class_code, conf,
1260614edcaeSEvan Yan 		    property_len / sizeof (pci_class_val_t), val);
1261614edcaeSEvan Yan 	kmem_free(conf, property_len);
1262614edcaeSEvan Yan 	return (val);
1263614edcaeSEvan Yan }
1264614edcaeSEvan Yan 
1265614edcaeSEvan Yan /*
1266614edcaeSEvan Yan  * pci_class_to_pil:
1267614edcaeSEvan Yan  *
1268614edcaeSEvan Yan  * Return the pil for a given PCI device.
1269614edcaeSEvan Yan  */
1270614edcaeSEvan Yan uint32_t
pci_class_to_pil(dev_info_t * rdip)1271614edcaeSEvan Yan pci_class_to_pil(dev_info_t *rdip)
1272614edcaeSEvan Yan {
1273614edcaeSEvan Yan 	uint32_t pil;
1274614edcaeSEvan Yan 
1275614edcaeSEvan Yan 	/* Default pil is 1 */
1276614edcaeSEvan Yan 	pil = pci_class_to_val(rdip,
1277614edcaeSEvan Yan 	    "pci-class-priorities", pci_default_pil,
1278614edcaeSEvan Yan 	    sizeof (pci_default_pil) / sizeof (pci_class_val_t), 1);
1279614edcaeSEvan Yan 
1280614edcaeSEvan Yan 	/* Range check the result */
1281614edcaeSEvan Yan 	if (pil >= 0xf)
1282614edcaeSEvan Yan 		pil = 1;
1283614edcaeSEvan Yan 
1284614edcaeSEvan Yan 	return (pil);
1285614edcaeSEvan Yan }
1286614edcaeSEvan Yan 
1287614edcaeSEvan Yan /*
1288614edcaeSEvan Yan  * pci_class_to_intr_weight:
1289614edcaeSEvan Yan  *
1290614edcaeSEvan Yan  * Return the intr_weight for a given PCI device.
1291614edcaeSEvan Yan  */
1292614edcaeSEvan Yan int32_t
pci_class_to_intr_weight(dev_info_t * rdip)1293614edcaeSEvan Yan pci_class_to_intr_weight(dev_info_t *rdip)
1294614edcaeSEvan Yan {
1295614edcaeSEvan Yan 	int32_t intr_weight;
1296614edcaeSEvan Yan 
1297614edcaeSEvan Yan 	/* default weight is 0% */
1298614edcaeSEvan Yan 	intr_weight = pci_class_to_val(rdip,
1299614edcaeSEvan Yan 	    "pci-class-intr-weights", pci_default_intr_weight,
1300614edcaeSEvan Yan 	    sizeof (pci_default_intr_weight) / sizeof (pci_class_val_t), 0);
1301614edcaeSEvan Yan 
1302614edcaeSEvan Yan 	/* range check the result */
1303614edcaeSEvan Yan 	if (intr_weight < 0)
1304614edcaeSEvan Yan 		intr_weight = 0;
1305614edcaeSEvan Yan 	if (intr_weight > 1000)
1306614edcaeSEvan Yan 		intr_weight = 1000;
1307614edcaeSEvan Yan 
1308614edcaeSEvan Yan 	return (intr_weight);
1309614edcaeSEvan Yan }
1310