summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--sys/kern/subr_intr.c73
-rw-r--r--sys/sys/intr.h6
2 files changed, 79 insertions, 0 deletions
diff --git a/sys/kern/subr_intr.c b/sys/kern/subr_intr.c
index 7fcf7d7..c82e502 100644
--- a/sys/kern/subr_intr.c
+++ b/sys/kern/subr_intr.c
@@ -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)
diff --git a/sys/sys/intr.h b/sys/sys/intr.h
index 3d75efd..8318e70 100644
--- a/sys/sys/intr.h
+++ b/sys/sys/intr.h
@@ -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);
OpenPOWER on IntegriCloud