OpenSolaris_b135/uts/common/io/pci_intr_lib.c

Compare this file to the similar file:
Show the results in this format:

/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License (the "License").
 * You may not use this file except in compliance with the License.
 *
 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 * or http://www.opensolaris.org/os/licensing.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information: Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 */
/*
 * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */

/*
 * Support for MSI, MSIX and INTx
 */

#include <sys/conf.h>
#include <sys/debug.h>
#include <sys/pci.h>
#include <sys/pci_cap.h>
#include <sys/pci_intr_lib.h>
#include <sys/sunddi.h>
#include <sys/bitmap.h>

/*
 * MSI-X BIR Index Table:
 *
 * BAR indicator register (BIR) to Base Address register.
 */
static	uchar_t pci_msix_bir_index[8] = {0x10, 0x14, 0x18, 0x1c,
					0x20, 0x24, 0xff, 0xff};

/* default class to pil value mapping */
pci_class_val_t pci_default_pil [] = {
	{0x000000, 0xff0000, 0x1},	/* Class code for pre-2.0 devices */
	{0x010000, 0xff0000, 0x5},	/* Mass Storage Controller */
	{0x020000, 0xff0000, 0x6},	/* Network Controller */
	{0x030000, 0xff0000, 0x9},	/* Display Controller */
	{0x040000, 0xff0000, 0x8},	/* Multimedia Controller */
	{0x050000, 0xff0000, 0x9},	/* Memory Controller */
	{0x060000, 0xff0000, 0x9},	/* Bridge Controller */
	{0x0c0000, 0xffff00, 0x9},	/* Serial Bus, FireWire (IEEE 1394) */
	{0x0c0100, 0xffff00, 0x4},	/* Serial Bus, ACCESS.bus */
	{0x0c0200, 0xffff00, 0x4},	/* Serial Bus, SSA */
	{0x0c0300, 0xffff00, 0x9},	/* Serial Bus Universal Serial Bus */
/*
 * XXX - This is a temporary workaround and it will be removed
 *       after x86 interrupt scalability support.
 */
#if defined(__i386) || defined(__amd64)
	{0x0c0400, 0xffff00, 0x5},	/* Serial Bus, Fibre Channel */
#else
	{0x0c0400, 0xffff00, 0x6},	/* Serial Bus, Fibre Channel */
#endif
	{0x0c0600, 0xffff00, 0x6}	/* Serial Bus, Infiniband */
};

/*
 * Default class to intr_weight value mapping (% of CPU).  A driver.conf
 * entry on or above the pci node like
 *
 *	pci-class-intr-weights= 0x020000, 0xff0000, 30;
 *
 * can be used to augment or override entries in the default table below.
 *
 * NB: The values below give NICs preference on redistribution, and provide
 * NICs some isolation from other interrupt sources. We need better interfaces
 * that allow the NIC driver to identify a specific NIC instance as high
 * bandwidth, and thus deserving of separation from other low bandwidth
 * NICs additional isolation from other interrupt sources.
 *
 * NB: We treat Infiniband like a NIC.
 */
pci_class_val_t pci_default_intr_weight [] = {
	{0x020000, 0xff0000, 35},	/* Network Controller */
	{0x010000, 0xff0000, 10},	/* Mass Storage Controller */
	{0x0c0400, 0xffff00, 10},	/* Serial Bus, Fibre Channel */
	{0x0c0600, 0xffff00, 50}	/* Serial Bus, Infiniband */
};

/*
 * Library utility functions
 */

/*
 * pci_get_msi_ctrl:
 *
 *	Helper function that returns with 'cfg_hdl', MSI/X ctrl pointer,
 *	and caps_ptr for MSI/X if these are found.
 */
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)
{
	*msi_ctrl = *caps_ptr = 0;

	if (pci_config_setup(dip, h) != DDI_SUCCESS) {
		DDI_INTR_NEXDBG((CE_CONT, "pci_get_msi_ctrl: "
		    "%s%d can't get config handle",
		    ddi_driver_name(dip), ddi_get_instance(dip)));

		return (DDI_FAILURE);
	}

	if ((PCI_CAP_LOCATE(*h, PCI_CAP_ID_MSI, caps_ptr) == DDI_SUCCESS) &&
	    (type == DDI_INTR_TYPE_MSI)) {
		if ((*msi_ctrl = PCI_CAP_GET16(*h, NULL, *caps_ptr,
		    PCI_MSI_CTRL)) == PCI_CAP_EINVAL16)
			goto done;

		DDI_INTR_NEXDBG((CE_CONT, "pci_get_msi_ctrl: MSI "
		    "caps_ptr=%x msi_ctrl=%x\n", *caps_ptr, *msi_ctrl));

		return (DDI_SUCCESS);
	}

	if ((PCI_CAP_LOCATE(*h, PCI_CAP_ID_MSI_X, caps_ptr) == DDI_SUCCESS) &&
	    (type == DDI_INTR_TYPE_MSIX)) {
		if ((*msi_ctrl = PCI_CAP_GET16(*h, NULL, *caps_ptr,
		    PCI_MSIX_CTRL)) == PCI_CAP_EINVAL16)
			goto done;

		DDI_INTR_NEXDBG((CE_CONT, "pci_get_msi_ctrl: MSI-X "
		    "caps_ptr=%x msi_ctrl=%x\n", *caps_ptr, *msi_ctrl));

		return (DDI_SUCCESS);
	}

done:
	pci_config_teardown(h);
	return (DDI_FAILURE);
}


/*
 * pci_msi_get_cap:
 *
 * Get the capabilities of the MSI/X interrupt
 */
int
pci_msi_get_cap(dev_info_t *rdip, int type, int *flagsp)
{
	ushort_t		caps_ptr, msi_ctrl;
	ddi_acc_handle_t	cfg_hdle;

	DDI_INTR_NEXDBG((CE_CONT, "pci_msi_get_cap: rdip = 0x%p\n",
	    (void *)rdip));

	*flagsp = 0;

	if (pci_get_msi_ctrl(rdip, type, &msi_ctrl,
	    &caps_ptr, &cfg_hdle) != DDI_SUCCESS)
		return (DDI_FAILURE);

	if (type == DDI_INTR_TYPE_MSI) {
		if (msi_ctrl &  PCI_MSI_64BIT_MASK)
			*flagsp |= DDI_INTR_FLAG_MSI64;
		if (msi_ctrl & PCI_MSI_PVM_MASK)
			*flagsp |= (DDI_INTR_FLAG_MASKABLE |
			    DDI_INTR_FLAG_PENDING);
		else
			*flagsp |= DDI_INTR_FLAG_BLOCK;
	} else if (type == DDI_INTR_TYPE_MSIX) {
		/* MSI-X supports PVM, 64bit by default */
		*flagsp |= (DDI_INTR_FLAG_MASKABLE | DDI_INTR_FLAG_MSI64 |
		    DDI_INTR_FLAG_PENDING);
	}

	*flagsp |= DDI_INTR_FLAG_EDGE;

	DDI_INTR_NEXDBG((CE_CONT, "pci_msi_get_cap: flags = 0x%x\n", *flagsp));

	pci_config_teardown(&cfg_hdle);
	return (DDI_SUCCESS);
}


