interrupt framework

Svatopluk Kraus onwahe at gmail.com
Wed Jan 28 14:16:50 UTC 2015


New ARM interrupt framework is implemented fully now. At this moment,
only GIC interrupt controller is working with this framework and it
was tested on pandaboard. I'm going to change all other ARM interrupt
controllers in source tree, however, I will else be able to test only
the ones related to BBB and Raspberry Pi.

The testing was done only according to old framework at this moment.
The new features like fast interrupt handling, cascaded controllers,
explicit interrupt binding and describing were not tested.

However, interrupts are now shuffled among all running CPUs by
round-robin method according to x86 interrupt framework. Nevertheless,
it is just a proof of concept. The way on which CPU(s) an interrupt
will be served should be changed as on capable controllers the not
bound interrupt could be served on all running CPUs in controller'c
CPU cluster. The decision should be always taken by controller (with
help of framework if needed).

Thus there is still some work on the framework, but I do not suppose
any drastic changes. Therefore it is not cleaned up now. It includes
changes done in other files too.

---------------------------------------

Now, a few words about new framework which have not been said yet. It
lays between system and controllers. Communication between system and
framework is done by an interrupt number which is opaque to
controllers and more like resource handle. AFAIK there are five bus
methods:

BUS_CONFIG_INTR,
BUS_SETUP_INTR,
BUS_BIND_INTR,
BUS_DESCRIBE_INTR
BUS_TEARDOWN_INTR.

The order in which I wrote them is much the order in which should be
called by a consumer. However, the simplest consumer calls
BUS_SETUP_INTR only. New framework is more strict than man pages are
in that an interrupt configuration is not permited on active (enabled)
interrupt. In other words, BUS_CONFIG_INTR must be called before
BUS_SETUP_INTR. BUS_BIND_INTR and BUS_DESCRIBE_INTR are connected to
specific handler (MI interrupt framework) so they must be called after
BUS_SETUP_INTR. BUS_BIND_INTR binds interrupt's ithread if exists too
and BUS_DESCRIBE_INTR describes associated handler. Of course, the
order of these two methods can be switched.

Communication between framework and a controller is done by an
interrupt source (ISRC) which is now made public. It makes an access
simpler for controllers, but if there will be many controllers (on
more arch possibly), it makes framework modifications harder. So this
is still opened question: privite or public? However, for now it's not
problem to switch from one way to another. There are six main pic
methods:

PIC_REGISTER,
PIC_ENABLE_INTR,
PIC_ENABLE_SOURCE,
PIC_DISABLE_SOURCE,
PIC_DISABLE_INTR,
PIC_UNREGISTER,

three suplementary methods:

PIC_POST_FILTER,
PIC_PRE_ITHREAD,
PIC_POST_ITHREAD,

and three SMP methods:

PIC_BIND,
PIC_IPI_SEND,
PIC_INIT_SECONDARY.

As was said before, an ISRC is created by some mapping function which
provides description of the source. This can happen before ISRC's
controller is presented in system. So the creation of ISRC is
independent on related controller. Provided description must be well
known for that controller while it's opaque for framework except one
thing. Each mapping fuction must provide an opaque key to ISRC which
identificates a controller. Likewise, each controller must register
itself to framework with its opaque key.

There are two mapping function in the framework now:

u_int arm_namespace_map_irq(device_t dev, uint16_t type, uint16_t num);
u_int arm_fdt_map_irq(phandle_t node, pcell_t *cells, u_int ncells);

Thus ISRC is not connected to related controller at the beginning and
must be registered (mapped) to it later. It's done by PIC_REGISTER,
but before that a related controller (if not known already) must be
found according to saved key. Then in PIC_REGISTER, a controller
should evaluate provided ISRC desription and if recognized, do any
allocation of related data and save ISRC inside itself for interrupt
managing reason.

When ISRC is going to be enabled, PIC_ENABLE_INTR and
PIC_ENABLE_SOURCE are called. In PIC_ENABLE_INTR, a controller is
supposed to prepare everything for real enabling of related interrupt.
It means doing configuration, binding, whatever is needed.
PIC_ENABLE_SOURCE should be simple unmasking of related interrupt and
it's called repeatedly during interrupt dispaching.

Likewise PIC_DISABLE_SOURCE should be simple masking of related
interrupt, PIC_DISABLE_INTR should clean up, and PIC_UNREGISTER should
clear any evidence about.

While PIC_REGISTER and PIC_UNREGISTER are called without framework
locking and it's controller's resposibility to ensure atomicity of
registration process, the other four methods are called with framework
locking. It has simple reason as a controller is supposed to do all
hard work for an ISRC (allocation) in PIC_REGISTER and PIC_UNREGISTER.

The suplementary methods are presented due to using of MI interrupt
framework and should be tailored exactly for its needs.

The using of SMP methods are obvious. The IPIs are setup on a
controller by same PIC_REGISTER method as standard interrupts with use
of namespace mapping functions. The cpu argument passed to PIC_BIND is
an integer (not cpuset_t) as MI interface binding methods works only
with single cpu.

---------------------------------------

Finally a few words about removable controllers. It should be no
problem as we are in kernel. At least as long as everybody will act
correctly. Suprisingly, it will be only problem of interrupt counting
because of the way how sysctl and DDB work with it.

However, for now, controllers are not removable and once registered
ISRC is never unregistered (which is safe as an ISRC should be
described always same). Nevertheless, I have pretty ideas for how to
make controllers removable.

---------------------------------------

The attached diff is made against today's 277826 revision. New
framework must be anabled by option ARM_INTRNG in board's
configuration file. And debug framework's prints are enabled now.
Tomorow I'm leaving the town and will be back on next Thursday, so
sorry for any late reply if will be needed in that time.


Svata



On Thu, Jan 15, 2015 at 2:51 PM, Svatopluk Kraus <onwahe at gmail.com> wrote:
> Hi community,
>
> I and Michal Meloun have done some work on ARM interrupt framework and
> this is the result.
>
> We've started with intrng project with Ian's WIP changes, have looked
> at Andrew's ARM64 git repository, and this is how we think an
> interrupt framework should look like. We've implemented it with
> removable interrupt controllers in mind (PCI world). It's not finished
> from this point of view, however some functions are more complex
> because of it.
>
> It's tested on pandaboard and only GIC is implemented now. There is no
> problem to implement it to other controllers. We are open to questions
> and can finish our work considering any comments. Whoever is waiting
> for ARM interrupt framework as we were, you are welcome to test it.
> Whoever is welcome. The patches are done against FreeBSD-11-current
> revision 277210. There are two new files.
>
> ARM_INTRNG option must be added to board configuration file for new framework.
>
> There are still some things not implemented and some things which
> should be discussed like PPI support. For example, how to enable PPI
> interrupt on other CPUs when they are already running?
>
> We keep in mind that an interrupt framework should be helpfull but
> general enough to not dictate interrupt controlles too much. Thus we
> try to keep some things as much separated as possible. Each interrupt
> is represented by an interrupt source (ISRC) in the framework. An ISRC
> is described by an interrupt number which is much more an unique
> resource handle - totally independent on internal representation of
> interrupts in any interrupt controller.
>
> An interrupt is described by cells in FDT world. The cells can be
> decoded only by associated interrupt controller and as such, they are
> transparent for interrupt framework. The framework provides
> arm_fdt_map_irq() function which maps this transparent cells to an
> interrupt number. It creates an ISRC, saves cells on it, and once when
> associated interrupt controller is registered, it provides the ISRC
> with cells into the controller.
>
> It's a controller responsibility to save an ISRC associated with
> cells. An ISRC is transparent for any controller. However, an
> controller can set/get its data to/from an ISRC. Further, an
> controller should set a name to an ISRC according to internal
> representation of associated interrupt.
>
> An controller interrupt dispatch function can call framework only if
> it has associated ISRC to received interrupt.
>
> For legacy reason, there is arm_namespace_map_irq() function. An
> interrupt is described by namespace type and a number from the
> namespace. It's intented for use with no FDT drivers. Now, it's used
> for mapping an IPI on a controller.
>
> We think that it's better to call chained controllers (with filter
> only) without MI interrupt framework overhead, so we implemented
> shortcut. It could be utilized by INTR_SOLO flag during
> bus_setup_intr().
>
> Only an interrupt controller can really know its position in interrupt
> controller's tree. So root controller must claim itself as a root. In
> FDT world, according to ePAPR approved version 1.1 from 08 April 2011,
> page 30:
>
> "The root of the interrupt tree is determined when traversal of the
> interrupt tree reaches an interrupt controller node without an
> interrupts property and thus no explicit interrupt parent."
>
> Thus there are no need for any non-standard things in DTS files.
>
> Svata
-------------- next part --------------
Index: sys/arm/arm/gic.c
===================================================================
--- sys/arm/arm/gic.c	(revision 277826)
+++ sys/arm/arm/gic.c	(working copy)
@@ -34,6 +34,8 @@
 #include <sys/cdefs.h>
 __FBSDID("$FreeBSD$");
 
