ddi_intr.c revision a120541c237dc0cda26d48983b1b9e2a13577a61
/*
* 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
*/
/*
*/
#include <sys/sysmacros.h>
#include <sys/autoconf.h>
/*
* New DDI interrupt framework
*/
/*
* 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;
"Invalid input args\n"));
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
{
"Invalid input args\n"));
return (DDI_EINVAL);
}
return (DDI_INTR_NOTFOUND);
return (DDI_SUCCESS);
}
/*
*/
int
{
/* Validate parameters */
"Invalid input args\n"));
return (DDI_EINVAL);
}
/* Validate interrupt type */
if (!DDI_INTR_TYPE_FLAG_VALID(type) ||
"supported\n", type));
return (DDI_EINVAL);
}
/* Validate inum not previously allocated */
if ((type == DDI_INTR_TYPE_FIXED) &&
"in use, cannot allocate again!!\n", inum));
return (DDI_EINVAL);
}
/* Get how many interrupts the device supports */
"interrupts found of type %d\n", type));
return (DDI_INTR_NOTFOUND);
}
}
/* Get how many interrupts the device is already using */
"is already being used\n", curr_type));
}
/* Validate interrupt type consistency */
"interrupt type %x is different from interrupt type %x"
return (DDI_EINVAL);
}
/* Validate count does not exceed what device supports */
return (DDI_EINVAL);
"+ intrs in use %d exceeds supported %d intrs\n",
return (DDI_EINVAL);
}
/* Validate power of 2 requirements for MSI */
"MSI count %d is not a power of two\n", count));
return (DDI_EINVAL);
}
/*
* Initialize the device's interrupt information structure,
* and establish an association with IRM if it is supported.
*
* NOTE: IRM checks minimum support, and can return DDI_EAGAIN.
*/
if (curr_nintrs == 0) {
"cannot fit into interrupt pool\n");
return (DDI_EAGAIN);
}
}
/* Synchronously adjust IRM associations for non-IRM aware drivers */
/* Get how many interrupts are currently available */
/* Validate that requested number of interrupts are available */
if (curr_nintrs == navail) {
"already allocated\n", navail));
return (DDI_EAGAIN);
}
"intrs %d exceeds # of available intrs %d\n", count,
navail - curr_nintrs));
if (behavior == DDI_INTR_ALLOC_STRICT) {
"DDI_INTR_ALLOC_STRICT flag is passed, "
"return failure\n"));
if (curr_nintrs == 0)
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" */
(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);
}
/* Set the number of interrupts to free */
if (ret == DDI_SUCCESS) {
/* This would be the dup vector */
else {
int n, curr_type;
!= DDI_SUCCESS) && (n > 0))
}
}
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) */
"input args\n"));
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);
}
/*
*/
int
{
int ret;
(void *)hdlp));
return (DDI_EINVAL);
return (DDI_EINVAL);
}
*tgt_p));
if (ret == DDI_SUCCESS)
return (ret);
}
int
{
int ret;
return (DDI_EINVAL);
return (DDI_EINVAL);
}
if (ret == DDI_SUCCESS)
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);
}
/*
* Set the number of interrupts requested from IRM
*/
int
{
/* Sanity check inputs */
return (DDI_EINVAL);
/* Only valid for IRM drivers actively using interrupts */
if ((curr_type == 0) ||
return (DDI_ENOTSUP);
/* Range check */
return (DDI_FAILURE);
return (DDI_EINVAL);
}
/*
* 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.
*/
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);
if (hdl_sz)
return (0);
}
/* Free the handle allocated here only if no existing handle exists */
if (hdl_sz) {
(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.
*/
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);
if (hdl_sz)
return (DDI_FAILURE);
}
/* Free the handle allocated here only if no existing handle exists */
if (hdl_sz) {
(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));
return (DDI_FAILURE);
}
"ddi_intr_add_handler failed, ret 0x%x\n", ret));
return (DDI_FAILURE);
}
"ddi_intr_enable failed, ret 0x%x\n", ret));
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 "
}
}