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 {

Reply via email to