+#include "opt_platform.h"
+
 #include <sys/param.h>
 #include <sys/systm.h>
 #include <sys/bus.h>
@@ -40,6 +42,7 @@
 #include <sys/kernel.h>
 #include <sys/ktr.h>
 #include <sys/module.h>
+#include <sys/malloc.h>
 #include <sys/rman.h>
 #include <sys/pcpu.h>
 #include <sys/proc.h>
@@ -55,6 +58,11 @@
 #include <dev/ofw/ofw_bus.h>
 #include <dev/ofw/ofw_bus_subr.h>
 
+#ifdef ARM_INTRNG
+#include <sys/sched.h>
+#include "pic_if.h"
+#endif
+
 /* We are using GICv2 register naming */
 
 /* Distributor Registers */
@@ -83,8 +91,8 @@
 #define GICC_ABPR		0x001C			/* v1 ICCABPR */
 #define GICC_IIDR		0x00FC			/* v1 ICCIIDR*/
 
-#define	GIC_FIRST_IPI		 0	/* Irqs 0-15 are SGIs/IPIs. */
-#define	GIC_LAST_IPI		15
+#define	GIC_FIRST_SGI		 0	/* Irqs 0-15 are SGIs/IPIs. */
+#define	GIC_LAST_SGI		15
 #define	GIC_FIRST_PPI		16	/* Irqs 16-31 are private (per */
 #define	GIC_LAST_PPI		31	/* core) peripheral interrupts. */
 #define	GIC_FIRST_SPI		32	/* Irqs 32+ are shared peripherals. */
@@ -98,8 +106,22 @@
 #define GICD_ICFGR_TRIG_EDGE	(1 << 1)
 #define GICD_ICFGR_TRIG_MASK	0x2
 
+#ifdef ARM_INTRNG
+#define GIC_INTRNAME_LEN	32
+
+struct arm_gic_irq {
+	struct arm_irqsrc *	gi_isrc;
+};
+
+static int arm_gic_intr(void *, struct trapframe *);
+#endif
+
 struct arm_gic_softc {
 	device_t		gic_dev;
+#ifdef ARM_INTRNG
+	void *			gic_intrhand;
+	struct arm_gic_irq *	gic_irqs;
+#endif
 	struct resource *	gic_res[3];
 	bus_space_tag_t		gic_c_bst;
 	bus_space_tag_t		gic_d_bst;
@@ -113,10 +135,13 @@
 static struct resource_spec arm_gic_spec[] = {
 	{ SYS_RES_MEMORY,	0,	RF_ACTIVE },	/* Distributor registers */
 	{ SYS_RES_MEMORY,	1,	RF_ACTIVE },	/* CPU Interrupt Intf. registers */
+#ifdef ARM_INTRNG
+	{ SYS_RES_IRQ,	  0, RF_ACTIVE | RF_OPTIONAL }, /* Parent interrupt */
+#endif
 	{ -1, 0 }
 };
 
-static struct arm_gic_softc *arm_gic_sc = NULL;
+static struct arm_gic_softc *gic_sc = NULL;
 
 #define	gic_c_read_4(_sc, _reg)		\
     bus_space_read_4((_sc)->gic_c_bst, (_sc)->gic_c_bsh, (_reg))
@@ -127,9 +152,11 @@
 #define	gic_d_write_4(_sc, _reg, _val)		\
     bus_space_write_4((_sc)->gic_d_bst, (_sc)->gic_d_bsh, (_reg), (_val))
 
+#ifndef ARM_INTRNG
 static int gic_config_irq(int irq, enum intr_trigger trig,
     enum intr_polarity pol);
 static void gic_post_filter(void *);
+#endif
 
 static struct ofw_compat_data compat_data[] = {
 	{"arm,gic",		true},	/* Non-standard, used in FreeBSD dts. */
@@ -155,10 +182,58 @@
 	return (BUS_PROBE_DEFAULT);
 }
 
+#ifdef ARM_INTRNG
+static inline void
+gic_irq_unmask(struct arm_gic_softc *sc, u_int irq)
+{
+
+	gic_d_write_4(sc, GICD_ISENABLER(irq >> 5), (1UL << (irq & 0x1F)));
+}
+
+static inline void
+gic_irq_mask(struct arm_gic_softc *sc, u_int irq)
+{
+
+	gic_d_write_4(sc, GICD_ICENABLER(irq >> 5), (1UL << (irq & 0x1F)));
+}
+#endif
+
+#ifdef SMP
+#ifdef ARM_INTRNG
 static void
 arm_gic_init_secondary(device_t dev)
 {
 	struct arm_gic_softc *sc = device_get_softc(dev);
+	u_int irq;
+
+	for (irq = 0; irq < sc->nirqs; irq += 4)
+		gic_d_write_4(sc, GICD_IPRIORITYR(irq >> 2), 0);
+
+	/* Set all the interrupts to be in Group 0 (secure) */
+	for (irq = 0; irq < sc->nirqs; irq += 32) {
+		gic_d_write_4(sc, GICD_IGROUPR(irq >> 5), 0);
+	}
+
+	/* Enable CPU interface */
+	gic_c_write_4(sc, GICC_CTLR, 1);
+
+	/* Set priority mask register. */
+	gic_c_write_4(sc, GICC_PMR, 0xff);
+
+	/* Enable interrupt distribution */
+	gic_d_write_4(sc, GICD_CTLR, 0x01);
+
+	/* Unmask attached PPI interrupts. */
+	for (irq = GIC_FIRST_PPI; irq <= GIC_LAST_PPI; irq++) {
+		if (sc->gic_irqs[irq].gi_isrc != NULL)
+			gic_irq_unmask(sc, irq);
+	}
+}
+#else
+static void
+arm_gic_init_secondary(device_t dev)
+{
+	struct arm_gic_softc *sc = device_get_softc(dev);
 	int i;
 
 	for (i = 0; i < sc->nirqs; i += 4)
@@ -185,7 +260,10 @@
 	gic_d_write_4(sc, GICD_ISENABLER(29 >> 5), (1UL << (29 & 0x1F)));
 	gic_d_write_4(sc, GICD_ISENABLER(30 >> 5), (1UL << (30 & 0x1F)));
 }
+#endif /* ARM_INTRNG */
+#endif /* SMP */
 
+#ifndef ARM_INTRNG
 int
 gic_decode_fdt(uint32_t iparent, uint32_t *intr, int *interrupt,
     int *trig, int *pol)
@@ -230,6 +308,7 @@
 	}
 	return (0);
 }
