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:
parent
ec6689059d
commit
d1605cda2b
@ -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)
|
||||
|
@ -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);
|
||||
|
Loading…
Reference in New Issue
Block a user