/*
 * pci_msi_configure:
 *
 * Configure address/data and number MSI/Xs fields in the MSI/X
 * capability structure.
 */
/* ARGSUSED */
int
pci_msi_configure(dev_info_t *rdip, int type, int count, int inum,
    uint64_t addr, uint64_t data)
{
	ushort_t		caps_ptr, msi_ctrl;
	ddi_acc_handle_t	h;

	DDI_INTR_NEXDBG((CE_CONT, "pci_msi_configure: rdip = 0x%p type 0x%x "
	    "count 0x%x inum 0x%x addr 0x%" PRIx64 " data 0x%" PRIx64 "\n",
	    (void *)rdip, type, count, inum, addr, data));

	if (pci_get_msi_ctrl(rdip, type, &msi_ctrl,
	    &caps_ptr, &h) != DDI_SUCCESS)
		return (DDI_FAILURE);

	if (type == DDI_INTR_TYPE_MSI) {
		/* Set the bits to inform how many MSIs are enabled */
		msi_ctrl |= ((highbit(count) -1) << PCI_MSI_MME_SHIFT);
		PCI_CAP_PUT16(h, NULL, caps_ptr, PCI_MSI_CTRL, msi_ctrl);

		DDI_INTR_NEXDBG((CE_CONT, "pci_msi_configure: msi_ctrl = %x\n",
		    PCI_CAP_GET16(h, NULL, caps_ptr, PCI_MSI_CTRL)));

		/* Set the "data" and "addr" bits */
		PCI_CAP_PUT32(h, NULL, caps_ptr, PCI_MSI_ADDR_OFFSET, addr);

		DDI_INTR_NEXDBG((CE_CONT, "pci_msi_configure: msi_addr = %x\n",
		    PCI_CAP_GET32(h, NULL, caps_ptr, PCI_MSI_ADDR_OFFSET)));

		if (msi_ctrl &  PCI_MSI_64BIT_MASK) {
			PCI_CAP_PUT32(h, NULL, caps_ptr, PCI_MSI_ADDR_OFFSET
			    + 4, addr >> 32);

			DDI_INTR_NEXDBG((CE_CONT, "pci_msi_configure: upper "
			    "32bit msi_addr = %x\n", PCI_CAP_GET32(h, NULL,
			    caps_ptr, PCI_MSI_ADDR_OFFSET + 4)));

			PCI_CAP_PUT16(h, NULL, caps_ptr, PCI_MSI_64BIT_DATA,
			    data);

			DDI_INTR_NEXDBG((CE_CONT, "pci_msi_configure: msi_data "
			    "= %x\n", PCI_CAP_GET16(h, NULL, caps_ptr,
			    PCI_MSI_64BIT_DATA)));
		} else {
			PCI_CAP_PUT16(h, NULL, caps_ptr, PCI_MSI_32BIT_DATA,
			    data);

			DDI_INTR_NEXDBG((CE_CONT, "pci_msi_configure: msi_data "
			    "= %x\n", PCI_CAP_GET16(h, NULL, caps_ptr,
			    PCI_MSI_32BIT_DATA)));
		}
	} else if (type == DDI_INTR_TYPE_MSIX) {
		uintptr_t	off;
		ddi_intr_msix_t	*msix_p = i_ddi_get_msix(rdip);

		/* Offset into the "inum"th entry in the MSI-X table */
		off = (uintptr_t)msix_p->msix_tbl_addr +
		    (inum * PCI_MSIX_VECTOR_SIZE);

		/* Set the "data" and "addr" bits */
		ddi_put32(msix_p->msix_tbl_hdl,
		    (uint32_t *)(off + PCI_MSIX_DATA_OFFSET), data);

		ddi_put64(msix_p->msix_tbl_hdl,
		    (uint64_t *)(off + PCI_MSIX_LOWER_ADDR_OFFSET), addr);

		DDI_INTR_NEXDBG((CE_CONT, "pci_msi_configure: "
		    "msix_addr 0x%" PRIx64 " msix_data 0x%x\n",
		    ddi_get64(msix_p->msix_tbl_hdl,
		    (uint64_t *)(off + PCI_MSIX_LOWER_ADDR_OFFSET)),
		    ddi_get32(msix_p->msix_tbl_hdl,
		    (uint32_t *)(off + PCI_MSIX_DATA_OFFSET))));
	}

	pci_config_teardown(&h);
	return (DDI_SUCCESS);
}


/*
 * pci_msi_unconfigure:
 *
 * Unconfigure address/data and number MSI/Xs fields in the MSI/X
 * capability structure.
 */