+#endif
 
 static int
 arm_gic_attach(device_t dev)
@@ -237,11 +316,17 @@
 	struct		arm_gic_softc *sc;
 	int		i;
 	uint32_t	icciidr;
+	intptr_t	xref;
 
-	if (arm_gic_sc)
+	if (gic_sc)
 		return (ENXIO);
 
 	sc = device_get_softc(dev);
+#ifdef FDT
+	xref = OF_xref_from_node(ofw_bus_get_node(dev));
+#else
+	xref = NULL;
+#endif
 
 	if (bus_alloc_resources(dev, arm_gic_spec, sc->gic_res)) {
 		device_printf(dev, "could not allocate resources\n");
@@ -249,7 +334,7 @@
 	}
 
 	sc->gic_dev = dev;
-	arm_gic_sc = sc;
+	gic_sc = sc;
 
 	/* Initialize mutex */
 	mtx_init(&sc->mutex, "GIC lock", "", MTX_SPIN);
@@ -269,9 +354,14 @@
 	sc->nirqs = gic_d_read_4(sc, GICD_TYPER);
 	sc->nirqs = 32 * ((sc->nirqs & 0x1f) + 1);
 
+#ifdef ARM_INTRNG
+	sc->gic_irqs = malloc(sc->nirqs * sizeof (*sc->gic_irqs), M_DEVBUF,
+	    M_WAITOK | M_ZERO);
+#else
 	/* Set up function pointers */
 	arm_post_filter = gic_post_filter;
 	arm_config_irq = gic_config_irq;
+#endif
 
 	icciidr = gic_c_read_4(sc, GICC_IIDR);
 	device_printf(dev,"pn 0x%x, arch 0x%x, rev 0x%x, implementer 0x%x irqs %u\n",
@@ -307,11 +397,464 @@
 
 	/* Enable interrupt distribution */
 	gic_d_write_4(sc, GICD_CTLR, 0x01);
+#ifndef ARM_INTRNG
+	return (0);
+#else
+	/*
+	 * Now, when everything is initialized, it's right time to
+	 * register interrupt controller to interrupt framefork.
+	 */
+	if (arm_register_pic(dev, xref) != 0) {
+		device_printf(dev, "could not register PIC\n");
+		goto cleanup;
+	}
 
+	if (sc->gic_res[2] == NULL) {
+		if (arm_irq_set_root(dev, xref, arm_gic_intr, sc,
+		    GIC_LAST_SGI - GIC_FIRST_SGI + 1) != 0) {
+			device_printf(dev, "could not set PIC as a root\n");
+			arm_unregister_pic(dev, xref);
+			goto cleanup;
+		}
+	} else {
+		if (bus_setup_intr(dev, sc->gic_res[2], INTR_TYPE_MISC |
+		    INTR_SOLO, (void*)arm_gic_intr, NULL, sc,
+		    &sc->gic_intrhand)) {
+			device_printf(dev, "could not setup irq handler\n");
+			arm_unregister_pic(dev, xref);
+			goto cleanup;
+		}
+	}
+
 	return (0);
+
+cleanup:
+	/*
+	 * XXX - not implemented arm_gic_detach() should be called !
+	 */
+	if (sc->gic_irqs != NULL)
+		free(sc->gic_irqs, M_DEVBUF);
+	bus_release_resources(dev, arm_gic_spec, sc->gic_res);
+	return(ENXIO);
+#endif
 }
 
+#ifdef ARM_INTRNG
 static int
