diff options
Diffstat (limited to 'sys/arch')
-rw-r--r-- | sys/arch/arm/xscale/pxa2x0_intr.c | 184 | ||||
-rw-r--r-- | sys/arch/arm/xscale/pxa2x0_intr.h | 6 |
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 */ |