diff options
author | 2016-08-01 14:17:00 +0000 | |
---|---|---|
committer | 2016-08-01 14:17:00 +0000 | |
commit | 0ffbfca45c62a5ad651139c1dbdf5fb395902d4c (patch) | |
tree | 658dd07e401fa5056d3265d90a9d151fd59de8d6 /sys | |
parent | Mark shared producer and consumer indices volatile (diff) | |
download | wireguard-openbsd-0ffbfca45c62a5ad651139c1dbdf5fb395902d4c.tar.xz wireguard-openbsd-0ffbfca45c62a5ad651139c1dbdf5fb395902d4c.zip |
Implement an FDT-aware interrupt establish API. This means the drivers
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@
Diffstat (limited to 'sys')
-rw-r--r-- | sys/arch/armv7/armv7/intr.c | 120 | ||||
-rw-r--r-- | sys/arch/armv7/include/intr.h | 19 |
2 files changed, 137 insertions, 2 deletions
diff --git a/sys/arch/armv7/armv7/intr.c b/sys/arch/armv7/armv7/intr.c index 5227ae9d46f..a0b93a2bc14 100644 --- a/sys/arch/armv7/armv7/intr.c +++ b/sys/arch/armv7/armv7/intr.c @@ -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> * @@ -19,12 +19,15 @@ #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) { diff --git a/sys/arch/armv7/include/intr.h b/sys/arch/armv7/include/intr.h index 4d8b42ea449..76f694b4a72 100644 --- a/sys/arch/armv7/include/intr.h +++ b/sys/arch/armv7/include/intr.h @@ -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 |