+arm_gic_intr(void *arg, struct trapframe *tf)
+{
+	struct arm_gic_softc *sc = (struct arm_gic_softc *)arg;
+	struct arm_gic_irq *gi;
+	uint32_t irq_active_reg, irq;
+
+	irq_active_reg = gic_c_read_4(sc, GICC_IAR);
+	irq = irq_active_reg & 0x3FF;
+
+	/*
+	 * 1. EOI must be done here as the other bits (i.e. CPU number), not
+	 *    just the IRQ number are required, and we do not have a method
+	 *    how to pass this information to dispatch function.
+	 * 2. EOI must be done on same CPU, where interrupt has fired. Thus
+	 *    we must ensure that interrupted thread does not migrate to
+	 *    another CPU.
+	 * 3. EOI cannot be delayed by any preemption, which could happen on
+	 *    critical_exit() used in MI intr code, when interrupt thread is
+	 *    scheduled. See next point.
+	 * 4. IPI_RENDEZVOUS assumes that no preemption is permitted during
+	 *    an action and any use of critical_exit() could break this
+	 *    assumption. See comments within smp_rendezvous_action().
+	 */
+
+	if (irq >= sc->nirqs) {
+		device_printf(sc->gic_dev, "Spurious interrupt detected\n");
+		gic_c_write_4(sc, GICC_EOIR, irq_active_reg);
+		return (0);
+	}
+
+	gi = &sc->gic_irqs[irq];
+	if (gi->gi_isrc == NULL) {
+		device_printf(sc->gic_dev, "Stray interrupt %u detected\n", irq);
+		gic_irq_mask(sc, irq);
+		gic_c_write_4(sc, GICC_EOIR, irq_active_reg);
+		return (0);
+	}
+
+	/*
+	 * Note that GIC_FIRST_SGI is zero and is not used in 'if' statement
+	 * as compiler complains that comparing u_int >= 0 is always true.
+	 */
+	if (irq <= GIC_LAST_SGI) {
+#ifdef SMP
+		/* Call EOI for all IPI before dispatch. */
+		gic_c_write_4(sc, GICC_EOIR, irq_active_reg);
+		arm_dispatch_sgi(gi->gi_isrc, tf);
+		return (FILTER_HANDLED);
+#else
+		printf("SGI %u on UP system detected\n", irq - GIC_FIRST_SGI);
+		gic_c_write_4(sc, GICC_EOIR, irq_active_reg);
+		return (0);
+#endif
+	}
+
+	critical_enter();
+	if (irq <= GIC_LAST_PPI)
+		arm_dispatch_ppi(gi->gi_isrc, tf);
+	else
+		arm_dispatch_spi(gi->gi_isrc, tf);
+	gic_c_write_4(sc, GICC_EOIR, irq_active_reg);
+	critical_exit();
+	arm_irq_memory_barrier(irq);
+	return (FILTER_HANDLED);
+}
+
+static int
+gic_namespace_to_irq(struct arm_gic_softc *sc, u_int type, u_int num,
+    u_int *irqp)
+{
+	u_int irq;
+
+	if (type == ARM_IRQ_TYPE_SGI)
+		irq = GIC_FIRST_SGI + num;
+	else if (type == ARM_IRQ_TYPE_PPI)
+		irq = GIC_FIRST_PPI + num;
+	else if (type == ARM_IRQ_TYPE_SPI)
+		irq = GIC_FIRST_SPI + num;
+	else
+		return (EINVAL);
+
+	if (irq >= sc->nirqs)
+		return (EINVAL);
+
+	*irqp = irq;
+	return (0);
+}
+
+static int
+gic_irq_to_namespace(struct arm_gic_softc *sc, u_int irq, u_int *typep,
+u_int *nump)
+{
+
+	if (irq <= GIC_LAST_SGI) {
+		*typep = ARM_IRQ_TYPE_SGI;
+		*nump = irq - GIC_FIRST_SGI;
+	} else if (irq <= GIC_LAST_PPI) {
+		*typep = ARM_IRQ_TYPE_PPI;
+		*nump = irq - GIC_FIRST_PPI;
+	} else if (irq < sc->nirqs) {
+		*typep = ARM_IRQ_TYPE_SPI;
+		*nump = irq - GIC_FIRST_SPI;
+	} else
+		return (EINVAL);
+
+	return (0);
+}
+
+static int
+gic_attach_isrc(struct arm_gic_softc *sc, struct arm_irqsrc *isrc, u_int irq)
+{
+	const char *name;
+	struct arm_gic_irq *gi = &sc->gic_irqs[irq];
+
+	if (gi->gi_isrc == isrc)
+		return (0);
+
+	if (gi->gi_isrc != NULL)
+		return (EEXIST);
+
+	name = device_get_nameunit(sc->gic_dev);
+
+	if (irq <= GIC_LAST_SGI)
+		arm_irq_set_name(isrc, "%s,i%u", name, irq - GIC_FIRST_SGI);
+	else if (irq <= GIC_LAST_PPI)
+		arm_irq_set_name(isrc, "%s,p%u", name, irq - GIC_FIRST_PPI);
+	else
+		arm_irq_set_name(isrc, "%s,s%u", name, irq - GIC_FIRST_SPI);
+
+	isrc->isrc_data = (void *)irq;
+	gi->gi_isrc = isrc;
+	return (0);
+}
+
+static int
+gic_detach_isrc(struct arm_gic_softc *sc, struct arm_irqsrc *isrc, u_int irq)
+{
+	struct arm_gic_irq *gi = &sc->gic_irqs[irq];
+
+	if (irq >= sc->nirqs || gi->gi_isrc != isrc)
+		return (EINVAL);
+
+	isrc->isrc_data = NULL;
+	arm_irq_set_name(isrc, "");
+
+	gi->gi_isrc = NULL;
+	return (0);
+}
+
+static void
+gic_config(struct arm_gic_softc *sc, u_int irq, enum intr_trigger trig,
+    enum intr_polarity pol)
+{
+	uint32_t reg;
+	uint32_t mask;
+
+	if (irq < GIC_FIRST_SPI)
+		return;
+
+	mtx_lock_spin(&sc->mutex);
+
+	reg = gic_d_read_4(sc, GICD_ICFGR(irq >> 4));
+	mask = (reg >> 2*(irq % 16)) & 0x3;
+
+	if (pol == INTR_POLARITY_LOW) {
+		mask &= ~GICD_ICFGR_POL_MASK;
+		mask |= GICD_ICFGR_POL_LOW;
+	} else if (pol == INTR_POLARITY_HIGH) {
+		mask &= ~GICD_ICFGR_POL_MASK;
+		mask |= GICD_ICFGR_POL_HIGH;
+	}
+
+	if (trig == INTR_TRIGGER_LEVEL) {
+		mask &= ~GICD_ICFGR_TRIG_MASK;
+		mask |= GICD_ICFGR_TRIG_LVL;
+	} else if (trig == INTR_TRIGGER_EDGE) {
+		mask &= ~GICD_ICFGR_TRIG_MASK;
+		mask |= GICD_ICFGR_TRIG_EDGE;
+	}
+
+	/* Set mask */
+	reg = reg & ~(0x3 << 2*(irq % 16));
+	reg = reg | (mask << 2*(irq % 16));
+	gic_d_write_4(sc, GICD_ICFGR(irq >> 4), reg);
+
+	mtx_unlock_spin(&sc->mutex);
+}
+
+static void
+gic_bind(struct arm_gic_softc *sc, u_int irq, u_int cpu)
+{
+	uint32_t mask, reg;
+
+	mask = (1 << cpu) & 0xFF;
+
+	reg = gic_d_read_4(sc, GICD_ITARGETSR(irq / 4));
+	reg &= ~(0xFF << (8 * (irq % 4)));
+	reg |= mask << (8 * (irq % 4));
+	gic_d_write_4(sc, GICD_ITARGETSR(irq / 4), reg);
+}
+
+static int
+gic_map_nspc(struct arm_gic_softc *sc, struct arm_irqsrc *isrc)
+{
+	u_int irq;
+	int error;
+
+	error = gic_namespace_to_irq(sc, isrc->isrc_nspc_type,
+	    isrc->isrc_nspc_num, &irq);
+	if (error != 0)
+		return (error);
+	return (gic_attach_isrc(sc, isrc, irq));
+}
+
+#ifdef FDT
+static int
+gic_map_fdt(struct arm_gic_softc *sc, struct arm_irqsrc *isrc)
+{
+	u_int irq, type, num, tripol;
+	enum intr_trigger trig;
+	enum intr_polarity pol;
+	int error;
+
+	if (isrc->isrc_ncells == 1) {
+		irq = isrc->isrc_cells[0];
+
+		error = gic_irq_to_namespace(sc, irq, &type, &num);
+		if (error != 0)
+			return (error);
+
+		pol = INTR_POLARITY_CONFORM;
+		trig = INTR_TRIGGER_CONFORM;
+	} else {
+
+		type = isrc->isrc_cells[0] == 0 ? ARM_IRQ_TYPE_SPI :
+		    ARM_IRQ_TYPE_PPI;
+		num = isrc->isrc_cells[1];
+
+		error = gic_namespace_to_irq(sc, type, num, &irq);
+		if (error != 0)
+			return (error);
+
+		/*
+		 * In intr[2], bits[3:0] are trigger type and level flags.
+		 *   1 = low-to-high edge triggered
+		 *   2 = high-to-low edge triggered
+		 *   4 = active high level-sensitive
+		 *   8 = active low level-sensitive
+		 * The hardware only supports active-high-level or rising-edge.
+		 */
+		tripol = isrc->isrc_cells[2];
+		if (tripol & 0x0a) {
+			printf("unsupported trigger/polarity configuration "
+			    "0x%2x\n", tripol & 0x0f);
+			return (ENOTSUP);
+		}
+		pol = INTR_POLARITY_CONFORM;
+		if (tripol & 0x01)
+			trig = INTR_TRIGGER_EDGE;
+		else
+			trig = INTR_TRIGGER_LEVEL;
+	}
+
+	error = gic_attach_isrc(sc, isrc, irq);
+	if (error != 0)
+		return (error);
+
+	isrc->isrc_nspc_type = type;
+	isrc->isrc_nspc_num = num;
+	isrc->isrc_trig = trig;
+	isrc->isrc_pol = pol;
+
+	return (0);
+}
+#endif
+
+static int
+arm_gic_register(device_t dev, struct arm_irqsrc *isrc)
+{
+	struct arm_gic_softc *sc = device_get_softc(dev);
+
+	if (isrc->isrc_type == ARM_ISRCT_NAMESPACE)
+		return (gic_map_nspc(sc, isrc));
+#ifdef FDT
+	else if (isrc->isrc_type == ARM_ISRCT_FDT)
+		return (gic_map_fdt(sc, isrc));
+#endif
+	else
+		return (EINVAL);
+}
+
+static void
+arm_gic_enable_intr(device_t dev, struct arm_irqsrc *isrc)
+{
+	struct arm_gic_softc *sc = device_get_softc(dev);
+	u_int irq;
+
+	irq = (u_int)isrc->isrc_data;
+	if (irq >= sc->nirqs || sc->gic_irqs[irq].gi_isrc != isrc)
+		return;
+
+	gic_config(sc, irq, isrc->isrc_trig, isrc->isrc_pol);
+	gic_bind(sc, irq, isrc->isrc_cpu);
+}
+
+static void
+arm_gic_enable_source(device_t dev, struct arm_irqsrc *isrc)
+{
+	struct arm_gic_softc *sc = device_get_softc(dev);
+	u_int irq;
+
+	irq = (u_int)isrc->isrc_data;
+	if (irq >= sc->nirqs || sc->gic_irqs[irq].gi_isrc != isrc)
+		return;
+
+	if (irq <= GIC_LAST_SGI)
+		return; /* v2 IMPLEMENTATION DEFINED */
+
+	arm_irq_memory_barrier(irq);
+	gic_irq_unmask(sc, irq);
+}
+
+static void
+arm_gic_disable_source(device_t dev, struct arm_irqsrc *isrc)
+{
+	struct arm_gic_softc *sc = device_get_softc(dev);
+	u_int irq;
+
+	irq = (u_int)isrc->isrc_data;
+	if (irq >= sc->nirqs || sc->gic_irqs[irq].gi_isrc != isrc)
+		return;
+
+	if (irq <= GIC_LAST_SGI)
+		return; /* v2 IMPLEMENTATION DEFINED */
+
+	gic_irq_mask(sc, irq);
+}
+
+static void
+arm_gic_disable_intr(device_t dev, struct arm_irqsrc *isrc)
+{
+}
+
+static int
+arm_gic_unregister(device_t dev, struct arm_irqsrc *isrc)
+{
+	struct arm_gic_softc *sc = device_get_softc(dev);
+	u_int irq;
+
+	irq = (u_int)isrc->isrc_data;
+	if (irq >= sc->nirqs || sc->gic_irqs[irq].gi_isrc != isrc)
+		return (EINVAL);
+
+	return (gic_detach_isrc(sc, isrc, irq));
+}
+
+static void
+arm_gic_pre_ithread(device_t dev, struct arm_irqsrc *isrc)
+{
+
+	arm_gic_disable_source(dev, isrc);
+}
+
+static void
+arm_gic_post_ithread(device_t dev, struct arm_irqsrc *isrc)
+{
+
+	arm_gic_enable_source(dev, isrc);
+}
+
+static void
+arm_gic_post_filter(device_t dev, struct arm_irqsrc *isrc)
+{
+
+	/* EOI must be done in controller's interrupt routine. */
+}
+
+#ifdef SMP
+static int
+arm_gic_bind(device_t dev, struct arm_irqsrc *isrc, u_int cpu)
+{
+	struct arm_gic_softc *sc = device_get_softc(dev);
+	uint32_t irq;
+
+	irq = (u_int)isrc->isrc_data;
+	if (irq >= sc->nirqs || sc->gic_irqs[irq].gi_isrc != isrc)
+		return (EINVAL);
+
+	if (irq < GIC_FIRST_SPI)
+		return (EINVAL);
+
+	gic_bind(sc, irq, cpu);
+	return (0);
+}
+
+static void
+arm_gic_ipi_send(device_t dev, struct arm_irqsrc *isrc, cpuset_t cpus)
+{
+	struct arm_gic_softc *sc = device_get_softc(dev);
+	uint32_t irq, val = 0, i;
+
+	irq = (u_int)isrc->isrc_data;
+	if (irq >= sc->nirqs || sc->gic_irqs[irq].gi_isrc != isrc)
+		return;
+
+	for (i = 0; i < MAXCPU; i++)
+		if (CPU_ISSET(i, &cpus))
+			val |= 1 << (16 + i);
+
+	gic_d_write_4(sc, GICD_SGIR(0), val | irq);
+}
+#endif
+#else
+static int
 arm_gic_next_irq(struct arm_gic_softc *sc, int last_irq)
 {
 	uint32_t active_irq;
@@ -323,7 +866,7 @@
 	 * bits (ie CPU number), not just the IRQ number, and we do not
 	 * have this information later.
 	 */
-	if ((active_irq & 0x3ff) <= GIC_LAST_IPI)
+	if ((active_irq & 0x3ff) <= GIC_LAST_SGI)
 		gic_c_write_4(sc, GICC_EOIR, active_irq);
 	active_irq &= 0x3FF;
 
@@ -396,7 +939,7 @@
 	struct arm_gic_softc *sc = device_get_softc(dev);
 
 	gic_d_write_4(sc, GICD_ICENABLER(irq >> 5), (1UL << (irq & 0x1F)));
-	gic_c_write_4(sc, GICC_EOIR, irq);
+	gic_c_write_4(sc, GICC_EOIR, irq); /* XXX - not allowed */
 }
 
 static void