/* ARGSUSED */
int
pci_msi_unconfigure(dev_info_t *rdip, int type, int inum)
{
	ushort_t		msi_ctrl, caps_ptr;
	ddi_acc_handle_t	h;

	DDI_INTR_NEXDBG((CE_CONT, "pci_msi_unconfigure: rdip = 0x%p type 0x%x "
	    "inum 0x%x\n", (void *)rdip, type, inum));

	if (pci_get_msi_ctrl(rdip, type, &msi_ctrl, &caps_ptr, &h) !=
	    DDI_SUCCESS)
		return (DDI_FAILURE);

	if (type == DDI_INTR_TYPE_MSI) {
		msi_ctrl &= (~PCI_MSI_MME_MASK);
		PCI_CAP_PUT16(h, NULL, caps_ptr, PCI_MSI_CTRL, msi_ctrl);

		PCI_CAP_PUT32(h, NULL, caps_ptr, PCI_MSI_ADDR_OFFSET, 0);

		if (msi_ctrl &  PCI_MSI_64BIT_MASK) {
			PCI_CAP_PUT16(h, NULL, caps_ptr, PCI_MSI_64BIT_DATA,
			    0);
			PCI_CAP_PUT32(h, NULL, caps_ptr, PCI_MSI_ADDR_OFFSET
			    + 4, 0);
		} else {
			PCI_CAP_PUT16(h, NULL, caps_ptr, PCI_MSI_32BIT_DATA,
			    0);
		}

		DDI_INTR_NEXDBG((CE_CONT, "pci_msi_unconfigure: msi_ctrl "
		    "= %x\n", PCI_CAP_GET16(h, NULL, caps_ptr, PCI_MSI_CTRL)));

	} else if (type == DDI_INTR_TYPE_MSIX) {
		uintptr_t	off;
		ddi_intr_msix_t	*msix_p = i_ddi_get_msix(rdip);

		/* Offset into the "inum"th entry in the MSI-X table */
		off = (uintptr_t)msix_p->msix_tbl_addr +
		    (inum * PCI_MSIX_VECTOR_SIZE);

		/* Reset the "data" and "addr" bits */
		ddi_put32(msix_p->msix_tbl_hdl,
		    (uint32_t *)(off + PCI_MSIX_DATA_OFFSET), 0);

		ddi_put64(msix_p->msix_tbl_hdl,
		    (uint64_t *)(off + PCI_MSIX_LOWER_ADDR_OFFSET), 0);
	}

	pci_config_teardown(&h);
	return (DDI_SUCCESS);
}


/*
 * pci_is_msi_enabled:
 *
 * This function returns DDI_SUCCESS if MSI/X is already enabled, otherwise
 * it returns DDI_FAILURE.
 */
int
pci_is_msi_enabled(dev_info_t *rdip, int type)
{
	ushort_t		caps_ptr, msi_ctrl;
	ddi_acc_handle_t	cfg_hdle;
	int			ret = DDI_FAILURE;

	DDI_INTR_NEXDBG((CE_CONT, "pci_is_msi_enabled: rdip = 0x%p, "
	    "type  = 0x%x\n", (void *)rdip, type));

	if (pci_get_msi_ctrl(rdip, type, &msi_ctrl,
	    &caps_ptr, &cfg_hdle) != DDI_SUCCESS)
		return (DDI_FAILURE);

	if ((type == DDI_INTR_TYPE_MSI) && (msi_ctrl & PCI_MSI_ENABLE_BIT))
		ret = DDI_SUCCESS;

	if ((type == DDI_INTR_TYPE_MSIX) && (msi_ctrl & PCI_MSIX_ENABLE_BIT))
		ret = DDI_SUCCESS;

	pci_config_teardown(&cfg_hdle);
	return (ret);
}


/*
 * pci_msi_enable_mode:
 *
 * This function sets the MSI_ENABLE bit in the capability structure
 * (for MSI) and MSIX_ENABLE bit in the MSI-X capability structure.
 *
 * NOTE: It is the nexus driver's responsibility to clear the MSI/X
 * interrupt's mask bit in the MSI/X capability structure before the
 * interrupt can be used.
 */
int
pci_msi_enable_mode(dev_info_t *rdip, int type)
{
	ushort_t		caps_ptr, msi_ctrl;
	ddi_acc_handle_t	cfg_hdle;

	DDI_INTR_NEXDBG((CE_CONT, "pci_msi_enable_mode: rdip = 0x%p\n",
	    (void *)rdip));

	if (pci_get_msi_ctrl(rdip, type, &msi_ctrl,
	    &caps_ptr, &cfg_hdle) != DDI_SUCCESS)
		return (DDI_FAILURE);

	if (type == DDI_INTR_TYPE_MSI) {
		if (msi_ctrl & PCI_MSI_ENABLE_BIT)
			goto finished;

		msi_ctrl |= PCI_MSI_ENABLE_BIT;
		PCI_CAP_PUT16(cfg_hdle, NULL, caps_ptr, PCI_MSI_CTRL, msi_ctrl);

	} else if (type == DDI_INTR_TYPE_MSIX) {
		if (msi_ctrl & PCI_MSIX_ENABLE_BIT)
			goto finished;

		msi_ctrl |= PCI_MSIX_ENABLE_BIT;
		PCI_CAP_PUT16(cfg_hdle, NULL, caps_ptr, PCI_MSIX_CTRL,
		    msi_ctrl);
	}

finished:
	DDI_INTR_NEXDBG((CE_CONT, "pci_msi_enable_mode: msi_ctrl = %x\n",
	    msi_ctrl));

	pci_config_teardown(&cfg_hdle);
	return (DDI_SUCCESS);
}


/*
 * pci_msi_disable_mode:
 *
 * This function resets the MSI_ENABLE bit in the capability structure
 * (for MSI) and MSIX_ENABLE bit in the MSI-X capability structure.
 *
 * NOTE: It is the nexus driver's responsibility to set the MSI/X
 * interrupt's mask bit in the MSI/X capability structure before the
 * interrupt can be disabled.
 */
int
pci_msi_disable_mode(dev_info_t *rdip, int type)
{
	ushort_t		caps_ptr, msi_ctrl;
	ddi_acc_handle_t	cfg_hdle;

	DDI_INTR_NEXDBG((CE_CONT, "pci_msi_disable_mode: rdip = 0x%p\n",
	    (void *)rdip));

	if (pci_get_msi_ctrl(rdip, type, &msi_ctrl,
	    &caps_ptr, &cfg_hdle) != DDI_SUCCESS)
		return (DDI_FAILURE);

	/* Reset the "enable" bit */
	if (type == DDI_INTR_TYPE_MSI) {
		if (!(msi_ctrl & PCI_MSI_ENABLE_BIT))
			goto finished;
		msi_ctrl &= ~PCI_MSI_ENABLE_BIT;
		PCI_CAP_PUT16(cfg_hdle, NULL, caps_ptr, PCI_MSI_CTRL, msi_ctrl);
	} else if (type == DDI_INTR_TYPE_MSIX) {
		if (!(msi_ctrl & PCI_MSIX_ENABLE_BIT))
			goto finished;

		msi_ctrl &= ~PCI_MSIX_ENABLE_BIT;
		PCI_CAP_PUT16(cfg_hdle, NULL, caps_ptr, PCI_MSIX_CTRL,
		    msi_ctrl);
	}

finished:
	DDI_INTR_NEXDBG((CE_CONT, "pci_msi_disable_mode: msi_ctrl = %x\n",
	    msi_ctrl));

	pci_config_teardown(&cfg_hdle);
	return (DDI_SUCCESS);
}


