svn commit: r301263 - in head/sys: kern sys

Andrew Turner andrew at FreeBSD.org
Fri Jun 3 10:13:19 UTC 2016


Author: andrew
Date: Fri Jun  3 10:13:18 2016
New Revision: 301263
URL: https://svnweb.freebsd.org/changeset/base/301263

Log:
  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

Modified:
  head/sys/kern/subr_intr.c
  head/sys/sys/intr.h

Modified: head/sys/kern/subr_intr.c
==============================================================================
--- head/sys/kern/subr_intr.c	Fri Jun  3 09:17:22 2016	(r301262)
+++ head/sys/kern/subr_intr.c	Fri Jun  3 10:13:18 2016	(r301263)
@@ -98,6 +98,15 @@ static intr_irq_filter_t *irq_root_filte
 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
 	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)

Modified: head/sys/sys/intr.h
==============================================================================
--- head/sys/sys/intr.h	Fri Jun  3 09:17:22 2016	(r301262)
+++ head/sys/sys/intr.h	Fri Jun  3 10:13:18 2016	(r301263)
@@ -74,6 +74,7 @@ typedef int intr_irq_filter_t(void *arg,
 #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_cp
 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 reso
 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);


More information about the svn-src-all mailing list