@@ -404,7 +947,7 @@
 {
 	struct arm_gic_softc *sc = device_get_softc(dev);
 
-	if (irq > GIC_LAST_IPI)
+	if (irq > GIC_LAST_SGI)
 		arm_irq_memory_barrier(irq);
 
 	gic_d_write_4(sc, GICD_ISENABLER(irq >> 5), (1UL << (irq & 0x1F)));
@@ -451,10 +994,10 @@
 static void
 gic_post_filter(void *arg)
 {
-	struct arm_gic_softc *sc = arm_gic_sc;
+	struct arm_gic_softc *sc = gic_sc;
 	uintptr_t irq = (uintptr_t) arg;
 
-	if (irq > GIC_LAST_IPI)
+	if (irq > GIC_LAST_SGI)
 		arm_irq_memory_barrier(irq);
 	gic_c_write_4(sc, GICC_EOIR, irq);
 }
@@ -463,7 +1006,7 @@
 gic_config_irq(int irq, enum intr_trigger trig, enum intr_polarity pol)
 {
 
-	return (arm_gic_config(arm_gic_sc->gic_dev, irq, trig, pol));
+	return (arm_gic_config(gic_sc->gic_dev, irq, trig, pol));
 }
 
 void
@@ -470,7 +1013,7 @@
 arm_mask_irq(uintptr_t nb)
 {
 
-	arm_gic_mask(arm_gic_sc->gic_dev, nb);
+	arm_gic_mask(gic_sc->gic_dev, nb);
 }
 
 void
@@ -477,7 +1020,7 @@
 arm_unmask_irq(uintptr_t nb)
 {
 
-	arm_gic_unmask(arm_gic_sc->gic_dev, nb);
+	arm_gic_unmask(gic_sc->gic_dev, nb);
 }
 
 int
@@ -484,7 +1027,7 @@
 arm_get_next_irq(int last_irq)
 {
 
-	return (arm_gic_next_irq(arm_gic_sc, last_irq));
+	return (arm_gic_next_irq(gic_sc, last_irq));
 }
 
 void
@@ -491,7 +1034,7 @@
 arm_init_secondary_ic(void)
 {
 
-	arm_gic_init_secondary(arm_gic_sc->gic_dev);
+	arm_gic_init_secondary(gic_sc->gic_dev);
 }
 
 #ifdef SMP
@@ -499,7 +1042,7 @@
 pic_ipi_send(cpuset_t cpus, u_int ipi)
 {
 
-	arm_gic_ipi_send(arm_gic_sc->gic_dev, cpus, ipi);
+	arm_gic_ipi_send(gic_sc->gic_dev, cpus, ipi);
 }
 
 int
@@ -506,7 +1049,7 @@
 pic_ipi_read(int i)
 {
 
-	return (arm_gic_ipi_read(arm_gic_sc->gic_dev, i));
+	return (arm_gic_ipi_read(gic_sc->gic_dev, i));
 }
 
 void
@@ -513,14 +1056,32 @@
 pic_ipi_clear(int ipi)
 {
 
-	arm_gic_ipi_clear(arm_gic_sc->gic_dev, ipi);
+	arm_gic_ipi_clear(gic_sc->gic_dev, ipi);
 }
 #endif
+#endif /* ARM_INTRNG */
 
 static device_method_t arm_gic_methods[] = {
 	/* Device interface */
 	DEVMETHOD(device_probe,		arm_gic_probe),
 	DEVMETHOD(device_attach,	arm_gic_attach),
+#ifdef ARM_INTRNG
+	/* Interrupt controller interface */
+	DEVMETHOD(pic_disable_intr,	arm_gic_disable_intr),
+	DEVMETHOD(pic_disable_source,	arm_gic_disable_source),
+	DEVMETHOD(pic_enable_intr,	arm_gic_enable_intr),
+	DEVMETHOD(pic_enable_source,	arm_gic_enable_source),
+	DEVMETHOD(pic_post_filter,	arm_gic_post_filter),
+	DEVMETHOD(pic_post_ithread,	arm_gic_post_ithread),
+	DEVMETHOD(pic_pre_ithread,	arm_gic_pre_ithread),
+	DEVMETHOD(pic_register,		arm_gic_register),
+	DEVMETHOD(pic_unregister,	arm_gic_unregister),
+#ifdef SMP
+	DEVMETHOD(pic_bind,		arm_gic_bind),
+	DEVMETHOD(pic_init_secondary,	arm_gic_init_secondary),
+	DEVMETHOD(pic_ipi_send,		arm_gic_ipi_send),
+#endif
+#endif
 	{ 0, 0 }
 };
 
Index: sys/arm/arm/mp_machdep.c
===================================================================
--- sys/arm/arm/mp_machdep.c	(revision 277826)
+++ sys/arm/arm/mp_machdep.c	(working copy)
@@ -73,7 +73,9 @@
 /* Set to 1 once we're ready to let the APs out of the pen. */
 volatile int aps_ready = 0;
 
+#ifndef ARM_INTRNG
 static int ipi_handler(void *arg);
+#endif
 void set_stackptrs(int cpu);
 
 /* Temporary variables for init_secondary()  */
@@ -131,8 +133,12 @@
 	if (error)
 		printf("WARNING: Some AP's failed to start\n");
 	else
-		for (i = 1; i < mp_ncpus; i++)
+		for (i = 1; i < mp_ncpus; i++) {
 			CPU_SET(i, &all_cpus);
+#ifdef ARM_INTRNG
+			arm_irq_add_cpu(i); /* XXX - what about sparse CPUs ? */
+#endif
+		}
 
 }
 