/*
 * pci_msi_set_mask:
 *
 * Set the mask bit in the MSI/X capability structure
 */
/* ARGSUSED */
int
pci_msi_set_mask(dev_info_t *rdip, int type, int inum)
{
	int			offset;
	int			ret = DDI_FAILURE;
	ushort_t		caps_ptr, msi_ctrl;
	ddi_acc_handle_t	cfg_hdle;
	uint32_t		mask_bits;

	DDI_INTR_NEXDBG((CE_CONT, "pci_msi_set_mask: rdip = 0x%p, "
	    "type = 0x%x\n", (void *)rdip, type));

	if (pci_get_msi_ctrl(rdip, type, &msi_ctrl,
	    &caps_ptr, &cfg_hdle) != DDI_SUCCESS)
		return (DDI_FAILURE);

	if (type == DDI_INTR_TYPE_MSI) {
		if (!(msi_ctrl &  PCI_MSI_PVM_MASK))
			goto done;

		offset = (msi_ctrl &  PCI_MSI_64BIT_MASK) ?
		    PCI_MSI_64BIT_MASKBITS : PCI_MSI_32BIT_MASK;

		if ((mask_bits = PCI_CAP_GET32(cfg_hdle, NULL, caps_ptr,
		    offset)) == PCI_CAP_EINVAL32)
			goto done;

		mask_bits |= (1 << inum);

		PCI_CAP_PUT32(cfg_hdle, NULL, caps_ptr, offset, mask_bits);

	} else if (type == DDI_INTR_TYPE_MSIX) {
		uintptr_t		off;
		ddi_intr_msix_t		*msix_p;

		/* Set function mask */
		if (msi_ctrl & PCI_MSIX_FUNCTION_MASK) {
			ret = DDI_SUCCESS;
			goto done;
		}

		msix_p = i_ddi_get_msix(rdip);

		/* Offset into the "inum"th entry in the MSI-X table */
		off = (uintptr_t)msix_p->msix_tbl_addr + (inum *
		    PCI_MSIX_VECTOR_SIZE) + PCI_MSIX_VECTOR_CTRL_OFFSET;

		/* Set the Mask bit */
		ddi_put32(msix_p->msix_tbl_hdl, (uint32_t *)off, 0x1);
	}

	ret = DDI_SUCCESS;
done:
	pci_config_teardown(&cfg_hdle);
	return (ret);
}


/*
 * pci_msi_clr_mask:
 *
 * Clear the mask bit in the MSI/X capability structure
 */
/* ARGSUSED */
int
pci_msi_clr_mask(dev_info_t *rdip, int type, int inum)
{
	ushort_t		caps_ptr, msi_ctrl;
	ddi_acc_handle_t	cfg_hdle;
	int			offset;
	int			ret = DDI_FAILURE;
	uint32_t		mask_bits;

	DDI_INTR_NEXDBG((CE_CONT, "pci_msi_clr_mask: rdip = 0x%p, "
	    "type = 0x%x\n", (void *)rdip, type));

	if (pci_get_msi_ctrl(rdip, type, &msi_ctrl,
	    &caps_ptr, &cfg_hdle) != DDI_SUCCESS)
		return (DDI_FAILURE);

	if (type == DDI_INTR_TYPE_MSI) {
		if (!(msi_ctrl &  PCI_MSI_PVM_MASK))
			goto done;

		offset = (msi_ctrl &  PCI_MSI_64BIT_MASK) ?
		    PCI_MSI_64BIT_MASKBITS : PCI_MSI_32BIT_MASK;
		if ((mask_bits = PCI_CAP_GET32(cfg_hdle, NULL, caps_ptr,
		    offset)) == PCI_CAP_EINVAL32)
			goto done;

		mask_bits &= ~(1 << inum);

		PCI_CAP_PUT32(cfg_hdle, NULL, caps_ptr, offset, mask_bits);

	} else if (type == DDI_INTR_TYPE_MSIX) {
		uintptr_t		off;
		ddi_intr_msix_t		*msix_p;

		if (msi_ctrl & PCI_MSIX_FUNCTION_MASK) {
			ret = DDI_SUCCESS;
			goto done;
		}

		msix_p = i_ddi_get_msix(rdip);

		/* Offset into the "inum"th entry in the MSI-X table */
		off = (uintptr_t)msix_p->msix_tbl_addr + (inum *
		    PCI_MSIX_VECTOR_SIZE) + PCI_MSIX_VECTOR_CTRL_OFFSET;

		/* Clear the Mask bit */
		ddi_put32(msix_p->msix_tbl_hdl, (uint32_t *)off, 0x0);
	}

	ret = DDI_SUCCESS;
done:
	pci_config_teardown(&cfg_hdle);
	return (ret);
}


/*
 * pci_msi_get_pending:
 *
 * Get the pending bit from the MSI/X capability structure
 */
/* ARGSUSED */
int
pci_msi_get_pending(dev_info_t *rdip, int type, int inum, int *pendingp)
{
	ushort_t		caps_ptr, msi_ctrl;
	ddi_acc_handle_t	cfg_hdle;
	int			offset;
	int			ret = DDI_FAILURE;

	DDI_INTR_NEXDBG((CE_CONT, "pci_msi_get_pending: rdip = 0x%p\n",
	    (void *)rdip));

	if (pci_get_msi_ctrl(rdip, type, &msi_ctrl,
	    &caps_ptr, &cfg_hdle) != DDI_SUCCESS)
		return (DDI_FAILURE);

	if (type == DDI_INTR_TYPE_MSI) {
		uint32_t	pending_bits;

		if (!(msi_ctrl &  PCI_MSI_PVM_MASK)) {
			DDI_INTR_NEXDBG((CE_CONT, "pci_msi_get_pending: "
			    "PVM is not supported\n"));
			goto done;
		}

		offset = (msi_ctrl &  PCI_MSI_64BIT_MASK) ?
		    PCI_MSI_64BIT_PENDING : PCI_MSI_32BIT_PENDING;

		if ((pending_bits = PCI_CAP_GET32(cfg_hdle, NULL, caps_ptr,
		    offset)) == PCI_CAP_EINVAL32)
			goto done;

		*pendingp = pending_bits & ~(1 >> inum);

	} else if (type == DDI_INTR_TYPE_MSIX) {
		uintptr_t	off;
		uint64_t	pending_bits;
		ddi_intr_msix_t	*msix_p = i_ddi_get_msix(rdip);

		/* Offset into the PBA array which has entry for "inum" */
		off = (uintptr_t)msix_p->msix_pba_addr + (inum / 64);

		/* Read the PBA array */
		pending_bits = ddi_get64(msix_p->msix_pba_hdl, (uint64_t *)off);

		*pendingp = pending_bits & ~(1 >> inum);
	}

	ret = DDI_SUCCESS;
done:
	pci_config_teardown(&cfg_hdle);
	return (ret);
}


