/*
* 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 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
/*
* CMU-CH nexus interrupt handling:
* PCI device interrupt handler wrapper
* pil lookup routine
* PCI device interrupt related initchild code
*/
#include <sys/machsystm.h>
#include <sys/ddi_impldefs.h>
/*
* interrupt jabber:
*
* When an interrupt line is jabbering, every time the state machine for the
* associated ino is idled, a new mondo will be sent and the ino will go into
* the pending state again. The mondo will cause a new call to
* pcmu_intr_wrapper() which normally idles the ino's state machine which would
* precipitate another trip round the loop.
* The loop can be broken by preventing the ino's state machine from being
* idled when an interrupt line is jabbering. See the comment at the
* beginning of pcmu_intr_wrapper() explaining how the 'interrupt jabber
* protection' code does this.
*/
/*
* If the unclaimed interrupt count has reached the limit set by
* pcmu_unclaimed_intr_max within the time limit, then all interrupts
* on this ino is blocked by not idling the interrupt state machine.
*/
static int
int i;
char *err_fmt_str;
return (DDI_INTR_CLAIMED);
}
if (!ino_p->pino_unclaimed) {
}
ino_p->pino_unclaimed++;
goto clear;
}
ino_p->pino_unclaimed = 0;
goto clear;
}
err_fmt_str = "%s%d: ino 0x%x blocked";
goto warn;
/* clear the pending state */
err_fmt_str = "!%s%d: spurious interrupt from ino 0x%x";
warn:
}
return (DDI_INTR_CLAIMED);
}
/*
* pcmu_intr_wrapper
*
* This routine is used as wrapper around interrupt handlers installed by child
* device drivers. This routine invokes the driver interrupt handlers and
* examines the return codes.
* There is a count of unclaimed interrupts kept on a per-ino basis. If at
* least one handler claims the interrupt then the counter is halved and the
* interrupt state machine is idled. If no handler claims the interrupt then
* the counter is incremented by one and the state machine is idled.
* If the count ever reaches the limit value set by pcmu_unclaimed_intr_max
* then the interrupt state machine is not idled thus preventing any further
* interrupts on that ino. The state machine will only be idled again if a
* handler is subsequently added or removed.
*
* return value: DDI_INTR_CLAIMED if any handlers claimed the interrupt,
* DDI_INTR_UNCLAIMED otherwise.
*/
{
int i;
#ifdef DEBUG
#endif
"pcmu_intr_wrapper: %s%d interrupt %d is "
continue;
}
result += r;
}
if (!result) {
return (pcmu_spurintr(ino_p));
}
ino_p->pino_unclaimed = 0;
/* clear the pending state */
return (DDI_INTR_CLAIMED);
}
int
{
int ret;
return (DDI_INTR_NOTFOUND);
}
goto fail1;
"dup intr #%d\n", intr_index);
goto fail3;
}
/*
* add default weight(0) to the cpu that we are
* already targeting
*/
goto ino_done;
}
/*
* Restore original interrupt handler
* and arguments in interrupt handle.
*/
if (ret != DDI_SUCCESS) {
goto fail4;
}
/* Save the pil for this ino */
/* clear and enable interrupt */
/* select cpu for sharing and removal */
done:
return (DDI_SUCCESS);
if (ih_p->ih_config_handle)
return (DDI_FAILURE);
}
int
{
/* Translate the interrupt property */
if (mondo == 0) {
"can't get mondo for ino %x\n", ino);
return (DDI_FAILURE);
}
if (!ino_p) {
return (DDI_SUCCESS);
}
return (DDI_FAILURE);
}
if (ino_p->pino_ih_size == 0) {
}
/* re-enable interrupt only if mapping register still shared */
if (ino_p->pino_ih_size) {
}
if (ino_p->pino_ih_size == 0) {
}
return (DDI_SUCCESS);
}
/*
* free the pcmu_inos array allocated during pcmu_intr_setup. the actual
* interrupts are torn down by their respective block destroy routines:
* cb_destroy, pcmu_pbm_destroy, and ib_destroy.
*/
void
{
pcmu_p->pcmu_inos_len = 0;
}