@@ -149,7 +155,6 @@
 {
 	struct pcpu *pc;
 	uint32_t loop_counter;
-	int start = 0, end = 0;
 
 	cpu_setup(NULL);
 	setttb(pmap_pa);
@@ -199,18 +204,6 @@
 
 	mtx_unlock_spin(&ap_boot_mtx);
 
-	/* Enable ipi */
-#ifdef IPI_IRQ_START
-	start = IPI_IRQ_START;
-#ifdef IPI_IRQ_END
-  	end = IPI_IRQ_END;
-#else
-	end = IPI_IRQ_START;
-#endif
-#endif
-				
-	for (int i = start; i <= end; i++)
-		arm_unmask_irq(i);
 	enable_interrupts(PSR_I);
 
 	loop_counter = 0;
@@ -233,6 +226,108 @@
 	/* NOTREACHED */
 }
 
+#ifdef ARM_INTRNG
+static void
+ipi_rendezvous(void *dummy __unused)
+{
+
+	CTR0(KTR_SMP, "IPI_RENDEZVOUS");
+	smp_rendezvous_action();
+}
+
+static void
+ipi_ast(void *dummy __unused)
+{
+
+	CTR0(KTR_SMP, "IPI_AST");
+}
+
+static void
+ipi_stop(void *dummy __unused)
+{
+	u_int cpu;
+
+	/*
+	 * IPI_STOP_HARD is mapped to IPI_STOP.
+	 */
+	CTR0(KTR_SMP, "IPI_STOP or IPI_STOP_HARD");
+
+	cpu = PCPU_GET(cpuid);
+	savectx(&stoppcbs[cpu]);
+
+	/*
+	 * CPUs are stopped when entering the debugger and at
+	 * system shutdown, both events which can precede a
+	 * panic dump.  For the dump to be correct, all caches
+	 * must be flushed and invalidated, but on ARM there's
+	 * no way to broadcast a wbinv_all to other cores.
+	 * Instead, we have each core do the local wbinv_all as
+	 * part of stopping the core.  The core requesting the
+	 * stop will do the l2 cache flush after all other cores
+	 * have done their l1 flushes and stopped.
+	 */
+	cpu_idcache_wbinv_all();
+
+	/* Indicate we are stopped */
+	CPU_SET_ATOMIC(cpu, &stopped_cpus);
+
+	/* Wait for restart */
+	while (!CPU_ISSET(cpu, &started_cpus))
+		cpu_spinwait();
+
+	CPU_CLR_ATOMIC(cpu, &started_cpus);
+	CPU_CLR_ATOMIC(cpu, &stopped_cpus);
+	CTR0(KTR_SMP, "IPI_STOP (restart)");
+}
+
+static void
+ipi_preempt(void *arg)
+{
+	struct trapframe *oldframe;
+	struct thread *td;
+
+	critical_enter();
+	td = curthread;
+	td->td_intr_nesting_level++;
+	oldframe = td->td_intr_frame;
+	td->td_intr_frame = (struct trapframe *)arg;
+
+	CTR1(KTR_SMP, "%s: IPI_PREEMPT", __func__);
+	sched_preempt(td);
+
+	td->td_intr_frame = oldframe;
+	td->td_intr_nesting_level--;
+	critical_exit();
+}
+
+static void
+ipi_hardclock(void *arg)
+{
+	struct trapframe *oldframe;
+	struct thread *td;
+
+	critical_enter();
+	td = curthread;
+	td->td_intr_nesting_level++;
+	oldframe = td->td_intr_frame;
+	td->td_intr_frame = (struct trapframe *)arg;
+
+	CTR1(KTR_SMP, "%s: IPI_HARDCLOCK", __func__);
+	hardclockintr();
+
+	td->td_intr_frame = oldframe;
+	td->td_intr_nesting_level--;
+	critical_exit();
+}
+
+static void
+ipi_tlb(void *dummy __unused)
+{
+
+	CTR1(KTR_SMP, "%s: IPI_TLB", __func__);
+	cpufuncs.cf_tlb_flushID();
+}
+#else
 static int
 ipi_handler(void *arg)
 {
@@ -308,15 +403,32 @@
 
 	return (FILTER_HANDLED);
 }
+#endif
 
 static void
 release_aps(void *dummy __unused)
 {
 	uint32_t loop_counter;
+#ifndef ARM_INTRNG
 	int start = 0, end = 0;
+#endif
 
 	if (mp_ncpus == 1)
 		return;
+
+#ifdef ARM_INTRNG
+	arm_irq_add_cpu(0); /* XXX - it should be boot CPU */
+
+	arm_setup_ipihandler(IPI_RENDEZVOUS, "rendezvous", ipi_rendezvous, NULL,
+	    0);
+	arm_setup_ipihandler(IPI_AST, "ast", ipi_ast, NULL, 0);
+	arm_setup_ipihandler(IPI_STOP, "stop", ipi_stop, NULL, 0);
+	arm_setup_ipihandler(IPI_PREEMPT, "preempt", ipi_preempt, NULL, 0);
+	arm_setup_ipihandler(IPI_HARDCLOCK, "hardclock", ipi_hardclock, NULL,
+	    0);
+	arm_setup_ipihandler(IPI_TLB, "tlb", ipi_tlb, NULL, 0);
+
+#else
 #ifdef IPI_IRQ_START
 	start = IPI_IRQ_START;
 #ifdef IPI_IRQ_END
@@ -341,6 +453,7 @@
 		/* Enable ipi */
 		arm_unmask_irq(i);
 	}
+#endif
 	atomic_store_rel_int(&aps_ready, 1);
 
 	printf("Release APs\n");
Index: sys/arm/arm/nexus.c
===================================================================
--- sys/arm/arm/nexus.c	(revision 277826)
+++ sys/arm/arm/nexus.c	(working copy)
@@ -85,8 +85,13 @@
     u_long, u_long, u_long, u_int);
 static	int nexus_activate_resource(device_t, device_t, int, int,
     struct resource *);
+#ifdef SMP
+static	int nexus_bind_intr(device_t, device_t, struct resource *, int);
+#endif
 static int nexus_config_intr(device_t dev, int irq, enum intr_trigger trig,
     enum intr_polarity pol);