/*
 * pci_msi_get_nintrs:
 *
 * For a given type (MSI/X) returns the number of interrupts supported
 */
int
pci_msi_get_nintrs(dev_info_t *rdip, int type, int *nintrs)
{
	ushort_t		caps_ptr, msi_ctrl;
	ddi_acc_handle_t	cfg_hdle;

	DDI_INTR_NEXDBG((CE_CONT, "pci_msi_get_nintrs: rdip = 0x%p\n",
	    (void *)rdip));

	if (pci_get_msi_ctrl(rdip, type, &msi_ctrl,
	    &caps_ptr, &cfg_hdle) != DDI_SUCCESS)
		return (DDI_FAILURE);

	if (type == DDI_INTR_TYPE_MSI) {
		*nintrs = 1 << ((msi_ctrl & PCI_MSI_MMC_MASK) >>
		    PCI_MSI_MMC_SHIFT);
	} else if (type == DDI_INTR_TYPE_MSIX) {
		if (msi_ctrl &  PCI_MSIX_TBL_SIZE_MASK)
			*nintrs = (msi_ctrl & PCI_MSIX_TBL_SIZE_MASK) + 1;
	}

	DDI_INTR_NEXDBG((CE_CONT, "pci_msi_get_nintrs: "
	    "nintr = 0x%x\n", *nintrs));

	pci_config_teardown(&cfg_hdle);
	return (DDI_SUCCESS);
}


/*
 * pci_msi_set_nintrs:
 *
 * For a given type (MSI/X) sets the number of interrupts supported
 * by the system.
 * For MSI: Return an error if this func is called for navail > 32
 * For MSI-X: Return an error if this func is called for navail > 2048
 */
int
pci_msi_set_nintrs(dev_info_t *rdip, int type, int navail)
{
	ushort_t		caps_ptr, msi_ctrl;
	ddi_acc_handle_t	cfg_hdle;

	DDI_INTR_NEXDBG((CE_CONT, "pci_msi_set_nintrs: rdip = 0x%p, "
	    "navail = 0x%x\n", (void *)rdip, navail));

	/* Check for valid input argument */
	if (((type == DDI_INTR_TYPE_MSI) && (navail > PCI_MSI_MAX_INTRS)) ||
	    ((type == DDI_INTR_TYPE_MSIX) && (navail >  PCI_MSIX_MAX_INTRS)))
		return (DDI_EINVAL);

	if (pci_get_msi_ctrl(rdip, type, &msi_ctrl,
	    &caps_ptr, &cfg_hdle) != DDI_SUCCESS)
		return (DDI_FAILURE);

	if (type == DDI_INTR_TYPE_MSI) {
		msi_ctrl |= ((highbit(navail) -1) << PCI_MSI_MME_SHIFT);

		PCI_CAP_PUT16(cfg_hdle, NULL, caps_ptr, PCI_MSI_CTRL, msi_ctrl);
	} else if (type == DDI_INTR_TYPE_MSIX) {
		DDI_INTR_NEXDBG((CE_CONT, "pci_msi_set_nintrs: unsupported\n"));
	}

	pci_config_teardown(&cfg_hdle);
	return (DDI_SUCCESS);
}


/*
 * pci_msi_get_supported_type:
 *
 * Returns DDI_INTR_TYPE_MSI and/or DDI_INTR_TYPE_MSIX as supported
 * types if device supports them. A DDI_FAILURE is returned otherwise.
 */
int
pci_msi_get_supported_type(dev_info_t *rdip, int *typesp)
{
	ushort_t		caps_ptr, msi_ctrl;
	ddi_acc_handle_t	cfg_hdle;

	DDI_INTR_NEXDBG((CE_CONT, "pci_msi_get_supported_type: "
	    "rdip = 0x%p\n", (void *)rdip));

	*typesp = 0;

	if (pci_get_msi_ctrl(rdip, DDI_INTR_TYPE_MSI, &msi_ctrl,
	    &caps_ptr, &cfg_hdle) == DDI_SUCCESS) {
		*typesp |= DDI_INTR_TYPE_MSI;
		pci_config_teardown(&cfg_hdle);
	}

	if (pci_get_msi_ctrl(rdip, DDI_INTR_TYPE_MSIX, &msi_ctrl,
	    &caps_ptr, &cfg_hdle) == DDI_SUCCESS) {
		*typesp |= DDI_INTR_TYPE_MSIX;
		pci_config_teardown(&cfg_hdle);
	}

	DDI_INTR_NEXDBG((CE_CONT, "pci_msi_get_supported_type: "
	    "rdip = 0x%p types 0x%x\n", (void *)rdip, *typesp));

	return (*typesp == 0 ? DDI_FAILURE : DDI_SUCCESS);
}


/*
 * pci_msix_init:
 *	This function initializes the various handles/addrs etc.
 *	needed for MSI-X support. It also allocates a private
 *	structure to keep track of these.
 */
