800 lines
17 KiB
C
800 lines
17 KiB
C
/* $NetBSD: intr.c,v 1.6 2008/04/29 06:53:02 martin Exp $ */
|
|
|
|
/*-
|
|
* 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.
|
|
*/
|
|
|
|
#include <sys/cdefs.h>
|
|
__KERNEL_RCSID(0, "$NetBSD: intr.c,v 1.6 2008/04/29 06:53:02 martin Exp $");
|
|
|
|
#include "opt_multiprocessor.h"
|
|
|
|
#include <sys/param.h>
|
|
#include <sys/malloc.h>
|
|
#include <sys/kernel.h>
|
|
#include <sys/cpu.h>
|
|
|
|
#include <uvm/uvm_extern.h>
|
|
|
|
#include <arch/powerpc/pic/picvar.h>
|
|
#include "opt_pic.h"
|
|
#include "opt_interrupt.h"
|
|
#if defined(PIC_I8259) || defined (PIC_PREPIVR)
|
|
#include <machine/isa_machdep.h>
|
|
#endif
|
|
|
|
#ifdef MULTIPROCESSOR
|
|
#include <arch/powerpc/pic/ipivar.h>
|
|
#endif
|
|
|
|
#define MAX_PICS 8 /* 8 PICs ought to be enough for everyone */
|
|
|
|
#define NVIRQ 32 /* 32 virtual IRQs */
|
|
#define NIRQ 128 /* up to 128 HW IRQs */
|
|
|
|
#define HWIRQ_MAX (NVIRQ - 4 - 1)
|
|
#define HWIRQ_MASK 0x0fffffff
|
|
#define LEGAL_VIRQ(x) ((x) >= 0 && (x) < NVIRQ)
|
|
|
|
struct pic_ops *pics[MAX_PICS];
|
|
int num_pics = 0;
|
|
int max_base = 0;
|
|
uint8_t virq[NIRQ];
|
|
int virq_max = 0;
|
|
int imask[NIPL];
|
|
int primary_pic = 0;
|
|
|
|
static int fakeintr(void *);
|
|
static int mapirq(uint32_t);
|
|
static void intr_calculatemasks(void);
|
|
static struct pic_ops *find_pic_by_irq(int);
|
|
|
|
static struct intr_source intrsources[NVIRQ];
|
|
|
|
void
|
|
pic_init(void)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < NIRQ; i++)
|
|
virq[i] = 0;
|
|
memset(intrsources, 0, sizeof(intrsources));
|
|
}
|
|
|
|
int
|
|
pic_add(struct pic_ops *pic)
|
|
{
|
|
|
|
if (num_pics >= MAX_PICS)
|
|
return -1;
|
|
|
|
pics[num_pics] = pic;
|
|
pic->pic_intrbase = max_base;
|
|
max_base += pic->pic_numintrs;
|
|
num_pics++;
|
|
|
|
return pic->pic_intrbase;
|
|
}
|
|
|
|
void
|
|
pic_finish_setup(void)
|
|
{
|
|
struct pic_ops *pic;
|
|
int i;
|
|
|
|
for (i = 0; i < num_pics; i++) {
|
|
pic = pics[i];
|
|
if (pic->pic_finish_setup != NULL)
|
|
pic->pic_finish_setup(pic);
|
|
}
|
|
}
|
|
|
|
static struct pic_ops *
|
|
find_pic_by_irq(int irq)
|
|
{
|
|
struct pic_ops *current;
|
|
int base = 0;
|
|
|
|
while (base < num_pics) {
|
|
|
|
current = pics[base];
|
|
if ((irq >= current->pic_intrbase) &&
|
|
(irq < (current->pic_intrbase + current->pic_numintrs))) {
|
|
|
|
return current;
|
|
}
|
|
base++;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
static int
|
|
fakeintr(void *arg)
|
|
{
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Register an interrupt handler.
|
|
*/
|
|
void *
|
|
intr_establish(int hwirq, int type, int level, int (*ih_fun)(void *),
|
|
void *ih_arg)
|
|
{
|
|
struct intrhand **p, *q, *ih;
|
|
struct intr_source *is;
|
|
struct pic_ops *pic;
|
|
static struct intrhand fakehand;
|
|
int irq, maxlevel = level;
|
|
|
|
if (maxlevel == IPL_NONE)
|
|
maxlevel = IPL_HIGH;
|
|
|
|
if (hwirq >= max_base) {
|
|
|
|
panic("%s: bogus IRQ %d, max is %d", __func__, hwirq,
|
|
max_base - 1);
|
|
}
|
|
|
|
pic = find_pic_by_irq(hwirq);
|
|
if (pic == NULL) {
|
|
|
|
panic("%s: cannot find a pic for IRQ %d", __func__, hwirq);
|
|
}
|
|
|
|
irq = mapirq(hwirq);
|
|
|
|
/* no point in sleeping unless someone can free memory. */
|
|
ih = malloc(sizeof *ih, M_DEVBUF, cold ? M_NOWAIT : M_WAITOK);
|
|
if (ih == NULL)
|
|
panic("intr_establish: can't malloc handler info");
|
|
|
|
if (!LEGAL_VIRQ(irq) || type == IST_NONE)
|
|
panic("intr_establish: bogus irq (%d) or type (%d)", irq, type);
|
|
|
|
is = &intrsources[irq];
|
|
|
|
switch (is->is_type) {
|
|
case IST_NONE:
|
|
is->is_type = type;
|
|
break;
|
|
case IST_EDGE:
|
|
case IST_LEVEL:
|
|
if (type == is->is_type)
|
|
break;
|
|
case IST_PULSE:
|
|
if (type != IST_NONE)
|
|
panic("intr_establish: can't share %s with %s",
|
|
intr_typename(is->is_type),
|
|
intr_typename(type));
|
|
break;
|
|
}
|
|
if (is->is_hand == NULL) {
|
|
snprintf(is->is_source, sizeof(is->is_source), "irq %d",
|
|
is->is_hwirq);
|
|
evcnt_attach_dynamic(&is->is_ev, EVCNT_TYPE_INTR, NULL,
|
|
pic->pic_name, is->is_source);
|
|
}
|
|
|
|
/*
|
|
* 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) {
|
|
|
|
maxlevel = max(maxlevel, q->ih_level);
|
|
}
|
|
|
|
/*
|
|
* 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_level = level;
|
|
fakehand.ih_fun = fakeintr;
|
|
*p = &fakehand;
|
|
|
|
/*
|
|
* Poke the real handler in now.
|
|
*/
|
|
ih->ih_fun = ih_fun;
|
|
ih->ih_arg = ih_arg;
|
|
ih->ih_next = NULL;
|
|
ih->ih_level = level;
|
|
ih->ih_irq = irq;
|
|
*p = ih;
|
|
|
|
if (pic->pic_establish_irq != NULL)
|
|
pic->pic_establish_irq(pic, hwirq - pic->pic_intrbase,
|
|
is->is_type, maxlevel);
|
|
|
|
/*
|
|
* now that the handler is established we're actually ready to
|
|
* calculate the masks
|
|
*/
|
|
intr_calculatemasks();
|
|
|
|
|
|
return ih;
|
|
}
|
|
|
|
void
|
|
dummy_pic_establish_intr(struct pic_ops *pic, int irq, int type, int pri)
|
|
{
|
|
}
|
|
|
|
/*
|
|
* Deregister an interrupt handler.
|
|
*/
|
|
void
|
|
intr_disestablish(void *arg)
|
|
{
|
|
struct intrhand *ih = arg;
|
|
int irq = ih->ih_irq;
|
|
struct intr_source *is = &intrsources[irq];
|
|
struct intrhand **p, *q;
|
|
|
|
if (!LEGAL_VIRQ(irq))
|
|
panic("intr_disestablish: bogus irq %d", irq);
|
|
|
|
/*
|
|
* Remove the handler from the chain.
|
|
* This is O(n^2), too.
|
|
*/
|
|
for (p = &is->is_hand; (q = *p) != NULL && q != ih; p = &q->ih_next)
|
|
;
|
|
if (q)
|
|
*p = q->ih_next;
|
|
else
|
|
panic("intr_disestablish: handler not registered");
|
|
free((void *)ih, M_DEVBUF);
|
|
|
|
intr_calculatemasks();
|
|
|
|
if (is->is_hand == NULL) {
|
|
is->is_type = IST_NONE;
|
|
evcnt_detach(&is->is_ev);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Map max_base irqs into 32 (bits).
|
|
*/
|
|
static int
|
|
mapirq(uint32_t irq)
|
|
{
|
|
struct pic_ops *pic;
|
|
int v;
|
|
|
|
if (irq >= max_base)
|
|
panic("invalid irq %d", irq);
|
|
|
|
if ((pic = find_pic_by_irq(irq)) == NULL)
|
|
panic("%s: cannot find PIC for IRQ %d", __func__, irq);
|
|
|
|
if (virq[irq])
|
|
return virq[irq];
|
|
|
|
virq_max++;
|
|
v = virq_max;
|
|
if (v > HWIRQ_MAX)
|
|
panic("virq overflow");
|
|
|
|
intrsources[v].is_hwirq = irq;
|
|
intrsources[v].is_pic = pic;
|
|
virq[irq] = v;
|
|
#ifdef PIC_DEBUG
|
|
printf("mapping irq %d to virq %d\n", irq, v);
|
|
#endif
|
|
return v;
|
|
}
|
|
|
|
static const char * const intr_typenames[] = {
|
|
[IST_NONE] = "none",
|
|
[IST_PULSE] = "pulsed",
|
|
[IST_EDGE] = "edge-triggered",
|
|
[IST_LEVEL] = "level-triggered",
|
|
};
|
|
|
|
const char *
|
|
intr_typename(int type)
|
|
{
|
|
KASSERT((unsigned int) type < __arraycount(intr_typenames));
|
|
KASSERT(intr_typenames[type] != NULL);
|
|
return intr_typenames[type];
|
|
}
|
|
|
|
/*
|
|
* 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)
|
|
{
|
|
struct intr_source *is;
|
|
struct intrhand *q;
|
|
struct pic_ops *current;
|
|
int irq, level, i, base;
|
|
|
|
/* First, figure out which levels each IRQ uses. */
|
|
for (irq = 0, is = intrsources; irq < NVIRQ; irq++, is++) {
|
|
register int levels = 0;
|
|
for (q = is->is_hand; q; q = q->ih_next)
|
|
levels |= 1 << q->ih_level;
|
|
is->is_level = levels;
|
|
}
|
|
|
|
/* Then figure out which IRQs use each level. */
|
|
for (level = 0; level < NIPL; level++) {
|
|
register int irqs = 0;
|
|
for (irq = 0, is = intrsources; irq < NVIRQ; irq++, is++)
|
|
if (is->is_level & (1 << level))
|
|
irqs |= 1 << irq;
|
|
imask[level] = irqs;
|
|
}
|
|
|
|
/*
|
|
* IPL_CLOCK should mask clock interrupt even if interrupt handler
|
|
* is not registered.
|
|
*/
|
|
imask[IPL_CLOCK] |= 1 << SPL_CLOCK;
|
|
|
|
/*
|
|
* Initialize soft interrupt masks to block themselves.
|
|
*/
|
|
imask[IPL_SOFTCLOCK] = 1 << SIR_CLOCK;
|
|
imask[IPL_SOFTNET] = 1 << SIR_NET;
|
|
imask[IPL_SOFTSERIAL] = 1 << SIR_SERIAL;
|
|
|
|
/*
|
|
* IPL_NONE is used for hardware interrupts that are never blocked,
|
|
* and do not block anything else.
|
|
*/
|
|
imask[IPL_NONE] = 0;
|
|
|
|
#ifdef SLOPPY_IPLS
|
|
/*
|
|
* Enforce a sloppy hierarchy as in spl(9)
|
|
*/
|
|
/* everything above softclock must block softclock */
|
|
for (i = IPL_SOFTCLOCK; i < NIPL; i++)
|
|
imask[i] |= imask[IPL_SOFTCLOCK];
|
|
|
|
/* everything above softnet must block softnet */
|
|
for (i = IPL_SOFTNET; i < NIPL; i++)
|
|
imask[i] |= imask[IPL_SOFTNET];
|
|
|
|
/* IPL_TTY must block softserial */
|
|
imask[IPL_TTY] |= imask[IPL_SOFTSERIAL];
|
|
|
|
/* IPL_VM must block net, block IO and tty */
|
|
imask[IPL_VM] |= (imask[IPL_NET] | imask[IPL_BIO] | imask[IPL_TTY]);
|
|
|
|
/* IPL_SERIAL must block IPL_TTY */
|
|
imask[IPL_SERIAL] |= imask[IPL_TTY];
|
|
|
|
/* IPL_HIGH must block all other priority levels */
|
|
for (i = IPL_NONE; i < IPL_HIGH; i++)
|
|
imask[IPL_HIGH] |= imask[i];
|
|
#else /* !SLOPPY_IPLS */
|
|
/*
|
|
* strict hierarchy - all IPLs block everything blocked by any lower
|
|
* IPL
|
|
*/
|
|
for (i = 1; i < NIPL; i++)
|
|
imask[i] |= imask[i - 1];
|
|
#endif /* !SLOPPY_IPLS */
|
|
|
|
#ifdef DEBUG_IPL
|
|
for (i = 0; i < NIPL; i++) {
|
|
printf("%2d: %08x\n", i, imask[i]);
|
|
}
|
|
#endif
|
|
|
|
/* And eventually calculate the complete masks. */
|
|
for (irq = 0, is = intrsources; irq < NVIRQ; irq++, is++) {
|
|
register int irqs = 1 << irq;
|
|
for (q = is->is_hand; q; q = q->ih_next)
|
|
irqs |= imask[q->ih_level];
|
|
is->is_mask = irqs;
|
|
}
|
|
|
|
/* Lastly, enable IRQs actually in use. */
|
|
for (base = 0; base < num_pics; base++) {
|
|
current = pics[base];
|
|
for (i = 0; i < current->pic_numintrs; i++)
|
|
current->pic_disable_irq(current, i);
|
|
}
|
|
|
|
for (irq = 0, is = intrsources; irq < NVIRQ; irq++, is++) {
|
|
if (is->is_hand)
|
|
pic_enable_irq(is->is_hwirq);
|
|
}
|
|
}
|
|
|
|
void
|
|
pic_enable_irq(int num)
|
|
{
|
|
struct pic_ops *current;
|
|
int type;
|
|
|
|
current = find_pic_by_irq(num);
|
|
if (current == NULL)
|
|
panic("%s: bogus IRQ %d", __func__, num);
|
|
type = intrsources[virq[num]].is_type;
|
|
current->pic_enable_irq(current, num - current->pic_intrbase, type);
|
|
}
|
|
|
|
void
|
|
pic_mark_pending(int irq)
|
|
{
|
|
struct cpu_info * const ci = curcpu();
|
|
int v, msr;
|
|
|
|
v = virq[irq];
|
|
if (v == 0)
|
|
printf("IRQ %d maps to 0\n", irq);
|
|
|
|
msr = mfmsr();
|
|
mtmsr(msr & ~PSL_EE);
|
|
ci->ci_ipending |= 1 << v;
|
|
mtmsr(msr);
|
|
}
|
|
|
|
void
|
|
pic_do_pending_int(void)
|
|
{
|
|
struct cpu_info * const ci = curcpu();
|
|
struct intr_source *is;
|
|
struct intrhand *ih;
|
|
struct pic_ops *pic;
|
|
int irq;
|
|
int pcpl;
|
|
int hwpend;
|
|
int emsr, dmsr;
|
|
|
|
if (ci->ci_iactive)
|
|
return;
|
|
|
|
ci->ci_iactive = 1;
|
|
emsr = mfmsr();
|
|
KASSERT(emsr & PSL_EE);
|
|
dmsr = emsr & ~PSL_EE;
|
|
mtmsr(dmsr);
|
|
|
|
pcpl = ci->ci_cpl;
|
|
#ifdef __HAVE_FAST_SOFTINTS
|
|
again:
|
|
#endif
|
|
|
|
/* Do now unmasked pendings */
|
|
ci->ci_idepth++;
|
|
while ((hwpend = (ci->ci_ipending & ~pcpl & HWIRQ_MASK)) != 0) {
|
|
irq = 31 - cntlzw(hwpend);
|
|
KASSERT(irq <= virq_max);
|
|
ci->ci_ipending &= ~(1 << irq);
|
|
if (irq == 0) {
|
|
printf("VIRQ0");
|
|
continue;
|
|
}
|
|
is = &intrsources[irq];
|
|
pic = is->is_pic;
|
|
|
|
splraise(is->is_mask);
|
|
mtmsr(emsr);
|
|
ih = is->is_hand;
|
|
while (ih) {
|
|
#ifdef DIAGNOSTIC
|
|
if (!ih->ih_fun) {
|
|
printf("NULL interrupt handler!\n");
|
|
panic("irq %02d, hwirq %02d, is %p\n",
|
|
irq, is->is_hwirq, is);
|
|
}
|
|
#endif
|
|
if (ih->ih_level == IPL_VM) {
|
|
KERNEL_LOCK(1, NULL);
|
|
}
|
|
(*ih->ih_fun)(ih->ih_arg);
|
|
if (ih->ih_level == IPL_VM) {
|
|
KERNEL_UNLOCK_ONE(NULL);
|
|
}
|
|
ih = ih->ih_next;
|
|
}
|
|
mtmsr(dmsr);
|
|
ci->ci_cpl = pcpl;
|
|
|
|
is->is_ev.ev_count++;
|
|
pic->pic_reenable_irq(pic, is->is_hwirq - pic->pic_intrbase,
|
|
is->is_type);
|
|
}
|
|
ci->ci_idepth--;
|
|
|
|
#ifdef __HAVE_FAST_SOFTINTS
|
|
if ((ci->ci_ipending & ~pcpl) & (1 << SIR_SERIAL)) {
|
|
ci->ci_ipending &= ~(1 << SIR_SERIAL);
|
|
splsoftserial();
|
|
mtmsr(emsr);
|
|
softintr__run(IPL_SOFTSERIAL);
|
|
mtmsr(dmsr);
|
|
ci->ci_cpl = pcpl;
|
|
ci->ci_ev_softserial.ev_count++;
|
|
goto again;
|
|
}
|
|
if ((ci->ci_ipending & ~pcpl) & (1 << SIR_NET)) {
|
|
ci->ci_ipending &= ~(1 << SIR_NET);
|
|
splsoftnet();
|
|
mtmsr(emsr);
|
|
softintr__run(IPL_SOFTNET);
|
|
mtmsr(dmsr);
|
|
ci->ci_cpl = pcpl;
|
|
ci->ci_ev_softnet.ev_count++;
|
|
goto again;
|
|
}
|
|
if ((ci->ci_ipending & ~pcpl) & (1 << SIR_CLOCK)) {
|
|
ci->ci_ipending &= ~(1 << SIR_CLOCK);
|
|
splsoftclock();
|
|
mtmsr(emsr);
|
|
softintr__run(IPL_SOFTCLOCK);
|
|
mtmsr(dmsr);
|
|
ci->ci_cpl = pcpl;
|
|
ci->ci_ev_softclock.ev_count++;
|
|
goto again;
|
|
}
|
|
#endif
|
|
|
|
ci->ci_cpl = pcpl; /* Don't use splx... we are here already! */
|
|
ci->ci_iactive = 0;
|
|
mtmsr(emsr);
|
|
}
|
|
|
|
int
|
|
pic_handle_intr(void *cookie)
|
|
{
|
|
struct pic_ops *pic = cookie;
|
|
struct cpu_info *ci = curcpu();
|
|
struct intr_source *is;
|
|
struct intrhand *ih;
|
|
int irq, realirq;
|
|
int pcpl, msr, r_imen, bail;
|
|
|
|
realirq = pic->pic_get_irq(pic, PIC_GET_IRQ);
|
|
if (realirq == 255)
|
|
return 0;
|
|
|
|
msr = mfmsr();
|
|
pcpl = ci->ci_cpl;
|
|
|
|
start:
|
|
|
|
#ifdef MULTIPROCESSOR
|
|
/* THIS IS WRONG XXX */
|
|
while (realirq == ipiops.ppc_ipi_vector) {
|
|
ppcipi_intr(NULL);
|
|
pic->pic_ack_irq(pic, realirq);
|
|
realirq = pic->pic_get_irq(pic, PIC_GET_RECHECK);
|
|
}
|
|
if (realirq == 255) {
|
|
return 0;
|
|
}
|
|
#endif
|
|
|
|
irq = virq[realirq + pic->pic_intrbase];
|
|
#ifdef PIC_DEBUG
|
|
if (irq == 0) {
|
|
printf("%s: %d virq 0\n", pic->pic_name, realirq);
|
|
goto boo;
|
|
}
|
|
#endif /* PIC_DEBUG */
|
|
KASSERT(realirq < pic->pic_numintrs);
|
|
r_imen = 1 << irq;
|
|
is = &intrsources[irq];
|
|
|
|
if ((pcpl & r_imen) != 0) {
|
|
|
|
ci->ci_ipending |= r_imen; /* Masked! Mark this as pending */
|
|
pic->pic_disable_irq(pic, realirq);
|
|
} else {
|
|
|
|
/* this interrupt is no longer pending */
|
|
ci->ci_ipending &= ~r_imen;
|
|
ci->ci_idepth++;
|
|
|
|
splraise(is->is_mask);
|
|
mtmsr(msr | PSL_EE);
|
|
ih = is->is_hand;
|
|
bail = 0;
|
|
while ((ih != NULL) && (bail < 10)) {
|
|
if (ih->ih_fun == NULL)
|
|
panic("bogus handler for IRQ %s %d",
|
|
pic->pic_name, realirq);
|
|
if (ih->ih_level == IPL_VM) {
|
|
KERNEL_LOCK(1, NULL);
|
|
}
|
|
(*ih->ih_fun)(ih->ih_arg);
|
|
if (ih->ih_level == IPL_VM) {
|
|
KERNEL_UNLOCK_ONE(NULL);
|
|
}
|
|
ih = ih->ih_next;
|
|
bail++;
|
|
}
|
|
mtmsr(msr);
|
|
ci->ci_cpl = pcpl;
|
|
|
|
uvmexp.intrs++;
|
|
is->is_ev.ev_count++;
|
|
ci->ci_idepth--;
|
|
}
|
|
#ifdef PIC_DEBUG
|
|
boo:
|
|
#endif /* PIC_DEBUG */
|
|
pic->pic_ack_irq(pic, realirq);
|
|
realirq = pic->pic_get_irq(pic, PIC_GET_RECHECK);
|
|
if (realirq != 255)
|
|
goto start;
|
|
|
|
mtmsr(msr | PSL_EE);
|
|
splx(pcpl); /* Process pendings. */
|
|
mtmsr(msr);
|
|
|
|
return 0;
|
|
}
|
|
|
|
void
|
|
pic_ext_intr(void)
|
|
{
|
|
|
|
KASSERT(pics[primary_pic] != NULL);
|
|
pic_handle_intr(pics[primary_pic]);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
int
|
|
splraise(int ncpl)
|
|
{
|
|
struct cpu_info *ci = curcpu();
|
|
int ocpl;
|
|
|
|
__asm volatile("sync; eieio"); /* don't reorder.... */
|
|
|
|
ocpl = ci->ci_cpl;
|
|
ci->ci_cpl = ocpl | ncpl;
|
|
__asm volatile("sync; eieio"); /* reorder protect */
|
|
return ocpl;
|
|
}
|
|
|
|
void
|
|
splx(int ncpl)
|
|
{
|
|
struct cpu_info *ci = curcpu();
|
|
|
|
__asm volatile("sync; eieio"); /* reorder protect */
|
|
ci->ci_cpl = ncpl;
|
|
if (ci->ci_ipending & ~ncpl)
|
|
pic_do_pending_int();
|
|
__asm volatile("sync; eieio"); /* reorder protect */
|
|
}
|
|
|
|
int
|
|
spllower(int ncpl)
|
|
{
|
|
struct cpu_info *ci = curcpu();
|
|
int ocpl;
|
|
|
|
__asm volatile("sync; eieio"); /* reorder protect */
|
|
ocpl = ci->ci_cpl;
|
|
ci->ci_cpl = ncpl;
|
|
if (ci->ci_ipending & ~ncpl)
|
|
pic_do_pending_int();
|
|
__asm volatile("sync; eieio"); /* reorder protect */
|
|
return ocpl;
|
|
}
|
|
|
|
/* Following code should be implemented with lwarx/stwcx to avoid
|
|
* the disable/enable. i need to read the manual once more.... */
|
|
void
|
|
softintr(int ipl)
|
|
{
|
|
int msrsave;
|
|
|
|
msrsave = mfmsr();
|
|
mtmsr(msrsave & ~PSL_EE);
|
|
curcpu()->ci_ipending |= 1 << ipl;
|
|
mtmsr(msrsave);
|
|
}
|
|
|
|
void
|
|
genppc_cpu_configure(void)
|
|
{
|
|
aprint_normal("biomask %x netmask %x ttymask %x\n",
|
|
imask[IPL_BIO] & 0x1fffffff,
|
|
imask[IPL_NET] & 0x1fffffff,
|
|
imask[IPL_TTY] & 0x1fffffff);
|
|
|
|
spl0();
|
|
}
|
|
|
|
#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[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
|