diff options
author | Patrick Wildt <patrick@cvs.openbsd.org> | 2018-07-06 13:08:11 +0000 |
---|---|---|
committer | Patrick Wildt <patrick@cvs.openbsd.org> | 2018-07-06 13:08:11 +0000 |
commit | 92d6b220b8fa497b019b22b270cb489771448176 (patch) | |
tree | e103ad064d0762415722cb79904b2b5a8c898ca4 /sys | |
parent | b20d35ab54d785e851907295beb54c6dc6427fd5 (diff) |
Implement an API for establishing legacy PCI interrupts. This specific
establish function parses the device tree's interrupt map to discover
the correct interrupt controller node and interrupt cells for the given
PCI node. After retrieving that information we can do the same the
normal FDT establish API already does.
MSI interrupts are established in a different way as well. Instead of
simply hooking up the interrupt handler and returning an MD cookie, we
need to pass back information for the PCI controller to configure its
interrupt correctly. For this, add another establish routine into the
FDT-based interrupt API which looks up msi-controller nodes and calls
their MSI-specific establish function if requested.
ok kettenis@
Diffstat (limited to 'sys')
-rw-r--r-- | sys/arch/armv7/armv7/intr.c | 111 | ||||
-rw-r--r-- | sys/arch/armv7/include/intr.h | 8 |
2 files changed, 116 insertions, 3 deletions
diff --git a/sys/arch/armv7/armv7/intr.c b/sys/arch/armv7/armv7/intr.c index a9488227872..5de9d0aabc3 100644 --- a/sys/arch/armv7/armv7/intr.c +++ b/sys/arch/armv7/armv7/intr.c @@ -1,4 +1,4 @@ -/* $OpenBSD: intr.c,v 1.12 2017/09/08 05:36:51 deraadt Exp $ */ +/* $OpenBSD: intr.c,v 1.13 2018/07/06 13:08:10 patrick Exp $ */ /* * Copyright (c) 2011 Dale Rahn <drahn@openbsd.org> * @@ -28,6 +28,7 @@ #include <dev/ofw/openfirm.h> uint32_t arm_intr_get_parent(int); +uint32_t arm_intr_msi_get_parent(int); void *arm_intr_prereg_establish_fdt(void *, int *, int, int (*)(void *), void *, char *); @@ -103,6 +104,19 @@ arm_intr_get_parent(int node) return phandle; } +uint32_t +arm_intr_msi_get_parent(int node) +{ + uint32_t phandle = 0; + + while (node && !phandle) { + phandle = OF_getpropint(node, "msi-parent", 0); + node = OF_parent(node); + } + + return phandle; +} + /* * Interrupt pre-registration. * @@ -209,7 +223,7 @@ 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) + if (ic->ic_phandle == 0) return; KASSERT(ic->ic_cells <= MAX_INTERRUPT_CELLS); @@ -317,6 +331,99 @@ arm_intr_establish_fdt_idx(int node, int idx, int level, int (*func)(void *), return ih; } +void * +arm_intr_establish_fdt_imap(int node, int *reg, int nreg, int level, + int (*func)(void *), void *cookie, char *name) +{ + struct interrupt_controller *ic; + struct arm_intr_handle *ih; + uint32_t *cell; + uint32_t map_mask[4], *map; + int i, len, acells, ncells; + void *val = NULL; + + if (nreg != sizeof(map_mask)) + return NULL; + + if (OF_getpropintarray(node, "interrupt-map-mask", map_mask, + sizeof(map_mask)) != sizeof(map_mask)) + return NULL; + + len = OF_getproplen(node, "interrupt-map"); + if (len <= 0) + return NULL; + + map = malloc(len, M_DEVBUF, M_WAITOK); + OF_getpropintarray(node, "interrupt-map", map, len); + + cell = map; + ncells = len / sizeof(uint32_t); + for (i = 0; ncells > 0; i++) { + LIST_FOREACH(ic, &interrupt_controllers, ic_list) { + if (ic->ic_phandle == cell[4]) + break; + } + + if (ic == NULL) + break; + + acells = OF_getpropint(ic->ic_node, "#address-cells", 0); + if (ncells >= (5 + acells + ic->ic_cells) && + (reg[0] & map_mask[0]) == cell[0] && + (reg[1] & map_mask[1]) == cell[1] && + (reg[2] & map_mask[2]) == cell[2] && + (reg[3] & map_mask[3]) == cell[3] && + ic->ic_establish) { + val = ic->ic_establish(ic->ic_cookie, &cell[5 + acells], + level, func, cookie, name); + break; + } + + cell += (5 + acells + ic->ic_cells); + ncells -= (5 + acells + ic->ic_cells); + } + + if (val == NULL) { + free(map, M_DEVBUF, len); + return NULL; + } + + ih = malloc(sizeof(*ih), M_DEVBUF, M_WAITOK); + ih->ih_ic = ic; + ih->ih_ih = val; + + free(map, M_DEVBUF, len); + return ih; +} + +void * +arm_intr_establish_fdt_msi(int node, uint64_t *addr, uint64_t *data, + int level, int (*func)(void *), void *cookie, char *name) +{ + struct interrupt_controller *ic; + struct arm_intr_handle *ih; + uint32_t phandle; + void *val = NULL; + + phandle = arm_intr_msi_get_parent(node); + LIST_FOREACH(ic, &interrupt_controllers, ic_list) { + if (ic->ic_phandle == phandle) + break; + } + + if (ic == NULL || ic->ic_establish_msi == NULL) + return NULL; + + val = ic->ic_establish_msi(ic->ic_cookie, addr, data, + level, func, cookie, name); + + ih = malloc(sizeof(*ih), M_DEVBUF, M_WAITOK); + ih->ih_ic = ic; + ih->ih_ih = val; + + return ih; +} + void arm_intr_disestablish_fdt(void *cookie) { diff --git a/sys/arch/armv7/include/intr.h b/sys/arch/armv7/include/intr.h index d583bf49136..61ff23e53f2 100644 --- a/sys/arch/armv7/include/intr.h +++ b/sys/arch/armv7/include/intr.h @@ -1,4 +1,4 @@ -/* $OpenBSD: intr.h,v 1.7 2018/05/30 13:54:09 mpi Exp $ */ +/* $OpenBSD: intr.h,v 1.8 2018/07/06 13:08:10 patrick Exp $ */ /* $NetBSD: intr.h,v 1.12 2003/06/16 20:00:59 thorpej Exp $ */ /* @@ -153,6 +153,8 @@ struct interrupt_controller { void *ic_cookie; void *(*ic_establish)(void *, int *, int, int (*)(void *), void *, char *); + void *(*ic_establish_msi)(void *, uint64_t *, uint64_t *, int, + int (*)(void *), void *, char *); void (*ic_disestablish)(void *); void (*ic_route)(void *, int, struct cpu_info *); @@ -167,6 +169,10 @@ void *arm_intr_establish_fdt(int, int, int (*)(void *), void *, char *); void *arm_intr_establish_fdt_idx(int, int, int, int (*)(void *), void *, char *); +void *arm_intr_establish_fdt_imap(int, int *, int, int, int (*)(void *), + void *, char *); +void *arm_intr_establish_fdt_msi(int, uint64_t *, uint64_t *, int , + int (*)(void *), void *, char *); void arm_intr_disestablish_fdt(void *); void arm_intr_route(void *, int, struct cpu_info *); |