ddi_intr_msix_t *
pci_msix_init(dev_info_t *rdip)
{
	uint_t			rnumber, breg, nregs;
	size_t			msix_tbl_size;
	size_t			pba_tbl_size;
	ushort_t		caps_ptr, msix_ctrl;
	ddi_intr_msix_t		*msix_p;
	ddi_acc_handle_t	cfg_hdle;
	pci_regspec_t		*rp;
	int			reg_size, addr_space, offset, *regs_list;
	int			i, ret;

	DDI_INTR_NEXDBG((CE_CONT, "pci_msix_init: rdip = %p\n", (void *)rdip));

	if (pci_get_msi_ctrl(rdip, DDI_INTR_TYPE_MSIX, &msix_ctrl,
	    &caps_ptr, &cfg_hdle) != DDI_SUCCESS)
		return (NULL);

	msix_p = kmem_zalloc(sizeof (ddi_intr_msix_t), KM_SLEEP);

	/*
	 * Initialize the devacc structure
	 */
	msix_p->msix_dev_attr.devacc_attr_version = DDI_DEVICE_ATTR_V0;
	msix_p->msix_dev_attr.devacc_attr_endian_flags =
	    DDI_STRUCTURE_LE_ACC;
	msix_p->msix_dev_attr.devacc_attr_dataorder = DDI_STRICTORDER_ACC;

	/* Map the entire MSI-X vector table */
	msix_p->msix_tbl_offset = PCI_CAP_GET32(cfg_hdle, NULL, caps_ptr,
	    PCI_MSIX_TBL_OFFSET);

	if ((breg = pci_msix_bir_index[msix_p->msix_tbl_offset &
	    PCI_MSIX_TBL_BIR_MASK]) == 0xff)
		goto fail1;

	msix_p->msix_tbl_offset = msix_p->msix_tbl_offset &
	    ~PCI_MSIX_TBL_BIR_MASK;
	msix_tbl_size = ((msix_ctrl & PCI_MSIX_TBL_SIZE_MASK) + 1) *
	    PCI_MSIX_VECTOR_SIZE;

	DDI_INTR_NEXDBG((CE_CONT, "pci_msix_init: MSI-X table offset 0x%x "
	    "breg 0x%x size 0x%lx\n", msix_p->msix_tbl_offset, breg,
	    msix_tbl_size));

	if ((ret = ddi_prop_lookup_int_array(DDI_DEV_T_ANY, rdip,
	    DDI_PROP_DONTPASS, "reg", (int **)&regs_list, &nregs))
	    != DDI_PROP_SUCCESS) {
		DDI_INTR_NEXDBG((CE_CONT, "pci_msix_init: "
		    "ddi_prop_lookup_int_array failed %d\n", ret));

		goto fail1;
	}

	reg_size = sizeof (pci_regspec_t) / sizeof (int);

	for (i = 1, rnumber = 0; i < nregs/reg_size; i++) {
		rp = (pci_regspec_t *)&regs_list[i * reg_size];
		addr_space = rp->pci_phys_hi & PCI_ADDR_MASK;
		offset = PCI_REG_REG_G(rp->pci_phys_hi);

		if ((offset == breg) && ((addr_space == PCI_ADDR_MEM32) ||
		    (addr_space == PCI_ADDR_MEM64))) {
			rnumber = i;
			break;
		}
	}

	DDI_INTR_NEXDBG((CE_CONT, "pci_msix_init: MSI-X rnum = %d\n", rnumber));

	if (rnumber == 0) {
		DDI_INTR_NEXDBG((CE_CONT, "pci_msix_init: "
		    "no mtaching reg number for offset 0x%x\n", breg));

		goto fail2;
	}

	if ((ret = ddi_regs_map_setup(rdip, rnumber,
	    (caddr_t *)&msix_p->msix_tbl_addr, msix_p->msix_tbl_offset,
	    msix_tbl_size, &msix_p->msix_dev_attr,
	    &msix_p->msix_tbl_hdl)) != DDI_SUCCESS) {
		DDI_INTR_NEXDBG((CE_CONT, "pci_msix_init: MSI-X Table "
		    "ddi_regs_map_setup failed %d\n", ret));

		goto fail2;
	}

	/*
	 * Map in the MSI-X Pending Bit Array
	 */
	msix_p->msix_pba_offset = PCI_CAP_GET32(cfg_hdle, NULL, caps_ptr,
	    PCI_MSIX_PBA_OFFSET);

	if ((breg = pci_msix_bir_index[msix_p->msix_pba_offset &
	    PCI_MSIX_PBA_BIR_MASK]) == 0xff)
		goto fail3;

	msix_p->msix_pba_offset = msix_p->msix_pba_offset &
	    ~PCI_MSIX_PBA_BIR_MASK;
	pba_tbl_size = ((msix_ctrl & PCI_MSIX_TBL_SIZE_MASK) + 1)/8;

	DDI_INTR_NEXDBG((CE_CONT, "pci_msix_init: PBA table offset 0x%x "
	    "breg 0x%x size 0x%lx\n", msix_p->msix_pba_offset, breg,
	    pba_tbl_size));

	for (i = 1, rnumber = 0; i < nregs/reg_size; i++) {
		rp = (pci_regspec_t *)&regs_list[i * reg_size];
		addr_space = rp->pci_phys_hi & PCI_ADDR_MASK;
		offset = PCI_REG_REG_G(rp->pci_phys_hi);

		if ((offset == breg) && ((addr_space == PCI_ADDR_MEM32) ||
		    (addr_space == PCI_ADDR_MEM64))) {
			rnumber = i;
			break;
		}
	}

	DDI_INTR_NEXDBG((CE_CONT, "pci_msix_init: PBA rnum = %d\n", rnumber));

	if (rnumber == 0) {
		DDI_INTR_NEXDBG((CE_CONT, "pci_msix_init: "
		    "no matching reg number for offset 0x%x\n", breg));

		goto fail3;
	}

	if ((ret = ddi_regs_map_setup(rdip, rnumber,
	    (caddr_t *)&msix_p->msix_pba_addr, msix_p->msix_pba_offset,
	    pba_tbl_size, &msix_p->msix_dev_attr,
	    &msix_p->msix_pba_hdl)) != DDI_SUCCESS) {
		DDI_INTR_NEXDBG((CE_CONT, "pci_msix_init: PBA "
		    "ddi_regs_map_setup failed %d\n", ret));

		goto fail3;
	}

	DDI_INTR_NEXDBG((CE_CONT, "pci_msix_init: msix_p = 0x%p DONE!!\n",
	    (void *)msix_p));

	ddi_prop_free(regs_list);
	goto done;

fail3:
	ddi_regs_map_free(&msix_p->msix_tbl_hdl);
fail2:
	ddi_prop_free(regs_list);
fail1:
	kmem_free(msix_p, sizeof (ddi_intr_msix_t));
	msix_p = NULL;
done:
	pci_config_teardown(&cfg_hdle);
	return (msix_p);
}


/*
 * pci_msix_fini:
 *	This function cleans up previously allocated handles/addrs etc.
 *	It is only called if no more MSI-X interrupts are being used.
 */
