ddi_intr.c revision 20036fe5e9e9df6dd3168c764d777e19b1f0acdd
/*
* 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
* 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 2006 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
#include <sys/sysmacros.h>
#include <sys/autoconf.h>
/*
* New DDI interrupt framework
*/
/*
* MSI/X allocation limit.
* This limit will change with Resource Management support.
*/
/*
* ddi_intr_get_supported_types:
* Return, as a bit mask, the hardware interrupt types supported by
* both the device and by the host in the integer pointed
* to be the 'typesp' argument.
*/
int
{
int ret;
return (DDI_EINVAL);
(void *)dip));
return (DDI_SUCCESS);
(void *)typesp);
if (ret != DDI_SUCCESS)
return (DDI_INTR_NOTFOUND);
*typesp));
return (ret);
}
/*
* ddi_intr_get_nintrs:
* Return as an integer in the integer pointed to by the argument
* *nintrsp*, the number of interrupts the device supports for the
* given interrupt type.
*/
int
{
int ret;
*nintrsp = 0;
return (DDI_EINVAL);
}
*nintrsp = 0;
return (DDI_EINVAL);
}
return (DDI_SUCCESS);
(void *)nintrsp);
*nintrsp));
return (ret);
}
/*
* ddi_intr_get_navail:
* Bus nexus driver will return availble interrupt count value for
* a given interrupt type.
*
* Return as an integer in the integer pointed to by the argument
* *navailp*, the number of interrupts currently available for the
* given interrupt type.
*/
int
{
int ret;
*navailp = 0;
return (DDI_EINVAL);
}
*navailp = 0;
return (DDI_EINVAL);
}
/*
* In future, this interface implementation will change
* with Resource Management support.
*/
(void *)navailp);
}
/*
*/
int
{
/* Validate parameters */
return (DDI_EINVAL);
}
/* Validate interrupt type */
"supported\n", type));
return (DDI_EINVAL);
}
/* First, get how many interrupts the device supports */
"interrupts found of type %d\n", type));
return (DDI_INTR_NOTFOUND);
}
}
/* Is this function invoked with more interrupt than device supports? */
return (DDI_EINVAL);
}
/*
* Check if requested interrupt type is not same as interrupt
* type is in use if any.
*/
"interrupt type %x is different from interrupt type %x"
return (DDI_EINVAL);
}
/*
* Check if requested interrupt type is in use and requested number
* of interrupts and number of interrupts already in use exceeds the
* number of interrupts supported by this device.
*/
if (intr_type) {
"is already being used\n", type));
"+ intrs in use %d exceeds supported %d intrs\n",
return (DDI_EINVAL);
}
}
/*
* For MSI, ensure that the requested interrupt count is a power of 2
*/
"MSI count %d is not a power of two\n", count));
return (DDI_EINVAL);
}
/*
* Limit max MSI/X allocation to ddi_msix_alloc_limit.
* This limit will change with Resource Management support.
*/
if (behavior == DDI_INTR_ALLOC_STRICT) {
"DDI_INTR_ALLOC_STRICT flag is passed, "
"return failure\n"));
return (DDI_EAGAIN);
}
}
/* Now allocate required number of interrupts */
"failed\n"));
}
"failed\n"));
goto fail;
}
"failed\n"));
goto fail;
}
/*
* Save current interrupt type, supported and current intr count.
*/
/* Now, go and handle each "handle" */
for (i = 0; i < *actualp; i++) {
(sizeof (ddi_intr_handle_impl_t)), KM_SLEEP);
if (type & DDI_INTR_TYPE_FIXED)
(void *)h_array[i]));
}
return (DDI_SUCCESS);
fail:
return (ret);
}
int
{
int ret;
return (DDI_EINVAL);
return (DDI_EINVAL);
}
if (ret == DDI_SUCCESS) {
/* This would be the dup vector */
else {
} else {
}
}
}
return (ret);
}
/*
*
* The logic used to figure this out is shown here:
*
* Device level Platform level Intr source
* 1. Fixed interrupts
* (non-PCI)
* No Block Enable
* o navail 1
*
* 2. PCI Fixed interrupts
* No Block enable
* o navail N/A 1
*
* 3. PCI MSI
* Block Enable (if drvr doesn't) Block Enable
* o navail N/A #vectors - #used N/A
*
* 4. PCI MSI-X
* Block Enable Block Enable
* o navail N/A #vectors - #used N/A
*
* where:
* #vectors - Total numbers of vectors available
* #used - Total numbers of vectors currently being used
*
* For devices complying to PCI2.3 or greater, see bit10 of Command Register
* 0 - enables assertion of INTx
* 1 - disables assertion of INTx
*
* For non MSI/X interrupts; if the IRQ is shared then all ddi_intr_set_*()
* operations return failure.
*/
int
{
int ret;
(void *)hdlp));
*flagsp = 0;
return (DDI_EINVAL);
return (DDI_SUCCESS);
}
if (ret == DDI_SUCCESS) {
/* Mask out MSI/X 64-bit support to the consumer */
*flagsp &= ~DDI_INTR_FLAG_MSI64;
}
return (ret);
}
int
{
int ret;
return (DDI_EINVAL);
return (DDI_EINVAL);
}
/* Only DDI_INTR_FLAG_LEVEL or DDI_INTR_FLAG_EDGE are allowed */
return (DDI_EINVAL);
}
return (DDI_ENOTSUP);
}
return (ret);
}
/*
* Priority related functions
*/
/*
* ddi_intr_get_hilevel_pri:
* Returns the minimum priority level for a
* high-level interrupt on a platform.
*/
ddi_intr_get_hilevel_pri(void)
{
return (LOCK_LEVEL + 1);
}
int
{
int ret;
(void *)hdlp));
*prip = 0;
return (DDI_EINVAL);
/* Already initialized, just return that */
return (DDI_SUCCESS);
}
if (ret == DDI_SUCCESS)
return (ret);
}
int
{
int ret;
return (DDI_EINVAL);
/* Validate priority argument */
"specified = %x\n", pri));
return (DDI_EINVAL);
}
return (DDI_EINVAL);
}
/* If the passed priority is same as existing priority; do nothing */
return (DDI_SUCCESS);
}
if (ret == DDI_SUCCESS)
return (ret);
}
/*
*/
int
{
int ret;
(void *)hdlp));
return (DDI_EINVAL);
return (DDI_EINVAL);
}
if (ret != DDI_SUCCESS) {
} else
return (ret);
}
int
{
int ret;
(void *)hdlp));
/* Do some input argument checking ("dup" handle is not allocated) */
return (DDI_EINVAL);
/* Do some input argument checking */
return (DDI_EINVAL);
}
if (ret == DDI_SUCCESS) {
/* These fields are unique to each dupped msi-x vector */
dup_hdlp->ih_dup_cnt = 0;
/* Point back to original vector */
}
return (ret);
}
int
{
int ret = DDI_SUCCESS;
(void *)hdlp));
return (DDI_EINVAL);
ret = DDI_EINVAL;
goto done;
goto done;
if (hdlp->ih_dup_cnt > 0) {
ret = DDI_FAILURE;
goto done;
}
if (ret == DDI_SUCCESS) {
}
done:
return (ret);
}
/*
* Interrupt enable/disable/block_enable/block_disable handlers
*/
int
{
int ret;
(void *)hdlp));
return (DDI_EINVAL);
return (DDI_EINVAL);
}
if (ret == DDI_SUCCESS)
return (ret);
}
int
{
int ret;
(void *)hdlp));
return (DDI_EINVAL);
return (DDI_EINVAL);
}
if (ret == DDI_SUCCESS)
return (ret);
}
int
{
int i, ret;
(void *)h_array));
return (DDI_EINVAL);
for (i = 0; i < count; i++) {
return (DDI_EINVAL);
}
}
if (ret == DDI_SUCCESS) {
for (i = 0; i < count; i++) {
}
}
return (ret);
}
int
{
int i, ret;
(void *)h_array));
return (DDI_EINVAL);
for (i = 0; i < count; i++) {
return (DDI_EINVAL);
}
}
if (ret == DDI_SUCCESS) {
for (i = 0; i < count; i++) {
}
}
return (ret);
}
/*
*/
int
{
int ret;
(void *)hdlp));
return (DDI_EINVAL);
return (DDI_EINVAL);
}
return (ret);
}
int
{
int ret;
(void *)hdlp));
return (DDI_EINVAL);
return (DDI_EINVAL);
}
return (ret);
}
/*
* Interrupt get_pending handler
*/
int
{
int ret;
(void *)hdlp));
return (DDI_EINVAL);
return (DDI_EINVAL);
}
return (ret);
}
/*
* Soft interrupt handlers
*/
/*
* Add a soft interrupt and register its handler
*/
/* ARGSUSED */
int
{
int ret;
"invalid arguments"));
return (DDI_EINVAL);
}
/* Validate input arguments */
if (soft_pri < DDI_INTR_SOFTPRI_MIN ||
"soft_pri input given = %x\n", soft_pri));
return (DDI_EINVAL);
}
sizeof (ddi_softint_hdl_impl_t), KM_SLEEP);
/* fill up internally */
(void *)hdlp));
/* do the platform specific calls */
return (ret);
}
return (ret);
}
/*
* Remove the soft interrupt
*/
int
{
(void *)hdlp));
return (DDI_EINVAL);
/* kmem_free the hdl impl_t structure allocated earlier */
return (DDI_SUCCESS);
}
/*
* Trigger a soft interrupt
*/
int
{
int ret;
return (DDI_EINVAL);
" ret 0%x\n", ret));
return (ret);
}
return (DDI_SUCCESS);
}
/*
* Get the soft interrupt priority
*/
int
{
(void *)h));
return (DDI_EINVAL);
return (DDI_SUCCESS);
}
/*
* Set the soft interrupt priority
*/
int
{
int ret;
(void *)h));
return (DDI_EINVAL);
/* Validate priority argument */
if (soft_pri < DDI_INTR_SOFTPRI_MIN ||
"soft_pri input given = %x\n", soft_pri));
return (DDI_EINVAL);
}
" ret 0%x\n", ret));
}
return (ret);
}
/*
* Old DDI interrupt framework
*
* The following DDI interrupt interfaces are obsolete.
* Use the above new DDI interrupt interfaces instead.
*/
int
{
/*
* The device driver may have already registed with the
* framework. If so, first try to get the existing interrupt handle
* for that given inumber and use that handle.
*/
if (existing_hdlp) {
} else {
DDI_INTR_ALLOC_NORMAL)) != DDI_SUCCESS) {
"ddi_intr_alloc failed, ret 0x%x\n", ret));
return (0);
}
}
"ddi_intr_get_pri failed, ret 0x%x\n", ret));
(void) ddi_intr_free(hdl);
return (0);
}
/* Free the handle allocated here only if no existing handle exists */
if (existing_hdlp == NULL)
(void) ddi_intr_free(hdl);
}
int
{
result) != DDI_SUCCESS) {
"ddi_intr_get_nintrs failed\n"));
*result = 0;
}
return (DDI_SUCCESS);
}
int
{
/*
* The device driver may have already registed with the
* framework. If so, first try to get the existing interrupt handle
* for that given inumber and use that handle.
*/
if (existing_hdlp) {
} else {
DDI_INTR_ALLOC_NORMAL)) != DDI_SUCCESS) {
"ddi_intr_alloc failed, ret 0x%x\n", ret));
return (DDI_INTR_NOTFOUND);
}
}
"ddi_intr_get_pri failed, ret 0x%x\n", ret));
(void) ddi_intr_free(hdl);
return (DDI_FAILURE);
}
/* Free the handle allocated here only if no existing handle exists */
if (existing_hdlp == NULL)
(void) ddi_intr_free(hdl);
return (DDI_SUCCESS);
}
int
{
"ddi_intr_alloc failed, ret 0x%x\n", ret));
return (DDI_INTR_NOTFOUND);
}
"ddi_intr_get_pri failed, ret 0x%x\n", ret));
(void) ddi_intr_free(hdl_p[0]);
return (DDI_FAILURE);
}
"ddi_intr_add_handler failed, ret 0x%x\n", ret));
(void) ddi_intr_free(hdl_p[0]);
return (DDI_FAILURE);
}
"ddi_intr_enable failed, ret 0x%x\n", ret));
(void) ddi_intr_remove_handler(hdl_p[0]);
(void) ddi_intr_free(hdl_p[0]);
return (DDI_FAILURE);
}
if (iblock_cookiep)
if (idevice_cookiep) {
idevice_cookiep->idev_vector = 0;
}
return (DDI_SUCCESS);
}
/* ARGSUSED */
int
uint_t (*hi_int_handler)(void))
{
return (DDI_FAILURE);
}
/* ARGSUSED */
void
{
int ret;
"found\n"));
return;
}
"ddi_intr_disable failed, ret 0x%x\n", ret));
return;
}
"ddi_intr_remove_handler failed, ret 0x%x\n", ret));
return;
}
"ddi_intr_free failed, ret 0x%x\n", ret));
return;
}
}
/* ARGSUSED */
int
{
(void *)dip, preference));
if (preference == DDI_SOFTINT_FIXED)
return (DDI_FAILURE);
return (DDI_SUCCESS);
}
int
{
int ret;
(void *)dip, preference));
(iblock_cookiep == NULL)))
return (DDI_FAILURE);
/* Translate the priority preference */
if (preference == DDI_SOFTINT_FIXED) {
} else {
}
DDI_SUCCESS) {
"ddi_intr_add_softint failed, ret 0x%x\n", ret));
return (DDI_FAILURE);
}
if (iblock_cookiep)
if (idevice_cookiep) {
idevice_cookiep->idev_vector = 0;
}
return (DDI_SUCCESS);
}
void
{
(void *)id));
return;
(void *)h_p));
(void) ddi_intr_remove_softint(*h_p);
}
void
{
int ret;
return;
"ddi_intr_trigger_softint failed, hdlp 0x%p "
}
}