+static	int nexus_describe_intr(device_t dev, device_t child,
+    struct resource *irq, void *cookie, const char *descr);
 static	int nexus_deactivate_resource(device_t, device_t, int, int,
     struct resource *);
 static int nexus_release_resource(device_t, device_t, int, int,
@@ -118,6 +123,13 @@
 #ifdef FDT
 	DEVMETHOD(ofw_bus_map_intr,	nexus_ofw_map_intr),
 #endif
+#ifdef ARM_INTRNG
+#ifdef SMP
+	DEVMETHOD(bus_bind_intr,	nexus_bind_intr),
+#endif
+	DEVMETHOD(bus_describe_intr,	nexus_describe_intr),
+#endif
+
 	{ 0, 0 }
 };
 
@@ -251,9 +263,12 @@
 {
 	int ret = ENODEV;
 
+#ifdef ARM_INTRNG
+	ret = arm_irq_config(irq, trig, pol);
+#else
 	if (arm_config_irq)
 		ret = (*arm_config_irq)(irq, trig, pol);
-
+#endif
 	return (ret);
 }
 
@@ -267,9 +282,14 @@
 		flags |= INTR_EXCL;
 
 	for (irq = rman_get_start(res); irq <= rman_get_end(res); irq++) {
+#if defined(ARM_INTRNG)
+		arm_setup_irqhandler(child, filt, intr, arg, irq, flags,
+		    cookiep);
+#else
 		arm_setup_irqhandler(device_get_nameunit(child),
 		    filt, intr, arg, irq, flags, cookiep);
 		arm_unmask_irq(irq);
+#endif
 	}
 	return (0);
 }
@@ -278,11 +298,32 @@
 nexus_teardown_intr(device_t dev, device_t child, struct resource *r, void *ih)
 {
 
+#if defined(ARM_INTRNG)
+	return (arm_remove_irqhandler(child, rman_get_start(r), ih));
+#else
 	return (arm_remove_irqhandler(rman_get_start(r), ih));
+#endif
 }
 
+#ifdef ARM_INTRNG
+static int
+nexus_describe_intr(device_t dev, device_t child, struct resource *irq,
+    void *cookie, const char *descr)
+{
 
+	return (arm_irq_describe(rman_get_start(irq), cookie, descr));
+}
+
+#ifdef SMP
 static int