void
pci_msix_fini(ddi_intr_msix_t *msix_p)
{
	DDI_INTR_NEXDBG((CE_CONT, "pci_msix_fini: msix_p = 0x%p\n",
	    (void *)msix_p));

	ddi_regs_map_free(&msix_p->msix_pba_hdl);
	ddi_regs_map_free(&msix_p->msix_tbl_hdl);
	kmem_free(msix_p, sizeof (ddi_intr_msix_t));
}


/*
 * pci_msix_dup:
 *	This function duplicates the address and data pair of one msi-x
 *	vector to another msi-x vector.
 */
int
pci_msix_dup(dev_info_t *rdip, int org_inum, int dup_inum)
{
	ddi_intr_msix_t	*msix_p = i_ddi_get_msix(rdip);
	uint64_t	addr;
	uint64_t	data;
	uintptr_t	off;

	DDI_INTR_NEXDBG((CE_CONT, "pci_msix_dup: dip = %p, inum = 0x%x, "
	    "to_vector = 0x%x\n", (void *)rdip, org_inum, dup_inum));

	/* Offset into the original inum's entry in the MSI-X table */
	off = (uintptr_t)msix_p->msix_tbl_addr +
	    (org_inum * PCI_MSIX_VECTOR_SIZE);

	/* For the MSI-X number passed in, get the "data" and "addr" fields */
	addr = ddi_get64(msix_p->msix_tbl_hdl,
	    (uint64_t *)(off + PCI_MSIX_LOWER_ADDR_OFFSET));

	data = ddi_get32(msix_p->msix_tbl_hdl,
	    (uint32_t *)(off + PCI_MSIX_DATA_OFFSET));

	/* Program new vector with these existing values */
	return (pci_msi_configure(rdip, DDI_INTR_TYPE_MSIX, 1, dup_inum, addr,
	    data));
}


/*
 * Next set of routines are for INTx (legacy) PCI interrupt
 * support only.
 */

/*
 * pci_intx_get_cap:
 *	For non-MSI devices that comply to PCI v2.3 or greater;
 *	read the command register. Bit 10 implies interrupt disable.
 *	Set this bit and then read the status register bit 3.
 *	Bit 3 of status register is Interrupt state.
 *	If it is set; then the device supports 'Masking'
 *
 *	Reset the device back to the original state.
 */
int
pci_intx_get_cap(dev_info_t *dip, int *flagsp)
{
	uint16_t		cmdreg, savereg;
	ddi_acc_handle_t	cfg_hdl;
#ifdef	DEBUG
	uint16_t		statreg;
#endif /* DEBUG */

	*flagsp = 0;
	DDI_INTR_NEXDBG((CE_CONT, "pci_intx_get_cap: %s%d: called\n",
	    ddi_driver_name(dip), ddi_get_instance(dip)));

	if (pci_config_setup(dip, &cfg_hdl) != DDI_SUCCESS) {
		DDI_INTR_NEXDBG((CE_CONT, "pci_intx_get_cap: can't get "
		    "config handle\n"));
		return (DDI_FAILURE);
	}

	savereg = pci_config_get16(cfg_hdl, PCI_CONF_COMM);
	DDI_INTR_NEXDBG((CE_CONT, "pci_intx_get_cap: "
	    "command register was 0x%x\n", savereg));

	/* Disable the interrupts */
	cmdreg = savereg | PCI_COMM_INTX_DISABLE;
	pci_config_put16(cfg_hdl, PCI_CONF_COMM, cmdreg);

#ifdef	DEBUG
	statreg = pci_config_get16(cfg_hdl, PCI_CONF_STAT);
	DDI_INTR_NEXDBG((CE_CONT, "pci_intx_get_cap: "
	    "status register is 0x%x\n", statreg));
#endif /* DEBUG */

	/* Read the bit back */
	cmdreg = pci_config_get16(cfg_hdl, PCI_CONF_COMM);
	DDI_INTR_NEXDBG((CE_CONT, "pci_intx_get_cap: "
	    "command register is now 0x%x\n", cmdreg));

	*flagsp = DDI_INTR_FLAG_LEVEL;

	if (cmdreg & PCI_COMM_INTX_DISABLE) {
		DDI_INTR_NEXDBG((CE_CONT, "pci_intx_get_cap: "
		    "masking supported\n"));
		*flagsp |= (DDI_INTR_FLAG_MASKABLE |
		    DDI_INTR_FLAG_PENDING);
	}

	/* Restore the device back to the original state and return */
	pci_config_put16(cfg_hdl, PCI_CONF_COMM, savereg);

	pci_config_teardown(&cfg_hdl);
	return (DDI_SUCCESS);
}


/*
 * pci_intx_clr_mask:
 *	For non-MSI devices that comply to PCI v2.3 or greater;
 *	clear the bit10 in the command register.
 */
int
pci_intx_clr_mask(dev_info_t *dip)
{
	uint16_t		cmdreg;
	ddi_acc_handle_t	cfg_hdl;

	DDI_INTR_NEXDBG((CE_CONT, "pci_intx_clr_mask: %s%d: called\n",
	    ddi_driver_name(dip), ddi_get_instance(dip)));

	if (pci_config_setup(dip, &cfg_hdl) != DDI_SUCCESS) {
		DDI_INTR_NEXDBG((CE_CONT, "pci_intx_clr_mask: can't get "
		    "config handle\n"));
		return (DDI_FAILURE);
	}

	cmdreg = pci_config_get16(cfg_hdl, PCI_CONF_COMM);
	DDI_INTR_NEXDBG((CE_CONT, "pci_intx_clr_mask: "
	    "command register was 0x%x\n", cmdreg));

	/* Enable the interrupts */
	cmdreg &= ~PCI_COMM_INTX_DISABLE;
	pci_config_put16(cfg_hdl, PCI_CONF_COMM, cmdreg);
	pci_config_teardown(&cfg_hdl);
	return (DDI_SUCCESS);
}


/*
 * pci_intx_set_mask:
 *	For non-MSI devices that comply to PCI v2.3 or greater;
 *	set the bit10 in the command register.
 */
