/*-
* Copyright (c) 2007 Michael Lorenz
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
/*
* Register an interrupt handler.
*/
void *
intr_establish(int hwirq, int type, int ipl, int (*ih_fun)(void *),
void *ih_arg)
{
return intr_establish_xname(hwirq, type, ipl, ih_fun, ih_arg, NULL);
}
void *
intr_establish_xname(int hwirq, int type, int ipl, int (*ih_fun)(void *),
void *ih_arg, const char *xname)
{
struct intrhand **p, *q, *ih;
struct pic_ops *pic;
static struct intrhand fakehand;
int maxipl = ipl;
if (maxipl == IPL_NONE)
maxipl = IPL_HIGH;
if (hwirq >= max_base) {
panic("%s: bogus IRQ %d, max is %d", __func__, hwirq,
max_base - 1);
}
pic = find_pic_by_hwirq(hwirq);
if (pic == NULL) {
panic("%s: cannot find a pic for IRQ %d", __func__, hwirq);
}
const int virq = mapirq(hwirq);
/* no point in sleeping unless someone can free memory. */
ih = kmem_intr_alloc(sizeof(*ih), cold ? KM_NOSLEEP : KM_SLEEP);
if (ih == NULL)
panic("intr_establish: can't allocate handler info");
if (!PIC_VIRQ_LEGAL_P(virq) || type == IST_NONE)
panic("intr_establish: bogus irq (%d) or type (%d)",
hwirq, type);
switch (is->is_type) {
case IST_NONE:
is->is_type = type;
is->is_cascaded = cascaded;
break;
case IST_EDGE_FALLING:
case IST_EDGE_RISING:
case IST_LEVEL_LOW:
case IST_LEVEL_HIGH:
if (type == is->is_type)
break;
/* FALLTHROUGH */
case IST_PULSE:
if (type != IST_NONE) {
panic("intr_establish: can't share %s with %s",
intr_typename(is->is_type),
intr_typename(type));
}
if (cascaded != is->is_cascaded) {
panic("intr_establish: can't share cascaded with "
"non-cascaded interrupt");
}
break;
}
if (is->is_hand == NULL) {
snprintf(is->is_intrid, sizeof(is->is_intrid), "%s irq %d",
pic->pic_name, is->is_hwirq);
snprintf(is->is_evname, sizeof(is->is_evname), "irq %d",
is->is_hwirq);
evcnt_attach_dynamic(&is->is_ev, EVCNT_TYPE_INTR, NULL,
pic->pic_name, is->is_evname);
}
/*
* Figure out where to put the handler.
* This is O(N^2), but we want to preserve the order, and N is
* generally small.
*/
for (p = &is->is_hand; (q = *p) != NULL; p = &q->ih_next) {
maxipl = uimax(maxipl, q->ih_ipl);
}
/*
* Actually install a fake handler momentarily, since we might be doing
* this with interrupts enabled and don't want the real routine called
* until masking is set up.
*/
fakehand.ih_ipl = ipl;
fakehand.ih_fun = fakeintr;
*p = &fakehand;
/*
* Recalculate the interrupt masks from scratch.
* We could code special registry and deregistry versions of this function that
* would be faster, but the code would be nastier, and we don't expect this to
* happen very much anyway.
*/
static void
intr_calculatemasks(void)
{
imask_t newmask[NIPL];
struct intr_source *is;
struct intrhand *ih;
int irq;
/* First, figure out which ipl each IRQ uses. */
for (irq = 0, is = intrsources; irq < NVIRQ; irq++, is++) {
for (ih = is->is_hand; ih != NULL; ih = ih->ih_next) {
newmask[ih->ih_ipl] |= PIC_VIRQ_TO_MASK(irq);
}
}
/*
* IPL_NONE is used for hardware interrupts that are never blocked,
* and do not block anything else.
*/
newmask[IPL_NONE] = 0;
/*
* strict hierarchy - all IPLs block everything blocked by any lower
* IPL
*/
for (u_int ipl = 1; ipl < NIPL; ipl++) {
newmask[ipl] |= newmask[ipl - 1];
}
if (!is->is_cascaded) {
splraise(is->is_ipl);
mtmsr(emsr);
}
intr_deliver(is, virq);
if (!is->is_cascaded) {
mtmsr(dmsr);
ci->ci_cpl = pcpl; /* Don't use splx... we are here already! */
}
if ((imask[pcpl] & v_imen) != 0) {
ci->ci_ipending |= v_imen; /* Masked! Mark this as pending */
pic->pic_disable_irq(pic, picirq);
} else {
/* this interrupt is no longer pending */
ci->ci_ipending &= ~v_imen;
ci->ci_idepth++;
if (!is->is_cascaded) {
splraise(is->is_ipl);
mtmsr(msr | PSL_EE);
}
intr_deliver(is, virq);
if (!is->is_cascaded) {
mtmsr(msr & ~PSL_EE);
ci->ci_cpl = pcpl;
}
#if defined(PIC_PREPIVR) || defined(PIC_I8259)
/*
* isa_intr_alloc needs to be done here, because it needs direct access to
* the various interrupt handler structures.
*/
int
genppc_isa_intr_alloc(isa_chipset_tag_t ic, struct pic_ops *pic,
int mask, int type, int *irq_p)
{
int irq, vi;
int maybe_irq = -1;
int shared_depth = 0;
struct intr_source *is;
if (pic == NULL)
return 1;
for (irq = 0; (mask != 0 && irq < pic->pic_numintrs);
mask >>= 1, irq++) {
if ((mask & 1) == 0)
continue;
vi = virq_map[irq + pic->pic_intrbase];
if (!vi) {
*irq_p = irq;
return 0;
}
is = &intrsources[vi];
if (is->is_type == IST_NONE) {
*irq_p = irq;
return 0;
}
/* Level interrupts can be shared */
if (type == IST_LEVEL && is->is_type == IST_LEVEL) {
struct intrhand *ih = is->is_hand;
int depth;
if (maybe_irq == -1) {
maybe_irq = irq;
continue;
}
for (depth = 0; ih != NULL; ih = ih->ih_next)
depth++;
if (depth < shared_depth) {
maybe_irq = irq;
shared_depth = depth;
}
}
}
if (maybe_irq != -1) {
*irq_p = maybe_irq;
return 0;
}
return 1;
}
#endif