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 <[email protected]>
@@ -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 <[email protected]>
@@ -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 <[email protected]>
@@ -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 <[email protected]>
@@ -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 {