int
pci_intx_set_mask(dev_info_t *dip)
{
	uint16_t		cmdreg;
	ddi_acc_handle_t	cfg_hdl;

	DDI_INTR_NEXDBG((CE_CONT, "pci_intx_set_mask: %s%d: called\n",
	    ddi_driver_name(dip), ddi_get_instance(dip)));

	if (pci_config_setup(dip, &cfg_hdl) != DDI_SUCCESS) {
		DDI_INTR_NEXDBG((CE_CONT, "pci_intx_set_mask: can't get "
		    "config handle\n"));
		return (DDI_FAILURE);
	}

	cmdreg = pci_config_get16(cfg_hdl, PCI_CONF_COMM);
	DDI_INTR_NEXDBG((CE_CONT, "pci_intx_set_mask: "
	    "command register was 0x%x\n", cmdreg));

	/* Disable the interrupts */
	cmdreg |= PCI_COMM_INTX_DISABLE;
	pci_config_put16(cfg_hdl, PCI_CONF_COMM, cmdreg);
	pci_config_teardown(&cfg_hdl);
	return (DDI_SUCCESS);
}

/*
 * pci_intx_get_pending:
 *	For non-MSI devices that comply to PCI v2.3 or greater;
 *	read the status register. Bit 3 of status register is
 *	Interrupt state. If it is set; then the interrupt is
 *	'Pending'.
 */
int
pci_intx_get_pending(dev_info_t *dip, int *pendingp)
{
	uint16_t		statreg;
	ddi_acc_handle_t	cfg_hdl;

	*pendingp = 0;
	DDI_INTR_NEXDBG((CE_CONT, "pci_intx_get_pending: %s%d: called\n",
	    ddi_driver_name(dip), ddi_get_instance(dip)));

	if (pci_config_setup(dip, &cfg_hdl) != DDI_SUCCESS) {
		DDI_INTR_NEXDBG((CE_CONT, "pci_intx_get_pending: can't get "
		    "config handle\n"));
		return (DDI_FAILURE);
	}

	statreg = pci_config_get16(cfg_hdl, PCI_CONF_STAT);

	if (statreg & PCI_STAT_INTR) {
		DDI_INTR_NEXDBG((CE_CONT, "pci_intx_get_pending: "
		    "interrupt is pending\n"));
		*pendingp = 1;
	}

	pci_config_teardown(&cfg_hdl);
	return (DDI_SUCCESS);
}


/*
 * pci_intx_get_ispec:
 *	Get intrspec for PCI devices (legacy support)
 *	NOTE: This is moved here from x86 pci.c and is
 *	needed here as pci-ide.c uses it as well
 */
/*ARGSUSED*/
ddi_intrspec_t
pci_intx_get_ispec(dev_info_t *dip, dev_info_t *rdip, int inum)
{
	int				*intpriorities;
	uint_t				num_intpriorities;
	struct intrspec			*ispec;
	ddi_acc_handle_t		cfg_hdl;
	struct ddi_parent_private_data	*pdptr;

	if ((pdptr = ddi_get_parent_data(rdip)) == NULL)
		return (NULL);

	ispec = pdptr->par_intr;
	ASSERT(ispec);

	/* check if the intrspec_pri has been initialized */
	if (!ispec->intrspec_pri) {
		if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, rdip,
		    DDI_PROP_DONTPASS, "interrupt-priorities",
		    &intpriorities, &num_intpriorities) == DDI_PROP_SUCCESS) {
			if (inum < num_intpriorities)
				ispec->intrspec_pri = intpriorities[inum];
			ddi_prop_free(intpriorities);
		}

		/* If still no priority, guess based on the class code */
		if (ispec->intrspec_pri == 0)
			ispec->intrspec_pri = pci_class_to_pil(rdip);
	}

	/* Get interrupt line value */
	if (!ispec->intrspec_vec) {
		if (pci_config_setup(rdip, &cfg_hdl) != DDI_SUCCESS) {
			DDI_INTR_NEXDBG((CE_CONT, "pci_intx_get_iline: "
			    "can't get config handle\n"));
			return ((ddi_intrspec_t)ispec);
		}

		ispec->intrspec_vec = pci_config_get8(cfg_hdl, PCI_CONF_ILINE);
		pci_config_teardown(&cfg_hdl);
	}

	return ((ddi_intrspec_t)ispec);
}

static uint32_t
pci_match_class_val(uint32_t key, pci_class_val_t *rec_p, int nrec,
    uint32_t default_val)
{
	int i;

	for (i = 0; i < nrec; rec_p++, i++) {
		if ((rec_p->class_code & rec_p->class_mask) ==
		    (key & rec_p->class_mask))
			return (rec_p->class_val);
	}

	return (default_val);
}

/*
 * Return the configuration value, based on class code and sub class code,
 * from the specified property based or default pci_class_val_t table.
 */
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)
{
	int property_len;
	uint32_t class_code;
	pci_class_val_t *conf;
	uint32_t val = default_val;

	/*
	 * Use the "class-code" property to get the base and sub class
	 * codes for the requesting device.
	 */
	class_code = (uint32_t)ddi_prop_get_int(DDI_DEV_T_ANY, rdip,
	    DDI_PROP_DONTPASS, "class-code", -1);

	if (class_code == -1)
		return (val);

	/* look up the val from the default table */
	val = pci_match_class_val(class_code, rec_p, nrec, val);


	/* see if there is a more specific property specified value */
	if (ddi_getlongprop(DDI_DEV_T_ANY, rdip, DDI_PROP_NOTPROM,
	    property_name, (caddr_t)&conf, &property_len))
			return (val);

	if ((property_len % sizeof (pci_class_val_t)) == 0)
		val = pci_match_class_val(class_code, conf,
		    property_len / sizeof (pci_class_val_t), val);
	kmem_free(conf, property_len);
	return (val);
}

/*
 * pci_class_to_pil:
 *
 * Return the pil for a given PCI device.
 */
uint32_t
pci_class_to_pil(dev_info_t *rdip)
{
	uint32_t pil;

	/* Default pil is 1 */
	pil = pci_class_to_val(rdip,
	    "pci-class-priorities", pci_default_pil,
	    sizeof (pci_default_pil) / sizeof (pci_class_val_t), 1);

	/* Range check the result */
	if (pil >= 0xf)
		pil = 1;

	return (pil);
}

/*
 * pci_class_to_intr_weight:
 *
 * Return the intr_weight for a given PCI device.
 */
int32_t
pci_class_to_intr_weight(dev_info_t *rdip)
{
	int32_t intr_weight;

	/* default weight is 0% */
	intr_weight = pci_class_to_val(rdip,
	    "pci-class-intr-weights", pci_default_intr_weight,
	    sizeof (pci_default_intr_weight) / sizeof (pci_class_val_t), 0);

	/* range check the result */
	if (intr_weight < 0)
		intr_weight = 0;
	if (intr_weight > 1000)
		intr_weight = 1000;

	return (intr_weight);
}