xref: /titanic_41/usr/src/uts/common/io/pci_intr_lib.c (revision 1a95defd9ad2425f3c74030860dd9eb24a3e7930)
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.
24*f8c90ccaSGarrett 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 
397c478bd9Sstevel@tonic-gate /*
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  */
644f3b09fdSEvan Yan #if defined(__i386) || defined(__amd64)
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 
959c75c6bfSgovinda /*
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) &&
12027255037Spjha 	    (type == DDI_INTR_TYPE_MSI)) {
12127255037Spjha 		if ((*msi_ctrl = PCI_CAP_GET16(*h, NULL, *caps_ptr,
122dc5d169bSpjha 		    PCI_MSI_CTRL)) == PCI_CAP_EINVAL16)
12327255037Spjha 			goto done;
1247c478bd9Sstevel@tonic-gate 
1257c478bd9Sstevel@tonic-gate 		DDI_INTR_NEXDBG((CE_CONT, "pci_get_msi_ctrl: MSI "
1267c478bd9Sstevel@tonic-gate 		    "caps_ptr=%x msi_ctrl=%x\n", *caps_ptr, *msi_ctrl));
1277c478bd9Sstevel@tonic-gate 
1287c478bd9Sstevel@tonic-gate 		return (DDI_SUCCESS);
1297c478bd9Sstevel@tonic-gate 	}
1307c478bd9Sstevel@tonic-gate 
13127255037Spjha 	if ((PCI_CAP_LOCATE(*h, PCI_CAP_ID_MSI_X, caps_ptr) == DDI_SUCCESS) &&
13227255037Spjha 	    (type == DDI_INTR_TYPE_MSIX)) {
13327255037Spjha 		if ((*msi_ctrl = PCI_CAP_GET16(*h, NULL, *caps_ptr,
134dc5d169bSpjha 		    PCI_MSIX_CTRL)) == PCI_CAP_EINVAL16)
13527255037Spjha 			goto done;
1367c478bd9Sstevel@tonic-gate 
1377c478bd9Sstevel@tonic-gate 		DDI_INTR_NEXDBG((CE_CONT, "pci_get_msi_ctrl: MSI-X "
1387c478bd9Sstevel@tonic-gate 		    "caps_ptr=%x msi_ctrl=%x\n", *caps_ptr, *msi_ctrl));
1397c478bd9Sstevel@tonic-gate 
1407c478bd9Sstevel@tonic-gate 		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);
21727255037Spjha 		PCI_CAP_PUT16(h, NULL, caps_ptr, PCI_MSI_CTRL, msi_ctrl);
2187c478bd9Sstevel@tonic-gate 
2199c75c6bfSgovinda 		DDI_INTR_NEXDBG((CE_CONT, "pci_msi_configure: msi_ctrl = %x\n",
22027255037Spjha 		    PCI_CAP_GET16(h, NULL, caps_ptr, PCI_MSI_CTRL)));
2219c75c6bfSgovinda 
2227c478bd9Sstevel@tonic-gate 		/* Set the "data" and "addr" bits */
22327255037Spjha 		PCI_CAP_PUT32(h, NULL, caps_ptr, PCI_MSI_ADDR_OFFSET, addr);
2247c478bd9Sstevel@tonic-gate 
2259c75c6bfSgovinda 		DDI_INTR_NEXDBG((CE_CONT, "pci_msi_configure: msi_addr = %x\n",
22627255037Spjha 		    PCI_CAP_GET32(h, NULL, caps_ptr, PCI_MSI_ADDR_OFFSET)));
2279c75c6bfSgovinda 
2287c478bd9Sstevel@tonic-gate 		if (msi_ctrl &  PCI_MSI_64BIT_MASK) {
22927255037Spjha 			PCI_CAP_PUT32(h, NULL, caps_ptr, PCI_MSI_ADDR_OFFSET
23027255037Spjha 			    + 4, addr >> 32);
2319c75c6bfSgovinda 
23227255037Spjha 			DDI_INTR_NEXDBG((CE_CONT, "pci_msi_configure: upper "
23327255037Spjha 			    "32bit msi_addr = %x\n", PCI_CAP_GET32(h, NULL,
23427255037Spjha 			    caps_ptr, PCI_MSI_ADDR_OFFSET + 4)));
2359c75c6bfSgovinda 
23627255037Spjha 			PCI_CAP_PUT16(h, NULL, caps_ptr, PCI_MSI_64BIT_DATA,
23727255037Spjha 			    data);
2389c75c6bfSgovinda 
23927255037Spjha 			DDI_INTR_NEXDBG((CE_CONT, "pci_msi_configure: msi_data "
24027255037Spjha 			    "= %x\n", PCI_CAP_GET16(h, NULL, caps_ptr,
24127255037Spjha 			    PCI_MSI_64BIT_DATA)));
2427c478bd9Sstevel@tonic-gate 		} else {
24327255037Spjha 			PCI_CAP_PUT16(h, NULL, caps_ptr, PCI_MSI_32BIT_DATA,
24427255037Spjha 			    data);
2459c75c6bfSgovinda 
24627255037Spjha 			DDI_INTR_NEXDBG((CE_CONT, "pci_msi_configure: msi_data "
24727255037Spjha 			    "= %x\n", PCI_CAP_GET16(h, NULL, caps_ptr,
24827255037Spjha 			    PCI_MSI_32BIT_DATA)));
2497c478bd9Sstevel@tonic-gate 		}
2507c478bd9Sstevel@tonic-gate 	} else if (type == DDI_INTR_TYPE_MSIX) {
251abdbd06dSagiri 		uintptr_t	off;
2527c478bd9Sstevel@tonic-gate 		ddi_intr_msix_t	*msix_p = i_ddi_get_msix(rdip);
2537c478bd9Sstevel@tonic-gate 
2547c478bd9Sstevel@tonic-gate 		/* Offset into the "inum"th entry in the MSI-X table */
255abdbd06dSagiri 		off = (uintptr_t)msix_p->msix_tbl_addr +
2569c75c6bfSgovinda 		    (inum * PCI_MSIX_VECTOR_SIZE);
2577c478bd9Sstevel@tonic-gate 
2587c478bd9Sstevel@tonic-gate 		/* Set the "data" and "addr" bits */
2597c478bd9Sstevel@tonic-gate 		ddi_put32(msix_p->msix_tbl_hdl,
2609c75c6bfSgovinda 		    (uint32_t *)(off + PCI_MSIX_DATA_OFFSET), data);
2617c478bd9Sstevel@tonic-gate 
262*f8c90ccaSGarrett D'Amore 		/*
263*f8c90ccaSGarrett D'Amore 		 * Note that the spec only requires 32-bit accesses
264*f8c90ccaSGarrett D'Amore 		 * to be supported.  Apparently some chipsets don't
265*f8c90ccaSGarrett D'Amore 		 * support 64-bit accesses.
266*f8c90ccaSGarrett D'Amore 		 */
267*f8c90ccaSGarrett D'Amore 		ddi_put32(msix_p->msix_tbl_hdl,
268*f8c90ccaSGarrett D'Amore 		    (uint32_t *)(off + PCI_MSIX_LOWER_ADDR_OFFSET), addr);
269*f8c90ccaSGarrett D'Amore 		ddi_put32(msix_p->msix_tbl_hdl,
270*f8c90ccaSGarrett D'Amore 		    (uint32_t *)(off + PCI_MSIX_UPPER_ADDR_OFFSET),
271*f8c90ccaSGarrett D'Amore 		    addr >> 32);
2729c75c6bfSgovinda 
2739c75c6bfSgovinda 		DDI_INTR_NEXDBG((CE_CONT, "pci_msi_configure: "
274*f8c90ccaSGarrett D'Amore 		    "msix_addr 0x%x.%x msix_data 0x%x\n",
275*f8c90ccaSGarrett D'Amore 		    ddi_get32(msix_p->msix_tbl_hdl,
276*f8c90ccaSGarrett D'Amore 		    (uint32_t *)(off + PCI_MSIX_UPPER_ADDR_OFFSET)),
277*f8c90ccaSGarrett D'Amore 		    ddi_get32(msix_p->msix_tbl_hdl,
278*f8c90ccaSGarrett D'Amore 		    (uint32_t *)(off + PCI_MSIX_LOWER_ADDR_OFFSET)),
2799c75c6bfSgovinda 		    ddi_get32(msix_p->msix_tbl_hdl,
2809c75c6bfSgovinda 		    (uint32_t *)(off + PCI_MSIX_DATA_OFFSET))));
2817c478bd9Sstevel@tonic-gate 	}
2827c478bd9Sstevel@tonic-gate 
28327255037Spjha 	pci_config_teardown(&h);
2847c478bd9Sstevel@tonic-gate 	return (DDI_SUCCESS);
2857c478bd9Sstevel@tonic-gate }
2867c478bd9Sstevel@tonic-gate 
2877c478bd9Sstevel@tonic-gate 
2887c478bd9Sstevel@tonic-gate /*
2897c478bd9Sstevel@tonic-gate  * pci_msi_unconfigure:
2907c478bd9Sstevel@tonic-gate  *
2917c478bd9Sstevel@tonic-gate  * Unconfigure address/data and number MSI/Xs fields in the MSI/X
2927c478bd9Sstevel@tonic-gate  * capability structure.
2937c478bd9Sstevel@tonic-gate  */
2947c478bd9Sstevel@tonic-gate /* ARGSUSED */
2957c478bd9Sstevel@tonic-gate int
pci_msi_unconfigure(dev_info_t * rdip,int type,int inum)2967c478bd9Sstevel@tonic-gate pci_msi_unconfigure(dev_info_t *rdip, int type, int inum)
2977c478bd9Sstevel@tonic-gate {
2987c478bd9Sstevel@tonic-gate 	ushort_t		msi_ctrl, caps_ptr;
29927255037Spjha 	ddi_acc_handle_t	h;
3007c478bd9Sstevel@tonic-gate 
30195003185Segillett 	DDI_INTR_NEXDBG((CE_CONT, "pci_msi_unconfigure: rdip = 0x%p type 0x%x "
30295003185Segillett 	    "inum 0x%x\n", (void *)rdip, type, inum));
3037c478bd9Sstevel@tonic-gate 
30427255037Spjha 	if (pci_get_msi_ctrl(rdip, type, &msi_ctrl, &caps_ptr, &h) !=
30527255037Spjha 	    DDI_SUCCESS)
3067c478bd9Sstevel@tonic-gate 		return (DDI_FAILURE);
3077c478bd9Sstevel@tonic-gate 
3087c478bd9Sstevel@tonic-gate 	if (type == DDI_INTR_TYPE_MSI) {
3097c478bd9Sstevel@tonic-gate 		msi_ctrl &= (~PCI_MSI_MME_MASK);
31027255037Spjha 		PCI_CAP_PUT16(h, NULL, caps_ptr, PCI_MSI_CTRL, msi_ctrl);
3117c478bd9Sstevel@tonic-gate 
31227255037Spjha 		PCI_CAP_PUT32(h, NULL, caps_ptr, PCI_MSI_ADDR_OFFSET, 0);
31327255037Spjha 
3147c478bd9Sstevel@tonic-gate 		if (msi_ctrl &  PCI_MSI_64BIT_MASK) {
31527255037Spjha 			PCI_CAP_PUT16(h, NULL, caps_ptr, PCI_MSI_64BIT_DATA,
31627255037Spjha 			    0);
31727255037Spjha 			PCI_CAP_PUT32(h, NULL, caps_ptr, PCI_MSI_ADDR_OFFSET
31827255037Spjha 			    + 4, 0);
3197c478bd9Sstevel@tonic-gate 		} else {
32027255037Spjha 			PCI_CAP_PUT16(h, NULL, caps_ptr, PCI_MSI_32BIT_DATA,
32127255037Spjha 			    0);
3227c478bd9Sstevel@tonic-gate 		}
3237c478bd9Sstevel@tonic-gate 
32427255037Spjha 		DDI_INTR_NEXDBG((CE_CONT, "pci_msi_unconfigure: msi_ctrl "
32527255037Spjha 		    "= %x\n", PCI_CAP_GET16(h, NULL, caps_ptr, PCI_MSI_CTRL)));
3267c478bd9Sstevel@tonic-gate 
3277c478bd9Sstevel@tonic-gate 	} else if (type == DDI_INTR_TYPE_MSIX) {
328abdbd06dSagiri 		uintptr_t	off;
3297c478bd9Sstevel@tonic-gate 		ddi_intr_msix_t	*msix_p = i_ddi_get_msix(rdip);
3307c478bd9Sstevel@tonic-gate 
3317c478bd9Sstevel@tonic-gate 		/* Offset into the "inum"th entry in the MSI-X table */
332abdbd06dSagiri 		off = (uintptr_t)msix_p->msix_tbl_addr +
3339c75c6bfSgovinda 		    (inum * PCI_MSIX_VECTOR_SIZE);
3347c478bd9Sstevel@tonic-gate 
3357c478bd9Sstevel@tonic-gate 		/* Reset the "data" and "addr" bits */
3367c478bd9Sstevel@tonic-gate 		ddi_put32(msix_p->msix_tbl_hdl,
3379c75c6bfSgovinda 		    (uint32_t *)(off + PCI_MSIX_DATA_OFFSET), 0);
3387c478bd9Sstevel@tonic-gate 
339*f8c90ccaSGarrett D'Amore 		/*
340*f8c90ccaSGarrett D'Amore 		 * Note that the spec only requires 32-bit accesses
341*f8c90ccaSGarrett D'Amore 		 * to be supported.  Apparently some chipsets don't
342*f8c90ccaSGarrett D'Amore 		 * support 64-bit accesses.
343*f8c90ccaSGarrett D'Amore 		 */
344*f8c90ccaSGarrett D'Amore 		ddi_put32(msix_p->msix_tbl_hdl,
345*f8c90ccaSGarrett D'Amore 		    (uint32_t *)(off + PCI_MSIX_LOWER_ADDR_OFFSET), 0);
346*f8c90ccaSGarrett D'Amore 		ddi_put32(msix_p->msix_tbl_hdl,
347*f8c90ccaSGarrett D'Amore 		    (uint32_t *)(off + PCI_MSIX_UPPER_ADDR_OFFSET), 0);
3487c478bd9Sstevel@tonic-gate 	}
3497c478bd9Sstevel@tonic-gate 
35027255037Spjha 	pci_config_teardown(&h);
3517c478bd9Sstevel@tonic-gate 	return (DDI_SUCCESS);
3527c478bd9Sstevel@tonic-gate }
3537c478bd9Sstevel@tonic-gate 
3547c478bd9Sstevel@tonic-gate 
3557c478bd9Sstevel@tonic-gate /*
3567c478bd9Sstevel@tonic-gate  * pci_is_msi_enabled:
3577c478bd9Sstevel@tonic-gate  *
3587c478bd9Sstevel@tonic-gate  * This function returns DDI_SUCCESS if MSI/X is already enabled, otherwise
3597c478bd9Sstevel@tonic-gate  * it returns DDI_FAILURE.
3607c478bd9Sstevel@tonic-gate  */
3617c478bd9Sstevel@tonic-gate int
pci_is_msi_enabled(dev_info_t * rdip,int type)3627c478bd9Sstevel@tonic-gate pci_is_msi_enabled(dev_info_t *rdip, int type)
3637c478bd9Sstevel@tonic-gate {
3647c478bd9Sstevel@tonic-gate 	ushort_t		caps_ptr, msi_ctrl;
3657c478bd9Sstevel@tonic-gate 	ddi_acc_handle_t	cfg_hdle;
3667c478bd9Sstevel@tonic-gate 	int			ret = DDI_FAILURE;
3677c478bd9Sstevel@tonic-gate 
3687c478bd9Sstevel@tonic-gate 	DDI_INTR_NEXDBG((CE_CONT, "pci_is_msi_enabled: rdip = 0x%p, "
3697c478bd9Sstevel@tonic-gate 	    "type  = 0x%x\n", (void *)rdip, type));
3707c478bd9Sstevel@tonic-gate 
3717c478bd9Sstevel@tonic-gate 	if (pci_get_msi_ctrl(rdip, type, &msi_ctrl,
3727c478bd9Sstevel@tonic-gate 	    &caps_ptr, &cfg_hdle) != DDI_SUCCESS)
3737c478bd9Sstevel@tonic-gate 		return (DDI_FAILURE);
3747c478bd9Sstevel@tonic-gate 
3757c478bd9Sstevel@tonic-gate 	if ((type == DDI_INTR_TYPE_MSI) && (msi_ctrl & PCI_MSI_ENABLE_BIT))
3767c478bd9Sstevel@tonic-gate 		ret = DDI_SUCCESS;
3777c478bd9Sstevel@tonic-gate 
3787c478bd9Sstevel@tonic-gate 	if ((type == DDI_INTR_TYPE_MSIX) && (msi_ctrl & PCI_MSIX_ENABLE_BIT))
3797c478bd9Sstevel@tonic-gate 		ret = DDI_SUCCESS;
3807c478bd9Sstevel@tonic-gate 
3817c478bd9Sstevel@tonic-gate 	pci_config_teardown(&cfg_hdle);
3827c478bd9Sstevel@tonic-gate 	return (ret);
3837c478bd9Sstevel@tonic-gate }
3847c478bd9Sstevel@tonic-gate 
3857c478bd9Sstevel@tonic-gate 
3867c478bd9Sstevel@tonic-gate /*
3877c478bd9Sstevel@tonic-gate  * pci_msi_enable_mode:
3887c478bd9Sstevel@tonic-gate  *
3897c478bd9Sstevel@tonic-gate  * This function sets the MSI_ENABLE bit in the capability structure
3907c478bd9Sstevel@tonic-gate  * (for MSI) and MSIX_ENABLE bit in the MSI-X capability structure.
39195003185Segillett  *
39295003185Segillett  * NOTE: It is the nexus driver's responsibility to clear the MSI/X
39395003185Segillett  * interrupt's mask bit in the MSI/X capability structure before the
39495003185Segillett  * interrupt can be used.
3957c478bd9Sstevel@tonic-gate  */
3967c478bd9Sstevel@tonic-gate int
pci_msi_enable_mode(dev_info_t * rdip,int type)39795003185Segillett pci_msi_enable_mode(dev_info_t *rdip, int type)
3987c478bd9Sstevel@tonic-gate {
3997c478bd9Sstevel@tonic-gate 	ushort_t		caps_ptr, msi_ctrl;
4007c478bd9Sstevel@tonic-gate 	ddi_acc_handle_t	cfg_hdle;
4017c478bd9Sstevel@tonic-gate 
40295003185Segillett 	DDI_INTR_NEXDBG((CE_CONT, "pci_msi_enable_mode: rdip = 0x%p\n",
40395003185Segillett 	    (void *)rdip));
4047c478bd9Sstevel@tonic-gate 
4057c478bd9Sstevel@tonic-gate 	if (pci_get_msi_ctrl(rdip, type, &msi_ctrl,
4067c478bd9Sstevel@tonic-gate 	    &caps_ptr, &cfg_hdle) != DDI_SUCCESS)
4077c478bd9Sstevel@tonic-gate 		return (DDI_FAILURE);
4087c478bd9Sstevel@tonic-gate 
4097c478bd9Sstevel@tonic-gate 	if (type == DDI_INTR_TYPE_MSI) {
4107c478bd9Sstevel@tonic-gate 		if (msi_ctrl & PCI_MSI_ENABLE_BIT)
4117c478bd9Sstevel@tonic-gate 			goto finished;
4127c478bd9Sstevel@tonic-gate 
4137c478bd9Sstevel@tonic-gate 		msi_ctrl |= PCI_MSI_ENABLE_BIT;
41427255037Spjha 		PCI_CAP_PUT16(cfg_hdle, NULL, caps_ptr, PCI_MSI_CTRL, msi_ctrl);
4157c478bd9Sstevel@tonic-gate 
4167c478bd9Sstevel@tonic-gate 	} else if (type == DDI_INTR_TYPE_MSIX) {
4177c478bd9Sstevel@tonic-gate 		if (msi_ctrl & PCI_MSIX_ENABLE_BIT)
4187c478bd9Sstevel@tonic-gate 			goto finished;
4197c478bd9Sstevel@tonic-gate 
4207c478bd9Sstevel@tonic-gate 		msi_ctrl |= PCI_MSIX_ENABLE_BIT;
42127255037Spjha 		PCI_CAP_PUT16(cfg_hdle, NULL, caps_ptr, PCI_MSIX_CTRL,
42227255037Spjha 		    msi_ctrl);
4237c478bd9Sstevel@tonic-gate 	}
4247c478bd9Sstevel@tonic-gate 
4257c478bd9Sstevel@tonic-gate finished:
4267c478bd9Sstevel@tonic-gate 	DDI_INTR_NEXDBG((CE_CONT, "pci_msi_enable_mode: msi_ctrl = %x\n",
4277c478bd9Sstevel@tonic-gate 	    msi_ctrl));
4287c478bd9Sstevel@tonic-gate 
4297c478bd9Sstevel@tonic-gate 	pci_config_teardown(&cfg_hdle);
4307c478bd9Sstevel@tonic-gate 	return (DDI_SUCCESS);
4317c478bd9Sstevel@tonic-gate }
4327c478bd9Sstevel@tonic-gate 
4337c478bd9Sstevel@tonic-gate 
4347c478bd9Sstevel@tonic-gate /*
4357c478bd9Sstevel@tonic-gate  * pci_msi_disable_mode:
4367c478bd9Sstevel@tonic-gate  *
4377c478bd9Sstevel@tonic-gate  * This function resets the MSI_ENABLE bit in the capability structure
4387c478bd9Sstevel@tonic-gate  * (for MSI) and MSIX_ENABLE bit in the MSI-X capability structure.
43995003185Segillett  *
44095003185Segillett  * NOTE: It is the nexus driver's responsibility to set the MSI/X
44195003185Segillett  * interrupt's mask bit in the MSI/X capability structure before the
44295003185Segillett  * interrupt can be disabled.
4437c478bd9Sstevel@tonic-gate  */
4447c478bd9Sstevel@tonic-gate int
pci_msi_disable_mode(dev_info_t * rdip,int type)44509b1eac2SEvan Yan pci_msi_disable_mode(dev_info_t *rdip, int type)
4467c478bd9Sstevel@tonic-gate {
4477c478bd9Sstevel@tonic-gate 	ushort_t		caps_ptr, msi_ctrl;
4487c478bd9Sstevel@tonic-gate 	ddi_acc_handle_t	cfg_hdle;
4497c478bd9Sstevel@tonic-gate 
45009b1eac2SEvan Yan 	DDI_INTR_NEXDBG((CE_CONT, "pci_msi_disable_mode: rdip = 0x%p\n",
45109b1eac2SEvan Yan 	    (void *)rdip));
4527c478bd9Sstevel@tonic-gate 
4537c478bd9Sstevel@tonic-gate 	if (pci_get_msi_ctrl(rdip, type, &msi_ctrl,
4547c478bd9Sstevel@tonic-gate 	    &caps_ptr, &cfg_hdle) != DDI_SUCCESS)
4557c478bd9Sstevel@tonic-gate 		return (DDI_FAILURE);
4567c478bd9Sstevel@tonic-gate 
4577c478bd9Sstevel@tonic-gate 	/* Reset the "enable" bit */
4587c478bd9Sstevel@tonic-gate 	if (type == DDI_INTR_TYPE_MSI) {
4597c478bd9Sstevel@tonic-gate 		if (!(msi_ctrl & PCI_MSI_ENABLE_BIT))
4607c478bd9Sstevel@tonic-gate 			goto finished;
4617c478bd9Sstevel@tonic-gate 		msi_ctrl &= ~PCI_MSI_ENABLE_BIT;
46227255037Spjha 		PCI_CAP_PUT16(cfg_hdle, NULL, caps_ptr, PCI_MSI_CTRL, msi_ctrl);
4637c478bd9Sstevel@tonic-gate 	} else if (type == DDI_INTR_TYPE_MSIX) {
4647c478bd9Sstevel@tonic-gate 		if (!(msi_ctrl & PCI_MSIX_ENABLE_BIT))
4657c478bd9Sstevel@tonic-gate 			goto finished;
4667c478bd9Sstevel@tonic-gate 
46795003185Segillett 		msi_ctrl &= ~PCI_MSIX_ENABLE_BIT;
46895003185Segillett 		PCI_CAP_PUT16(cfg_hdle, NULL, caps_ptr, PCI_MSIX_CTRL,
46995003185Segillett 		    msi_ctrl);
4707c478bd9Sstevel@tonic-gate 	}
4717c478bd9Sstevel@tonic-gate 
4727c478bd9Sstevel@tonic-gate finished:
4737c478bd9Sstevel@tonic-gate 	DDI_INTR_NEXDBG((CE_CONT, "pci_msi_disable_mode: msi_ctrl = %x\n",
4747c478bd9Sstevel@tonic-gate 	    msi_ctrl));
4757c478bd9Sstevel@tonic-gate 
4767c478bd9Sstevel@tonic-gate 	pci_config_teardown(&cfg_hdle);
4777c478bd9Sstevel@tonic-gate 	return (DDI_SUCCESS);
4787c478bd9Sstevel@tonic-gate }
4797c478bd9Sstevel@tonic-gate 
4807c478bd9Sstevel@tonic-gate 
4817c478bd9Sstevel@tonic-gate /*
4827c478bd9Sstevel@tonic-gate  * pci_msi_set_mask:
4837c478bd9Sstevel@tonic-gate  *
4847c478bd9Sstevel@tonic-gate  * Set the mask bit in the MSI/X capability structure
4857c478bd9Sstevel@tonic-gate  */
4867c478bd9Sstevel@tonic-gate /* ARGSUSED */
4877c478bd9Sstevel@tonic-gate int
pci_msi_set_mask(dev_info_t * rdip,int type,int inum)4887c478bd9Sstevel@tonic-gate pci_msi_set_mask(dev_info_t *rdip, int type, int inum)
4897c478bd9Sstevel@tonic-gate {
4907c478bd9Sstevel@tonic-gate 	int			offset;
4917c478bd9Sstevel@tonic-gate 	int			ret = DDI_FAILURE;
4927c478bd9Sstevel@tonic-gate 	ushort_t		caps_ptr, msi_ctrl;
4937c478bd9Sstevel@tonic-gate 	ddi_acc_handle_t	cfg_hdle;
4949c75c6bfSgovinda 	uint32_t		mask_bits;
4957c478bd9Sstevel@tonic-gate 
4967c478bd9Sstevel@tonic-gate 	DDI_INTR_NEXDBG((CE_CONT, "pci_msi_set_mask: rdip = 0x%p, "
4977c478bd9Sstevel@tonic-gate 	    "type = 0x%x\n", (void *)rdip, type));
4987c478bd9Sstevel@tonic-gate 
4997c478bd9Sstevel@tonic-gate 	if (pci_get_msi_ctrl(rdip, type, &msi_ctrl,
5007c478bd9Sstevel@tonic-gate 	    &caps_ptr, &cfg_hdle) != DDI_SUCCESS)
5017c478bd9Sstevel@tonic-gate 		return (DDI_FAILURE);
5027c478bd9Sstevel@tonic-gate 
5037c478bd9Sstevel@tonic-gate 	if (type == DDI_INTR_TYPE_MSI) {
5047c478bd9Sstevel@tonic-gate 		if (!(msi_ctrl &  PCI_MSI_PVM_MASK))
5057c478bd9Sstevel@tonic-gate 			goto done;
5067c478bd9Sstevel@tonic-gate 
5077c478bd9Sstevel@tonic-gate 		offset = (msi_ctrl &  PCI_MSI_64BIT_MASK) ?
5087c478bd9Sstevel@tonic-gate 		    PCI_MSI_64BIT_MASKBITS : PCI_MSI_32BIT_MASK;
5097c478bd9Sstevel@tonic-gate 
51027255037Spjha 		if ((mask_bits = PCI_CAP_GET32(cfg_hdle, NULL, caps_ptr,
511dc5d169bSpjha 		    offset)) == PCI_CAP_EINVAL32)
51227255037Spjha 			goto done;
5137c478bd9Sstevel@tonic-gate 
5147c478bd9Sstevel@tonic-gate 		mask_bits |= (1 << inum);
5157c478bd9Sstevel@tonic-gate 
51627255037Spjha 		PCI_CAP_PUT32(cfg_hdle, NULL, caps_ptr, offset, mask_bits);
5177c478bd9Sstevel@tonic-gate 
5187c478bd9Sstevel@tonic-gate 	} else if (type == DDI_INTR_TYPE_MSIX) {
519abdbd06dSagiri 		uintptr_t		off;
5207c478bd9Sstevel@tonic-gate 		ddi_intr_msix_t		*msix_p;
5217c478bd9Sstevel@tonic-gate 
5227c478bd9Sstevel@tonic-gate 		/* Set function mask */
5237c478bd9Sstevel@tonic-gate 		if (msi_ctrl & PCI_MSIX_FUNCTION_MASK) {
5247c478bd9Sstevel@tonic-gate 			ret = DDI_SUCCESS;
5257c478bd9Sstevel@tonic-gate 			goto done;
5267c478bd9Sstevel@tonic-gate 		}
5277c478bd9Sstevel@tonic-gate 
5287c478bd9Sstevel@tonic-gate 		msix_p = i_ddi_get_msix(rdip);
5297c478bd9Sstevel@tonic-gate 
5307c478bd9Sstevel@tonic-gate 		/* Offset into the "inum"th entry in the MSI-X table */
5319c75c6bfSgovinda 		off = (uintptr_t)msix_p->msix_tbl_addr + (inum *
5327c478bd9Sstevel@tonic-gate 		    PCI_MSIX_VECTOR_SIZE) + PCI_MSIX_VECTOR_CTRL_OFFSET;
5337c478bd9Sstevel@tonic-gate 
5347c478bd9Sstevel@tonic-gate 		/* Set the Mask bit */
5359c75c6bfSgovinda 		ddi_put32(msix_p->msix_tbl_hdl, (uint32_t *)off, 0x1);
5367c478bd9Sstevel@tonic-gate 	}
5377c478bd9Sstevel@tonic-gate 
5387c478bd9Sstevel@tonic-gate 	ret = DDI_SUCCESS;
5397c478bd9Sstevel@tonic-gate done:
5407c478bd9Sstevel@tonic-gate 	pci_config_teardown(&cfg_hdle);
5417c478bd9Sstevel@tonic-gate 	return (ret);
5427c478bd9Sstevel@tonic-gate }
5437c478bd9Sstevel@tonic-gate 
5447c478bd9Sstevel@tonic-gate 
5457c478bd9Sstevel@tonic-gate /*
5467c478bd9Sstevel@tonic-gate  * pci_msi_clr_mask:
5477c478bd9Sstevel@tonic-gate  *
5487c478bd9Sstevel@tonic-gate  * Clear the mask bit in the MSI/X capability structure
5497c478bd9Sstevel@tonic-gate  */
5507c478bd9Sstevel@tonic-gate /* ARGSUSED */
5517c478bd9Sstevel@tonic-gate int
pci_msi_clr_mask(dev_info_t * rdip,int type,int inum)5527c478bd9Sstevel@tonic-gate pci_msi_clr_mask(dev_info_t *rdip, int type, int inum)
5537c478bd9Sstevel@tonic-gate {
5547c478bd9Sstevel@tonic-gate 	ushort_t		caps_ptr, msi_ctrl;
5557c478bd9Sstevel@tonic-gate 	ddi_acc_handle_t	cfg_hdle;
5567c478bd9Sstevel@tonic-gate 	int			offset;
5577c478bd9Sstevel@tonic-gate 	int			ret = DDI_FAILURE;
5589c75c6bfSgovinda 	uint32_t		mask_bits;
5597c478bd9Sstevel@tonic-gate 
5607c478bd9Sstevel@tonic-gate 	DDI_INTR_NEXDBG((CE_CONT, "pci_msi_clr_mask: rdip = 0x%p, "
5617c478bd9Sstevel@tonic-gate 	    "type = 0x%x\n", (void *)rdip, type));
5627c478bd9Sstevel@tonic-gate 
5637c478bd9Sstevel@tonic-gate 	if (pci_get_msi_ctrl(rdip, type, &msi_ctrl,
5647c478bd9Sstevel@tonic-gate 	    &caps_ptr, &cfg_hdle) != DDI_SUCCESS)
5657c478bd9Sstevel@tonic-gate 		return (DDI_FAILURE);
5667c478bd9Sstevel@tonic-gate 
5677c478bd9Sstevel@tonic-gate 	if (type == DDI_INTR_TYPE_MSI) {
5687c478bd9Sstevel@tonic-gate 		if (!(msi_ctrl &  PCI_MSI_PVM_MASK))
5697c478bd9Sstevel@tonic-gate 			goto done;
5707c478bd9Sstevel@tonic-gate 
5717c478bd9Sstevel@tonic-gate 		offset = (msi_ctrl &  PCI_MSI_64BIT_MASK) ?
5727c478bd9Sstevel@tonic-gate 		    PCI_MSI_64BIT_MASKBITS : PCI_MSI_32BIT_MASK;
57327255037Spjha 		if ((mask_bits = PCI_CAP_GET32(cfg_hdle, NULL, caps_ptr,
574dc5d169bSpjha 		    offset)) == PCI_CAP_EINVAL32)
57527255037Spjha 			goto done;
5767c478bd9Sstevel@tonic-gate 
5777c478bd9Sstevel@tonic-gate 		mask_bits &= ~(1 << inum);
5787c478bd9Sstevel@tonic-gate 
57927255037Spjha 		PCI_CAP_PUT32(cfg_hdle, NULL, caps_ptr, offset, mask_bits);
5807c478bd9Sstevel@tonic-gate 
5817c478bd9Sstevel@tonic-gate 	} else if (type == DDI_INTR_TYPE_MSIX) {
582abdbd06dSagiri 		uintptr_t		off;
5837c478bd9Sstevel@tonic-gate 		ddi_intr_msix_t		*msix_p;
5847c478bd9Sstevel@tonic-gate 
5857c478bd9Sstevel@tonic-gate 		if (msi_ctrl & PCI_MSIX_FUNCTION_MASK) {
5867c478bd9Sstevel@tonic-gate 			ret = DDI_SUCCESS;
5877c478bd9Sstevel@tonic-gate 			goto done;
5887c478bd9Sstevel@tonic-gate 		}
5897c478bd9Sstevel@tonic-gate 
5907c478bd9Sstevel@tonic-gate 		msix_p = i_ddi_get_msix(rdip);
5917c478bd9Sstevel@tonic-gate 
5927c478bd9Sstevel@tonic-gate 		/* Offset into the "inum"th entry in the MSI-X table */
5939c75c6bfSgovinda 		off = (uintptr_t)msix_p->msix_tbl_addr + (inum *
5947c478bd9Sstevel@tonic-gate 		    PCI_MSIX_VECTOR_SIZE) + PCI_MSIX_VECTOR_CTRL_OFFSET;
5957c478bd9Sstevel@tonic-gate 
5967c478bd9Sstevel@tonic-gate 		/* Clear the Mask bit */
5979c75c6bfSgovinda 		ddi_put32(msix_p->msix_tbl_hdl, (uint32_t *)off, 0x0);
5987c478bd9Sstevel@tonic-gate 	}
5997c478bd9Sstevel@tonic-gate 
6007c478bd9Sstevel@tonic-gate 	ret = DDI_SUCCESS;
6017c478bd9Sstevel@tonic-gate done:
6027c478bd9Sstevel@tonic-gate 	pci_config_teardown(&cfg_hdle);
6037c478bd9Sstevel@tonic-gate 	return (ret);
6047c478bd9Sstevel@tonic-gate }
6057c478bd9Sstevel@tonic-gate 
6067c478bd9Sstevel@tonic-gate 
6077c478bd9Sstevel@tonic-gate /*
6087c478bd9Sstevel@tonic-gate  * pci_msi_get_pending:
6097c478bd9Sstevel@tonic-gate  *
6107c478bd9Sstevel@tonic-gate  * Get the pending bit from the MSI/X capability structure
6117c478bd9Sstevel@tonic-gate  */
6127c478bd9Sstevel@tonic-gate /* ARGSUSED */
6137c478bd9Sstevel@tonic-gate int
pci_msi_get_pending(dev_info_t * rdip,int type,int inum,int * pendingp)6147c478bd9Sstevel@tonic-gate pci_msi_get_pending(dev_info_t *rdip, int type, int inum, int *pendingp)
6157c478bd9Sstevel@tonic-gate {
6167c478bd9Sstevel@tonic-gate 	ushort_t		caps_ptr, msi_ctrl;
6177c478bd9Sstevel@tonic-gate 	ddi_acc_handle_t	cfg_hdle;
6187c478bd9Sstevel@tonic-gate 	int			offset;
6197c478bd9Sstevel@tonic-gate 	int			ret = DDI_FAILURE;
6207c478bd9Sstevel@tonic-gate 
6217c478bd9Sstevel@tonic-gate 	DDI_INTR_NEXDBG((CE_CONT, "pci_msi_get_pending: rdip = 0x%p\n",
6227c478bd9Sstevel@tonic-gate 	    (void *)rdip));
6237c478bd9Sstevel@tonic-gate 
6247c478bd9Sstevel@tonic-gate 	if (pci_get_msi_ctrl(rdip, type, &msi_ctrl,
6257c478bd9Sstevel@tonic-gate 	    &caps_ptr, &cfg_hdle) != DDI_SUCCESS)
6267c478bd9Sstevel@tonic-gate 		return (DDI_FAILURE);
6277c478bd9Sstevel@tonic-gate 
6287c478bd9Sstevel@tonic-gate 	if (type == DDI_INTR_TYPE_MSI) {
6297c478bd9Sstevel@tonic-gate 		uint32_t	pending_bits;
6307c478bd9Sstevel@tonic-gate 
6317c478bd9Sstevel@tonic-gate 		if (!(msi_ctrl &  PCI_MSI_PVM_MASK)) {
6327c478bd9Sstevel@tonic-gate 			DDI_INTR_NEXDBG((CE_CONT, "pci_msi_get_pending: "
6337c478bd9Sstevel@tonic-gate 			    "PVM is not supported\n"));
6347c478bd9Sstevel@tonic-gate 			goto done;
6357c478bd9Sstevel@tonic-gate 		}
6367c478bd9Sstevel@tonic-gate 
6377c478bd9Sstevel@tonic-gate 		offset = (msi_ctrl &  PCI_MSI_64BIT_MASK) ?
6387c478bd9Sstevel@tonic-gate 		    PCI_MSI_64BIT_PENDING : PCI_MSI_32BIT_PENDING;
6397c478bd9Sstevel@tonic-gate 
64027255037Spjha 		if ((pending_bits = PCI_CAP_GET32(cfg_hdle, NULL, caps_ptr,
641dc5d169bSpjha 		    offset)) == PCI_CAP_EINVAL32)
64227255037Spjha 			goto done;
6437c478bd9Sstevel@tonic-gate 
6447c478bd9Sstevel@tonic-gate 		*pendingp = pending_bits & ~(1 >> inum);
6457c478bd9Sstevel@tonic-gate 
6467c478bd9Sstevel@tonic-gate 	} else if (type == DDI_INTR_TYPE_MSIX) {
647abdbd06dSagiri 		uintptr_t	off;
6487c478bd9Sstevel@tonic-gate 		uint64_t	pending_bits;
6497c478bd9Sstevel@tonic-gate 		ddi_intr_msix_t	*msix_p = i_ddi_get_msix(rdip);
6507c478bd9Sstevel@tonic-gate 
6517c478bd9Sstevel@tonic-gate 		/* Offset into the PBA array which has entry for "inum" */
6529c75c6bfSgovinda 		off = (uintptr_t)msix_p->msix_pba_addr + (inum / 64);
6537c478bd9Sstevel@tonic-gate 
6547c478bd9Sstevel@tonic-gate 		/* Read the PBA array */
6559c75c6bfSgovinda 		pending_bits = ddi_get64(msix_p->msix_pba_hdl, (uint64_t *)off);
6567c478bd9Sstevel@tonic-gate 
6577c478bd9Sstevel@tonic-gate 		*pendingp = pending_bits & ~(1 >> inum);
6587c478bd9Sstevel@tonic-gate 	}
6597c478bd9Sstevel@tonic-gate 
6607c478bd9Sstevel@tonic-gate 	ret = DDI_SUCCESS;
6617c478bd9Sstevel@tonic-gate done:
6627c478bd9Sstevel@tonic-gate 	pci_config_teardown(&cfg_hdle);
6637c478bd9Sstevel@tonic-gate 	return (ret);
6647c478bd9Sstevel@tonic-gate }
6657c478bd9Sstevel@tonic-gate 
6667c478bd9Sstevel@tonic-gate 
6677c478bd9Sstevel@tonic-gate /*
6687c478bd9Sstevel@tonic-gate  * pci_msi_get_nintrs:
6697c478bd9Sstevel@tonic-gate  *
6707c478bd9Sstevel@tonic-gate  * For a given type (MSI/X) returns the number of interrupts supported
6717c478bd9Sstevel@tonic-gate  */
6727c478bd9Sstevel@tonic-gate int
pci_msi_get_nintrs(dev_info_t * rdip,int type,int * nintrs)6737c478bd9Sstevel@tonic-gate pci_msi_get_nintrs(dev_info_t *rdip, int type, int *nintrs)
6747c478bd9Sstevel@tonic-gate {
6757c478bd9Sstevel@tonic-gate 	ushort_t		caps_ptr, msi_ctrl;
6767c478bd9Sstevel@tonic-gate 	ddi_acc_handle_t	cfg_hdle;
6777c478bd9Sstevel@tonic-gate 
6787c478bd9Sstevel@tonic-gate 	DDI_INTR_NEXDBG((CE_CONT, "pci_msi_get_nintrs: rdip = 0x%p\n",
6797c478bd9Sstevel@tonic-gate 	    (void *)rdip));
6807c478bd9Sstevel@tonic-gate 
6817c478bd9Sstevel@tonic-gate 	if (pci_get_msi_ctrl(rdip, type, &msi_ctrl,
6827c478bd9Sstevel@tonic-gate 	    &caps_ptr, &cfg_hdle) != DDI_SUCCESS)
6837c478bd9Sstevel@tonic-gate 		return (DDI_FAILURE);
6847c478bd9Sstevel@tonic-gate 
6857c478bd9Sstevel@tonic-gate 	if (type == DDI_INTR_TYPE_MSI) {
6867c478bd9Sstevel@tonic-gate 		*nintrs = 1 << ((msi_ctrl & PCI_MSI_MMC_MASK) >>
6877c478bd9Sstevel@tonic-gate 		    PCI_MSI_MMC_SHIFT);
6887c478bd9Sstevel@tonic-gate 	} else if (type == DDI_INTR_TYPE_MSIX) {
6897c478bd9Sstevel@tonic-gate 		if (msi_ctrl &  PCI_MSIX_TBL_SIZE_MASK)
6907c478bd9Sstevel@tonic-gate 			*nintrs = (msi_ctrl & PCI_MSIX_TBL_SIZE_MASK) + 1;
6917c478bd9Sstevel@tonic-gate 	}
6927c478bd9Sstevel@tonic-gate 
6937c478bd9Sstevel@tonic-gate 	DDI_INTR_NEXDBG((CE_CONT, "pci_msi_get_nintrs: "
6947c478bd9Sstevel@tonic-gate 	    "nintr = 0x%x\n", *nintrs));
6957c478bd9Sstevel@tonic-gate 
6967c478bd9Sstevel@tonic-gate 	pci_config_teardown(&cfg_hdle);
6977c478bd9Sstevel@tonic-gate 	return (DDI_SUCCESS);
6987c478bd9Sstevel@tonic-gate }
6997c478bd9Sstevel@tonic-gate 
7007c478bd9Sstevel@tonic-gate 
7017c478bd9Sstevel@tonic-gate /*
7027c478bd9Sstevel@tonic-gate  * pci_msi_set_nintrs:
7037c478bd9Sstevel@tonic-gate  *
7047c478bd9Sstevel@tonic-gate  * For a given type (MSI/X) sets the number of interrupts supported
7057c478bd9Sstevel@tonic-gate  * by the system.
7067c478bd9Sstevel@tonic-gate  * For MSI: Return an error if this func is called for navail > 32
7077c478bd9Sstevel@tonic-gate  * For MSI-X: Return an error if this func is called for navail > 2048
7087c478bd9Sstevel@tonic-gate  */
7097c478bd9Sstevel@tonic-gate int
pci_msi_set_nintrs(dev_info_t * rdip,int type,int navail)7107c478bd9Sstevel@tonic-gate pci_msi_set_nintrs(dev_info_t *rdip, int type, int navail)
7117c478bd9Sstevel@tonic-gate {
7127c478bd9Sstevel@tonic-gate 	ushort_t		caps_ptr, msi_ctrl;
7137c478bd9Sstevel@tonic-gate 	ddi_acc_handle_t	cfg_hdle;
7147c478bd9Sstevel@tonic-gate 
7157c478bd9Sstevel@tonic-gate 	DDI_INTR_NEXDBG((CE_CONT, "pci_msi_set_nintrs: rdip = 0x%p, "
7167c478bd9Sstevel@tonic-gate 	    "navail = 0x%x\n", (void *)rdip, navail));
7177c478bd9Sstevel@tonic-gate 
7187c478bd9Sstevel@tonic-gate 	/* Check for valid input argument */
7197c478bd9Sstevel@tonic-gate 	if (((type == DDI_INTR_TYPE_MSI) && (navail > PCI_MSI_MAX_INTRS)) ||
7207c478bd9Sstevel@tonic-gate 	    ((type == DDI_INTR_TYPE_MSIX) && (navail >  PCI_MSIX_MAX_INTRS)))
7217c478bd9Sstevel@tonic-gate 		return (DDI_EINVAL);
7227c478bd9Sstevel@tonic-gate 
7237c478bd9Sstevel@tonic-gate 	if (pci_get_msi_ctrl(rdip, type, &msi_ctrl,
7247c478bd9Sstevel@tonic-gate 	    &caps_ptr, &cfg_hdle) != DDI_SUCCESS)
7257c478bd9Sstevel@tonic-gate 		return (DDI_FAILURE);
7267c478bd9Sstevel@tonic-gate 
7277c478bd9Sstevel@tonic-gate 	if (type == DDI_INTR_TYPE_MSI) {
7287c478bd9Sstevel@tonic-gate 		msi_ctrl |= ((highbit(navail) -1) << PCI_MSI_MME_SHIFT);
7297c478bd9Sstevel@tonic-gate 
73027255037Spjha 		PCI_CAP_PUT16(cfg_hdle, NULL, caps_ptr, PCI_MSI_CTRL, msi_ctrl);
7317c478bd9Sstevel@tonic-gate 	} else if (type == DDI_INTR_TYPE_MSIX) {
7327c478bd9Sstevel@tonic-gate 		DDI_INTR_NEXDBG((CE_CONT, "pci_msi_set_nintrs: unsupported\n"));
7337c478bd9Sstevel@tonic-gate 	}
7347c478bd9Sstevel@tonic-gate 
7357c478bd9Sstevel@tonic-gate 	pci_config_teardown(&cfg_hdle);
7367c478bd9Sstevel@tonic-gate 	return (DDI_SUCCESS);
7377c478bd9Sstevel@tonic-gate }
7387c478bd9Sstevel@tonic-gate 
7397c478bd9Sstevel@tonic-gate 
7407c478bd9Sstevel@tonic-gate /*
7417c478bd9Sstevel@tonic-gate  * pci_msi_get_supported_type:
7427c478bd9Sstevel@tonic-gate  *
7437c478bd9Sstevel@tonic-gate  * Returns DDI_INTR_TYPE_MSI and/or DDI_INTR_TYPE_MSIX as supported
7447c478bd9Sstevel@tonic-gate  * types if device supports them. A DDI_FAILURE is returned otherwise.
7457c478bd9Sstevel@tonic-gate  */
7467c478bd9Sstevel@tonic-gate int
pci_msi_get_supported_type(dev_info_t * rdip,int * typesp)7477c478bd9Sstevel@tonic-gate pci_msi_get_supported_type(dev_info_t *rdip, int *typesp)
7487c478bd9Sstevel@tonic-gate {
7497c478bd9Sstevel@tonic-gate 	ushort_t		caps_ptr, msi_ctrl;
7507c478bd9Sstevel@tonic-gate 	ddi_acc_handle_t	cfg_hdle;
7517c478bd9Sstevel@tonic-gate 
7527c478bd9Sstevel@tonic-gate 	DDI_INTR_NEXDBG((CE_CONT, "pci_msi_get_supported_type: "
7537c478bd9Sstevel@tonic-gate 	    "rdip = 0x%p\n", (void *)rdip));
7547c478bd9Sstevel@tonic-gate 
7557c478bd9Sstevel@tonic-gate 	*typesp = 0;
7567c478bd9Sstevel@tonic-gate 
7577c478bd9Sstevel@tonic-gate 	if (pci_get_msi_ctrl(rdip, DDI_INTR_TYPE_MSI, &msi_ctrl,
7587c478bd9Sstevel@tonic-gate 	    &caps_ptr, &cfg_hdle) == DDI_SUCCESS) {
7597c478bd9Sstevel@tonic-gate 		*typesp |= DDI_INTR_TYPE_MSI;
7607c478bd9Sstevel@tonic-gate 		pci_config_teardown(&cfg_hdle);
7617c478bd9Sstevel@tonic-gate 	}
7627c478bd9Sstevel@tonic-gate 
7637c478bd9Sstevel@tonic-gate 	if (pci_get_msi_ctrl(rdip, DDI_INTR_TYPE_MSIX, &msi_ctrl,
7647c478bd9Sstevel@tonic-gate 	    &caps_ptr, &cfg_hdle) == DDI_SUCCESS) {
7657c478bd9Sstevel@tonic-gate 		*typesp |= DDI_INTR_TYPE_MSIX;
7667c478bd9Sstevel@tonic-gate 		pci_config_teardown(&cfg_hdle);
7677c478bd9Sstevel@tonic-gate 	}
7687c478bd9Sstevel@tonic-gate 
7697c478bd9Sstevel@tonic-gate 	DDI_INTR_NEXDBG((CE_CONT, "pci_msi_get_supported_type: "
7707c478bd9Sstevel@tonic-gate 	    "rdip = 0x%p types 0x%x\n", (void *)rdip, *typesp));
7717c478bd9Sstevel@tonic-gate 
7727c478bd9Sstevel@tonic-gate 	return (*typesp == 0 ? DDI_FAILURE : DDI_SUCCESS);
7737c478bd9Sstevel@tonic-gate }
7747c478bd9Sstevel@tonic-gate 
7757c478bd9Sstevel@tonic-gate 
7767c478bd9Sstevel@tonic-gate /*
7777c478bd9Sstevel@tonic-gate  * pci_msix_init:
7787c478bd9Sstevel@tonic-gate  *	This function initializes the various handles/addrs etc.
7797c478bd9Sstevel@tonic-gate  *	needed for MSI-X support. It also allocates a private
7807c478bd9Sstevel@tonic-gate  *	structure to keep track of these.
7817c478bd9Sstevel@tonic-gate  */
7827c478bd9Sstevel@tonic-gate ddi_intr_msix_t *
pci_msix_init(dev_info_t * rdip)7837c478bd9Sstevel@tonic-gate pci_msix_init(dev_info_t *rdip)
7847c478bd9Sstevel@tonic-gate {
7859c75c6bfSgovinda 	uint_t			rnumber, breg, nregs;
7867c478bd9Sstevel@tonic-gate 	size_t			msix_tbl_size;
7877c478bd9Sstevel@tonic-gate 	size_t			pba_tbl_size;
7889c75c6bfSgovinda 	ushort_t		caps_ptr, msix_ctrl;
7897c478bd9Sstevel@tonic-gate 	ddi_intr_msix_t		*msix_p;
7907c478bd9Sstevel@tonic-gate 	ddi_acc_handle_t	cfg_hdle;
7919c75c6bfSgovinda 	pci_regspec_t		*rp;
7929c75c6bfSgovinda 	int			reg_size, addr_space, offset, *regs_list;
7939c75c6bfSgovinda 	int			i, ret;
7947c478bd9Sstevel@tonic-gate 
7957c478bd9Sstevel@tonic-gate 	DDI_INTR_NEXDBG((CE_CONT, "pci_msix_init: rdip = %p\n", (void *)rdip));
7967c478bd9Sstevel@tonic-gate 
7979c75c6bfSgovinda 	if (pci_get_msi_ctrl(rdip, DDI_INTR_TYPE_MSIX, &msix_ctrl,
7987c478bd9Sstevel@tonic-gate 	    &caps_ptr, &cfg_hdle) != DDI_SUCCESS)
7997c478bd9Sstevel@tonic-gate 		return (NULL);
8007c478bd9Sstevel@tonic-gate 
8017c478bd9Sstevel@tonic-gate 	msix_p = kmem_zalloc(sizeof (ddi_intr_msix_t), KM_SLEEP);
8027c478bd9Sstevel@tonic-gate 
8037c478bd9Sstevel@tonic-gate 	/*
8047c478bd9Sstevel@tonic-gate 	 * Initialize the devacc structure
8057c478bd9Sstevel@tonic-gate 	 */
8067c478bd9Sstevel@tonic-gate 	msix_p->msix_dev_attr.devacc_attr_version = DDI_DEVICE_ATTR_V0;
8077c478bd9Sstevel@tonic-gate 	msix_p->msix_dev_attr.devacc_attr_endian_flags =
8087c478bd9Sstevel@tonic-gate 	    DDI_STRUCTURE_LE_ACC;
8097c478bd9Sstevel@tonic-gate 	msix_p->msix_dev_attr.devacc_attr_dataorder = DDI_STRICTORDER_ACC;
8107c478bd9Sstevel@tonic-gate 
8119c75c6bfSgovinda 	/* Map the entire MSI-X vector table */
81227255037Spjha 	msix_p->msix_tbl_offset = PCI_CAP_GET32(cfg_hdle, NULL, caps_ptr,
81327255037Spjha 	    PCI_MSIX_TBL_OFFSET);
8147c478bd9Sstevel@tonic-gate 
8159c75c6bfSgovinda 	if ((breg = pci_msix_bir_index[msix_p->msix_tbl_offset &
8169c75c6bfSgovinda 	    PCI_MSIX_TBL_BIR_MASK]) == 0xff)
8179c75c6bfSgovinda 		goto fail1;
8187c478bd9Sstevel@tonic-gate 
8199c75c6bfSgovinda 	msix_p->msix_tbl_offset = msix_p->msix_tbl_offset &
8209c75c6bfSgovinda 	    ~PCI_MSIX_TBL_BIR_MASK;
8219c75c6bfSgovinda 	msix_tbl_size = ((msix_ctrl & PCI_MSIX_TBL_SIZE_MASK) + 1) *
8229c75c6bfSgovinda 	    PCI_MSIX_VECTOR_SIZE;
8239c75c6bfSgovinda 
8249c75c6bfSgovinda 	DDI_INTR_NEXDBG((CE_CONT, "pci_msix_init: MSI-X table offset 0x%x "
8259c75c6bfSgovinda 	    "breg 0x%x size 0x%lx\n", msix_p->msix_tbl_offset, breg,
8269c75c6bfSgovinda 	    msix_tbl_size));
8279c75c6bfSgovinda 
8289c75c6bfSgovinda 	if ((ret = ddi_prop_lookup_int_array(DDI_DEV_T_ANY, rdip,
8299c75c6bfSgovinda 	    DDI_PROP_DONTPASS, "reg", (int **)&regs_list, &nregs))
8309c75c6bfSgovinda 	    != DDI_PROP_SUCCESS) {
8319c75c6bfSgovinda 		DDI_INTR_NEXDBG((CE_CONT, "pci_msix_init: "
8329c75c6bfSgovinda 		    "ddi_prop_lookup_int_array failed %d\n", ret));
8339c75c6bfSgovinda 
8349c75c6bfSgovinda 		goto fail1;
8359c75c6bfSgovinda 	}
8369c75c6bfSgovinda 
8379c75c6bfSgovinda 	reg_size = sizeof (pci_regspec_t) / sizeof (int);
8389c75c6bfSgovinda 
8399c75c6bfSgovinda 	for (i = 1, rnumber = 0; i < nregs/reg_size; i++) {
8409c75c6bfSgovinda 		rp = (pci_regspec_t *)&regs_list[i * reg_size];
8419c75c6bfSgovinda 		addr_space = rp->pci_phys_hi & PCI_ADDR_MASK;
8429c75c6bfSgovinda 		offset = PCI_REG_REG_G(rp->pci_phys_hi);
8439c75c6bfSgovinda 
8449c75c6bfSgovinda 		if ((offset == breg) && ((addr_space == PCI_ADDR_MEM32) ||
8459c75c6bfSgovinda 		    (addr_space == PCI_ADDR_MEM64))) {
8469c75c6bfSgovinda 			rnumber = i;
8479c75c6bfSgovinda 			break;
8489c75c6bfSgovinda 		}
8499c75c6bfSgovinda 	}
8509c75c6bfSgovinda 
8519c75c6bfSgovinda 	DDI_INTR_NEXDBG((CE_CONT, "pci_msix_init: MSI-X rnum = %d\n", rnumber));
8529c75c6bfSgovinda 
8539c75c6bfSgovinda 	if (rnumber == 0) {
8549c75c6bfSgovinda 		DDI_INTR_NEXDBG((CE_CONT, "pci_msix_init: "
8559c75c6bfSgovinda 		    "no mtaching reg number for offset 0x%x\n", breg));
8569c75c6bfSgovinda 
8579c75c6bfSgovinda 		goto fail2;
8589c75c6bfSgovinda 	}
8599c75c6bfSgovinda 
8609c75c6bfSgovinda 	if ((ret = ddi_regs_map_setup(rdip, rnumber,
8619c75c6bfSgovinda 	    (caddr_t *)&msix_p->msix_tbl_addr, msix_p->msix_tbl_offset,
8629c75c6bfSgovinda 	    msix_tbl_size, &msix_p->msix_dev_attr,
8639c75c6bfSgovinda 	    &msix_p->msix_tbl_hdl)) != DDI_SUCCESS) {
8649c75c6bfSgovinda 		DDI_INTR_NEXDBG((CE_CONT, "pci_msix_init: MSI-X Table "
8659c75c6bfSgovinda 		    "ddi_regs_map_setup failed %d\n", ret));
8669c75c6bfSgovinda 
8679c75c6bfSgovinda 		goto fail2;
8687c478bd9Sstevel@tonic-gate 	}
8697c478bd9Sstevel@tonic-gate 
8707c478bd9Sstevel@tonic-gate 	/*
8717c478bd9Sstevel@tonic-gate 	 * Map in the MSI-X Pending Bit Array
8727c478bd9Sstevel@tonic-gate 	 */
87327255037Spjha 	msix_p->msix_pba_offset = PCI_CAP_GET32(cfg_hdle, NULL, caps_ptr,
87427255037Spjha 	    PCI_MSIX_PBA_OFFSET);
8757c478bd9Sstevel@tonic-gate 
8769c75c6bfSgovinda 	if ((breg = pci_msix_bir_index[msix_p->msix_pba_offset &
8779c75c6bfSgovinda 	    PCI_MSIX_PBA_BIR_MASK]) == 0xff)
8789c75c6bfSgovinda 		goto fail3;
8799c75c6bfSgovinda 
8809c75c6bfSgovinda 	msix_p->msix_pba_offset = msix_p->msix_pba_offset &
8819c75c6bfSgovinda 	    ~PCI_MSIX_PBA_BIR_MASK;
8829c75c6bfSgovinda 	pba_tbl_size = ((msix_ctrl & PCI_MSIX_TBL_SIZE_MASK) + 1)/8;
8839c75c6bfSgovinda 
8849c75c6bfSgovinda 	DDI_INTR_NEXDBG((CE_CONT, "pci_msix_init: PBA table offset 0x%x "
8859c75c6bfSgovinda 	    "breg 0x%x size 0x%lx\n", msix_p->msix_pba_offset, breg,
8869c75c6bfSgovinda 	    pba_tbl_size));
8879c75c6bfSgovinda 
8889c75c6bfSgovinda 	for (i = 1, rnumber = 0; i < nregs/reg_size; i++) {
8899c75c6bfSgovinda 		rp = (pci_regspec_t *)&regs_list[i * reg_size];
8909c75c6bfSgovinda 		addr_space = rp->pci_phys_hi & PCI_ADDR_MASK;
8919c75c6bfSgovinda 		offset = PCI_REG_REG_G(rp->pci_phys_hi);
8929c75c6bfSgovinda 
8939c75c6bfSgovinda 		if ((offset == breg) && ((addr_space == PCI_ADDR_MEM32) ||
8949c75c6bfSgovinda 		    (addr_space == PCI_ADDR_MEM64))) {
8959c75c6bfSgovinda 			rnumber = i;
8969c75c6bfSgovinda 			break;
8979c75c6bfSgovinda 		}
8989c75c6bfSgovinda 	}
8999c75c6bfSgovinda 
9009c75c6bfSgovinda 	DDI_INTR_NEXDBG((CE_CONT, "pci_msix_init: PBA rnum = %d\n", rnumber));
9019c75c6bfSgovinda 
9029c75c6bfSgovinda 	if (rnumber == 0) {
9039c75c6bfSgovinda 		DDI_INTR_NEXDBG((CE_CONT, "pci_msix_init: "
90420036fe5Segillett 		    "no matching reg number for offset 0x%x\n", breg));
9059c75c6bfSgovinda 
9069c75c6bfSgovinda 		goto fail3;
9079c75c6bfSgovinda 	}
9089c75c6bfSgovinda 
9099c75c6bfSgovinda 	if ((ret = ddi_regs_map_setup(rdip, rnumber,
9109c75c6bfSgovinda 	    (caddr_t *)&msix_p->msix_pba_addr, msix_p->msix_pba_offset,
9119c75c6bfSgovinda 	    pba_tbl_size, &msix_p->msix_dev_attr,
9129c75c6bfSgovinda 	    &msix_p->msix_pba_hdl)) != DDI_SUCCESS) {
9139c75c6bfSgovinda 		DDI_INTR_NEXDBG((CE_CONT, "pci_msix_init: PBA "
9149c75c6bfSgovinda 		    "ddi_regs_map_setup failed %d\n", ret));
9159c75c6bfSgovinda 
9169c75c6bfSgovinda 		goto fail3;
9177c478bd9Sstevel@tonic-gate 	}
9187c478bd9Sstevel@tonic-gate 
9197c478bd9Sstevel@tonic-gate 	DDI_INTR_NEXDBG((CE_CONT, "pci_msix_init: msix_p = 0x%p DONE!!\n",
9207c478bd9Sstevel@tonic-gate 	    (void *)msix_p));
9217c478bd9Sstevel@tonic-gate 
922a7639048Sjohnny 	ddi_prop_free(regs_list);
9239c75c6bfSgovinda 	goto done;
9249c75c6bfSgovinda 
9259c75c6bfSgovinda fail3:
9269c75c6bfSgovinda 	ddi_regs_map_free(&msix_p->msix_tbl_hdl);
9279c75c6bfSgovinda fail2:
9289c75c6bfSgovinda 	ddi_prop_free(regs_list);
9299c75c6bfSgovinda fail1:
9309c75c6bfSgovinda 	kmem_free(msix_p, sizeof (ddi_intr_msix_t));
9319c75c6bfSgovinda 	msix_p = NULL;
9329c75c6bfSgovinda done:
9337c478bd9Sstevel@tonic-gate 	pci_config_teardown(&cfg_hdle);
9347c478bd9Sstevel@tonic-gate 	return (msix_p);
9357c478bd9Sstevel@tonic-gate }
9367c478bd9Sstevel@tonic-gate 
9377c478bd9Sstevel@tonic-gate 
9387c478bd9Sstevel@tonic-gate /*
9397c478bd9Sstevel@tonic-gate  * pci_msix_fini:
9407c478bd9Sstevel@tonic-gate  *	This function cleans up previously allocated handles/addrs etc.
9417c478bd9Sstevel@tonic-gate  *	It is only called if no more MSI-X interrupts are being used.
9427c478bd9Sstevel@tonic-gate  */
9437c478bd9Sstevel@tonic-gate void
pci_msix_fini(ddi_intr_msix_t * msix_p)9447c478bd9Sstevel@tonic-gate pci_msix_fini(ddi_intr_msix_t *msix_p)
9457c478bd9Sstevel@tonic-gate {
9467c478bd9Sstevel@tonic-gate 	DDI_INTR_NEXDBG((CE_CONT, "pci_msix_fini: msix_p = 0x%p\n",
9477c478bd9Sstevel@tonic-gate 	    (void *)msix_p));
9487c478bd9Sstevel@tonic-gate 
9497c478bd9Sstevel@tonic-gate 	ddi_regs_map_free(&msix_p->msix_pba_hdl);
9507c478bd9Sstevel@tonic-gate 	ddi_regs_map_free(&msix_p->msix_tbl_hdl);
9517c478bd9Sstevel@tonic-gate 	kmem_free(msix_p, sizeof (ddi_intr_msix_t));
9527c478bd9Sstevel@tonic-gate }
9537c478bd9Sstevel@tonic-gate 
9547c478bd9Sstevel@tonic-gate 
95520036fe5Segillett /*
95620036fe5Segillett  * pci_msix_dup:
95720036fe5Segillett  *	This function duplicates the address and data pair of one msi-x
95820036fe5Segillett  *	vector to another msi-x vector.
95920036fe5Segillett  */
96020036fe5Segillett int
pci_msix_dup(dev_info_t * rdip,int org_inum,int dup_inum)96120036fe5Segillett pci_msix_dup(dev_info_t *rdip, int org_inum, int dup_inum)
96220036fe5Segillett {
96320036fe5Segillett 	ddi_intr_msix_t	*msix_p = i_ddi_get_msix(rdip);
96420036fe5Segillett 	uint64_t	addr;
96520036fe5Segillett 	uint64_t	data;
96620036fe5Segillett 	uintptr_t	off;
96720036fe5Segillett 
96820036fe5Segillett 	DDI_INTR_NEXDBG((CE_CONT, "pci_msix_dup: dip = %p, inum = 0x%x, "
96920036fe5Segillett 	    "to_vector = 0x%x\n", (void *)rdip, org_inum, dup_inum));
97020036fe5Segillett 
97120036fe5Segillett 	/* Offset into the original inum's entry in the MSI-X table */
97220036fe5Segillett 	off = (uintptr_t)msix_p->msix_tbl_addr +
97320036fe5Segillett 	    (org_inum * PCI_MSIX_VECTOR_SIZE);
97420036fe5Segillett 
975*f8c90ccaSGarrett D'Amore 	/*
976*f8c90ccaSGarrett D'Amore 	 * For the MSI-X number passed in, get the "data" and "addr" fields.
977*f8c90ccaSGarrett D'Amore 	 *
978*f8c90ccaSGarrett D'Amore 	 * Note that the spec only requires 32-bit accesses to be supported.
979*f8c90ccaSGarrett D'Amore 	 * Apparently some chipsets don't support 64-bit accesses.
980*f8c90ccaSGarrett D'Amore 	 */
981*f8c90ccaSGarrett D'Amore 	addr = ddi_get32(msix_p->msix_tbl_hdl,
982*f8c90ccaSGarrett D'Amore 	    (uint32_t *)(off + PCI_MSIX_UPPER_ADDR_OFFSET));
983*f8c90ccaSGarrett D'Amore 	addr = (addr << 32) | ddi_get32(msix_p->msix_tbl_hdl,
984*f8c90ccaSGarrett D'Amore 	    (uint32_t *)(off + PCI_MSIX_LOWER_ADDR_OFFSET));
98520036fe5Segillett 
98620036fe5Segillett 	data = ddi_get32(msix_p->msix_tbl_hdl,
98720036fe5Segillett 	    (uint32_t *)(off + PCI_MSIX_DATA_OFFSET));
98820036fe5Segillett 
98920036fe5Segillett 	/* Program new vector with these existing values */
99020036fe5Segillett 	return (pci_msi_configure(rdip, DDI_INTR_TYPE_MSIX, 1, dup_inum, addr,
99120036fe5Segillett 	    data));
99220036fe5Segillett }
99320036fe5Segillett 
9947c478bd9Sstevel@tonic-gate 
9957c478bd9Sstevel@tonic-gate /*
9967c478bd9Sstevel@tonic-gate  * Next set of routines are for INTx (legacy) PCI interrupt
9977c478bd9Sstevel@tonic-gate  * support only.
9987c478bd9Sstevel@tonic-gate  */
9997c478bd9Sstevel@tonic-gate 
10007c478bd9Sstevel@tonic-gate /*
10017c478bd9Sstevel@tonic-gate  * pci_intx_get_cap:
10027c478bd9Sstevel@tonic-gate  *	For non-MSI devices that comply to PCI v2.3 or greater;
10037c478bd9Sstevel@tonic-gate  *	read the command register. Bit 10 implies interrupt disable.
10047c478bd9Sstevel@tonic-gate  *	Set this bit and then read the status register bit 3.
10057c478bd9Sstevel@tonic-gate  *	Bit 3 of status register is Interrupt state.
10067c478bd9Sstevel@tonic-gate  *	If it is set; then the device supports 'Masking'
10077c478bd9Sstevel@tonic-gate  *
10087c478bd9Sstevel@tonic-gate  *	Reset the device back to the original state.
10097c478bd9Sstevel@tonic-gate  */
10107c478bd9Sstevel@tonic-gate int
pci_intx_get_cap(dev_info_t * dip,int * flagsp)10117c478bd9Sstevel@tonic-gate pci_intx_get_cap(dev_info_t *dip, int *flagsp)
10127c478bd9Sstevel@tonic-gate {
10137c478bd9Sstevel@tonic-gate 	uint16_t		cmdreg, savereg;
10147c478bd9Sstevel@tonic-gate 	ddi_acc_handle_t	cfg_hdl;
10157c478bd9Sstevel@tonic-gate #ifdef	DEBUG
10167c478bd9Sstevel@tonic-gate 	uint16_t		statreg;
10177c478bd9Sstevel@tonic-gate #endif /* DEBUG */
10187c478bd9Sstevel@tonic-gate 
10197c478bd9Sstevel@tonic-gate 	*flagsp = 0;
10207c478bd9Sstevel@tonic-gate 	DDI_INTR_NEXDBG((CE_CONT, "pci_intx_get_cap: %s%d: called\n",
10217c478bd9Sstevel@tonic-gate 	    ddi_driver_name(dip), ddi_get_instance(dip)));
10227c478bd9Sstevel@tonic-gate 
10237c478bd9Sstevel@tonic-gate 	if (pci_config_setup(dip, &cfg_hdl) != DDI_SUCCESS) {
10247c478bd9Sstevel@tonic-gate 		DDI_INTR_NEXDBG((CE_CONT, "pci_intx_get_cap: can't get "
10257c478bd9Sstevel@tonic-gate 		    "config handle\n"));
10267c478bd9Sstevel@tonic-gate 		return (DDI_FAILURE);
10277c478bd9Sstevel@tonic-gate 	}
10287c478bd9Sstevel@tonic-gate 
10297c478bd9Sstevel@tonic-gate 	savereg = pci_config_get16(cfg_hdl, PCI_CONF_COMM);
10307c478bd9Sstevel@tonic-gate 	DDI_INTR_NEXDBG((CE_CONT, "pci_intx_get_cap: "
10317c478bd9Sstevel@tonic-gate 	    "command register was 0x%x\n", savereg));
10327c478bd9Sstevel@tonic-gate 
10337c478bd9Sstevel@tonic-gate 	/* Disable the interrupts */
10347c478bd9Sstevel@tonic-gate 	cmdreg = savereg | PCI_COMM_INTX_DISABLE;
10357c478bd9Sstevel@tonic-gate 	pci_config_put16(cfg_hdl, PCI_CONF_COMM, cmdreg);
10367c478bd9Sstevel@tonic-gate 
10377c478bd9Sstevel@tonic-gate #ifdef	DEBUG
10387c478bd9Sstevel@tonic-gate 	statreg = pci_config_get16(cfg_hdl, PCI_CONF_STAT);
10397c478bd9Sstevel@tonic-gate 	DDI_INTR_NEXDBG((CE_CONT, "pci_intx_get_cap: "
10407c478bd9Sstevel@tonic-gate 	    "status register is 0x%x\n", statreg));
10417c478bd9Sstevel@tonic-gate #endif /* DEBUG */
10427c478bd9Sstevel@tonic-gate 
10437c478bd9Sstevel@tonic-gate 	/* Read the bit back */
10447c478bd9Sstevel@tonic-gate 	cmdreg = pci_config_get16(cfg_hdl, PCI_CONF_COMM);
10457c478bd9Sstevel@tonic-gate 	DDI_INTR_NEXDBG((CE_CONT, "pci_intx_get_cap: "
10467c478bd9Sstevel@tonic-gate 	    "command register is now 0x%x\n", cmdreg));
10477c478bd9Sstevel@tonic-gate 
1048a195726fSgovinda 	*flagsp = DDI_INTR_FLAG_LEVEL;
1049a195726fSgovinda 
10507c478bd9Sstevel@tonic-gate 	if (cmdreg & PCI_COMM_INTX_DISABLE) {
10517c478bd9Sstevel@tonic-gate 		DDI_INTR_NEXDBG((CE_CONT, "pci_intx_get_cap: "
10527c478bd9Sstevel@tonic-gate 		    "masking supported\n"));
1053a195726fSgovinda 		*flagsp |= (DDI_INTR_FLAG_MASKABLE |
1054a195726fSgovinda 		    DDI_INTR_FLAG_PENDING);
10557c478bd9Sstevel@tonic-gate 	}
10567c478bd9Sstevel@tonic-gate 
10577c478bd9Sstevel@tonic-gate 	/* Restore the device back to the original state and return */
10587c478bd9Sstevel@tonic-gate 	pci_config_put16(cfg_hdl, PCI_CONF_COMM, savereg);
10597c478bd9Sstevel@tonic-gate 
10607c478bd9Sstevel@tonic-gate 	pci_config_teardown(&cfg_hdl);
10617c478bd9Sstevel@tonic-gate 	return (DDI_SUCCESS);
10627c478bd9Sstevel@tonic-gate }
10637c478bd9Sstevel@tonic-gate 
10647c478bd9Sstevel@tonic-gate 
10657c478bd9Sstevel@tonic-gate /*
10667c478bd9Sstevel@tonic-gate  * pci_intx_clr_mask:
10677c478bd9Sstevel@tonic-gate  *	For non-MSI devices that comply to PCI v2.3 or greater;
10687c478bd9Sstevel@tonic-gate  *	clear the bit10 in the command register.
10697c478bd9Sstevel@tonic-gate  */
10707c478bd9Sstevel@tonic-gate int
pci_intx_clr_mask(dev_info_t * dip)10717c478bd9Sstevel@tonic-gate pci_intx_clr_mask(dev_info_t *dip)
10727c478bd9Sstevel@tonic-gate {
10737c478bd9Sstevel@tonic-gate 	uint16_t		cmdreg;
10747c478bd9Sstevel@tonic-gate 	ddi_acc_handle_t	cfg_hdl;
10757c478bd9Sstevel@tonic-gate 
10767c478bd9Sstevel@tonic-gate 	DDI_INTR_NEXDBG((CE_CONT, "pci_intx_clr_mask: %s%d: called\n",
10777c478bd9Sstevel@tonic-gate 	    ddi_driver_name(dip), ddi_get_instance(dip)));
10787c478bd9Sstevel@tonic-gate 
10797c478bd9Sstevel@tonic-gate 	if (pci_config_setup(dip, &cfg_hdl) != DDI_SUCCESS) {
10807c478bd9Sstevel@tonic-gate 		DDI_INTR_NEXDBG((CE_CONT, "pci_intx_clr_mask: can't get "
10817c478bd9Sstevel@tonic-gate 		    "config handle\n"));
10827c478bd9Sstevel@tonic-gate 		return (DDI_FAILURE);
10837c478bd9Sstevel@tonic-gate 	}
10847c478bd9Sstevel@tonic-gate 
10857c478bd9Sstevel@tonic-gate 	cmdreg = pci_config_get16(cfg_hdl, PCI_CONF_COMM);
10867c478bd9Sstevel@tonic-gate 	DDI_INTR_NEXDBG((CE_CONT, "pci_intx_clr_mask: "
10877c478bd9Sstevel@tonic-gate 	    "command register was 0x%x\n", cmdreg));
10887c478bd9Sstevel@tonic-gate 
10897c478bd9Sstevel@tonic-gate 	/* Enable the interrupts */
10907c478bd9Sstevel@tonic-gate 	cmdreg &= ~PCI_COMM_INTX_DISABLE;
10917c478bd9Sstevel@tonic-gate 	pci_config_put16(cfg_hdl, PCI_CONF_COMM, cmdreg);
10927c478bd9Sstevel@tonic-gate 	pci_config_teardown(&cfg_hdl);
10937c478bd9Sstevel@tonic-gate 	return (DDI_SUCCESS);
10947c478bd9Sstevel@tonic-gate }
10957c478bd9Sstevel@tonic-gate 
10967c478bd9Sstevel@tonic-gate 
10977c478bd9Sstevel@tonic-gate /*
10987c478bd9Sstevel@tonic-gate  * pci_intx_set_mask:
10997c478bd9Sstevel@tonic-gate  *	For non-MSI devices that comply to PCI v2.3 or greater;
11007c478bd9Sstevel@tonic-gate  *	set the bit10 in the command register.
11017c478bd9Sstevel@tonic-gate  */
11027c478bd9Sstevel@tonic-gate int
pci_intx_set_mask(dev_info_t * dip)11037c478bd9Sstevel@tonic-gate pci_intx_set_mask(dev_info_t *dip)
11047c478bd9Sstevel@tonic-gate {
11057c478bd9Sstevel@tonic-gate 	uint16_t		cmdreg;
11067c478bd9Sstevel@tonic-gate 	ddi_acc_handle_t	cfg_hdl;
11077c478bd9Sstevel@tonic-gate 
11087c478bd9Sstevel@tonic-gate 	DDI_INTR_NEXDBG((CE_CONT, "pci_intx_set_mask: %s%d: called\n",
11097c478bd9Sstevel@tonic-gate 	    ddi_driver_name(dip), ddi_get_instance(dip)));
11107c478bd9Sstevel@tonic-gate 
11117c478bd9Sstevel@tonic-gate 	if (pci_config_setup(dip, &cfg_hdl) != DDI_SUCCESS) {
11127c478bd9Sstevel@tonic-gate 		DDI_INTR_NEXDBG((CE_CONT, "pci_intx_set_mask: can't get "
11137c478bd9Sstevel@tonic-gate 		    "config handle\n"));
11147c478bd9Sstevel@tonic-gate 		return (DDI_FAILURE);
11157c478bd9Sstevel@tonic-gate 	}
11167c478bd9Sstevel@tonic-gate 
11177c478bd9Sstevel@tonic-gate 	cmdreg = pci_config_get16(cfg_hdl, PCI_CONF_COMM);
11187c478bd9Sstevel@tonic-gate 	DDI_INTR_NEXDBG((CE_CONT, "pci_intx_set_mask: "
11197c478bd9Sstevel@tonic-gate 	    "command register was 0x%x\n", cmdreg));
11207c478bd9Sstevel@tonic-gate 
11217c478bd9Sstevel@tonic-gate 	/* Disable the interrupts */
11227c478bd9Sstevel@tonic-gate 	cmdreg |= PCI_COMM_INTX_DISABLE;
11237c478bd9Sstevel@tonic-gate 	pci_config_put16(cfg_hdl, PCI_CONF_COMM, cmdreg);
11247c478bd9Sstevel@tonic-gate 	pci_config_teardown(&cfg_hdl);
11257c478bd9Sstevel@tonic-gate 	return (DDI_SUCCESS);
11267c478bd9Sstevel@tonic-gate }
11277c478bd9Sstevel@tonic-gate 
11287c478bd9Sstevel@tonic-gate /*
11297c478bd9Sstevel@tonic-gate  * pci_intx_get_pending:
11307c478bd9Sstevel@tonic-gate  *	For non-MSI devices that comply to PCI v2.3 or greater;
11317c478bd9Sstevel@tonic-gate  *	read the status register. Bit 3 of status register is
11327c478bd9Sstevel@tonic-gate  *	Interrupt state. If it is set; then the interrupt is
11337c478bd9Sstevel@tonic-gate  *	'Pending'.
11347c478bd9Sstevel@tonic-gate  */
11357c478bd9Sstevel@tonic-gate int
pci_intx_get_pending(dev_info_t * dip,int * pendingp)11367c478bd9Sstevel@tonic-gate pci_intx_get_pending(dev_info_t *dip, int *pendingp)
11377c478bd9Sstevel@tonic-gate {
11387c478bd9Sstevel@tonic-gate 	uint16_t		statreg;
11397c478bd9Sstevel@tonic-gate 	ddi_acc_handle_t	cfg_hdl;
11407c478bd9Sstevel@tonic-gate 
11417c478bd9Sstevel@tonic-gate 	*pendingp = 0;
11427c478bd9Sstevel@tonic-gate 	DDI_INTR_NEXDBG((CE_CONT, "pci_intx_get_pending: %s%d: called\n",
11437c478bd9Sstevel@tonic-gate 	    ddi_driver_name(dip), ddi_get_instance(dip)));
11447c478bd9Sstevel@tonic-gate 
11457c478bd9Sstevel@tonic-gate 	if (pci_config_setup(dip, &cfg_hdl) != DDI_SUCCESS) {
11467c478bd9Sstevel@tonic-gate 		DDI_INTR_NEXDBG((CE_CONT, "pci_intx_get_pending: can't get "
11477c478bd9Sstevel@tonic-gate 		    "config handle\n"));
11487c478bd9Sstevel@tonic-gate 		return (DDI_FAILURE);
11497c478bd9Sstevel@tonic-gate 	}
11507c478bd9Sstevel@tonic-gate 
11517c478bd9Sstevel@tonic-gate 	statreg = pci_config_get16(cfg_hdl, PCI_CONF_STAT);
11527c478bd9Sstevel@tonic-gate 
11537c478bd9Sstevel@tonic-gate 	if (statreg & PCI_STAT_INTR) {
11547c478bd9Sstevel@tonic-gate 		DDI_INTR_NEXDBG((CE_CONT, "pci_intx_get_pending: "
11557c478bd9Sstevel@tonic-gate 		    "interrupt is pending\n"));
11567c478bd9Sstevel@tonic-gate 		*pendingp = 1;
11577c478bd9Sstevel@tonic-gate 	}
11587c478bd9Sstevel@tonic-gate 
11597c478bd9Sstevel@tonic-gate 	pci_config_teardown(&cfg_hdl);
11607c478bd9Sstevel@tonic-gate 	return (DDI_SUCCESS);
11617c478bd9Sstevel@tonic-gate }
11627c478bd9Sstevel@tonic-gate 
11637c478bd9Sstevel@tonic-gate 
11647c478bd9Sstevel@tonic-gate /*
11657c478bd9Sstevel@tonic-gate  * pci_intx_get_ispec:
11667c478bd9Sstevel@tonic-gate  *	Get intrspec for PCI devices (legacy support)
11677c478bd9Sstevel@tonic-gate  *	NOTE: This is moved here from x86 pci.c and is
11687c478bd9Sstevel@tonic-gate  *	needed here as pci-ide.c uses it as well
11697c478bd9Sstevel@tonic-gate  */
11707c478bd9Sstevel@tonic-gate /*ARGSUSED*/
11717c478bd9Sstevel@tonic-gate ddi_intrspec_t
pci_intx_get_ispec(dev_info_t * dip,dev_info_t * rdip,int inum)11727c478bd9Sstevel@tonic-gate pci_intx_get_ispec(dev_info_t *dip, dev_info_t *rdip, int inum)
11737c478bd9Sstevel@tonic-gate {
1174614edcaeSEvan Yan 	int				*intpriorities;
11757c478bd9Sstevel@tonic-gate 	uint_t				num_intpriorities;
11767c478bd9Sstevel@tonic-gate 	struct intrspec			*ispec;
11777c478bd9Sstevel@tonic-gate 	ddi_acc_handle_t		cfg_hdl;
11787c478bd9Sstevel@tonic-gate 	struct ddi_parent_private_data	*pdptr;
11797c478bd9Sstevel@tonic-gate 
11807c478bd9Sstevel@tonic-gate 	if ((pdptr = ddi_get_parent_data(rdip)) == NULL)
11817c478bd9Sstevel@tonic-gate 		return (NULL);
11827c478bd9Sstevel@tonic-gate 
11837c478bd9Sstevel@tonic-gate 	ispec = pdptr->par_intr;
11847c478bd9Sstevel@tonic-gate 	ASSERT(ispec);
11857c478bd9Sstevel@tonic-gate 
11867c478bd9Sstevel@tonic-gate 	/* check if the intrspec_pri has been initialized */
11877c478bd9Sstevel@tonic-gate 	if (!ispec->intrspec_pri) {
11887c478bd9Sstevel@tonic-gate 		if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, rdip,
11897c478bd9Sstevel@tonic-gate 		    DDI_PROP_DONTPASS, "interrupt-priorities",
11907c478bd9Sstevel@tonic-gate 		    &intpriorities, &num_intpriorities) == DDI_PROP_SUCCESS) {
11917c478bd9Sstevel@tonic-gate 			if (inum < num_intpriorities)
11927c478bd9Sstevel@tonic-gate 				ispec->intrspec_pri = intpriorities[inum];
11937c478bd9Sstevel@tonic-gate 			ddi_prop_free(intpriorities);
11947c478bd9Sstevel@tonic-gate 		}
11957c478bd9Sstevel@tonic-gate 
11967c478bd9Sstevel@tonic-gate 		/* If still no priority, guess based on the class code */
1197614edcaeSEvan Yan 		if (ispec->intrspec_pri == 0)
1198614edcaeSEvan Yan 			ispec->intrspec_pri = pci_class_to_pil(rdip);
11997c478bd9Sstevel@tonic-gate 	}
12007c478bd9Sstevel@tonic-gate 
12017c478bd9Sstevel@tonic-gate 	/* Get interrupt line value */
12027c478bd9Sstevel@tonic-gate 	if (!ispec->intrspec_vec) {
12037c478bd9Sstevel@tonic-gate 		if (pci_config_setup(rdip, &cfg_hdl) != DDI_SUCCESS) {
12047c478bd9Sstevel@tonic-gate 			DDI_INTR_NEXDBG((CE_CONT, "pci_intx_get_iline: "
12057c478bd9Sstevel@tonic-gate 			    "can't get config handle\n"));
12068d9a0e26Sanish 			return ((ddi_intrspec_t)ispec);
12077c478bd9Sstevel@tonic-gate 		}
12087c478bd9Sstevel@tonic-gate 
12097c478bd9Sstevel@tonic-gate 		ispec->intrspec_vec = pci_config_get8(cfg_hdl, PCI_CONF_ILINE);
12107c478bd9Sstevel@tonic-gate 		pci_config_teardown(&cfg_hdl);
12117c478bd9Sstevel@tonic-gate 	}
12127c478bd9Sstevel@tonic-gate 
12137c478bd9Sstevel@tonic-gate 	return ((ddi_intrspec_t)ispec);
12147c478bd9Sstevel@tonic-gate }
1215614edcaeSEvan Yan 
1216614edcaeSEvan Yan static uint32_t
pci_match_class_val(uint32_t key,pci_class_val_t * rec_p,int nrec,uint32_t default_val)1217614edcaeSEvan Yan pci_match_class_val(uint32_t key, pci_class_val_t *rec_p, int nrec,
1218614edcaeSEvan Yan     uint32_t default_val)
1219614edcaeSEvan Yan {
1220614edcaeSEvan Yan 	int i;
1221614edcaeSEvan Yan 
1222614edcaeSEvan Yan 	for (i = 0; i < nrec; rec_p++, i++) {
1223614edcaeSEvan Yan 		if ((rec_p->class_code & rec_p->class_mask) ==
1224614edcaeSEvan Yan 		    (key & rec_p->class_mask))
1225614edcaeSEvan Yan 			return (rec_p->class_val);
1226614edcaeSEvan Yan 	}
1227614edcaeSEvan Yan 
1228614edcaeSEvan Yan 	return (default_val);
1229614edcaeSEvan Yan }
1230614edcaeSEvan Yan 
1231614edcaeSEvan Yan /*
1232614edcaeSEvan Yan  * Return the configuration value, based on class code and sub class code,
1233614edcaeSEvan Yan  * from the specified property based or default pci_class_val_t table.
1234614edcaeSEvan Yan  */
1235614edcaeSEvan 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)1236614edcaeSEvan Yan pci_class_to_val(dev_info_t *rdip, char *property_name, pci_class_val_t *rec_p,
1237614edcaeSEvan Yan     int nrec, uint32_t default_val)
1238614edcaeSEvan Yan {
1239614edcaeSEvan Yan 	int property_len;
1240614edcaeSEvan Yan 	uint32_t class_code;
1241614edcaeSEvan Yan 	pci_class_val_t *conf;
1242614edcaeSEvan Yan 	uint32_t val = default_val;
1243614edcaeSEvan Yan 
1244614edcaeSEvan Yan 	/*
1245614edcaeSEvan Yan 	 * Use the "class-code" property to get the base and sub class
1246614edcaeSEvan Yan 	 * codes for the requesting device.
1247614edcaeSEvan Yan 	 */
1248614edcaeSEvan Yan 	class_code = (uint32_t)ddi_prop_get_int(DDI_DEV_T_ANY, rdip,
1249614edcaeSEvan Yan 	    DDI_PROP_DONTPASS, "class-code", -1);
1250614edcaeSEvan Yan 
1251614edcaeSEvan Yan 	if (class_code == -1)
1252614edcaeSEvan Yan 		return (val);
1253614edcaeSEvan Yan 
1254614edcaeSEvan Yan 	/* look up the val from the default table */
1255614edcaeSEvan Yan 	val = pci_match_class_val(class_code, rec_p, nrec, val);
1256614edcaeSEvan Yan 
1257614edcaeSEvan Yan 
1258614edcaeSEvan Yan 	/* see if there is a more specific property specified value */
1259614edcaeSEvan Yan 	if (ddi_getlongprop(DDI_DEV_T_ANY, rdip, DDI_PROP_NOTPROM,
1260614edcaeSEvan Yan 	    property_name, (caddr_t)&conf, &property_len))
1261614edcaeSEvan Yan 			return (val);
1262614edcaeSEvan Yan 
1263614edcaeSEvan Yan 	if ((property_len % sizeof (pci_class_val_t)) == 0)
1264614edcaeSEvan Yan 		val = pci_match_class_val(class_code, conf,
1265614edcaeSEvan Yan 		    property_len / sizeof (pci_class_val_t), val);
1266614edcaeSEvan Yan 	kmem_free(conf, property_len);
1267614edcaeSEvan Yan 	return (val);
1268614edcaeSEvan Yan }
1269614edcaeSEvan Yan 
1270614edcaeSEvan Yan /*
1271614edcaeSEvan Yan  * pci_class_to_pil:
1272614edcaeSEvan Yan  *
1273614edcaeSEvan Yan  * Return the pil for a given PCI device.
1274614edcaeSEvan Yan  */
1275614edcaeSEvan Yan uint32_t
pci_class_to_pil(dev_info_t * rdip)1276614edcaeSEvan Yan pci_class_to_pil(dev_info_t *rdip)
1277614edcaeSEvan Yan {
1278614edcaeSEvan Yan 	uint32_t pil;
1279614edcaeSEvan Yan 
1280614edcaeSEvan Yan 	/* Default pil is 1 */
1281614edcaeSEvan Yan 	pil = pci_class_to_val(rdip,
1282614edcaeSEvan Yan 	    "pci-class-priorities", pci_default_pil,
1283614edcaeSEvan Yan 	    sizeof (pci_default_pil) / sizeof (pci_class_val_t), 1);
1284614edcaeSEvan Yan 
1285614edcaeSEvan Yan 	/* Range check the result */
1286614edcaeSEvan Yan 	if (pil >= 0xf)
1287614edcaeSEvan Yan 		pil = 1;
1288614edcaeSEvan Yan 
1289614edcaeSEvan Yan 	return (pil);
1290614edcaeSEvan Yan }
1291614edcaeSEvan Yan 
1292614edcaeSEvan Yan /*
1293614edcaeSEvan Yan  * pci_class_to_intr_weight:
1294614edcaeSEvan Yan  *
1295614edcaeSEvan Yan  * Return the intr_weight for a given PCI device.
1296614edcaeSEvan Yan  */
1297614edcaeSEvan Yan int32_t
pci_class_to_intr_weight(dev_info_t * rdip)1298614edcaeSEvan Yan pci_class_to_intr_weight(dev_info_t *rdip)
1299614edcaeSEvan Yan {
1300614edcaeSEvan Yan 	int32_t intr_weight;
1301614edcaeSEvan Yan 
1302614edcaeSEvan Yan 	/* default weight is 0% */
1303614edcaeSEvan Yan 	intr_weight = pci_class_to_val(rdip,
1304614edcaeSEvan Yan 	    "pci-class-intr-weights", pci_default_intr_weight,
1305614edcaeSEvan Yan 	    sizeof (pci_default_intr_weight) / sizeof (pci_class_val_t), 0);
1306614edcaeSEvan Yan 
1307614edcaeSEvan Yan 	/* range check the result */
1308614edcaeSEvan Yan 	if (intr_weight < 0)
1309614edcaeSEvan Yan 		intr_weight = 0;
1310614edcaeSEvan Yan 	if (intr_weight > 1000)
1311614edcaeSEvan Yan 		intr_weight = 1000;
1312614edcaeSEvan Yan 
1313614edcaeSEvan Yan 	return (intr_weight);
1314614edcaeSEvan Yan }
1315