Add an interface to handle interrupt controllers that have a contiguous

range of interrupts they pass to a second controller driver to handle.
The parent driver is expected to detect when one of these interrupts has
been triggered and call intr_child_irq_handler to pass the interrupt to
a child. The children controllers are then expected to manage the range
by allocating interrupts as needed.

This will initially be used by the ARM GICv3 driver, but is is expected to
be useful for other driver where this type of allocation applies.

Obtained from:	ABT Systems Ltd
Sponsored by:	The FreeBSD Foundation
Differential Revision:	https://reviews.freebsd.org/D6436
This commit is contained in:
Andrew Turner 2016-06-03 10:13:18 +00:00
parent ec6689059d
commit d1605cda2b
2 changed files with 79 additions and 0 deletions

View File

@ -98,6 +98,15 @@ static intr_irq_filter_t *irq_root_filter;
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 @@ struct intr_pic {
#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 @@ intr_irq_handler(struct trapframe *tf)
#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_create(device_t dev, intptr_t xref)
}
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 @@ intr_pic_claim_root(device_t dev, intptr_t xref, intr_irq_filter_t *filter,
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)

View File

@ -74,6 +74,7 @@ typedef int intr_irq_filter_t(void *arg, struct trapframe *tf);
#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 @@ typedef int intr_irq_filter_t(void *arg);
#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 @@ u_int intr_irq_next_cpu(u_int current_cpu, cpuset_t *cpumask);
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_setup_irq(device_t, struct resource *, driver_filter_t, driver_intr_t,
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);