+nexus_bind_intr(device_t dev, device_t child, struct resource *irq, int cpu)
+{
+	return (arm_irq_bind(rman_get_start(irq), cpu));
+}
+#endif
+#endif
+
+static int
 nexus_activate_resource(device_t bus, device_t child, int type, int rid,
     struct resource *r)
 {
Index: sys/arm/include/fdt.h
===================================================================
--- sys/arm/include/fdt.h	(revision 277826)
+++ sys/arm/include/fdt.h	(working copy)
@@ -32,6 +32,7 @@
 #ifndef _MACHINE_FDT_H_
 #define _MACHINE_FDT_H_
 
+#include "opt_platform.h"
 #include <dev/ofw/openfirm.h>
 
 #include <vm/vm.h>
@@ -40,12 +41,24 @@
 #include <machine/bus.h>
 #include <machine/intr.h>
 
+#ifdef ARM_INTRNG
+
 /* Max interrupt number */
+#define	FDT_INTR_MAX	(0xffff)
+
+/* Map phandle/intpin pair to global IRQ number */
+#define	FDT_MAP_IRQ(node, pin)	(arm_fdt_map_irq(node, pin))
+
+#else
+
+/* Max interrupt number */
 #define FDT_INTR_MAX	NIRQ
 
 /* Map phandle/intpin pair to global IRQ number */
 #define	FDT_MAP_IRQ(node, pin)	(pin)
 
+#endif /* ARM_INTRNG */
+
 /*
  * Bus space tag. XXX endianess info needs to be derived from the blob.
  */
Index: sys/arm/include/intr.h
===================================================================
--- sys/arm/include/intr.h	(revision 277826)
+++ sys/arm/include/intr.h	(working copy)
@@ -43,6 +43,97 @@
 #include <dev/ofw/openfirm.h>
 #endif
 
+#ifdef ARM_INTRNG
+
+#ifndef NIRQ
+#define	NIRQ		1024	/* XXX - It should be an option. */
+#endif
+
+#define	INTR_SOLO	INTR_MD1
+
+#define ARM_ISRC_NAMELEN	(MAXCOMLEN + 1)
+
+typedef int arm_irq_filter_t(void *arg, struct trapframe *tf);
+typedef void arm_ipi_filter_t(void *arg);
+
+enum arm_isrc_type {
+	ARM_ISRCT_NAMESPACE,
+	ARM_ISRCT_FDT
+};
+
+/* Interrupt source definition. */
+struct arm_irqsrc {
+	device_t		isrc_dev;	/* where isrc is mapped */
+	intptr_t		isrc_xref;	/* device reference key */
+	void *			isrc_data;	/* device data for isrc */
+	u_int			isrc_irq;	/* unique identificator */
+	enum arm_isrc_type	isrc_type;	/* how is isrc decribed */
+	u_int			isrc_flags;
+	char			isrc_name[ARM_ISRC_NAMELEN];
+	uint16_t		isrc_nspc_type;
+	uint16_t		isrc_nspc_num;
+	enum intr_trigger	isrc_trig;
+	enum intr_polarity	isrc_pol;
+	int			isrc_cpu;
+	u_int			isrc_index;
+	u_long *		isrc_count;
+	u_int			isrc_handlers;
+	struct intr_event *	isrc_event;
+	arm_irq_filter_t *	isrc_filter;
+	arm_ipi_filter_t *	isrc_ipifilter;
+	void *			isrc_arg;
+#ifdef FDT
+	u_int			isrc_ncells;
+	pcell_t			isrc_cells[];	/* leave it last */
+#endif
+};
+
+void arm_irq_set_name(struct arm_irqsrc *isrc, const char *fmt, ...)
+    __printflike(2, 3);
+
+void arm_dispatch_spi(struct arm_irqsrc *isrc, struct trapframe *tf);
+#define arm_dispatch_ppi	arm_dispatch_spi
+
+#define ARM_IRQ_TYPE_NONE	0
+#define ARM_IRQ_TYPE_SPI	1
+#define ARM_IRQ_TYPE_PPI	2
+#define ARM_IRQ_TYPE_SGI	3
+
+u_int arm_namespace_map_irq(device_t dev, uint16_t type, uint16_t num);
+
+int arm_irq_set_root(device_t dev, intptr_t xref, arm_irq_filter_t *filter,
+    void *arg, u_int ipicount);
+
+int arm_register_pic(device_t dev, intptr_t xref);
+int arm_unregister_pic(device_t dev, intptr_t xref);
+
+int arm_setup_irqhandler(device_t dev, driver_filter_t, driver_intr_t, void *,
+    u_int, int, void **);
+int arm_remove_irqhandler(device_t dev, u_int, void *);
+
+int arm_irq_config(u_int, enum intr_trigger, enum intr_polarity);
+int arm_irq_describe(u_int, void *, const char *);
+
+#ifdef FDT
+u_int arm_fdt_map_irq(phandle_t, pcell_t *, u_int);
+#endif
+
+#ifdef SMP
+int arm_irq_bind(u_int, int);
+void arm_irq_add_cpu(u_int cpu);
+
+void arm_dispatch_sgi(struct arm_irqsrc *isrc, struct trapframe *tf);
+
+#define ASIF_NOALLOC	0x0001
+
+int arm_setup_ipihandler(u_int ipi, const char *name, arm_ipi_filter_t *filter,
+    void *arg, u_int flags);
+
+void arm_init_secondary_pic(void);
+#endif
+
+#else /* ARM_INTRNG */
+
 /* XXX move to std.* files? */
 #ifdef CPU_XSCALE_81342
 #define NIRQ		128
@@ -71,7 +162,6 @@
 #define NIRQ		32
 #endif
 
-
 int arm_get_next_irq(int);
 void arm_mask_irq(uintptr_t);
 void arm_unmask_irq(uintptr_t);
@@ -83,8 +173,6 @@
 extern int (*arm_config_irq)(int irq, enum intr_trigger trig,
     enum intr_polarity pol);
 
-void arm_irq_memory_barrier(uintptr_t);
-
 void arm_init_secondary_ic(void);
 int  gic_decode_fdt(uint32_t iparentnode, uint32_t *intrcells, int *interrupt,
     int *trig, int *pol);
@@ -93,4 +181,8 @@
 int arm_fdt_map_irq(phandle_t, pcell_t *, int);
 #endif
 
+#endif /* ARM_INTRNG */
+
+void arm_irq_memory_barrier(uintptr_t);
+
 #endif	/* _MACHINE_INTR_H */
Index: sys/arm/include/smp.h
===================================================================
--- sys/arm/include/smp.h	(revision 277826)
+++ sys/arm/include/smp.h	(working copy)
@@ -6,6 +6,18 @@
 #include <sys/_cpuset.h>
 #include <machine/pcb.h>
 
+#ifdef ARM_INTRNG
+enum {
+	IPI_AST,
+	IPI_PREEMPT,
+	IPI_RENDEZVOUS,
+	IPI_STOP,
+	IPI_STOP_HARD = IPI_STOP, /* These are synonyms on arm. */
+	IPI_HARDCLOCK,
+	IPI_TLB,
+	ARM_IPI_COUNT
+};
+#else
 #define IPI_AST		0
 #define IPI_PREEMPT	2
 #define IPI_RENDEZVOUS	3
@@ -13,6 +25,7 @@
 #define IPI_STOP_HARD	4
 #define IPI_HARDCLOCK	6
 #define IPI_TLB		7
+#endif
 
 void	init_secondary(int cpu);
 void	mpentry(void);
@@ -23,8 +36,10 @@
 
 /* PIC interface */
 void	pic_ipi_send(cpuset_t cpus, u_int ipi);
+#ifndef ARM_INTRNG
 void	pic_ipi_clear(int ipi);
 int	pic_ipi_read(int arg);
+#endif
 
 /* Platform interface */
 void	platform_mp_setmaxid(void);
Index: sys/arm/ti/omap4/omap4_mp.c
===================================================================
--- sys/arm/ti/omap4/omap4_mp.c	(revision 277826)
+++ sys/arm/ti/omap4/omap4_mp.c	(working copy)
@@ -41,7 +41,12 @@
 void
 platform_mp_init_secondary(void)
 {
+
+#ifdef ARM_INTRNG
+	arm_init_secondary_pic();
+#else
 	arm_init_secondary_ic();
+#endif
 }
 
 void
Index: sys/conf/files.arm
===================================================================
--- sys/conf/files.arm	(revision 277826)
+++ sys/conf/files.arm	(working copy)
@@ -29,7 +29,8 @@
 arm/arm/identcpu.c		standard
 arm/arm/in_cksum.c		optional	inet | inet6
 arm/arm/in_cksum_arm.S		optional	inet | inet6
-arm/arm/intr.c			standard
+arm/arm/intr.c			optional	!arm_intrng
+arm/arm/intrng.c		optional	arm_intrng
 arm/arm/locore.S		standard	no-obj
 arm/arm/machdep.c		standard
 arm/arm/mem.c			optional	mem
@@ -37,6 +38,7 @@
 arm/arm/mp_machdep.c		optional	smp
 arm/arm/nexus.c			standard
 arm/arm/physmem.c		standard
+arm/arm/pic_if.m		optional	arm_intrng
 arm/arm/pl190.c			optional	pl190
 arm/arm/pl310.c			optional	pl310
 arm/arm/platform.c		optional	platform
Index: sys/conf/options.arm
===================================================================
--- sys/conf/options.arm	(revision 277826)
+++ sys/conf/options.arm	(working copy)
@@ -2,6 +2,7 @@
 ARM9_CACHE_WRITE_THROUGH	opt_global.h
 ARMV6			opt_global.h
 ARM_CACHE_LOCK_ENABLE	opt_global.h
+ARM_INTRNG		opt_global.h
 ARM_KERN_DIRECTMAP	opt_vm.h
 ARM_L2_PIPT		opt_global.h
 ARM_MANY_BOARD		opt_global.h
Index: sys/dev/fdt/fdt_common.h
===================================================================
--- sys/dev/fdt/fdt_common.h	(revision 277826)
+++ sys/dev/fdt/fdt_common.h	(working copy)
@@ -79,6 +79,7 @@
 int fdt_addrsize_cells(phandle_t, int *, int *);
 u_long fdt_data_get(void *, int);
 int fdt_data_to_res(pcell_t *, int, int, u_long *, u_long *);
+int fdt_describe_irq(char *, u_int, u_int);
 phandle_t fdt_find_compatible(phandle_t, const char *, int);
 phandle_t fdt_depth_search_compatible(phandle_t, const char *, int);
 int fdt_get_mem_regions(struct mem_region *, int *, uint32_t *);
Index: sys/dev/fdt/simplebus.c
===================================================================
--- sys/dev/fdt/simplebus.c	(revision 277826)
+++ sys/dev/fdt/simplebus.c	(working copy)
@@ -38,6 +38,8 @@
 #include <dev/ofw/ofw_bus.h>
 #include <dev/ofw/ofw_bus_subr.h>
 
+#include <dev/fdt/fdt_common.h>
+
 struct simplebus_range {
 	uint64_t bus;
 	uint64_t host;
@@ -68,6 +70,9 @@
     int *, u_long, u_long, u_long, u_int);
 static void		simplebus_probe_nomatch(device_t bus, device_t child);
 static int		simplebus_print_child(device_t bus, device_t child);
+#ifdef ARM_INTRNG
+static int		simplebus_print_irqs(struct resource_list *rl);
+#endif
 
 /*
  * ofw_bus interface
@@ -344,7 +349,11 @@
 
 	rv = 0;
 	rv += resource_list_print_type(&di->rl, "mem", SYS_RES_MEMORY, "%#lx");
+#ifdef ARM_INTRNG
+	rv += simplebus_print_irqs(&di->rl);
+#else
 	rv += resource_list_print_type(&di->rl, "irq", SYS_RES_IRQ, "%ld");
+#endif
 	return (rv);
 }
 
@@ -383,3 +392,30 @@
 	rv += bus_print_child_footer(bus, child);
 	return (rv);
 }
+
+#ifdef ARM_INTRNG
+static int
+simplebus_print_irqs(struct resource_list *rl)
+{
+	struct resource_list_entry *rle;
+	int err, printed, retval;
+	char buf[16];
+
+	printed = 0;
+	retval = 0;
+
+	STAILQ_FOREACH(rle, rl, link) {
+		if (rle->type != SYS_RES_IRQ)
+			continue;
+
+		err = fdt_describe_irq(buf, sizeof(buf), rle->start);
+		if (err < 0)
+			snprintf(buf, sizeof(buf), "???");
+
+		retval += printf("%s%s", printed ? "," : " irq ", buf);
+		printed++;
+	}
+
+	return (retval);
+}
+#endif
-------------- next part --------------
A non-text attachment was scrubbed...
Name: pic_if.m
Type: application/octet-stream
Size: 2761 bytes
Desc: not available
URL: <http://lists.freebsd.org/pipermail/freebsd-arm/attachments/20150128/650be922/attachment.obj>


More information about the freebsd-arm mailing list