Module Name: src Committed By: marty Date: Tue Jan 5 21:53:48 UTC 2016
Modified Files: src/sys/arch/arm/fdt: gic_fdt.c src/sys/arch/arm/nvidia: tegra_lic.c src/sys/arch/arm/samsung: exynos_combiner.c mct.c src/sys/arch/evbarm/conf: EXYNOS src/sys/dev/fdt: fdt_intr.c fdtvar.h Log Message: FDT: Interrupts -- add support for interrupt maps The mct on exynos uses an interrupt map so we add support now. Devices represent their interrupts either through a combination of interrupt-parent and interrupts properties, where the 'interrupts' property is an array of one or more interrupt specifiers; or through a combination of an interrupt-parent that points to an interrupt-map, where the interrupt-map contains 2 or more entries consisting of an index, a pointer to an interrupt-controller, and a specifier for that controller. This code adds the ability to walk the interrupt-map and return a specifier. Unfortunately, the addition requires changing the interface to the interrupt-controllers' _establish and _intstr functions, so this check in contains a rototill of the three existing fdt interrupt controllers to use the new interface. To generate a diff of this commit: cvs rdiff -u -r1.1 -r1.2 src/sys/arch/arm/fdt/gic_fdt.c cvs rdiff -u -r1.2 -r1.3 src/sys/arch/arm/nvidia/tegra_lic.c cvs rdiff -u -r1.5 -r1.6 src/sys/arch/arm/samsung/exynos_combiner.c cvs rdiff -u -r1.8 -r1.9 src/sys/arch/arm/samsung/mct.c cvs rdiff -u -r1.9 -r1.10 src/sys/arch/evbarm/conf/EXYNOS cvs rdiff -u -r1.3 -r1.4 src/sys/dev/fdt/fdt_intr.c cvs rdiff -u -r1.6 -r1.7 src/sys/dev/fdt/fdtvar.h Please note that diffs are not public domain; they are subject to the copyright notices on the relevant files.
Modified files: Index: src/sys/arch/arm/fdt/gic_fdt.c diff -u src/sys/arch/arm/fdt/gic_fdt.c:1.1 src/sys/arch/arm/fdt/gic_fdt.c:1.2 --- src/sys/arch/arm/fdt/gic_fdt.c:1.1 Sun Dec 13 17:45:37 2015 +++ src/sys/arch/arm/fdt/gic_fdt.c Tue Jan 5 21:53:48 2016 @@ -1,4 +1,4 @@ -/* $NetBSD: gic_fdt.c,v 1.1 2015/12/13 17:45:37 jmcneill Exp $ */ +/* $NetBSD: gic_fdt.c,v 1.2 2016/01/05 21:53:48 marty Exp $ */ /*- * Copyright (c) 2015 Jared D. McNeill <jmcne...@invisible.ca> @@ -27,7 +27,7 @@ */ #include <sys/cdefs.h> -__KERNEL_RCSID(0, "$NetBSD: gic_fdt.c,v 1.1 2015/12/13 17:45:37 jmcneill Exp $"); +__KERNEL_RCSID(0, "$NetBSD: gic_fdt.c,v 1.2 2016/01/05 21:53:48 marty Exp $"); #include <sys/param.h> #include <sys/bus.h> @@ -44,10 +44,10 @@ __KERNEL_RCSID(0, "$NetBSD: gic_fdt.c,v static int gic_fdt_match(device_t, cfdata_t, void *); static void gic_fdt_attach(device_t, device_t, void *); -static void * gic_fdt_establish(device_t, int, u_int, int, int, +static void * gic_fdt_establish(device_t, u_int *, int, int, int (*)(void *), void *); static void gic_fdt_disestablish(device_t, void *); -static bool gic_fdt_intrstr(device_t, int, u_int, char *, size_t); +static bool gic_fdt_intrstr(device_t, u_int *, char *, size_t); struct fdtbus_interrupt_controller_func gic_fdt_funcs = { .establish = gic_fdt_establish, @@ -100,49 +100,21 @@ gic_fdt_attach(device_t parent, device_t } static void * -gic_fdt_establish(device_t dev, int phandle, u_int index, int ipl, int flags, +gic_fdt_establish(device_t dev, u_int *specifier, int ipl, int flags, int (*func)(void *), void *arg) { - struct gic_fdt_softc * const sc = device_private(dev); int iflags = (flags & FDT_INTR_MPSAFE) ? IST_MPSAFE : 0; - u_int *interrupts; - int interrupt_cells, len; - - len = OF_getprop(sc->sc_phandle, "#interrupt-cells", &interrupt_cells, - sizeof(interrupt_cells)); - if (len != sizeof(interrupt_cells) || interrupt_cells <= 0) - return NULL; - interrupt_cells = be32toh(interrupt_cells); - - len = OF_getproplen(phandle, "interrupts"); - if (len <= 0) - return NULL; - - const u_int clen = interrupt_cells * 4; - const u_int nintr = len / interrupt_cells; - - if (index >= nintr) - return NULL; - - interrupts = kmem_alloc(len, KM_SLEEP); - - if (OF_getprop(phandle, "interrupts", interrupts, len) != len) { - kmem_free(interrupts, len); - return NULL; - } /* 1st cell is the interrupt type; 0 is SPI, 1 is PPI */ /* 2nd cell is the interrupt number */ /* 3rd cell is flags */ - const u_int type = be32toh(interrupts[index * clen + 0]); - const u_int intr = be32toh(interrupts[index * clen + 1]); + const u_int type = be32toh(specifier[0]); + const u_int intr = be32toh(specifier[1]); const u_int irq = type == 0 ? IRQ_SPI(intr) : IRQ_PPI(intr); - const u_int trig = be32toh(interrupts[index * clen + 2]) & 0xf; + const u_int trig = be32toh(specifier[2]) & 0xf; const u_int level = (trig & 0x3) ? IST_EDGE : IST_LEVEL; - kmem_free(interrupts, len); - return intr_establish(irq, ipl, level | iflags, func, arg); } @@ -153,49 +125,18 @@ gic_fdt_disestablish(device_t dev, void } static bool -gic_fdt_intrstr(device_t dev, int phandle, u_int index, char *buf, - size_t buflen) +gic_fdt_intrstr(device_t dev, u_int *specifier, char *buf, size_t buflen) { - struct gic_fdt_softc * const sc = device_private(dev); - u_int *interrupts; - int interrupt_cells, len; - - len = OF_getprop(sc->sc_phandle, "#interrupt-cells", &interrupt_cells, - sizeof(interrupt_cells)); - if (len != sizeof(interrupt_cells) || interrupt_cells <= 0) { - return false; - } - interrupt_cells = be32toh(interrupt_cells); - - len = OF_getproplen(phandle, "interrupts"); - if (len <= 0) { - return false; - } - - const u_int clen = interrupt_cells * 4; - const u_int nintr = len / interrupt_cells; - - if (index >= nintr) { - return false; - } - - interrupts = kmem_alloc(len, KM_SLEEP); - - if (OF_getprop(phandle, "interrupts", interrupts, len) != len) { - kmem_free(interrupts, len); - return false; - } - /* 1st cell is the interrupt type; 0 is SPI, 1 is PPI */ /* 2nd cell is the interrupt number */ /* 3rd cell is flags */ - const u_int type = be32toh(interrupts[index * clen + 0]); - const u_int intr = be32toh(interrupts[index * clen + 1]); + if (!specifier) + return false; + const u_int type = be32toh(specifier[0]); + const u_int intr = be32toh(specifier[1]); const u_int irq = type == 0 ? IRQ_SPI(intr) : IRQ_PPI(intr); - kmem_free(interrupts, len); - snprintf(buf, buflen, "GIC irq %d", irq); return true; Index: src/sys/arch/arm/nvidia/tegra_lic.c diff -u src/sys/arch/arm/nvidia/tegra_lic.c:1.2 src/sys/arch/arm/nvidia/tegra_lic.c:1.3 --- src/sys/arch/arm/nvidia/tegra_lic.c:1.2 Wed Dec 16 19:46:55 2015 +++ src/sys/arch/arm/nvidia/tegra_lic.c Tue Jan 5 21:53:48 2016 @@ -1,4 +1,4 @@ -/* $NetBSD: tegra_lic.c,v 1.2 2015/12/16 19:46:55 jmcneill Exp $ */ +/* $NetBSD: tegra_lic.c,v 1.3 2016/01/05 21:53:48 marty Exp $ */ /*- * Copyright (c) 2015 Jared D. McNeill <jmcne...@invisible.ca> @@ -27,7 +27,7 @@ */ #include <sys/cdefs.h> -__KERNEL_RCSID(0, "$NetBSD: tegra_lic.c,v 1.2 2015/12/16 19:46:55 jmcneill Exp $"); +__KERNEL_RCSID(0, "$NetBSD: tegra_lic.c,v 1.3 2016/01/05 21:53:48 marty Exp $"); #include <sys/param.h> #include <sys/bus.h> @@ -47,10 +47,10 @@ __KERNEL_RCSID(0, "$NetBSD: tegra_lic.c, static int tegra_lic_match(device_t, cfdata_t, void *); static void tegra_lic_attach(device_t, device_t, void *); -static void * tegra_lic_establish(device_t, int, u_int, int, int, +static void * tegra_lic_establish(device_t, u_int *, int, int, int (*)(void *), void *); static void tegra_lic_disestablish(device_t, void *); -static bool tegra_lic_intrstr(device_t, int, u_int, char *, size_t); +static bool tegra_lic_intrstr(device_t, u_int *, char *, size_t); struct fdtbus_interrupt_controller_func tegra_lic_funcs = { .establish = tegra_lic_establish, @@ -97,48 +97,21 @@ tegra_lic_attach(device_t parent, device } static void * -tegra_lic_establish(device_t dev, int phandle, u_int index, int ipl, int flags, +tegra_lic_establish(device_t dev, u_int *specifier, int ipl, int flags, int (*func)(void *), void *arg) { - struct tegra_lic_softc * const sc = device_private(dev); int iflags = (flags & FDT_INTR_MPSAFE) ? IST_MPSAFE : 0; - u_int *interrupts; - int interrupt_cells, len; - - if (of_getprop_uint32(sc->sc_phandle, "#interrupt-cells", - &interrupt_cells)) { - return NULL; - } - - len = OF_getproplen(phandle, "interrupts"); - if (len <= 0) - return NULL; - - const u_int clen = interrupt_cells * 4; - const u_int nintr = len / interrupt_cells; - - if (index >= nintr) - return NULL; - - interrupts = kmem_alloc(len, KM_SLEEP); - - if (OF_getprop(phandle, "interrupts", interrupts, len) != len) { - kmem_free(interrupts, len); - return NULL; - } /* 1st cell is the interrupt type; 0 is SPI, 1 is PPI */ /* 2nd cell is the interrupt number */ /* 3rd cell is flags */ - const u_int type = be32toh(interrupts[index * clen + 0]); - const u_int intr = be32toh(interrupts[index * clen + 1]); + const u_int type = be32toh(specifier[0]); + const u_int intr = be32toh(specifier[1]); const u_int irq = type == 0 ? IRQ_SPI(intr) : IRQ_PPI(intr); - const u_int trig = be32toh(interrupts[index * clen + 2]) & 0xf; + const u_int trig = be32toh(specifier[2]) & 0xf; const u_int level = (trig & 0x3) ? IST_EDGE : IST_LEVEL; - kmem_free(interrupts, len); - return intr_establish(irq, ipl, level | iflags, func, arg); } @@ -149,47 +122,17 @@ tegra_lic_disestablish(device_t dev, voi } static bool -tegra_lic_intrstr(device_t dev, int phandle, u_int index, char *buf, +tegra_lic_intrstr(device_t dev, u_int *specifier, char *buf, size_t buflen) { - struct tegra_lic_softc * const sc = device_private(dev); - u_int *interrupts; - int interrupt_cells, len; - - if (of_getprop_uint32(sc->sc_phandle, "#interrupt-cells", - &interrupt_cells)) { - return false; - } - - len = OF_getproplen(phandle, "interrupts"); - if (len <= 0) { - return false; - } - - const u_int clen = interrupt_cells * 4; - const u_int nintr = len / interrupt_cells; - - if (index >= nintr) { - return false; - } - - interrupts = kmem_alloc(len, KM_SLEEP); - - if (OF_getprop(phandle, "interrupts", interrupts, len) != len) { - kmem_free(interrupts, len); - return false; - } - /* 1st cell is the interrupt type; 0 is SPI, 1 is PPI */ /* 2nd cell is the interrupt number */ /* 3rd cell is flags */ - const u_int type = be32toh(interrupts[index * clen + 0]); - const u_int intr = be32toh(interrupts[index * clen + 1]); + const u_int type = be32toh(specifier[0]); + const u_int intr = be32toh(specifier[1]); const u_int irq = type == 0 ? IRQ_SPI(intr) : IRQ_PPI(intr); - kmem_free(interrupts, len); - snprintf(buf, buflen, "LIC irq %d", irq); return true; Index: src/sys/arch/arm/samsung/exynos_combiner.c diff -u src/sys/arch/arm/samsung/exynos_combiner.c:1.5 src/sys/arch/arm/samsung/exynos_combiner.c:1.6 --- src/sys/arch/arm/samsung/exynos_combiner.c:1.5 Sun Jan 3 04:10:58 2016 +++ src/sys/arch/arm/samsung/exynos_combiner.c Tue Jan 5 21:53:48 2016 @@ -1,4 +1,4 @@ -/* $NetBSD: exynos_combiner.c,v 1.5 2016/01/03 04:10:58 marty Exp $ */ +/* $NetBSD: exynos_combiner.c,v 1.6 2016/01/05 21:53:48 marty Exp $ */ /*- * Copyright (c) 2015 The NetBSD Foundation, Inc. @@ -34,7 +34,7 @@ #include "gpio.h" #include <sys/cdefs.h> -__KERNEL_RCSID(1, "$NetBSD: exynos_combiner.c,v 1.5 2016/01/03 04:10:58 marty Exp $"); +__KERNEL_RCSID(1, "$NetBSD: exynos_combiner.c,v 1.6 2016/01/05 21:53:48 marty Exp $"); #include <sys/param.h> #include <sys/bus.h> @@ -86,10 +86,11 @@ struct exynos_combiner_softc { static int exynos_combiner_match(device_t, cfdata_t, void *); static void exynos_combiner_attach(device_t, device_t, void *); -static void * exynos_combiner_establish(device_t, int, u_int, int, int, +static void * exynos_combiner_establish(device_t, u_int *, int, int, int (*)(void *), void *); static void exynos_combiner_disestablish(device_t, void *); -static bool exynos_combiner_intrstr(device_t, int, u_int, char *, size_t); +static bool exynos_combiner_intrstr(device_t, u_int *, char *, + size_t); struct fdtbus_interrupt_controller_func exynos_combiner_funcs = { .establish = exynos_combiner_establish, @@ -221,46 +222,16 @@ static int exynos_combiner_irq(void *coo } static void * -exynos_combiner_establish(device_t dev, int phandle, u_int index, int ipl, - int flags, +exynos_combiner_establish(device_t dev, u_int *specifier, + int ipl, int flags, int (*func)(void *), void *arg) { struct exynos_combiner_softc * const sc = device_private(dev); struct exynos_combiner_irq_block *blockp; struct exynos_combiner_irq_entry *entryp; - /* MJF: Most combiner clients don't have the #interrupt-cells prop. */ - u_int *interrupts; - int interrupt_cells = 2; - int len = OF_getproplen(phandle, "interrupts"); - - if (len <= 0) { - printf("%s: phandle has no interrupts property.\n", __func__); - return NULL; - } - - const u_int clen = interrupt_cells * 4; - const u_int nintr = len / interrupt_cells; - - if (index >= nintr) { - printf("%s: asking for index %d but only %d entries.\n", - __func__, index, nintr); - return NULL; - } - interrupts = kmem_alloc(len, KM_SLEEP); - - if (OF_getprop(phandle, "interrupts", interrupts, len) != len) { - kmem_free(interrupts, len); - return NULL; - } - - /* 1st cell is the interrupt block */ - /* 2nd cell is the interrupt number */ - - const u_int intr = be32toh(interrupts[index * clen + 0]); - const u_int irq = be32toh(interrupts[index * clen + 1]); - - kmem_free(interrupts, len); + const u_int intr = be32toh(specifier[0]); + const u_int irq = be32toh(specifier[1]); int iblock = intr / COMBINER_BLOCKS_PER_GROUP * COMBINER_GROUP_SIZE @@ -295,38 +266,15 @@ exynos_combiner_disestablish(device_t de } static bool -exynos_combiner_intrstr(device_t dev, int phandle, u_int index, char *buf, - size_t buflen) +exynos_combiner_intrstr(device_t dev, u_int *specifier, char *buf, + size_t buflen) { - u_int *interrupts; - int interrupt_cells = 2, len; - - len = OF_getproplen(phandle, "interrupts"); - if (len <= 0) { - return false; - } - - const u_int clen = interrupt_cells * 4; - const u_int nintr = len / interrupt_cells; - - if (index >= nintr) { - return false; - } - - interrupts = kmem_alloc(len, KM_SLEEP); - - if (OF_getprop(phandle, "interrupts", interrupts, len) != len) { - kmem_free(interrupts, len); - return false; - } /* 1st cell is the interrupt block */ /* 2nd cell is the interrupt number */ - const u_int intr = be32toh(interrupts[index * clen + 0]); - const u_int irq = be32toh(interrupts[index * clen + 1]); - - kmem_free(interrupts, len); + const u_int intr = be32toh(specifier[0]); + const u_int irq = be32toh(specifier[1]); snprintf(buf, buflen, "combiner intr %d irq %d", intr, irq); Index: src/sys/arch/arm/samsung/mct.c diff -u src/sys/arch/arm/samsung/mct.c:1.8 src/sys/arch/arm/samsung/mct.c:1.9 --- src/sys/arch/arm/samsung/mct.c:1.8 Sun Jan 3 04:10:58 2016 +++ src/sys/arch/arm/samsung/mct.c Tue Jan 5 21:53:48 2016 @@ -1,4 +1,4 @@ -/* $NetBSD: mct.c,v 1.8 2016/01/03 04:10:58 marty Exp $ */ +/* $NetBSD: mct.c,v 1.9 2016/01/05 21:53:48 marty Exp $ */ /*- * Copyright (c) 2014 The NetBSD Foundation, Inc. @@ -31,7 +31,7 @@ #include <sys/cdefs.h> -__KERNEL_RCSID(1, "$NetBSD: mct.c,v 1.8 2016/01/03 04:10:58 marty Exp $"); +__KERNEL_RCSID(1, "$NetBSD: mct.c,v 1.9 2016/01/05 21:53:48 marty Exp $"); #include <sys/param.h> #include <sys/bus.h> @@ -41,6 +41,7 @@ __KERNEL_RCSID(1, "$NetBSD: mct.c,v 1.8 #include <sys/proc.h> #include <sys/systm.h> #include <sys/timetc.h> +#include <sys/kmem.h> #include <prop/proplib.h> @@ -53,6 +54,7 @@ __KERNEL_RCSID(1, "$NetBSD: mct.c,v 1.8 static int mct_match(device_t, cfdata_t, void *); static void mct_attach(device_t, device_t, void *); +static int mct_intr(void *arg); //static int clockhandler(void *); @@ -129,7 +131,6 @@ mct_write_global(struct mct_softc *sc, b panic("MCT hangs after writing %#x at %#x", v, (uint32_t) o); } - static int mct_match(device_t parent, cfdata_t cf, void *aux) { @@ -145,8 +146,6 @@ static void mct_attach(device_t parent, device_t self, void *aux) { struct mct_softc * const sc = &mct_sc; -// prop_dictionary_t dict = device_properties(self); -// char freqbuf[sizeof("XXX SHz")]; struct fdt_attach_args * const faa = aux; bus_addr_t addr; bus_size_t size; @@ -160,8 +159,6 @@ mct_attach(device_t parent, device_t sel self->dv_private = sc; sc->sc_dev = self; sc->sc_bst = faa->faa_bst; - /* MJF: Need to get irqs from the dtd */ -// sc->sc_irq = exyo->exyo_loc.loc_intr; error = bus_space_map(sc->sc_bst, addr, size, 0, &sc->sc_bsh); if (error) { @@ -171,18 +168,19 @@ mct_attach(device_t parent, device_t sel } aprint_naive("\n"); - aprint_normal(": Exynos SoC multi core timer (64 bits)\n"); + aprint_normal(": Exynos SoC multi core timer (64 bits) - NOT IMPLEMENTED\n"); evcnt_attach_dynamic(&sc->sc_ev_missing_ticks, EVCNT_TYPE_MISC, NULL, device_xname(self), "missing interrupts"); -// sc->sc_global_ih = intr_establish(sc->sc_irq, IPL_CLOCK, IST_EDGE, -// clockhandler, NULL); -// if (sc->sc_global_ih == NULL) -// panic("%s: unable to register timer interrupt", __func__); -// aprint_normal_dev(sc->sc_dev, "interrupting on irq %d\n", sc->sc_irq); + for (int i = 0; i < 12; i++) + fdtbus_intr_establish(faa->faa_phandle, i, 0, 0, mct_intr, 0); } +static int mct_intr(void *arg) +{ + return 0; +} static inline uint64_t mct_gettime(struct mct_softc *sc) Index: src/sys/arch/evbarm/conf/EXYNOS diff -u src/sys/arch/evbarm/conf/EXYNOS:1.9 src/sys/arch/evbarm/conf/EXYNOS:1.10 --- src/sys/arch/evbarm/conf/EXYNOS:1.9 Sun Dec 27 12:26:42 2015 +++ src/sys/arch/evbarm/conf/EXYNOS Tue Jan 5 21:53:48 2016 @@ -1,5 +1,5 @@ # -# $NetBSD: EXYNOS,v 1.9 2015/12/27 12:26:42 jmcneill Exp $ +# $NetBSD: EXYNOS,v 1.10 2016/01/05 21:53:48 marty Exp $ # # ODROID-XU -- ODROID-XU4 Exynos5422 based kernel # @@ -225,6 +225,9 @@ exyowdt* at fdt? # watchdog # Exynos chip id chipid* at fdt? +# Exynos system MMUs +sysmmu* at fdt? + # Exynos RTC exyortc* at fdt? Index: src/sys/dev/fdt/fdt_intr.c diff -u src/sys/dev/fdt/fdt_intr.c:1.3 src/sys/dev/fdt/fdt_intr.c:1.4 --- src/sys/dev/fdt/fdt_intr.c:1.3 Wed Dec 16 19:33:55 2015 +++ src/sys/dev/fdt/fdt_intr.c Tue Jan 5 21:53:48 2016 @@ -1,4 +1,4 @@ -/* $NetBSD: fdt_intr.c,v 1.3 2015/12/16 19:33:55 jmcneill Exp $ */ +/* $NetBSD: fdt_intr.c,v 1.4 2016/01/05 21:53:48 marty Exp $ */ /*- * Copyright (c) 2015 Jared D. McNeill <jmcne...@invisible.ca> @@ -27,7 +27,7 @@ */ #include <sys/cdefs.h> -__KERNEL_RCSID(0, "$NetBSD: fdt_intr.c,v 1.3 2015/12/16 19:33:55 jmcneill Exp $"); +__KERNEL_RCSID(0, "$NetBSD: fdt_intr.c,v 1.4 2016/01/05 21:53:48 marty Exp $"); #include <sys/param.h> #include <sys/bus.h> @@ -46,6 +46,10 @@ struct fdtbus_interrupt_controller { static struct fdtbus_interrupt_controller *fdtbus_ic = NULL; +static bool has_interrupt_map(int phandle); +static u_int *get_entry_from_map(int phandle, int pindex); +static u_int *get_specifier_by_index(int phandle, int pindex); + static int fdtbus_get_interrupt_parent(int phandle) { @@ -75,22 +79,26 @@ fdtbus_get_interrupt_parent(int phandle) } static struct fdtbus_interrupt_controller * -fdtbus_get_interrupt_controller(int phandle) +fdtbus_get_interrupt_controller_ic(int phandle) { - struct fdtbus_interrupt_controller *ic; + struct fdtbus_interrupt_controller * ic; + for (ic = fdtbus_ic; ic; ic = ic->ic_next) { + if (ic->ic_phandle == phandle) { + return ic; + } + } + return NULL; +} +static struct fdtbus_interrupt_controller * +fdtbus_get_interrupt_controller(int phandle) +{ const int ic_phandle = fdtbus_get_interrupt_parent(phandle); if (ic_phandle < 0) { return NULL; } - for (ic = fdtbus_ic; ic; ic = ic->ic_next) { - if (ic->ic_phandle == ic_phandle) { - return ic; - } - } - - return NULL; + return fdtbus_get_interrupt_controller_ic(ic_phandle); } int @@ -114,14 +122,24 @@ void * fdtbus_intr_establish(int phandle, u_int index, int ipl, int flags, int (*func)(void *), void *arg) { + u_int *specifier; + int ihandle; struct fdtbus_interrupt_controller *ic; - ic = fdtbus_get_interrupt_controller(phandle); + if (has_interrupt_map(phandle)) { + specifier = get_entry_from_map(phandle, index); + ihandle = be32toh(specifier[1]); + ihandle = fdtbus_get_phandle_from_native(ihandle); + specifier += 2; + } else { + specifier = get_specifier_by_index(phandle, index); + ihandle = phandle; + } + ic = fdtbus_get_interrupt_controller(ihandle); if (ic == NULL) return NULL; - - return ic->ic_funcs->establish(ic->ic_dev, phandle, index, ipl, - flags, func, arg); + return ic->ic_funcs->establish(ic->ic_dev, specifier, + ipl, flags, func, arg); } void @@ -139,10 +157,160 @@ bool fdtbus_intr_str(int phandle, u_int index, char *buf, size_t buflen) { struct fdtbus_interrupt_controller *ic; + int ihandle; + u_int *specifier; - ic = fdtbus_get_interrupt_controller(phandle); + if (has_interrupt_map(phandle)) { + specifier = get_entry_from_map(phandle, index); + ihandle = be32toh(specifier[1]); + ihandle = fdtbus_get_phandle_from_native(ihandle); + } else { + ihandle = phandle; + specifier = get_specifier_by_index(phandle, index); + } + ic = fdtbus_get_interrupt_controller(ihandle); if (ic == NULL) return false; + return ic->ic_funcs->intrstr(ic->ic_dev, specifier, buf, buflen); +} - return ic->ic_funcs->intrstr(ic->ic_dev, phandle, index, buf, buflen); +/* + * Devices that have multiple interrupts, connected to two or more + * interrupt sources use an interrupt map rather than a simple + * interrupt parent to indicate which interrupt controller goes with + * which map. The interrupt map is contained in the node describing + * the first level parent and contains one entry per interrupt: + * index -- the index of the entry in the map + * &parent -- pointer to the node containing the actual interrupt parent + * for the specific interrupt + * [specifier 0 - specifier N-1] The N (usually 2 or 3) 32 bit words + * that make up the specifier. + * + * returns true if the device phandle has an interrupt-parent that + * contains an interrupt-map. + */ +static bool +has_interrupt_map(int phandle) +{ + int ic_phandle; + of_getprop_uint32(phandle, "interrupt-parent", &ic_phandle); + if (ic_phandle <= 0) + return false; + ic_phandle = fdtbus_get_phandle_from_native(ic_phandle); + if (ic_phandle <= 0) + return false; + int len = OF_getproplen(ic_phandle, "interrupt-map"); + if (len > 0) + return true; + return false; +} + +/* + * Walk the specifier map and return a pointer to the map entry + * associated with pindex. Return null if there is no entry. + * + * Because the length of the specifier depends on the interrupt + * controller, we need to repeatedly obtain interrupt-celss for + * the controller for the current index. + * + * this version leaks memory and needs a revisit + */ +static u_int * +get_entry_from_map(int phandle, int pindex) +{ + int intr_cells; + int intr_parent; + + of_getprop_uint32(phandle, "#interrupt-cells", &intr_cells); + of_getprop_uint32(phandle, "interrupt-parent", &intr_parent); + + intr_parent = fdtbus_get_phandle_from_native(intr_parent); + int len = OF_getproplen(intr_parent, "interrupt-map"); + if (len <= 0) { + printf(" no interrupt-map.\n"); + return NULL; + } + uint resid = len; + char *data = kmem_alloc(len, KM_SLEEP); + len = OF_getprop(intr_parent, "interrupt-map", data, len); + if (len <= 0) { + printf(" can't get property interrupt-map.\n"); + return NULL; + } + u_int *p = (u_int *)data; + + while (resid > 0) { + u_int index = be32toh(p[0]); + if (index == pindex) { + return &p[0]; + + } + /* Determine the length of the entry and skip that many + * 32 bit words + */ + const u_int parent = fdtbus_get_phandle_from_native(be32toh(p[intr_cells])); + u_int pintr_cells; + of_getprop_uint32(parent, "#interrupt-cells", &pintr_cells); + const u_int reclen = (intr_cells + pintr_cells + 1); + resid -= reclen * sizeof(u_int); + p += reclen; + } + return NULL; +} + + +/* + * Devices that don't connect to more than one interrupt source use + * an array of specifiers. Find the specifier that matches pindex + * and return a pointer to it. + * + * This version leaks memory and needs a revisit. + */ +static u_int *get_specifier_by_index(int phandle, int pindex) +{ + u_int *specifiers; + int interrupt_parent, interrupt_cells, len; + + printf("%s: phandle = %d pindex = %d\n", __func__, phandle, pindex); + + len = OF_getprop(phandle, "interrupt-parent", &interrupt_parent, + sizeof(interrupt_parent)); + interrupt_parent = be32toh(interrupt_parent); + interrupt_parent = fdtbus_get_phandle_from_native(interrupt_parent); + printf("%s: len = %d interrupt_parent = %d\n", __func__, len, + interrupt_parent); + if (len != sizeof(interrupt_parent) || interrupt_parent <= 0) { + printf("%s: interrupt_parent sanity check failed\n", __func__); + return NULL; + } + + len = OF_getprop(interrupt_parent, "#interrupt-cells", + &interrupt_cells, sizeof(interrupt_cells)); + interrupt_cells = be32toh(interrupt_cells); + printf("%s: len = %d interrupt_cells = %d\n", __func__, len, + interrupt_cells); + if (len != sizeof(interrupt_cells) || interrupt_cells <= 0) { + printf("%s: interrupt_celyls sanity check failed\n", __func__); + return NULL; + } + + len = OF_getproplen(phandle, "interrupts"); + if (len <= 0) { + printf("%s: Couldn't get property interrupts\n", __func__); + return NULL; + } + + const u_int clen = interrupt_cells * sizeof(u_int); + const u_int nintr = len / interrupt_cells; + + if (pindex >= nintr) + return NULL; + + specifiers = kmem_alloc(len, KM_SLEEP); + + if (OF_getprop(phandle, "interrupts", specifiers, len) != len) { + kmem_free(specifiers, len); + return NULL; + } + return &specifiers[pindex * clen]; } Index: src/sys/dev/fdt/fdtvar.h diff -u src/sys/dev/fdt/fdtvar.h:1.6 src/sys/dev/fdt/fdtvar.h:1.7 --- src/sys/dev/fdt/fdtvar.h:1.6 Fri Jan 1 22:35:44 2016 +++ src/sys/dev/fdt/fdtvar.h Tue Jan 5 21:53:48 2016 @@ -1,4 +1,4 @@ -/* $NetBSD: fdtvar.h,v 1.6 2016/01/01 22:35:44 marty Exp $ */ +/* $NetBSD: fdtvar.h,v 1.7 2016/01/05 21:53:48 marty Exp $ */ /*- * Copyright (c) 2015 Jared D. McNeill <jmcne...@invisible.ca> @@ -52,10 +52,10 @@ struct fdt_attach_args { #define FDT_INTR_MPSAFE __BIT(0) struct fdtbus_interrupt_controller_func { - void * (*establish)(device_t, int, u_int, int, int, + void * (*establish)(device_t, u_int *, int, int, int (*)(void *), void *); void (*disestablish)(device_t, void *); - bool (*intrstr)(device_t, int, u_int, char *, size_t); + bool (*intrstr)(device_t, u_int *, char *, size_t); }; struct fdtbus_i2c_controller_func {