summaryrefslogtreecommitdiff
path: root/sys/arch
diff options
context:
space:
mode:
Diffstat (limited to 'sys/arch')
-rw-r--r--sys/arch/arm/xscale/pxa2x0_intr.c184
-rw-r--r--sys/arch/arm/xscale/pxa2x0_intr.h6
2 files changed, 172 insertions, 18 deletions
diff --git a/sys/arch/arm/xscale/pxa2x0_intr.c b/sys/arch/arm/xscale/pxa2x0_intr.c
index 6309128cb18..d445ae01975 100644
--- a/sys/arch/arm/xscale/pxa2x0_intr.c
+++ b/sys/arch/arm/xscale/pxa2x0_intr.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: pxa2x0_intr.c,v 1.10 2005/04/06 01:31:05 pascoe Exp $ */
+/* $OpenBSD: pxa2x0_intr.c,v 1.11 2005/05/27 20:21:15 uwe Exp $ */
/* $NetBSD: pxa2x0_intr.c,v 1.5 2003/07/15 00:24:55 lukem Exp $ */
/*
@@ -50,6 +50,7 @@ __KERNEL_RCSID(0, "$NetBSD: pxa2x0_intr.c,v 1.5 2003/07/15 00:24:55 lukem Exp $"
#include <sys/systm.h>
#include <sys/malloc.h>
#include <sys/evcount.h>
+#include <sys/queue.h>
#include <uvm/uvm_extern.h>
#include <machine/bus.h>
@@ -89,11 +90,18 @@ void pxa2x0_init_interrupt_masks(void);
/*
* interrupt dispatch table.
*/
+#if 1
+#define MULTIPLE_HANDLERS_ON_ONE_IRQ
+#endif
#ifdef MULTIPLE_HANDLERS_ON_ONE_IRQ
struct intrhand {
- TAILQ_ENTRY(intrhand) ih_list; /* link on intrq list */
- int (*ih_func)(void *); /* handler */
- void *ih_arg; /* arg for handler */
+ TAILQ_ENTRY(intrhand) ih_list; /* link on intrq list */
+ int (*ih_func)(void *); /* handler */
+ void *ih_arg; /* arg for handler */
+ char *ih_name;
+ struct evcount ih_count;
+ int ih_irq;
+ int ih_level;
};
#endif
@@ -102,12 +110,11 @@ static struct intrhandler{
TAILQ_HEAD(,intrhand) list;
#else
pxa2x0_irq_handler_t func;
-#endif
- void *arg; /* NULL for stackframe */
- /* struct evbnt ev; */
char *name;
+ void *arg; /* NULL for stackframe */
int ih_irq;
struct evcount ih_count;
+#endif
} handler[ICU_LEN];
__volatile int softint_pending;
@@ -143,11 +150,16 @@ pxaintc_attach(struct device *parent, struct device *self, void *args)
write_icu(SAIPIC_MR, 0);
for(i = 0; i < sizeof handler / sizeof handler[0]; ++i){
+#ifdef MULTIPLE_HANDLERS_ON_ONE_IRQ
+ TAILQ_INIT(&handler[i].list);
+ extirq_level[i] = IPL_NONE;
+#else
handler[i].name = "stray";
handler[i].func = pxa2x0_stray_interrupt;
handler[i].arg = (void *)(u_int32_t) i;
-
extirq_level[i] = IPL_SERIAL;
+#endif
+
}
pxa2x0_init_interrupt_masks();
@@ -189,6 +201,9 @@ pxa2x0_irq_handler(void *arg)
uint32_t irqbits;
int irqno;
int saved_spl_level;
+#ifdef MULTIPLE_HANDLERS_ON_ONE_IRQ
+ struct intrhand *ih;
+#endif
saved_spl_level = current_spl_level;
@@ -211,8 +226,11 @@ pxa2x0_irq_handler(void *arg)
? frame : handler[irqno].arg );
handler[irqno].ih_count.ec_count++;
#else
- /* process all handlers for this interrupt.
- XXX not yet */
+ TAILQ_FOREACH(ih, &handler[irqno].list, ih_list) {
+ if ((ih->ih_func)( ih->ih_arg == 0
+ ? frame : ih->ih_arg))
+ ih->ih_count.ec_count++;
+ }
#endif
#ifdef notyet
@@ -251,18 +269,84 @@ pxa2x0_stray_interrupt(void *cookie)
* Interrupt Mask Handling
*/
+#ifdef MULTIPLE_HANDLERS_ON_ONE_IRQ
+void pxa2x0_update_intr_masks(void);
+
void
-pxa2x0_update_intr_masks(int irqno, int level)
+pxa2x0_update_intr_masks()
+#else
+void pxa2x0_update_intr_masks(int irqno, int level);
+
+void
+pxa2x0_update_intr_masks(int irqno, int irqlevel)
+#endif
{
+ int psw;
+
+#ifdef MULTIPLE_HANDLERS_ON_ONE_IRQ
+ int irq;
+#ifdef DEBUG
+ int level;
+#endif
+ struct intrhand *ih;
+ psw = disable_interrupts(I32_bit);
+
+ /* First figure out which levels each IRQ uses. */
+ for (irq = 0; irq < ICU_LEN; irq++) {
+ int i;
+ int max = IPL_NONE;
+ int min = IPL_HIGH; /* XXX kill IPL_SERIAL */
+ TAILQ_FOREACH(ih, &handler[irq].list, ih_list) {
+ if (ih->ih_level > max)
+ max = ih->ih_level;
+
+ if (ih->ih_level < min)
+ min = ih->ih_level;
+ }
+
+ extirq_level[irq] = max;
+
+ if (min == IPL_HIGH)
+ min = IPL_NONE;
+
+ /* Enable interrupt at lower level */
+ for(i = 0; i < min; ++i)
+ pxa2x0_imask[i] |= (1 << irq);
+
+ /* Disable interrupt at upper level */
+ for( ; i < NIPL-1; ++i)
+ pxa2x0_imask[i] &= ~(1 << irq);
+ }
+
+ /* fixup */
+ pxa2x0_imask[IPL_NONE] |=
+ SI_TO_IRQBIT(SI_SOFT) |
+ SI_TO_IRQBIT(SI_SOFTCLOCK) |
+ SI_TO_IRQBIT(SI_SOFTNET) |
+ SI_TO_IRQBIT(SI_SOFTSERIAL);
+ pxa2x0_imask[IPL_SOFT] |=
+ SI_TO_IRQBIT(SI_SOFTCLOCK) |
+ SI_TO_IRQBIT(SI_SOFTNET) |
+ SI_TO_IRQBIT(SI_SOFTSERIAL);
+ pxa2x0_imask[IPL_SOFTCLOCK] |=
+ SI_TO_IRQBIT(SI_SOFTNET) |
+ SI_TO_IRQBIT(SI_SOFTSERIAL);
+ pxa2x0_imask[IPL_SOFTNET] |=
+ SI_TO_IRQBIT(SI_SOFTSERIAL);
+ pxa2x0_imask[IPL_SOFTSERIAL] |=
+ 0;
+#else
+ int level; /* debug */
int mask = 1U<<irqno;
- int psw = disable_interrupts(I32_bit);
int i;
+ psw = disable_interrupts(I32_bit);
- for(i = 0; i < level; ++i)
+ for(i = 0; i < irqlevel; ++i)
pxa2x0_imask[i] |= mask; /* Enable interrupt at lower level */
for( ; i < NIPL-1; ++i)
pxa2x0_imask[i] &= ~mask; /* Disable interrupt at upper level */
+#endif
/*
* Enforce a hierarchy that gives "slow" device (or devices with
@@ -303,6 +387,23 @@ pxa2x0_update_intr_masks(int irqno, int level)
*/
pxa2x0_imask[IPL_SERIAL] &= pxa2x0_imask[IPL_HIGH];
+#ifdef DEBUG
+ for (level = IPL_NONE; level < NIPL; level++) {
+ printf("imask %d, %x\n", level, pxa2x0_imask[level]);
+ }
+#endif
+
+#ifdef MULTIPLE_HANDLERS_ON_ONE_IRQ
+ for (irq = 0; irq < ICU_LEN; irq++) {
+ int max_irq = IPL_NONE;
+ TAILQ_FOREACH(ih, &handler[irq].list, ih_list) {
+ if (ih->ih_level > max_irq)
+ max_irq = ih->ih_level;
+ }
+ extirq_level[irq] = max_irq;
+ }
+#endif
+
write_icu(SAIPIC_MR, pxa2x0_imask[current_spl_level]);
restore_interrupts(psw);
@@ -437,25 +538,47 @@ pxa2x0_intr_establish(int irqno, int level,
int (*func)(void *), void *arg, char *name)
{
int psw;
+#ifdef MULTIPLE_HANDLERS_ON_ONE_IRQ
+ struct intrhand *ih;
+#else
struct intrhandler *ih;
+#endif
if (irqno < PXA2X0_IRQ_MIN || irqno >= ICU_LEN)
panic("intr_establish: bogus irq number %d", irqno);
psw = disable_interrupts(I32_bit);
+#ifdef MULTIPLE_HANDLERS_ON_ONE_IRQ
+ /* no point in sleeping unless someone can free memory. */
+ MALLOC(ih, struct intrhand *, sizeof *ih, M_DEVBUF,
+ cold ? M_NOWAIT : M_WAITOK);
+ if (ih == NULL)
+ panic("intr_establish: can't malloc handler info");
+ ih->ih_func = func;
+ ih->ih_arg = arg;
+ ih->ih_level = level;
+ ih->ih_irq = irqno;
+
+ TAILQ_INSERT_TAIL(&handler[irqno].list, ih, ih_list);
+#else
ih = &handler[irqno];
ih->arg = arg;
ih->func = func;
ih->name = name;
ih->ih_irq = irqno;
extirq_level[irqno] = level;
+#endif
if (name != NULL)
evcount_attach(&ih->ih_count, name, (void *)&ih->ih_irq,
&evcount_intr);
+#ifdef MULTIPLE_HANDLERS_ON_ONE_IRQ
+ pxa2x0_update_intr_masks();
+#else
pxa2x0_update_intr_masks(irqno, level);
+#endif
restore_interrupts(psw);
@@ -465,11 +588,25 @@ pxa2x0_intr_establish(int irqno, int level,
void
pxa2x0_intr_disestablish(void *cookie)
{
+
+#ifdef MULTIPLE_HANDLERS_ON_ONE_IRQ
+ int psw;
+ struct intrhand *ih = cookie;
+ int irqno = ih->ih_irq;
+
+ psw = disable_interrupts(I32_bit);
+ TAILQ_REMOVE(&handler[irqno].list, ih, ih_list);
+
+ FREE(ih, M_DEVBUF);
+
+ pxa2x0_update_intr_masks();
+
+ restore_interrupts(psw);
+#else
struct intrhandler *lhandler = cookie;
int irqno;
int psw;
struct intrhandler *ih;
-
irqno = lhandler - handler;
if (irqno < PXA2X0_IRQ_MIN || irqno >= ICU_LEN)
@@ -488,6 +625,7 @@ pxa2x0_intr_disestablish(void *cookie)
pxa2x0_update_intr_masks(irqno, IPL_SERIAL);
restore_interrupts(psw);
+#endif
}
/*
@@ -571,3 +709,21 @@ pxa2x0_setsoftintr(int si)
if ( softint_pending & pxa2x0_imask[current_spl_level] )
pxa2x0_do_pending();
}
+
+const char *
+pxa2x0_intr_string(void *cookie)
+{
+#ifdef MULTIPLE_HANDLERS_ON_ONE_IRQ
+ struct intrhand *ih = cookie;
+#else
+ struct intrhandler *lhandler = cookie;
+#endif
+ static char irqstr[32];
+
+ if (ih == NULL)
+ snprintf(irqstr, sizeof irqstr, "couldn't establish interrupt");
+ else
+ snprintf(irqstr, sizeof irqstr, "irq %ld", ih->ih_irq);
+
+ return irqstr;
+}
diff --git a/sys/arch/arm/xscale/pxa2x0_intr.h b/sys/arch/arm/xscale/pxa2x0_intr.h
index 5c632db9a7b..d929c5896ad 100644
--- a/sys/arch/arm/xscale/pxa2x0_intr.h
+++ b/sys/arch/arm/xscale/pxa2x0_intr.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: pxa2x0_intr.h,v 1.6 2005/02/28 22:10:01 drahn Exp $ */
+/* $OpenBSD: pxa2x0_intr.h,v 1.7 2005/05/27 20:21:15 uwe Exp $ */
/* $NetBSD: pxa2x0_intr.h,v 1.4 2003/07/05 06:53:08 dogcow Exp $ */
/* Derived from i80321_intr.h */
@@ -105,9 +105,7 @@ void pxa2x0_irq_handler(void *);
void *pxa2x0_intr_establish(int irqno, int level, int (*func)(void *),
void *cookie, char *name);
void pxa2x0_intr_disestablish(void *cookie);
-
-void pxa2x0_update_intr_masks(int irqno, int level);
-extern __volatile int current_spl_level;
+const char *pxa2x0_intr_string(void *cookie);
#endif /* ! _LOCORE */