Implement an FDT-aware interrupt establish API. This means the drivers
authorpatrick <patrick@openbsd.org>
Mon, 1 Aug 2016 14:17:00 +0000 (14:17 +0000)
committerpatrick <patrick@openbsd.org>
Mon, 1 Aug 2016 14:17:00 +0000 (14:17 +0000)
don't need to know where to attach to.  Instead the API will take care
of finding the correct interrupt establish for a given device node and
will call it with the correct data.

Adapted from the OFW GPIO framework.

ok kettenis@

sys/arch/armv7/armv7/intr.c
sys/arch/armv7/include/intr.h

index 5227ae9..a0b93a2 100644 (file)
@@ -1,4 +1,4 @@
-/* $OpenBSD: intr.c,v 1.5 2016/04/03 10:29:41 jsg Exp $ */
+/* $OpenBSD: intr.c,v 1.6 2016/08/01 14:17:00 patrick Exp $ */
 /*
  * Copyright (c) 2011 Dale Rahn <drahn@openbsd.org>
  *
 #include <sys/systm.h>
 #include <sys/param.h>
 #include <sys/timetc.h>
+#include <sys/malloc.h>
 
 #include <dev/clock_subr.h>
 #include <arm/cpufunc.h>
 #include <machine/cpu.h>
 #include <machine/intr.h>
 
+#include <dev/ofw/openfirm.h>
+
 int arm_dflt_splraise(int);
 int arm_dflt_spllower(int);
 void arm_dflt_splx(int);
@@ -37,6 +40,7 @@ const char *arm_dflt_intr_string(void *cookie);
 void arm_dflt_intr(void *);
 void arm_intr(void *);
 
+int OF_get_interrupt_controller(int);
 
 #define SI_TO_IRQBIT(x) (1 << (x))
 uint32_t arm_smask[NIPL];
@@ -80,6 +84,120 @@ const char *arm_intr_string(void *cookie)
        return arm_intr_func.intr_string(cookie);
 }
 
+/*
+ * Find the interrupt controller either via:
+ *  - node's property "interrupt-parrent"
+ *  - parent's property "interrupt-parrent"
+ */
+int
+OF_get_interrupt_controller(int node)
+{
+       int phandle;
+
+       if (node == 0)
+               return 0;
+
+       do {
+               if ((phandle = OF_getpropint(node, "interrupt-parent", 0))) {
+                       node = OF_getnodebyphandle(phandle);
+               } else {
+                       node = OF_parent(node);
+               }
+       } while (node && OF_getproplen(node, "#interrupt-cells") < 0);
+
+       return node;
+}
+
+LIST_HEAD(, interrupt_controller) interrupt_controllers =
+       LIST_HEAD_INITIALIZER(interrupt_controllers);
+
+void
+arm_intr_register_fdt(struct interrupt_controller *ic)
+{
+       ic->ic_cells = OF_getpropint(ic->ic_node, "#interrupt-cells", 0);
+       ic->ic_phandle = OF_getpropint(ic->ic_node, "phandle", 0);
+       if (ic->ic_cells == 0 || ic->ic_phandle == 0)
+               return;
+
+       LIST_INSERT_HEAD(&interrupt_controllers, ic, ic_list);
+}
+
+void *
+arm_intr_establish_fdt(int phandle, int level, int (*func)(void *),
+    void *cookie, char *name)
+{
+       return arm_intr_establish_fdt_idx(phandle, 0, level, func, cookie, name);
+}
+
+void *
+arm_intr_establish_fdt_idx(int phandle, int idx, int level, int (*func)(void *),
+    void *cookie, char *name)
+{
+       struct interrupt_controller *ic;
+       uint32_t *cell, *cells;
+       int i, len, ncells, node, extended = 1;
+       void *val = NULL;
+
+       len = OF_getproplen(phandle, "interrupts-extended");
+       if (len <= 0) {
+               len = OF_getproplen(phandle, "interrupts");
+               extended = 0;
+       }
+       if (len <= 0 || (len % sizeof(uint32_t) != 0))
+               return NULL;
+
+       /* Old style. */
+       if (!extended) {
+               node = OF_get_interrupt_controller(phandle);
+               if (node == 0)
+                       return NULL;
+
+               LIST_FOREACH(ic, &interrupt_controllers, ic_list) {
+                       if (ic->ic_phandle == node)
+                               break;
+               }
+
+               if (ic == NULL)
+                       return NULL;
+       }
+
+       cell = cells = malloc(len, M_TEMP, M_WAITOK);
+       if (extended)
+               OF_getpropintarray(phandle, "interrupts-extended", cells, len);
+       else
+               OF_getpropintarray(phandle, "interrupts", cells, len);
+       ncells = len / sizeof(uint32_t);
+
+       for (i = 0; i <= idx && ncells > 0; i++) {
+               if (extended) {
+                       node = cell[0];
+
+                       LIST_FOREACH(ic, &interrupt_controllers, ic_list) {
+                               if (ic->ic_phandle == node)
+                                       break;
+                       }
+
+                       if (ic == NULL)
+                               break;
+
+                       cell++;
+                       ncells--;
+               }
+
+               if (i == idx && ncells >= ic->ic_cells && ic->ic_establish) {
+                       val = ic->ic_establish(ic->ic_cookie, cell, level,
+                           func, cookie, name);
+                       break;
+               }
+
+               cell += ic->ic_cells;
+               ncells -= ic->ic_cells;
+       }
+
+       free(cells, M_TEMP, len);
+       return val;
+}
+
 int
 arm_dflt_splraise(int newcpl)
 {
index 4d8b42e..76f694b 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: intr.h,v 1.2 2015/09/19 02:13:05 jsg Exp $    */
+/*     $OpenBSD: intr.h,v 1.3 2016/08/01 14:17:00 patrick Exp $        */
 /*     $NetBSD: intr.h,v 1.12 2003/06/16 20:00:59 thorpej Exp $        */
 
 /*
@@ -145,6 +145,23 @@ const char *arm_intr_string(void *cookie);
 void arm_clock_register(void (*)(void), void (*)(u_int), void (*)(int),
     void (*)(void));
 
+struct interrupt_controller {
+       int     ic_node;
+       void    *ic_cookie;
+       void    *(*ic_establish)(void *, int *, int, int (*)(void *),
+                   void *, char *);
+
+       LIST_ENTRY(interrupt_controller) ic_list;
+       uint32_t ic_phandle;
+       uint32_t ic_cells;
+};
+
+void    arm_intr_register_fdt(struct interrupt_controller *);
+void   *arm_intr_establish_fdt(int, int, int (*)(void *),
+           void *, char *);
+void   *arm_intr_establish_fdt_idx(int, int, int, int (*)(void *),
+           void *, char *);
+
 #ifdef DIAGNOSTIC
 /*
  * Although this function is implemented in MI code, it must be in this MD