Index: head/sys/kern/subr_intr.c =================================================================== --- head/sys/kern/subr_intr.c +++ head/sys/kern/subr_intr.c @@ -98,6 +98,15 @@ static void *irq_root_arg; static u_int irq_root_ipicount; +struct intr_pic_child { + SLIST_ENTRY(intr_pic_child) pc_next; + struct intr_pic *pc_pic; + intr_child_irq_filter_t *pc_filter; + void *pc_filter_arg; + uintptr_t pc_start; + uintptr_t pc_length; +}; + /* Interrupt controller definition. */ struct intr_pic { SLIST_ENTRY(intr_pic) pic_next; @@ -106,6 +115,8 @@ #define FLAG_PIC (1 << 0) #define FLAG_MSI (1 << 1) u_int pic_flags; + struct mtx pic_child_lock; + SLIST_HEAD(, intr_pic_child) pic_children; }; static struct mtx pic_list_lock; @@ -323,6 +334,29 @@ #endif } +int +intr_child_irq_handler(struct intr_pic *parent, uintptr_t irq) +{ + struct intr_pic_child *child; + bool found; + + found = false; + mtx_lock_spin(&parent->pic_child_lock); + SLIST_FOREACH(child, &parent->pic_children, pc_next) { + if (child->pc_start <= irq && + irq < (child->pc_start + child->pc_length)) { + found = true; + break; + } + } + mtx_unlock_spin(&parent->pic_child_lock); + + if (found) + return (child->pc_filter(child->pc_filter_arg, irq)); + + return (FILTER_STRAY); +} + /* * interrupt controller dispatch function for interrupts. It should * be called straight from the interrupt controller, when associated interrupt @@ -892,6 +926,7 @@ } pic->pic_xref = xref; pic->pic_dev = dev; + mtx_init(&pic->pic_child_lock, "pic child lock", NULL, MTX_SPIN); SLIST_INSERT_HEAD(&pic_list, pic, pic_next); mtx_unlock(&pic_list_lock); @@ -1001,6 +1036,44 @@ return (0); } +/* + * Add a handler to manage a sub range of a parents interrupts. + */ +struct intr_pic * +intr_pic_add_handler(device_t parent, struct intr_pic *pic, + intr_child_irq_filter_t *filter, void *arg, uintptr_t start, + uintptr_t length) +{ + struct intr_pic *parent_pic; + struct intr_pic_child *newchild; +#ifdef INVARIANTS + struct intr_pic_child *child; +#endif + + parent_pic = pic_lookup(parent, 0); + if (parent_pic == NULL) + return (NULL); + + newchild = malloc(sizeof(*newchild), M_INTRNG, M_WAITOK | M_ZERO); + newchild->pc_pic = pic; + newchild->pc_filter = filter; + newchild->pc_filter_arg = arg; + newchild->pc_start = start; + newchild->pc_length = length; + + mtx_lock_spin(&parent_pic->pic_child_lock); +#ifdef INVARIANTS + SLIST_FOREACH(child, &parent_pic->pic_children, pc_next) { + KASSERT(child->pc_pic != pic, ("%s: Adding a child PIC twice", + __func__)); + } +#endif + SLIST_INSERT_HEAD(&parent_pic->pic_children, newchild, pc_next); + mtx_unlock_spin(&parent_pic->pic_child_lock); + + return (pic); +} + int intr_map_irq(device_t dev, intptr_t xref, struct intr_map_data *data, u_int *irqp) Index: head/sys/sys/intr.h =================================================================== --- head/sys/sys/intr.h +++ head/sys/sys/intr.h @@ -74,6 +74,7 @@ #else typedef int intr_irq_filter_t(void *arg); #endif +typedef int intr_child_irq_filter_t(void *arg, uintptr_t irq); #define INTR_ISRC_NAMELEN (MAXCOMLEN + 1) @@ -81,6 +82,8 @@ #define INTR_ISRCF_PPI 0x02 /* PPI interrupt */ #define INTR_ISRCF_BOUND 0x04 /* bound to a CPU */ +struct intr_pic; + /* Interrupt source definition. */ struct intr_irqsrc { device_t isrc_dev; /* where isrc is mapped */ @@ -113,6 +116,8 @@ struct intr_pic *intr_pic_register(device_t, intptr_t); int intr_pic_deregister(device_t, intptr_t); int intr_pic_claim_root(device_t, intptr_t, intr_irq_filter_t *, void *, u_int); +struct intr_pic *intr_pic_add_handler(device_t, struct intr_pic *, + intr_child_irq_filter_t *, void *, uintptr_t, uintptr_t); extern device_t intr_irq_root_dev; @@ -127,6 +132,7 @@ int intr_teardown_irq(device_t, struct resource *, void *); int intr_describe_irq(device_t, struct resource *, void *, const char *); +int intr_child_irq_handler(struct intr_pic *, uintptr_t); /* MSI/MSI-X handling */ int intr_msi_register(device_t, intptr_t);