interrupt framework

Svatopluk Kraus onwahe at gmail.com
Fri Mar 6 17:38:05 UTC 2015


Hi,

I have finished work on new interrupt framework. The state is
reflected in intrng_full_01.diff. ARM_INTRNG define is used in all
files so old and new framework can live together until all controllers
will be reworked.

I have reworked GIC controller and tested it on panda.  I have
reworked AINTC controller and tested it on BBB. As more platforms use
these controllers, I use ARM_INTRNG define as well. The codes are not
clean up yet. The patches are intrng_gic_panda.diff and
intrng_aintc_01.diff.

I have rework TI_GPIO controller and tested it on panda and BBB. Only
panda and BBB use this code so ARM_INTRNG is not used. The patch is
intrng_ti_gpio_panda_bbb_01.diff. The code is not clean up yet too and
moreover there is test module left in diff. For panda, gpio_113 button
is printing some message. For BBB, pin 12 on P9 interface is doing
same.

I have reworked and tested controller for RPI-B, however I want to
rework its gpio controller before I will present it.

As we must decribe interrupts by controller name (due to cascaded
controllers), system tools and MI interrupt code does not work well
with now longer decriptions. The interrupt.h.diff could help a little
bit.

I would appreciate more testing of GIC on platforms I haven't got. And
some feedback would be nice too.

Svatopluk Kraus
-------------- next part --------------
Index: sys/arm/arm/intrng.c
===================================================================
--- sys/arm/arm/intrng.c	(revision 0)
+++ sys/arm/arm/intrng.c	(working copy)
@@ -0,0 +1,1442 @@
+/*-
+ * Copyright (c) 2012-2014 Jakub Wojciech Klama <jceel at FreeBSD.org>.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *	This product includes software developed by Mark Brinicombe
+ *	for the NetBSD Project.
+ * 4. The name of the company nor the name of the author may be used to
+ *    endorse or promote products derived from this software without specific
+ *    prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/*
+ *	ARM Interrupt Framework
+ *
+ *  TODO: - how to unmask PPI on other CPUs if already started ?
+ *        - complete things for removable PICs
+ */
+
+#include "opt_ddb.h"
+#include "opt_platform.h"
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/syslog.h>
+#include <sys/malloc.h>
+#include <sys/proc.h>
+#include <sys/queue.h>
+#include <sys/bus.h>
+#include <sys/interrupt.h>
+#include <sys/conf.h>
+#include <sys/cpuset.h>
+#include <sys/smp.h>
+#include <machine/atomic.h>
+#include <machine/intr.h>
+#include <machine/cpu.h>
+#include <machine/smp.h>
+#include <machine/stdarg.h>
+
+#include <dev/ofw/openfirm.h>
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#include <dev/fdt/fdt_common.h>
+
+#ifdef DDB
+#include <ddb/ddb.h>
+#endif
+
+#include "pic_if.h"
+
+#define	INTRNAME_LEN	(2*MAXCOMLEN + 1)
+
+#ifdef DEBUG
+#define debugf(fmt, args...) do { printf("%s(): ", __func__);	\
+    printf(fmt,##args); } while (0)
+#else
+#define debugf(fmt, args...)
+#endif
+
+MALLOC_DECLARE(M_INTRNG);
+MALLOC_DEFINE(M_INTRNG, "intrng", "ARM interrupt handling");
+
+/* Main ARM interrupt handler called from assembler -> 'hidden' for C code. */
+void arm_irq_handler(struct trapframe *tf);
+
+/* Root interrupt controller stuff. */
+static device_t irq_root_dev;
+static arm_irq_filter_t *irq_root_filter;
+static void *irq_root_arg;
+static u_int irq_root_ipicount;
+
+/* Interrupt controller definition. */
+struct arm_pic {
+	SLIST_ENTRY(arm_pic)	pic_next;
+	intptr_t		pic_xref;	/* hardware identification */
+	device_t		pic_dev;
+};
+
+static struct mtx pic_list_lock;
+static SLIST_HEAD(, arm_pic) pic_list;
+
+static struct arm_pic *pic_lookup(device_t dev, intptr_t xref);
+
+/* Interrupt source definition. */
+#define ISRCF_REGISTERED	0x01	/* registered in a controller */
+#define ISRCF_PERCPU		0x02	/* per CPU interrupt */
+#define ISRCF_BOUND		0x04	/* bound to a CPU */
+
+static struct mtx isrc_table_lock;
+static struct arm_irqsrc *irq_sources[NIRQ];
+u_int irq_next_free;
+
+#define IRQ_INVALID	nitems(irq_sources)
+
+#ifdef SMP
+static boolean_t irq_assign_cpu = FALSE;
+
+static struct arm_irqsrc ipi_sources[ARM_IPI_COUNT];
+static u_int ipi_next_num;
+#endif
+
+/*
+ * - 2 counters for each I/O interrupt.
+ * - MAXCPU counters for each IPI counters for SMP.
+ */
+#ifdef SMP
+#define INTRCNT_COUNT   (NIRQ * 2 + ARM_IPI_COUNT * MAXCPU)
+#else
+#define INTRCNT_COUNT   (NIRQ * 2)
+#endif
+
+/* Data for MI statistics reporting. */
+u_long intrcnt[INTRCNT_COUNT];
+char intrnames[INTRCNT_COUNT * INTRNAME_LEN];
+size_t sintrcnt = sizeof(intrcnt);
+size_t sintrnames = sizeof(intrnames);
+static u_int intrcnt_index;
+
+/*
+ *  Interrupt framework initialization routine.
+ */
+static void
+arm_irq_init(void *dummy __unused)
+{
+
+	SLIST_INIT(&pic_list);
+	mtx_init(&pic_list_lock, "arm pic list", NULL, MTX_DEF);
+	mtx_init(&isrc_table_lock, "arm isrc table", NULL, MTX_DEF);
+}
+SYSINIT(arm_irq_init, SI_SUB_INTR, SI_ORDER_FIRST, arm_irq_init, NULL);
+
+static void
+intrcnt_setname(const char *name, int index)
+{
+
+	snprintf(intrnames + INTRNAME_LEN * index, INTRNAME_LEN, "%-*s",
+	    INTRNAME_LEN - 1, name);
+}
+
+/*
+ *  Update name for interrupt source with interrupt event.
+ */
+static void
+intrcnt_updatename(struct arm_irqsrc *isrc)
+{
+
+	/* QQQ: What about stray counter name? */
+	mtx_assert(&isrc_table_lock, MA_OWNED);
+	intrcnt_setname(isrc->isrc_event->ie_fullname, isrc->isrc_index);
+}
+
+/*
+ *  Virtualization for interrupt source interrupt counter increment.
+ */
+static inline void
+isrc_increment_count(struct arm_irqsrc *isrc)
+{
+
+	/*
+	 * XXX - It should be atomic for PPI interrupts. It was proven that
+	 *       for timer PPI interrupts the lost is measurable easily.
+	 */
+	isrc->isrc_count[0]++;
+	/*atomic_add_long(&isrc->isrc_count[0], 1);*/
+}
+
+/*
+ *  Virtualization for interrupt source interrupt stray counter increment.
+ */
+static inline void
+isrc_increment_straycount(struct arm_irqsrc *isrc)
+{
+
+	isrc->isrc_count[1]++;
+}
+
+/*
+ *  Virtualization for interrupt source interrupt name update.
+ */
+static void
+isrc_update_name(struct arm_irqsrc *isrc, const char *name)
+{
+	char str[INTRNAME_LEN];
+
+	mtx_assert(&isrc_table_lock, MA_OWNED);
+
+	if (name != NULL) {
+		snprintf(str, INTRNAME_LEN, "%s: %s", isrc->isrc_name, name);
+		intrcnt_setname(str, isrc->isrc_index);
+		snprintf(str, INTRNAME_LEN, "stray %s: %s", isrc->isrc_name,
+		    name);
+		intrcnt_setname(str, isrc->isrc_index + 1);
+	} else {
+		snprintf(str, INTRNAME_LEN, "%s:", isrc->isrc_name);
+		intrcnt_setname(str, isrc->isrc_index);
+		snprintf(str, INTRNAME_LEN, "stray %s:", isrc->isrc_name);
+		intrcnt_setname(str, isrc->isrc_index + 1);
+	}
+}
+
+/*
+ *  Virtualization for interrupt source interrupt counters setup.
+ */
+static void
+isrc_setup_counters(struct arm_irqsrc *isrc)
+{
+	u_int index;
+
+	/*
+	 *  XXX - it does not work well with removable controllers and
+	 *        interrupt sources !!!
+	 */
+	index = atomic_fetchadd_int(&intrcnt_index, 2);
+	isrc->isrc_index = index;
+	isrc->isrc_count = &intrcnt[index];
+	isrc_update_name(isrc, NULL);
+}
+
+#ifdef SMP
+/*
+ *  Virtualization for interrupt source IPI counter increment.
+ */
+static inline void
+isrc_increment_ipi_count(struct arm_irqsrc *isrc, u_int cpu)
+{
+
+	isrc->isrc_count[cpu]++;
+}
+
+/*
+ *  Virtualization for interrupt source IPI counters setup.
+ */
+static void
+isrc_setup_ipi_counters(struct arm_irqsrc *isrc, const char *name)
+{
+	u_int index, i;
+	char str[INTRNAME_LEN];
+
+	index = atomic_fetchadd_int(&intrcnt_index, MAXCPU);
+	isrc->isrc_index = index;
+	isrc->isrc_count = &intrcnt[index];
+
+	for (i = 0; i < MAXCPU; i++) {
+		/*
+		 * We do not expect any race in IPI case here,
+		 * so locking is not needed.
+		 */
+		snprintf(str, INTRNAME_LEN, "cpu%d:%s", i, name);
+		intrcnt_setname(str, index + i);
+	}
+}
+#endif
+
+/*
+ *  Main ARM interrupt dispatch handler. It's called straight
+ *  from the assembler, where CPU interrupt is served.
+ */
+void
+arm_irq_handler(struct trapframe *tf)
+{
+
+	KASSERT(irq_root_filter != NULL, ("%s: no filter", __func__));
+
+	PCPU_INC(cnt.v_intr);
+	irq_root_filter(irq_root_arg, tf);
+}
+
+/*
+ *  ARM interrupt controller dispatch function for interrupts. It should
+ *  be called straight from the interrupt controller, when associated interrupt
+ *  source is learned.
+ */
+void
+arm_irq_dispatch(struct arm_irqsrc *isrc, struct trapframe *tf)
+{
+	int error;
+
+	KASSERT(isrc != NULL, ("%s: no source", __func__));
+
+	isrc_increment_count(isrc);
+
+	if (isrc->isrc_filter != NULL) {
+		error = isrc->isrc_filter(isrc->isrc_arg, tf);
+		PIC_POST_FILTER(isrc->isrc_dev, isrc);
+		if (error == FILTER_HANDLED)
+			return;
+	} else if (isrc->isrc_event != NULL) {
+		if (intr_event_handle(isrc->isrc_event, tf) == 0)
+			return;
+	}
+
+	isrc_increment_straycount(isrc);
+	PIC_DISABLE_SOURCE(isrc->isrc_dev, isrc);
+
+	device_printf(isrc->isrc_dev, "stray irq <%s> disabled",
+	    isrc->isrc_name);
+}
+
+/*
+ *  Allocate interrupt source.
+ */
+static struct arm_irqsrc *
+isrc_alloc(u_int type, u_int extsize)
+{
+	struct arm_irqsrc *isrc;
+
+	isrc = malloc(sizeof(*isrc) + extsize, M_INTRNG, M_WAITOK | M_ZERO);
+	isrc->isrc_irq = IRQ_INVALID;	/* just to be safe */
+	isrc->isrc_type = type;
+	isrc->isrc_nspc_type = ARM_IRQ_NSPC_NONE;
+	isrc->isrc_trig = INTR_TRIGGER_CONFORM;
+	isrc->isrc_pol = INTR_POLARITY_CONFORM;
+	CPU_ZERO(&isrc->isrc_cpu);
+	return (isrc);
+}
+
+/*
+ *  Free interrupt source.
+ */
+static void
+isrc_free(struct arm_irqsrc *isrc)
+{
+
+	free(isrc, M_INTRNG);
+}
+
+void
+arm_irq_set_name(struct arm_irqsrc *isrc, const char *fmt, ...)
+{
+	va_list ap;
+
+	va_start(ap, fmt);
+	vsnprintf(isrc->isrc_name, ARM_ISRC_NAMELEN, fmt, ap);
+	va_end(ap);
+}
+
+/*
+ *  Alloc unique interrupt number (resource handle) for interrupt source.
+ *
+ *  There could be various strategies how to allocate free interrupt number
+ *  (resource handle) for new interrupt source.
+ *
+ *  1. Handles are always allocated forward, so handles are not recycled
+ *     immediately. However, if only one free handle left which is reused
+ *     constantly...
+ */
+static int
+isrc_alloc_irq_locked(struct arm_irqsrc *isrc)
+{
+	u_int maxirqs, irq;
+
+	mtx_assert(&isrc_table_lock, MA_OWNED);
+
+	maxirqs = nitems(irq_sources);
+	if (irq_next_free >= maxirqs)
+		return (ENOSPC);
+
+	for (irq = irq_next_free; irq < maxirqs; irq++) {
+		if (irq_sources[irq] == NULL)
+			goto found;
+	}
+	for (irq = 0; irq < irq_next_free; irq++) {
+		if (irq_sources[irq] == NULL)
+			goto found;
+	}
+
+	irq_next_free = maxirqs;
+	return (ENOSPC);
+
+found:
+	isrc->isrc_irq = irq;
+	irq_sources[irq] = isrc;
+
+	arm_irq_set_name(isrc, "irq%u", irq);
+	isrc_setup_counters(isrc);
+
+	irq_next_free = irq + 1;
+	if (irq_next_free >= maxirqs)
+		irq_next_free = 0;
+	return (0);
+}
+#ifdef notyet
+/*
+ *  Free unique interrupt number (resource handle) from interrupt source.
+ */
+static int
+isrc_free_irq(struct arm_irqsrc *isrc)
+{
+	u_int maxirqs;
+
+	mtx_assert(&isrc_table_lock, MA_NOTOWNED);
+
+	maxirqs = nitems(irq_sources);
+	if (isrc->isrc_irq >= maxirqs)
+		return (EINVAL);
+
+	mtx_lock(&isrc_table_lock);
+	if (irq_sources[isrc->isrc_irq] != isrc) {
+		mtx_unlock(&isrc_table_lock);
+		return (EINVAL);
+	}
+
+	irq_sources[isrc->isrc_irq] = NULL;
+	isrc->isrc_irq = IRQ_INVALID;	/* just to be safe */
+	mtx_unlock(&isrc_table_lock);
+
+	return (0);
+}
+#endif
+/*
+ *  Lookup interrupt source by interrupt number (resource handle).
+ */
+static struct arm_irqsrc *
+isrc_lookup(u_int irq)
+{
+
+	if (irq < nitems(irq_sources))
+		return (irq_sources[irq]);
+	return (NULL);
+}
+
+/*
+ *  Lookup interrupt source by namespace data.
+ */
+static struct arm_irqsrc *
+isrc_namespace_lookup(device_t dev, uint16_t type, uint16_t num)
+{
+	u_int irq;
+	struct arm_irqsrc *isrc;
+
+	mtx_assert(&isrc_table_lock, MA_OWNED);
+
+	for (irq = 0; irq < nitems(irq_sources); irq++) {
+		isrc = irq_sources[irq];
+		if (isrc != NULL && isrc->isrc_dev == dev &&
+		    isrc->isrc_nspc_type == type && isrc->isrc_nspc_num == num)
+			return (isrc);
+	}
+	return (NULL);
+}
+
+/*
+ *  Map interrupt source according to namespace into framework. If such mapping
+ *  does not exist, create it. Return unique interrupt number (resource handle)
+ *  associated with mapped interrupt source.
+ */
+u_int
+arm_namespace_map_irq(device_t dev, uint16_t type, uint16_t num)
+{
+	struct arm_irqsrc *isrc, *new_isrc;
+	int error;
+
+	new_isrc = isrc_alloc(ARM_ISRCT_NAMESPACE, 0);
+
+	mtx_lock(&isrc_table_lock);
+	isrc = isrc_namespace_lookup(dev, type, num);
+	if (isrc != NULL) {
+		mtx_unlock(&isrc_table_lock);
+		isrc_free(new_isrc);
+		return (isrc->isrc_irq);	/* already mapped */
+	}
+
+	error = isrc_alloc_irq_locked(new_isrc);
+	if (error != 0) {
+		mtx_unlock(&isrc_table_lock);
+		isrc_free(new_isrc);
+		return (IRQ_INVALID);		/* no space left */
+	}
+
+	new_isrc->isrc_dev = dev;
+	new_isrc->isrc_nspc_type = type;
+	new_isrc->isrc_nspc_num = num;
+	mtx_unlock(&isrc_table_lock);
+
+	return (new_isrc->isrc_irq);
+}
+
+#ifdef FDT
+/*
+ *  Lookup interrupt source by FDT data.
+ */
+static struct arm_irqsrc *
+isrc_fdt_lookup(intptr_t xref, pcell_t *cells, u_int ncells)
+{
+	u_int irq, cellsize;
+	struct arm_irqsrc *isrc;
+
+	mtx_assert(&isrc_table_lock, MA_OWNED);
+
+	cellsize = ncells * sizeof(*cells);
+	for (irq = 0; irq < nitems(irq_sources); irq++) {
+		isrc = irq_sources[irq];
+		if (isrc != NULL && isrc->isrc_type == ARM_ISRCT_FDT &&
+		    isrc->isrc_xref == xref && isrc->isrc_ncells == ncells &&
+		    memcmp(isrc->isrc_cells, cells, cellsize) == 0)
+			return (isrc);
+	}
+	return (NULL);
+}
+
+/*
+ *  Map interrupt source according to FDT data into framework. If such mapping
+ *  does not exist, create it. Return unique interrupt number (resource handle)
+ *  associated with mapped interrupt source.
+ */
+u_int
+arm_fdt_map_irq(phandle_t node, pcell_t *cells, u_int ncells)
+{
+	struct arm_irqsrc *isrc, *new_isrc;
+	u_int cellsize;
+	intptr_t xref;
+	int error;
+
+	xref = (intptr_t)node;	/* It's so simple for now. */
+
+	cellsize = ncells * sizeof(*cells);
+	new_isrc = isrc_alloc(ARM_ISRCT_FDT, cellsize);
+
+	mtx_lock(&isrc_table_lock);
+	isrc = isrc_fdt_lookup(xref, cells, ncells);
+	if (isrc != NULL) {
+		mtx_unlock(&isrc_table_lock);
+		isrc_free(new_isrc);
+		return (isrc->isrc_irq);	/* already mapped */
+	}
+
+	error = isrc_alloc_irq_locked(new_isrc);
+	if (error != 0) {
+		mtx_unlock(&isrc_table_lock);
+		isrc_free(new_isrc);
+		return (IRQ_INVALID);		/* no space left */
+	}
+
+	new_isrc->isrc_xref = xref;
+	new_isrc->isrc_ncells = ncells;
+	memcpy(new_isrc->isrc_cells, cells, cellsize);
+	mtx_unlock(&isrc_table_lock);
+
+	return (new_isrc->isrc_irq);
+}
+#endif
+
+/*
+ *  Register interrupt source into interrupt controller.
+ */
+static int
+isrc_register(struct arm_irqsrc *isrc)
+{
+	struct arm_pic *pic;
+	boolean_t is_percpu;
+	int error;
+
+	if (isrc->isrc_flags & ISRCF_REGISTERED)
+		return (0);
+
+	if (isrc->isrc_dev == NULL) {
+		pic = pic_lookup(NULL, isrc->isrc_xref);
+		if (pic == NULL || pic->pic_dev == NULL)
+			return (ESRCH);
+		isrc->isrc_dev = pic->pic_dev;
+	}
+
+	error = PIC_REGISTER(isrc->isrc_dev, isrc, &is_percpu);
+	if (error != 0)
+		return (error);
+
+	mtx_lock(&isrc_table_lock);
+	isrc->isrc_flags |= ISRCF_REGISTERED;
+	if (is_percpu)
+		isrc->isrc_flags |= ISRCF_PERCPU;
+	isrc_update_name(isrc, NULL);
+	mtx_unlock(&isrc_table_lock);
+	return (0);
+}
+
+/*
+ *  Setup filter into interrupt source.
+ */
+static int
+iscr_setup_filter(struct arm_irqsrc *isrc, const char *name,
+    arm_irq_filter_t *filter, void *arg, void **cookiep)
+{
+
+	if (filter == NULL)
+		return (EINVAL);
+
+	mtx_lock(&isrc_table_lock);
+	/*
+	 * Make sure that we do not mix the two ways
+	 * how we handle interrupt sources.
+	 */
+	if (isrc->isrc_filter != NULL || isrc->isrc_event != NULL) {
+		mtx_unlock(&isrc_table_lock);
+		return (EBUSY);
+	}
+	isrc->isrc_filter = filter;
+	isrc->isrc_arg = arg;
+	isrc_update_name(isrc, name);
+	mtx_unlock(&isrc_table_lock);
+
+	*cookiep = isrc;
+	return (0);
+}
+
+/*
+ *  Interrupt source pre_ithread method for MI interrupt framework.
+ */
+static void
+arm_isrc_pre_ithread(void *arg)
+{
+	struct arm_irqsrc *isrc = arg;
+
+	PIC_PRE_ITHREAD(isrc->isrc_dev, isrc);
+}
+
+/*
+ *  Interrupt source post_ithread method for MI interrupt framework.
+ */
+static void
+arm_isrc_post_ithread(void *arg)
+{
+	struct arm_irqsrc *isrc = arg;
+
+	PIC_POST_ITHREAD(isrc->isrc_dev, isrc);
+}
+
+/*
+ *  Interrupt source post_filter method for MI interrupt framework.
+ */
+static void
+arm_isrc_post_filter(void *arg)
+{
+	struct arm_irqsrc *isrc = arg;
+
+	PIC_POST_FILTER(isrc->isrc_dev, isrc);
+}
+
+/*
+ *  Interrupt source assign_cpu method for MI interrupt framework.
+ */
+static int
+arm_isrc_assign_cpu(void *arg, int cpu)
+{
+#ifdef SMP
+	struct arm_irqsrc *isrc = arg;
+	int error;
+
+	if (isrc->isrc_dev != irq_root_dev)
+		return (EINVAL);
+
+	mtx_lock(&isrc_table_lock);
+	if (cpu == NOCPU) {
+		CPU_ZERO(&isrc->isrc_cpu);
+		isrc->isrc_flags &= ~ISRCF_BOUND;
+	} else {
+		CPU_SETOF(cpu, &isrc->isrc_cpu);
+		isrc->isrc_flags |= ISRCF_BOUND;
+	}
+
+	/*
+	 * In NOCPU case, it's up to PIC to leave ISRC either on same CPU or
+	 * re-balance it to another CPU or enable it on more CPUs. However,
+	 * PIC is expected to change isrc_cpu appropriately to keep us well
+	 * informed if the call is successfull.
+	 */
+	if (irq_assign_cpu) {
+		error = PIC_BIND(isrc->isrc_dev, isrc);
+		if (error) {
+			CPU_ZERO(&isrc->isrc_cpu);
+			mtx_unlock(&isrc_table_lock);
+			return (error);
+		}
+	}
+	mtx_unlock(&isrc_table_lock);
+	return (0);
+#else
+	return (EOPNOTSUPP);
+#endif
+}
+
+/*
+ *  Create interrupt event for interrupt source.
+ */
+static int
+isrc_event_create(struct arm_irqsrc *isrc)
+{
+	struct intr_event *ie;
+	int error;
+
+	error = intr_event_create(&ie, isrc, 0, isrc->isrc_irq,
+	    arm_isrc_pre_ithread, arm_isrc_post_ithread, arm_isrc_post_filter,
+	    arm_isrc_assign_cpu, "%s:", isrc->isrc_name);
+	if (error)
+		return (error);
+
+	mtx_lock(&isrc_table_lock);
+	/*
+	 * Make sure that we do not mix the two ways
+	 * how we handle interrupt sources. Let contested event wins.
+	 */
+	if (isrc->isrc_filter != NULL || isrc->isrc_event != NULL) {
+		mtx_unlock(&isrc_table_lock);
+		intr_event_destroy(ie);
+		return (isrc->isrc_event != NULL ? EBUSY : 0);
+	}
+	isrc->isrc_event = ie;
+	mtx_unlock(&isrc_table_lock);
+
+	return (0);
+}
+#ifdef notyet
+/*
+ *  Destroy interrupt event for interrupt source.
+ */
+static void
+isrc_event_destroy(struct arm_irqsrc *isrc)
+{
+	struct intr_event *ie;
+
+	mtx_lock(&isrc_table_lock);
+	ie = isrc->isrc_event;
+	isrc->isrc_event = NULL;
+	mtx_unlock(&isrc_table_lock);
+
+	if (ie != NULL)
+		intr_event_destroy(ie);
+}
+#endif
+/*
+ *  Add handler to interrupt source.
+ */
+static int
+isrc_add_handler(struct arm_irqsrc *isrc, const char *name,
+    driver_filter_t filter, driver_intr_t handler, void *arg,
+    enum intr_type flags, void **cookiep)
+{
+	int error;
+
+	if (isrc->isrc_event == NULL) {
+		error = isrc_event_create(isrc);
+		if (error)
+			return (error);
+	}
+
+	error = intr_event_add_handler(isrc->isrc_event, name, filter, handler,
+	    arg, intr_priority(flags), flags, cookiep);
+	if (error == 0) {
+		mtx_lock(&isrc_table_lock);
+		intrcnt_updatename(isrc);
+		mtx_unlock(&isrc_table_lock);
+	}
+
+	return (error);
+}
+
+/*
+ *  Lookup interrupt controller locked.
+ */
+static struct arm_pic *
+pic_lookup_locked(device_t dev, intptr_t xref)
+{
+	struct arm_pic *pic;
+
+	mtx_assert(&pic_list_lock, MA_OWNED);
+
+	SLIST_FOREACH(pic, &pic_list, pic_next) {
+		if (pic->pic_xref != xref)
+			continue;
+		if (pic->pic_xref != 0 || pic->pic_dev == dev)
+			return (pic);
+	}
+	return (NULL);
+}
+
+/*
+ *  Lookup interrupt controller.
+ */
+static struct arm_pic *
+pic_lookup(device_t dev, intptr_t xref)
+{
+	struct arm_pic *pic;
+
+	mtx_lock(&pic_list_lock);
+	pic = pic_lookup_locked(dev, xref);
+	mtx_unlock(&pic_list_lock);
+
+	return (pic);
+}
+
+/*
+ *  Create interrupt controller.
+ */
+static struct arm_pic *
+pic_create(device_t dev, intptr_t xref)
+{
+	struct arm_pic *pic;
+
+	mtx_lock(&pic_list_lock);
+	pic = pic_lookup_locked(dev, xref);
+	if (pic != NULL) {
+		mtx_unlock(&pic_list_lock);
+		return (pic);
+	}
+	pic = malloc(sizeof(*pic), M_INTRNG, M_NOWAIT | M_ZERO);
+	pic->pic_xref = xref;
+	pic->pic_dev = dev;
+	SLIST_INSERT_HEAD(&pic_list, pic, pic_next);
+	mtx_unlock(&pic_list_lock);
+
+	return (pic);
+}
+#ifdef notyet
+/*
+ *  Destroy interrupt controller.
+ */
+static void
+pic_destroy(device_t dev, intptr_t xref)
+{
+	struct arm_pic *pic;
+
+	mtx_lock(&pic_list_lock);
+	pic = pic_lookup_locked(dev, xref);
+	if (pic == NULL) {
+		mtx_unlock(&pic_list_lock);
+		return;
+	}
+	SLIST_REMOVE(&pic_list, pic, arm_pic, pic_next);
+	mtx_unlock(&pic_list_lock);
+
+	free(pic, M_INTRNG);
+}
+#endif
+/*
+ *  Register interrupt controller.
+ */
+int
+arm_pic_register(device_t dev, intptr_t xref)
+{
+	struct arm_pic *pic;
+
+	pic = pic_create(dev, xref);
+	if (pic == NULL)
+		return (ENOMEM);
+	if (pic->pic_dev != dev)
+		return (EINVAL);	/* XXX it could be many things. */
+
+	debugf("PIC %p registered for %s <xref %x>\n", pic,
+	    device_get_nameunit(dev), xref);
+	return (0);
+}
+
+/*
+ *  Unregister interrupt controller.
+ */
+int
+arm_pic_unregister(device_t dev, intptr_t xref)
+{
+
+	panic("%s: not implemented", __func__);
+}
+
+/*
+ *  Mark interrupt controller (itself) as a root one.
+ *
+ *  Note that only an interrupt controller can really know its position
+ *  in interrupt controller's tree. So root PIC must claim itself as a root.
+ *
+ *  In FDT case, 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."
+ */
+int
+arm_pic_claim_root(device_t dev, intptr_t xref, arm_irq_filter_t *filter,
+    void *arg, u_int ipicount)
+{
+
+	if (pic_lookup(dev, xref) == NULL) {
+		device_printf(dev, "not registered\n");
+		return (EINVAL);
+	}
+	if (filter == NULL) {
+		device_printf(dev, "filter missing\n");
+		return (EINVAL);
+	}
+
+	/*
+	 * Only one interrupt controllers could be on the root for now.
+	 * Further, we suppose that there is not threaded interrupt
+	 * routine (handler) on the root. See arm_irq_handler().
+	 */
+	if (irq_root_dev != NULL) {
+		device_printf(dev, "another root already set\n");
+		return (EBUSY);
+	}
+
+	irq_root_dev = dev;
+	irq_root_filter = filter;
+	irq_root_arg = arg;
+	irq_root_ipicount = ipicount;
+
+	debugf("irq root set to %s\n", device_get_nameunit(dev));
+	return (0);
+}
+
+int
+arm_irq_add_handler(device_t dev, driver_filter_t filt, driver_intr_t hand,
+    void *arg, u_int irq, int flags, void **cookiep)
+{
+	const char *name;
+	struct arm_irqsrc *isrc;
+	int error;
+
+	name = device_get_nameunit(dev);
+
+	/*
+	 * Standard handling is done thru MI interrupt framework. However,
+	 * some interrupts could request solely own special handling. This
+	 * non standard handling can be used for interrupt controllers without
+	 * handler (filter only), so in case that interrupt controllers are
+	 * chained, MI interrupt framework is called only in leaf controller.
+	 *
+	 * Note that root interrupt controller routine is served as well,
+	 * however in main system dispatch routine.
+	 */
+	if (flags & INTR_SOLO && hand != NULL) {
+		debugf("irq %u cannot solo on %s\n", irq, name);
+		return (EINVAL);
+	}
+
+	isrc = isrc_lookup(irq);
+	if (isrc == NULL) {
+		debugf("irq %u without source on %s\n", irq, name);
+		return (EINVAL);
+	}
+
+	error = isrc_register(isrc);
+	if (error != 0) {
+		debugf("irq %u map error %d on %s\n", irq, error, name);
+		return (error);
+	}
+
+	if (flags & INTR_SOLO) {
+		error = iscr_setup_filter(isrc, name, (arm_irq_filter_t *)filt,
+		    arg, cookiep);
+		debugf("irq %u setup filter error %d on %s\n", irq, error,
+		    name);
+	} else {
+		error = isrc_add_handler(isrc, name, filt, hand, arg, flags,
+		    cookiep);
+		debugf("irq %u add handler error %d on %s\n", irq, error, name);
+	}
+	if (error != 0)
+		return (error);
+
+	mtx_lock(&isrc_table_lock);
+	isrc->isrc_handlers++;
+	if (isrc->isrc_handlers == 1) {
+		PIC_ENABLE_INTR(isrc->isrc_dev, isrc);
+		PIC_ENABLE_SOURCE(isrc->isrc_dev, isrc);
+	}
+	mtx_unlock(&isrc_table_lock);
+	return (0);
+}
+
+int
+arm_irq_remove_handler(device_t dev, u_int irq, void *cookie)
+{
+	struct arm_irqsrc *isrc;
+	int error;
+
+	isrc = isrc_lookup(irq);
+	if (isrc == NULL || isrc->isrc_handlers == 0)
+		return (EINVAL);
+
+	if (isrc->isrc_filter != NULL) {
+		if (isrc != cookie)
+			return (EINVAL);
+
+		mtx_lock(&isrc_table_lock);
+		isrc->isrc_filter = NULL;
+		isrc->isrc_arg = NULL;
+		isrc->isrc_handlers = 0;
+		PIC_DISABLE_SOURCE(isrc->isrc_dev, isrc);
+		PIC_DISABLE_INTR(isrc->isrc_dev, isrc);
+		isrc_update_name(isrc, NULL);
+		mtx_unlock(&isrc_table_lock);
+		return (0);
+	}
+
+	if (isrc != intr_handler_source(cookie))
+		return (EINVAL);
+
+	error = intr_event_remove_handler(cookie);
+	if (error == 0) {
+		mtx_lock(&isrc_table_lock);
+		isrc->isrc_handlers--;
+		if (isrc->isrc_handlers == 0) {
+			PIC_DISABLE_SOURCE(isrc->isrc_dev, isrc);
+			PIC_DISABLE_INTR(isrc->isrc_dev, isrc);
+		}
+		intrcnt_updatename(isrc);
+		mtx_unlock(&isrc_table_lock);
+	}
+	return (error);
+}
+
+int
+arm_irq_config(u_int irq, enum intr_trigger trig, enum intr_polarity pol)
+{
+	struct arm_irqsrc *isrc;
+
+	isrc = isrc_lookup(irq);
+	if (isrc == NULL)
+		return (EINVAL);
+
+	if (isrc->isrc_handlers != 0)
+		return (EBUSY);	/* interrrupt is enabled (active) */
+
+	/*
+	 * Once an interrupt is enabled, we do not change its configuration.
+	 * A controller PIC_ENABLE_INTR() method is called when an interrupt
+	 * is goint to be enabled. In this method, a controller should setup
+	 * the interrupt according to saved configuration parameters.
+	 */
+	isrc->isrc_trig = trig;
+	isrc->isrc_pol = pol;
+
+	return (0);
+}
+
+int
+arm_irq_describe(u_int irq, void *cookie, const char *descr)
+{
+	struct arm_irqsrc *isrc;
+	int error;
+
+	isrc = isrc_lookup(irq);
+	if (isrc == NULL || isrc->isrc_handlers == 0)
+		return (EINVAL);
+
+	if (isrc->isrc_filter != NULL) {
+		if (isrc != cookie)
+			return (EINVAL);
+
+		mtx_lock(&isrc_table_lock);
+		isrc_update_name(isrc, descr);
+		mtx_unlock(&isrc_table_lock);
+		return (0);
+	}
+
+	error = intr_event_describe_handler(isrc->isrc_event, cookie, descr);
+	if (error == 0) {
+		mtx_lock(&isrc_table_lock);
+		intrcnt_updatename(isrc);
+		mtx_unlock(&isrc_table_lock);
+	}
+	return (error);
+}
+
+#ifdef SMP
+int
+arm_irq_bind(u_int irq, int cpu)
+{
+	struct arm_irqsrc *isrc;
+
+	isrc = isrc_lookup(irq);
+	if (isrc == NULL || isrc->isrc_handlers == 0)
+		return (EINVAL);
+
+	if (isrc->isrc_filter != NULL)
+		return (arm_isrc_assign_cpu(isrc, cpu));
+
+	return (intr_event_bind(isrc->isrc_event, cpu));
+}
+
+/*
+ * Return the CPU that the next interrupt source should use.
+ * For now just returns the next CPU according to round-robin.
+ */
+u_int
+arm_irq_next_cpu(u_int last_cpu, cpuset_t *cpumask)
+{
+
+	if (!irq_assign_cpu || mp_ncpus == 1)
+		return (PCPU_GET(cpuid));
+
+	do {
+		last_cpu++;
+		if (last_cpu > mp_maxid)
+			last_cpu = 0;
+	} while (!CPU_ISSET(last_cpu, cpumask));
+	return (last_cpu);
+}
+
+/*
+ *  Distribute all the interrupt sources among the available
+ *  CPUs once the AP's have been launched.
+ */
+static void
+arm_irq_shuffle(void *arg __unused)
+{
+	struct arm_irqsrc *isrc;
+	u_int i;
+
+	if (mp_ncpus == 1)
+		return;
+
+	mtx_lock(&isrc_table_lock);
+	irq_assign_cpu = TRUE;
+	for (i = 0; i < NIRQ; i++) {
+		isrc = irq_sources[i];
+		if (isrc == NULL || isrc->isrc_handlers == 0 ||
+		    isrc->isrc_flags & ISRCF_PERCPU)
+			continue;
+
+		if (isrc->isrc_event != NULL && isrc->isrc_flags & ISRCF_BOUND &&
+		    isrc->isrc_event->ie_cpu != CPU_FFS(&isrc->isrc_cpu) - 1)
+			panic("%s: CPU inconsistency", __func__);
+
+		if ((isrc->isrc_flags & ISRCF_BOUND) == 0)
+			CPU_ZERO(&isrc->isrc_cpu); /* start again */
+
+		/*
+		 * We are in wicked position here if the following call fails
+		 * for bound ISRC. The best thing we can do is to clear
+		 * isrc_cpu so inconsistency with ie_cpu will be detectable.
+		 */
+		if (PIC_BIND(isrc->isrc_dev, isrc) != 0)
+			CPU_ZERO(&isrc->isrc_cpu);
+	}
+	mtx_unlock(&isrc_table_lock);
+}
+SYSINIT(arm_irq_shuffle, SI_SUB_SMP, SI_ORDER_SECOND, arm_irq_shuffle, NULL);
+
+#else
+u_int
+arm_irq_next_cpu(u_int current_cpu, cpuset_t *cpumask)
+{
+
+	return (PCPU_GET(cpuid));
+}
+#endif
+
+#ifdef FDT
+/*
+ *  Describe interrupt source for FDT.
+ */
+int
+fdt_describe_irq(char *buf, u_int len, u_int irq)
+{
+	struct arm_irqsrc *isrc;
+	int rv;
+
+	isrc = isrc_lookup(irq);
+	if (isrc == NULL) {
+		return (snprintf(buf, len, "no irq source"));
+	}
+
+	(void)isrc_register(isrc);
+	if ((isrc->isrc_flags & ISRCF_REGISTERED) == 0) {
+		rv = snprintf(buf, len, "irq%u(not mapped)", irq);
+	} else {
+		rv = snprintf(buf, len, "irq%u(%s)", irq, isrc->isrc_name);
+	}
+	return (rv);
+}
+#endif
+
+void dosoftints(void);
+void
+dosoftints(void)
+{
+}
+
+/*
+ * arm_irq_memory_barrier()
+ *
+ * Ensure all writes to device memory have reached devices before proceeding.
+ *
+ * This is intended to be called from the post-filter and post-thread routines
+ * of an interrupt controller implementation.  A peripheral device driver should
+ * use bus_space_barrier() if it needs to ensure a write has reached the
+ * hardware for some reason other than clearing interrupt conditions.
+ *
+ * The need for this function arises from the ARM weak memory ordering model.
+ * Writes to locations mapped with the Device attribute bypass any caches, but
+ * are buffered.  Multiple writes to the same device will be observed by that
+ * device in the order issued by the cpu.  Writes to different devices may
+ * appear at those devices in a different order than issued by the cpu.  That
+ * is, if the cpu writes to device A then device B, the write to device B could
+ * complete before the write to device A.
+ *
+ * Consider a typical device interrupt handler which services the interrupt and
+ * writes to a device status-acknowledge register to clear the interrupt before
+ * returning.  That write is posted to the L2 controller which "immediately"
+ * places it in a store buffer and automatically drains that buffer.  This can
+ * be less immediate than you'd think... There may be no free slots in the store
+ * buffers, so an existing buffer has to be drained first to make room.  The
+ * target bus may be busy with other traffic (such as DMA for various devices),
+ * delaying the drain of the store buffer for some indeterminate time.  While
+ * all this delay is happening, execution proceeds on the CPU, unwinding its way
+ * out of the interrupt call stack to the point where the interrupt driver code
+ * is ready to EOI and unmask the interrupt.  The interrupt controller may be
+ * accessed via a faster bus than the hardware whose handler just ran; the write
+ * to unmask and EOI the interrupt may complete quickly while the device write
+ * to ack and clear the interrupt source is still lingering in a store buffer
+ * waiting for access to a slower bus.  With the interrupt unmasked at the
+ * interrupt controller but still active at the device, as soon as interrupts
+ * are enabled on the core the device re-interrupts immediately: now you've got
+ * a spurious interrupt on your hands.
+ *
+ * The right way to fix this problem is for every device driver to use the
+ * proper bus_space_barrier() calls in its interrupt handler.  For ARM a single
+ * barrier call at the end of the handler would work.  This would have to be
+ * done to every driver in the system, not just arm-specific drivers.
+ *
+ * Another potential fix is to map all device memory as Strongly-Ordered rather
+ * than Device memory, which takes the store buffers out of the picture.  This
+ * has a pretty big impact on overall system performance, because each strongly
+ * ordered memory access causes all L2 store buffers to be drained.
+ *
+ * A compromise solution is to have the interrupt controller implementation call
+ * this function to establish a barrier between writes to the interrupt-source
+ * device and writes to the interrupt controller device.
+ *
+ * This takes the interrupt number as an argument, and currently doesn't use it.
+ * The plan is that maybe some day there is a way to flag certain interrupts as
+ * "memory barrier safe" and we can avoid this overhead with them.
+ */
+void
+arm_irq_memory_barrier(uintptr_t irq)
+{
+
+	dsb();
+	cpu_l2cache_drain_writebuf();
+}
+
+#ifdef SMP
+/*
+ *  Lookup IPI source.
+ */
+static struct arm_irqsrc *
+arm_ipi_lookup(u_int ipi)
+{
+
+	if (ipi >= ARM_IPI_COUNT)
+		panic("%s: no such IPI %u", __func__, ipi);
+
+	return (&ipi_sources[ipi]);
+}
+
+/*
+ *  ARM interrupt controller dispatch function for IPIs. It should
+ *  be called straight from the interrupt controller, when associated
+ *  interrupt source is learned. Or from anybody who has aan interrupt
+ *  source mapped.
+ */
+void
+arm_ipi_dispatch(struct arm_irqsrc *isrc, struct trapframe *tf)
+{
+	void *arg;
+
+	KASSERT(isrc != NULL, ("%s: no source", __func__));
+
+	isrc_increment_ipi_count(isrc, PCPU_GET(cpuid));
+
+	/*
+	 * Supply ipi filter with trapframe argument
+	 * if none is registered.
+	 */
+	arg = isrc->isrc_arg != NULL ? isrc->isrc_arg : tf;
+	isrc->isrc_ipifilter(arg);
+}
+
+/*
+ *  Map IPI into interrupt controller.
+ *
+ *  Not SMP coherent.
+ */
+static int
+ipi_map(struct arm_irqsrc *isrc, u_int ipi)
+{
+	boolean_t is_percpu;
+	int error;
+
+	if (ipi >= ARM_IPI_COUNT)
+		panic("%s: no such IPI %u", __func__, ipi);
+
+	KASSERT(irq_root_dev != NULL, ("%s: no root attached", __func__));
+
+	isrc->isrc_type = ARM_ISRCT_NAMESPACE;
+	isrc->isrc_nspc_type = ARM_IRQ_NSPC_IPI;
+	isrc->isrc_nspc_num = ipi_next_num;
+
+	error = PIC_REGISTER(irq_root_dev, isrc, &is_percpu);
+
+	debugf("ipi %u mapped to %u on %s - error %d\n", ipi, ipi_next_num,
+	    device_get_nameunit(irq_root_dev), error);
+
+	if (error == 0)
+		ipi_next_num++;
+	return (error);
+}
+
+/*
+ *  Setup IPI handler to interrupt source.
+ *
+ *  Note that there could be more ways how to send and receive IPIs
+ *  on a platform like fast interrupts for example. In that case,
+ *  one can call this function with ASIF_NOALLOC flag set and then
+ *  call arm_ipi_dispatch() when appropriate.
+ *
+ *  Not SMP coherent.
+ */
+int
+arm_ipi_set_handler(u_int ipi, const char *name, arm_ipi_filter_t *filter,
+    void *arg, u_int flags)
+{
+	struct arm_irqsrc *isrc;
+	int error;
+
+	if (filter == NULL)
+		return(EINVAL);
+
+	isrc = arm_ipi_lookup(ipi);
+	if (isrc->isrc_ipifilter != NULL)
+		return (EEXIST);
+
+	if ((flags & AISHF_NOALLOC) == 0) {
+		error = ipi_map(isrc, ipi);
+		if (error != 0)
+			return (error);
+	}
+
+	isrc->isrc_ipifilter = filter;
+	isrc->isrc_arg = arg;
+
+	isrc_setup_ipi_counters(isrc, name);
+	return (0);
+}
+
+/*
+ *  Send IPI thru interrupt controller.
+ */
+void
+pic_ipi_send(cpuset_t cpus, u_int ipi)
+{
+	struct arm_irqsrc *isrc;
+
+	isrc = arm_ipi_lookup(ipi);
+
+	KASSERT(irq_root_dev != NULL, ("%s: no root attached", __func__));
+	PIC_IPI_SEND(irq_root_dev, isrc, cpus);
+}
+
+/*
+ *  Init interrupt controller on another CPU.
+ */
+void
+arm_pic_init_secondary(void)
+{
+
+	/*
+	 * QQQ: Only root PIC is aware of other CPUs ???
+	 */
+	KASSERT(irq_root_dev != NULL, ("%s: no root attached", __func__));
+
+	mtx_lock(&isrc_table_lock);
+	PIC_INIT_SECONDARY(irq_root_dev);
+	mtx_unlock(&isrc_table_lock);
+}
+#endif
+
+#ifdef DDB
+DB_SHOW_COMMAND(irq, db_show_irq)
+{
+	u_int i, irqsum;
+	struct arm_irqsrc *isrc;
+
+#ifdef SMP
+	for (i = 0; i <= mp_maxid; i++) {
+		struct pcpu *pc;
+		u_int ipi, ipisum;
+
+		pc = pcpu_find(i);
+		if (pc != NULL) {
+			for (ipisum = 0, ipi = 0; ipi < ARM_IPI_COUNT; ipi++) {
+				isrc = arm_ipi_lookup(ipi);
+				if (isrc->isrc_count != NULL)
+					ipisum += isrc->isrc_count[i];
+			}
+			printf ("cpu%u: total %u ipis %u\n", i,
+			    pc->pc_cnt.v_intr, ipisum);
+    		}
+	}
+	db_printf("\n");
+#endif
+
+	for (irqsum = 0, i = 0; i < NIRQ; i++) {
+		isrc = irq_sources[i];
+		if (isrc == NULL)
+			continue;
+
+		db_printf("irq%-3u <%s>: cpu %02lx%s cnt %lu\n", i,
+		    isrc->isrc_name, isrc->isrc_cpu.__bits[0],
+		    isrc->isrc_flags & ISRCF_BOUND ? " (bound)" : "",
+		    isrc->isrc_count[0]);
+		irqsum += isrc->isrc_count[0];
+	}
+	db_printf("irq total %u\n", irqsum);
+
+}
+#endif
Index: sys/arm/arm/mp_machdep.c
===================================================================
--- sys/arm/arm/mp_machdep.c	(revision 279685)
+++ 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()  */
@@ -133,7 +135,6 @@
 	else
 		for (i = 1; i < mp_ncpus; i++)
 			CPU_SET(i, &all_cpus);
-
 }
 
 /* Introduce rest of cores to the world */
@@ -149,7 +150,6 @@
 {
 	struct pcpu *pc;
 	uint32_t loop_counter;
-	int start = 0, end = 0;
 
 	cpu_setup(NULL);
 	setttb(pmap_pa);
@@ -199,18 +199,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 +221,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 +398,28 @@
 
 	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_ipi_set_handler(IPI_RENDEZVOUS, "rendezvous", ipi_rendezvous, NULL, 0);
+	arm_ipi_set_handler(IPI_AST, "ast", ipi_ast, NULL, 0);
+	arm_ipi_set_handler(IPI_STOP, "stop", ipi_stop, NULL, 0);
+	arm_ipi_set_handler(IPI_PREEMPT, "preempt", ipi_preempt, NULL, 0);
+	arm_ipi_set_handler(IPI_HARDCLOCK, "hardclock", ipi_hardclock, NULL, 0);
+	arm_ipi_set_handler(IPI_TLB, "tlb", ipi_tlb, NULL, 0);
+
+#else
 #ifdef IPI_IRQ_START
 	start = IPI_IRQ_START;
 #ifdef IPI_IRQ_END
@@ -341,6 +444,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 279685)
+++ 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,
@@ -115,6 +120,12 @@
 	DEVMETHOD(bus_release_resource,	nexus_release_resource),
 	DEVMETHOD(bus_setup_intr,	nexus_setup_intr),
 	DEVMETHOD(bus_teardown_intr,	nexus_teardown_intr),
+#ifdef ARM_INTRNG
+	DEVMETHOD(bus_describe_intr,	nexus_describe_intr),
+#ifdef SMP
+	DEVMETHOD(bus_bind_intr,	nexus_bind_intr),
+#endif
+#endif
 #ifdef FDT
 	DEVMETHOD(ofw_bus_map_intr,	nexus_ofw_map_intr),
 #endif
@@ -251,9 +262,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 +281,14 @@
 		flags |= INTR_EXCL;
 
 	for (irq = rman_get_start(res); irq <= rman_get_end(res); irq++) {
+#if defined(ARM_INTRNG)
+		arm_irq_add_handler(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 +297,33 @@
 nexus_teardown_intr(device_t dev, device_t child, struct resource *r, void *ih)
 {
 
+#if defined(ARM_INTRNG)
+	return (arm_irq_remove_handler(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/arm/pic_if.m
===================================================================
--- sys/arm/arm/pic_if.m	(revision 0)
+++ sys/arm/arm/pic_if.m	(working copy)
@@ -0,0 +1,126 @@
+#-
+# Copyright (c) 2012 Jakub Wojciech Klama <jceel at FreeBSD.org>
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+#    notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+#    notice, this list of conditions and the following disclaimer in the
+#    documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+# $FreeBSD$
+#
+
+#include "opt_platform.h"
+
+#include <sys/bus.h>
+#include <sys/cpuset.h>
+#include <dev/ofw/openfirm.h>
+#include <machine/frame.h>
+#include <machine/intr.h>
+
+INTERFACE pic;
+
+CODE {
+	static int null_pic_bind(device_t dev, struct arm_irqsrc *isrc)
+	{
+		return (EOPNOTSUPP);
+	}
+
+	static void null_pic_disable_intr(device_t dev,  struct arm_irqsrc *isrc)
+	{
+		return;
+	}
+
+	static void null_pic_enable_intr(device_t dev,  struct arm_irqsrc *isrc)
+	{
+		return;
+	}
+
+	static void null_pic_init_secondary(device_t dev)
+	{
+		return;
+	}
+
+	static void null_pic_ipi_send(device_t dev, cpuset_t cpus, u_int ipi)
+	{
+		return;
+	}
+};
+
+METHOD int register {
+	device_t		dev;
+	struct arm_irqsrc	*isrc;
+	boolean_t		*is_percpu;
+};
+
+METHOD int unregister {
+	device_t		dev;
+	struct arm_irqsrc	*isrc;
+};
+
+METHOD void disable_intr {
+	device_t		dev;
+	struct arm_irqsrc	*isrc;
+} DEFAULT null_pic_disable_intr;
+
+METHOD void disable_source {
+	device_t		dev;
+	struct arm_irqsrc	*isrc;
+};
+
+METHOD void enable_source {
+	device_t		dev;
+	struct arm_irqsrc	*isrc;
+};
+
+METHOD void enable_intr {
+	device_t		dev;
+	struct arm_irqsrc	*isrc;
+} DEFAULT null_pic_enable_intr;
+
+METHOD void pre_ithread {
+	device_t		dev;
+	struct arm_irqsrc	*isrc;
+};
+
+METHOD void post_ithread {
+	device_t		dev;
+	struct arm_irqsrc	*isrc;
+};
+
+METHOD void post_filter {
+	device_t		dev;
+	struct arm_irqsrc	*isrc;
+};
+
+METHOD int bind {
+	device_t		dev;
+	struct arm_irqsrc	*isrc;
+} DEFAULT null_pic_bind;
+
+METHOD void init_secondary {
+	device_t	dev;
+} DEFAULT null_pic_init_secondary;
+
+METHOD void ipi_send {
+	device_t		dev;
+	struct arm_irqsrc	*isrc;
+	cpuset_t		cpus;
+} DEFAULT null_pic_ipi_send;
+
Index: sys/arm/include/fdt.h
===================================================================
--- sys/arm/include/fdt.h	(revision 279685)
+++ 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 279685)
+++ sys/arm/include/intr.h	(working copy)
@@ -43,6 +43,94 @@
 #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;
+	cpuset_t		isrc_cpu;	/* on which CPUs is enabled */
+	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_irq_dispatch(struct arm_irqsrc *isrc, struct trapframe *tf);
+
+#define ARM_IRQ_NSPC_NONE	0
+#define ARM_IRQ_NSPC_PLAIN	1
+#define ARM_IRQ_NSPC_IRQ	2
+#define ARM_IRQ_NSPC_IPI	3
+
+u_int arm_namespace_map_irq(device_t dev, uint16_t type, uint16_t num);
+#ifdef FDT
+u_int arm_fdt_map_irq(phandle_t, pcell_t *, u_int);
+#endif
+
+int arm_pic_register(device_t dev, intptr_t xref);
+int arm_pic_unregister(device_t dev, intptr_t xref);
+int arm_pic_claim_root(device_t dev, intptr_t xref, arm_irq_filter_t *filter,
+    void *arg, u_int ipicount);
+
+int arm_irq_add_handler(device_t dev, driver_filter_t, driver_intr_t, void *,
+    u_int, int, void **);
+int arm_irq_remove_handler(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 *);
+
+u_int arm_irq_next_cpu(u_int current_cpu, cpuset_t *cpumask);
+
+#ifdef SMP
+int arm_irq_bind(u_int, int);
+
+void arm_ipi_dispatch(struct arm_irqsrc *isrc, struct trapframe *tf);
+
+#define AISHF_NOALLOC	0x0001
+
+int arm_ipi_set_handler(u_int ipi, const char *name, arm_ipi_filter_t *filter,
+    void *arg, u_int flags);
+    
+void arm_pic_init_secondary(void);
+#endif
+
+#else /* ARM_INTRNG */
+
 /* XXX move to std.* files? */
 #ifdef CPU_XSCALE_81342
 #define NIRQ		128
@@ -71,7 +159,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 +170,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 +178,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 279685)
+++ 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/conf/files.arm
===================================================================
--- sys/conf/files.arm	(revision 279685)
+++ 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 279685)
+++ 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 279685)
+++ 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 279685)
+++ sys/dev/fdt/simplebus.c	(working copy)
@@ -38,6 +38,7 @@
 #include <dev/ofw/ofw_bus.h>
 #include <dev/ofw/ofw_bus_subr.h>
 
+#include <dev/fdt/fdt_common.h>
 #include <dev/fdt/simplebus.h>
 
 /*
@@ -49,6 +50,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
@@ -323,7 +327,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);
 }
 
@@ -362,3 +370,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[32];
+
+	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 --------------
Index: sys/arm/arm/gic.c
===================================================================
--- sys/arm/arm/gic.c	(revision 279685)
+++ 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>
@@ -46,6 +49,7 @@
 #include <sys/cpuset.h>
 #include <sys/lock.h>
 #include <sys/mutex.h>
+#include <sys/smp.h>
 #include <machine/bus.h>
 #include <machine/intr.h>
 #include <machine/smp.h>
@@ -55,6 +59,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 +92,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 +107,17 @@
 #define GICD_ICFGR_TRIG_EDGE	(1 << 1)
 #define GICD_ICFGR_TRIG_MASK	0x2
 
+#ifdef ARM_INTRNG
+static u_int gic_irq_cpu;
+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_irqsrc **	gic_irqs;
+#endif
 	struct resource *	gic_res[3];
 	bus_space_tag_t		gic_c_bst;
 	bus_space_tag_t		gic_d_bst;
@@ -113,10 +131,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 +148,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 +178,62 @@
 	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);
+	struct arm_irqsrc *isrc;
+	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++) {
+		isrc = sc->gic_irqs[irq];
+		if (isrc != NULL) {
+			CPU_SET(PCPU_GET(cpuid), &isrc->isrc_cpu);
+			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,7 +308,18 @@
 	}
 	return (0);
 }
+#endif
 
+static inline intptr_t
+gic_xref(device_t dev)
+{
+#ifdef FDT
+	return (OF_xref_from_node(ofw_bus_get_node(dev)));
+#else
+	return (0);
+#endif
+}
+
 static int
 arm_gic_attach(device_t dev)
 {
@@ -237,8 +326,11 @@
 	struct		arm_gic_softc *sc;
 	int		i;
 	uint32_t	icciidr;
+#ifdef ARM_INTRNG
+	intptr_t	xref = gic_xref(dev);
+#endif
 
-	if (arm_gic_sc)
+	if (gic_sc)
 		return (ENXIO);
 
 	sc = device_get_softc(dev);
@@ -249,7 +341,7 @@
 	}
 
 	sc->gic_dev = dev;
-	arm_gic_sc = sc;
+	gic_sc = sc;
 
 	/* Initialize mutex */
 	mtx_init(&sc->mutex, "GIC lock", "", MTX_SPIN);
@@ -269,9 +361,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 +404,463 @@
 
 	/* 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_pic_register(dev, xref) != 0) {
+		device_printf(dev, "could not register PIC\n");
+		goto cleanup;
+	}
 
+	if (sc->gic_res[2] == NULL) {
+		if (arm_pic_claim_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_pic_unregister(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_pic_unregister(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 = arg;
+	struct arm_irqsrc *isrc;
+	uint32_t irq_active_reg, irq;
+
+	irq_active_reg = gic_c_read_4(sc, GICC_IAR);
+	irq = irq_active_reg & 0x3FF;
+
+	/*
+	 * 1. We do EOI here because recent read value from active interrupt
+	 *    register must be used for it. Another approach is to save this
+	 *    value into associated interrupt source.
+	 * 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().
+	 * 5. We always return FILTER_HANDLED as this is an interrupt
+	 *    controller dispatch function. Otherwise, in cascaded interrupt
+	 *    case, the whole interrupt subtree would be masked.
+	 */
+
+	if (irq >= sc->nirqs) {
+		device_printf(sc->gic_dev, "Spurious interrupt detected\n");
+		gic_c_write_4(sc, GICC_EOIR, irq_active_reg);
+		return (FILTER_HANDLED);
+	}
+
+dispatch_irq:
+	isrc = sc->gic_irqs[irq];
+	if (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);
+		goto next_irq;
+	}
+
+	/*
+	 * 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_ipi_dispatch(isrc, tf);
+		goto next_irq;
+#else
+		printf("SGI %u on UP system detected\n", irq - GIC_FIRST_SGI);
+		gic_c_write_4(sc, GICC_EOIR, irq_active_reg);
+		goto next_irq;
+#endif
+	}
+
+	if (isrc->isrc_trig == INTR_TRIGGER_LEVEL) {
+		critical_enter();
+		arm_irq_dispatch(isrc, tf);
+		gic_c_write_4(sc, GICC_EOIR, irq_active_reg);
+		critical_exit();
+	} else {
+		gic_c_write_4(sc, GICC_EOIR, irq_active_reg);
+		arm_irq_dispatch(isrc, tf);
+	}
+
+next_irq:
+	arm_irq_memory_barrier(irq); /* XXX */
+	irq_active_reg = gic_c_read_4(sc, GICC_IAR);
+	irq = irq_active_reg & 0x3FF;
+	if (irq < sc->nirqs)
+		goto dispatch_irq;
+
+	return (FILTER_HANDLED);
+}
+
+static int
+gic_attach_isrc(struct arm_gic_softc *sc, struct arm_irqsrc *isrc, u_int irq)
+{
+	const char *name;
+
+	/*
+	 * 1. The link between ISRC and controller must be set atomically.
+	 * 2. Just do things only once in rare case when consumers
+	 *    of shared interrupt came here at the same moment.
+	 */
+	mtx_lock_spin(&sc->mutex);
+	if (sc->gic_irqs[irq] != NULL) {
+		mtx_unlock_spin(&sc->mutex);
+		return (sc->gic_irqs[irq] == isrc ? 0 : EEXIST);
+	}
+	sc->gic_irqs[irq] = isrc;
+	isrc->isrc_data = (void *)irq;
+	mtx_unlock_spin(&sc->mutex);
+
+	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);
+	return (0);
+}
+
+static int
+gic_detach_isrc(struct arm_gic_softc *sc, struct arm_irqsrc *isrc, u_int irq)
+{
+
+	mtx_lock_spin(&sc->mutex);
+	if (sc->gic_irqs[irq] != isrc) {
+		mtx_unlock_spin(&sc->mutex);
+		return (sc->gic_irqs[irq] == NULL ? 0 : EINVAL);
+	}
+	sc->gic_irqs[irq] = NULL;
+	isrc->isrc_data = NULL;
+	mtx_unlock_spin(&sc->mutex);
+
+	arm_irq_set_name(isrc, "");
+	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 int
+gic_bind(struct arm_gic_softc *sc, u_int irq, cpuset_t *cpus)
+{
+	uint32_t cpu, end, mask, reg;
+
+	end = min(mp_ncpus, 8);
+	for (cpu = end; cpu < MAXCPU; cpu++)
+		if (CPU_ISSET(cpu, cpus))
+			return (EINVAL);
+
+	for (mask = 0, cpu = 0; cpu < end; cpu++)
+		if (CPU_ISSET(cpu, cpus))
+			mask |= 1 << cpu;
+
+	mtx_lock_spin(&sc->mutex);
+	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);
+	mtx_unlock_spin(&sc->mutex);
+	return (0);
+}
+
+static int
+gic_irq_from_nspc(struct arm_gic_softc *sc, u_int type, u_int num, u_int *irqp)
+{
+
+	switch (type) {
+	case ARM_IRQ_NSPC_PLAIN:
+		*irqp = num;
+		return (*irqp < sc->nirqs ? 0 : EINVAL);
+
+	case ARM_IRQ_NSPC_IRQ:
+		*irqp = num + GIC_FIRST_PPI;
+		return (*irqp < sc->nirqs ? 0 : EINVAL);
+
+	case ARM_IRQ_NSPC_IPI:
+		*irqp = num + GIC_FIRST_SGI;
+		return (*irqp < GIC_LAST_SGI ? 0 : EINVAL);
+
+	default:
+		return (EINVAL);
+	}
+}
+
+static int
+gic_map_nspc(struct arm_gic_softc *sc, struct arm_irqsrc *isrc, u_int *irqp)
+{
+	int error;
+
+	error = gic_irq_from_nspc(sc, isrc->isrc_nspc_type, isrc->isrc_nspc_num,
+	    irqp);
+	if (error != 0)
+		return (error);
+	return (gic_attach_isrc(sc, isrc, *irqp));
+}
+
+#ifdef FDT
+static int
+gic_map_fdt(struct arm_gic_softc *sc, struct arm_irqsrc *isrc, u_int *irqp)
+{
+	u_int irq, tripol;
+	enum intr_trigger trig;
+	enum intr_polarity pol;
+	int error;
+
+	if (isrc->isrc_ncells == 1) {
+		irq = isrc->isrc_cells[0];
+		pol = INTR_POLARITY_CONFORM;
+		trig = INTR_TRIGGER_CONFORM;
+	} else if (isrc->isrc_ncells == 3) {
+		if (isrc->isrc_cells[0] == 0)
+			irq = isrc->isrc_cells[1] + GIC_FIRST_PPI;
+		else
+			irq = isrc->isrc_cells[1] + GIC_FIRST_SPI;
+
+		/*
+		 * 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;
+	} else
+		return (EINVAL);
+
+	if (irq >= sc->nirqs)
+		return (EINVAL);
+
+	error = gic_attach_isrc(sc, isrc, irq);
+	if (error != 0)
+		return (error);
+
+	isrc->isrc_nspc_type = ARM_IRQ_NSPC_PLAIN;
+	isrc->isrc_nspc_num = irq;
+	isrc->isrc_trig = trig;
+	isrc->isrc_pol = pol;
+
+	*irqp = irq;
+	return (0);
+}
+#endif
+
+static int
+arm_gic_register(device_t dev, struct arm_irqsrc *isrc, boolean_t *is_percpu)
+{
+	struct arm_gic_softc *sc = device_get_softc(dev);
+	u_int irq;
+	int error;
+
+	if (isrc->isrc_type == ARM_ISRCT_NAMESPACE)
+		error = gic_map_nspc(sc, isrc, &irq);
+#ifdef FDT
+	else if (isrc->isrc_type == ARM_ISRCT_FDT)
+		error = gic_map_fdt(sc, isrc, &irq);
+#endif
+	else
+		return (EINVAL);
+
+	if (error == 0)
+		*is_percpu = irq < GIC_FIRST_SPI ? TRUE : FALSE;
+	return (error);
+}
+
+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 = (u_int)isrc->isrc_data;
+
+	if (isrc->isrc_trig == INTR_TRIGGER_CONFORM)
+		isrc->isrc_trig = INTR_TRIGGER_LEVEL;
+
+	gic_config(sc, irq, isrc->isrc_trig, isrc->isrc_pol);
+
+	if (CPU_EMPTY(&isrc->isrc_cpu)) {
+		gic_irq_cpu = arm_irq_next_cpu(gic_irq_cpu, &all_cpus);
+		CPU_SETOF(gic_irq_cpu, &isrc->isrc_cpu);
+	}
+	if (gic_bind(sc, irq, &isrc->isrc_cpu) != 0)
+		CPU_ZERO(&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 = (u_int)isrc->isrc_data;
+
+	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 = (u_int)isrc->isrc_data;
+
+	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 = (u_int)isrc->isrc_data;
+
+	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)
+{
+	struct arm_gic_softc *sc = device_get_softc(dev);
+	uint32_t irq = (u_int)isrc->isrc_data;
+
+	if (irq < GIC_FIRST_SPI)
+		return (EINVAL);
+
+	if (CPU_EMPTY(&isrc->isrc_cpu)) {
+		gic_irq_cpu = arm_irq_next_cpu(gic_irq_cpu, &all_cpus);
+		CPU_SETOF(gic_irq_cpu, &isrc->isrc_cpu);
+	}
+	return (gic_bind(sc, irq, &isrc->isrc_cpu));
+}
+
+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;
+
+	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 +872,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 +945,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 +953,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 +1000,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 +1012,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 +1019,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 +1026,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 +1033,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 +1040,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 +1048,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 +1055,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 +1062,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/conf/PANDABOARD
===================================================================
--- sys/arm/conf/PANDABOARD	(revision 279685)
+++ sys/arm/conf/PANDABOARD	(working copy)
@@ -32,6 +32,8 @@
 makeoptions	MODULES_OVERRIDE=""
 makeoptions	WITHOUT_MODULES="ahc"
 
+options		ARM_INTRNG		# new interrupt framework
+
 options 	HZ=100
 options 	SCHED_ULE		# ULE scheduler
 options 	PREEMPTION		# Enable kernel thread preemption
Index: sys/arm/ti/omap4/omap4_mp.c
===================================================================
--- sys/arm/ti/omap4/omap4_mp.c	(revision 279685)
+++ sys/arm/ti/omap4/omap4_mp.c	(working copy)
@@ -41,7 +41,8 @@
 void
 platform_mp_init_secondary(void)
 {
-	arm_init_secondary_ic();
+
+	arm_pic_init_secondary();
 }
 
 void
Index: sys/arm/ti/omap4/std.omap4
===================================================================
--- sys/arm/ti/omap4/std.omap4	(revision 279685)
+++ sys/arm/ti/omap4/std.omap4	(working copy)
@@ -17,7 +17,3 @@
 options		SOC_OMAP4
 
 options		ARM_L2_PIPT
-
-options		IPI_IRQ_START=0
-options		IPI_IRQ_END=15
-
-------------- next part --------------
Index: sys/arm/conf/BEAGLEBONE
===================================================================
--- sys/arm/conf/BEAGLEBONE	(revision 279685)
+++ sys/arm/conf/BEAGLEBONE	(working copy)
@@ -33,6 +33,8 @@
 makeoptions         WITH_CTF=1
 makeoptions     MODULES_OVERRIDE="opensolaris dtrace dtrace/lockstat dtrace/profile dtrace/fbt"

+options		ARM_INTRNG
+
 options 	HZ=100
 options 	SCHED_4BSD		# 4BSD scheduler
 options 	PREEMPTION		# Enable kernel thread preemption
Index: sys/arm/ti/aintc.c
===================================================================
--- sys/arm/ti/aintc.c	(revision 279685)
+++ sys/arm/ti/aintc.c	(working copy)
@@ -26,10 +26,11 @@
  * SUCH DAMAGE.
  */

-
 #include <sys/cdefs.h>
 __FBSDID("$FreeBSD$");

+#include "opt_platform.h"
+
 #include <sys/param.h>
 #include <sys/systm.h>
 #include <sys/bus.h>
@@ -45,6 +46,10 @@
 #include <dev/ofw/ofw_bus.h>
 #include <dev/ofw/ofw_bus_subr.h>

+#ifdef ARM_INTRNG
+#include "pic_if.h"
+#endif
+
 #define INTC_REVISION		0x00
 #define INTC_SYSCONFIG		0x10
 #define INTC_SYSSTATUS		0x14
@@ -56,8 +61,17 @@
 #define INTC_ISR_SET(x)		(0x90 + ((x) * 0x20))
 #define INTC_ISR_CLEAR(x)	(0x94 + ((x) * 0x20))

+#ifdef ARM_INTRNG
+static int ti_aintc_intr(void *, struct trapframe *);
+#endif
+
 struct ti_aintc_softc {
-	device_t		sc_dev;
+	device_t		aintc_dev;
+#ifdef ARM_INTRNG
+	struct arm_irqsrc **	aintc_irqs;
+	uint32_t		aintc_nirqs;
+	void *			aintc_intrhand;
+#endif
 	struct resource *	aintc_res[3];
 	bus_space_tag_t		aintc_bst;
 	bus_space_handle_t	aintc_bsh;
@@ -66,10 +80,12 @@

 static struct resource_spec ti_aintc_spec[] = {
 	{ SYS_RES_MEMORY,	0,	RF_ACTIVE },
+#ifdef ARM_INTRNG
+	{ SYS_RES_IRQ,	  0, RF_ACTIVE | RF_OPTIONAL }, /* Parent interrupt */
+#endif
 	{ -1, 0 }
 };

-
 static struct ti_aintc_softc *ti_aintc_sc = NULL;

 #define	aintc_read_4(_sc, reg)		\
@@ -77,8 +93,217 @@
 #define	aintc_write_4(_sc, reg, val)		\
     bus_space_write_4((_sc)->aintc_bst, (_sc)->aintc_bsh, (reg), (val))

+#ifdef ARM_INTRNG
+static inline void
+ti_aintc_irq_eoi(struct ti_aintc_softc *sc)
+{

+	aintc_write_4(sc, INTC_CONTROL, 1);
+}
+
+static inline void
+ti_aintc_irq_mask(struct ti_aintc_softc *sc, u_int irq)
+{
+
+	aintc_write_4(sc, INTC_MIR_SET(irq >> 5), (1UL << (irq & 0x1F)));
+}
+
+static inline void
+ti_aintc_irq_unmask(struct ti_aintc_softc *sc, u_int irq)
+{
+
+	aintc_write_4(sc, INTC_MIR_CLEAR(irq >> 5), (1UL << (irq & 0x1F)));
+}
+
 static int
+ti_aintc_intr(void *arg, struct trapframe *tf)
+{
+	struct ti_aintc_softc *sc = arg;
+	struct arm_irqsrc *isrc;
+	uint32_t active_irq, last_irq;
+
+	/* Get active interrupt */
+	active_irq = aintc_read_4(sc, INTC_SIR_IRQ);
+
+	/* Check for spurious interrupt */
+	if ((active_irq & 0xffffff80)) {
+		device_printf(sc->aintc_dev,
+		    "Spurious interrupt detected (0x%08x)\n", active_irq);
+		aintc_write_4(sc, INTC_SIR_IRQ, 0); /* QQQ */
+		return (FILTER_HANDLED);
+	}
+
+dispatch_irq:
+	/* Check for registered source */
+	isrc = sc->aintc_irqs[active_irq];
+	if (isrc == NULL) {
+		device_printf(sc->aintc_dev, "Stray interrupt %u detected\n",
+		    active_irq);
+		ti_aintc_irq_mask(sc, active_irq);
+		ti_aintc_irq_eoi(sc);
+		goto next_irq;
+	}
+
+	/* Only level-sensitive interrupts detection is supported. */
+	arm_irq_dispatch(isrc, tf);
+
+next_irq:
+	arm_irq_memory_barrier(active_irq); /* XXX */
+	last_irq = active_irq;
+	active_irq = aintc_read_4(sc, INTC_SIR_IRQ);
+	if (((active_irq & 0xffffff80) == 0) && (last_irq != active_irq))
+		goto dispatch_irq;
+
+	return (FILTER_HANDLED);
+}
+
+static int
+ti_aintc_attach_isrc(struct ti_aintc_softc *sc, struct arm_irqsrc *isrc,
+    u_int irq)
+{
+	const char *name;
+
+	/*
+	 * 1. The link between ISRC and controller must be set atomically.
+	 * 2. Just do things only once in rare case when consumers
+	 *    of shared interrupt came here at the same moment.
+	 */
+	if (!atomic_cmpset_acq_ptr((void *)&sc->aintc_irqs[irq], 0,
+	    (intptr_t)isrc))
+		return (sc->aintc_irqs[irq] == isrc ? 0 : EEXIST);
+	isrc->isrc_data = (void *)irq;
+
+	name = device_get_nameunit(sc->aintc_dev);
+	arm_irq_set_name(isrc, "%s,s%u", name, irq);
+	return (0);
+}
+
+static int
+ti_aintc_detach_isrc(struct ti_aintc_softc *sc, struct arm_irqsrc *isrc,
+    u_int irq)
+{
+
+	if (!atomic_cmpset_acq_ptr((void *)&sc->aintc_irqs[irq],
+	    (intptr_t)isrc, 0))
+		return (EINVAL);
+	isrc->isrc_data = NULL;
+
+	arm_irq_set_name(isrc, "");
+	return (0);
+}
+
+static int
+ti_aintc_map_nspc(struct ti_aintc_softc *sc, struct arm_irqsrc *isrc)
+{
+	u_int irq;
+
+	if (isrc->isrc_nspc_type != ARM_IRQ_NSPC_PLAIN &&
+	    isrc->isrc_nspc_type != ARM_IRQ_NSPC_IRQ)
+		return (EINVAL);
+
+	irq = isrc->isrc_nspc_num;
+	if (irq >= sc->aintc_nirqs)
+		return (EINVAL);
+
+	return (ti_aintc_attach_isrc(sc, isrc, irq));
+}
+
+#ifdef FDT
+static int
+ti_aintc_map_fdt(struct ti_aintc_softc *sc, struct arm_irqsrc *isrc)
+{
+	u_int irq;
+	int error;
+
+	if (isrc->isrc_ncells != 1)
+		return (EINVAL);
+
+	irq = isrc->isrc_cells[0];
+	if (irq >= sc->aintc_nirqs)
+		return (EINVAL);
+
+	error = ti_aintc_attach_isrc(sc, isrc, irq);
+	if (error != 0)
+		return (error);
+
+	isrc->isrc_nspc_type = ARM_IRQ_NSPC_PLAIN;
+	isrc->isrc_nspc_num = irq;
+	return (0);
+}
+#endif
+
+static int
+ti_aintc_register(device_t dev, struct arm_irqsrc *isrc, boolean_t *is_percpu)
+{
+	struct ti_aintc_softc *sc = device_get_softc(dev);
+
+	*is_percpu = FALSE;
+
+	if (isrc->isrc_type == ARM_ISRCT_NAMESPACE)
+		return (ti_aintc_map_nspc(sc, isrc));
+#ifdef FDT
+	else if (isrc->isrc_type == ARM_ISRCT_FDT)
+		return (ti_aintc_map_fdt(sc, isrc));
+#endif
+	else
+		return (EINVAL);
+}
+
+static void
+ti_aintc_enable_source(device_t dev, struct arm_irqsrc *isrc)
+{
+	struct ti_aintc_softc *sc = device_get_softc(dev);
+	u_int irq = (u_int)isrc->isrc_data;
+
+	arm_irq_memory_barrier(irq);
+	ti_aintc_irq_unmask(sc, irq);
+}
+
+static void
+ti_aintc_disable_source(device_t dev, struct arm_irqsrc *isrc)
+{
+	struct ti_aintc_softc *sc = device_get_softc(dev);
+	u_int irq = (u_int)isrc->isrc_data;
+
+	ti_aintc_irq_mask(sc, irq);
+}
+
+static int
+ti_aintc_unregister(device_t dev, struct arm_irqsrc *isrc)
+{
+	struct ti_aintc_softc *sc = device_get_softc(dev);
+	u_int irq = (u_int)isrc->isrc_data;
+
+	return (ti_aintc_detach_isrc(sc, isrc, irq));
+}
+
+static void
+ti_aintc_pre_ithread(device_t dev, struct arm_irqsrc *isrc)
+{
+	struct ti_aintc_softc *sc = device_get_softc(dev);
+	u_int irq = (u_int)isrc->isrc_data;
+
+	ti_aintc_irq_mask(sc, irq);
+	ti_aintc_irq_eoi(sc);
+}
+
+static void
+ti_aintc_post_ithread(device_t dev, struct arm_irqsrc *isrc)
+{
+
+	ti_aintc_enable_source(dev, isrc);
+}
+
+static void
+ti_aintc_post_filter(device_t dev, struct arm_irqsrc *isrc)
+{
+	struct ti_aintc_softc *sc = device_get_softc(dev);
+
+	ti_aintc_irq_eoi(sc);
+}
+#endif
+
+static int
 ti_aintc_probe(device_t dev)
 {
 	if (!ofw_bus_status_okay(dev))
@@ -91,13 +316,26 @@
 	return (BUS_PROBE_DEFAULT);
 }

+static inline intptr_t
+ti_aintc_xref(device_t dev)
+{
+#ifdef FDT
+	return (OF_xref_from_node(ofw_bus_get_node(dev)));
+#else
+	return (0);
+#endif
+}
+
 static int
 ti_aintc_attach(device_t dev)
 {
 	struct		ti_aintc_softc *sc = device_get_softc(dev);
 	uint32_t x;
+#ifdef ARM_INTRNG
+	intptr_t	xref = ti_aintc_xref(dev);
+#endif

-	sc->sc_dev = dev;
+	sc->aintc_dev = dev;

 	if (ti_aintc_sc)
 		return (ENXIO);
@@ -110,6 +348,11 @@
 	sc->aintc_bst = rman_get_bustag(sc->aintc_res[0]);
 	sc->aintc_bsh = rman_get_bushandle(sc->aintc_res[0]);

+#ifdef ARM_INTRNG
+	sc->aintc_nirqs = 128;
+	sc->aintc_irqs = malloc(sc->aintc_nirqs * sizeof (*sc->aintc_irqs),
+	    M_DEVBUF, M_WAITOK | M_ZERO);
+#endif
 	ti_aintc_sc = sc;

 	x = aintc_read_4(sc, INTC_REVISION);
@@ -124,12 +367,60 @@
 	/*Set Priority Threshold */
 	aintc_write_4(sc, INTC_THRESHOLD, 0xFF);

+#ifndef ARM_INTRNG
 	return (0);
+#else
+	/*
+	 * Now, when everything is initialized, it's right time to
+	 * register interrupt controller to interrupt framefork.
+	 */
+	if (arm_pic_register(dev, xref) != 0) {
+		device_printf(dev, "could not register PIC\n");
+		goto cleanup;
+	}
+
+	if (sc->aintc_res[1] == NULL) {
+		if (arm_pic_claim_root(dev, xref, ti_aintc_intr, sc, 0) != 0) {
+			device_printf(dev, "could not set PIC as a root\n");
+			arm_pic_unregister(dev, xref);
+			goto cleanup;
+		}
+	} else {
+		if (bus_setup_intr(dev, sc->aintc_res[1], INTR_TYPE_MISC |
+		    INTR_SOLO, (void*)ti_aintc_intr, NULL, sc,
+		    &sc->aintc_intrhand)) {
+			device_printf(dev, "could not setup irq handler\n");
+			arm_pic_unregister(dev, xref);
+			goto cleanup;
+		}
+	}
+
+	return (0);
+
+cleanup:
+	/*
+	 * XXX - not implemented ti_aintc_detach() should be called !
+	 */
+	if (sc->aintc_irqs != NULL)
+		free(sc->aintc_irqs, M_DEVBUF);
+	bus_release_resources(dev, ti_aintc_spec, sc->aintc_res);
+	return(ENXIO);
+#endif
 }

 static device_method_t ti_aintc_methods[] = {
 	DEVMETHOD(device_probe,		ti_aintc_probe),
 	DEVMETHOD(device_attach,	ti_aintc_attach),
+#ifdef ARM_INTRNG
+	/* Interrupt controller interface */
+	DEVMETHOD(pic_disable_source,	ti_aintc_disable_source),
+	DEVMETHOD(pic_enable_source,	ti_aintc_enable_source),
+	DEVMETHOD(pic_post_filter,	ti_aintc_post_filter),
+	DEVMETHOD(pic_post_ithread,	ti_aintc_post_ithread),
+	DEVMETHOD(pic_pre_ithread,	ti_aintc_pre_ithread),
+	DEVMETHOD(pic_register,		ti_aintc_register),
+	DEVMETHOD(pic_unregister,	ti_aintc_unregister),
+#endif
 	{ 0, 0 }
 };

@@ -143,6 +434,7 @@

 DRIVER_MODULE(aintc, simplebus, ti_aintc_driver, ti_aintc_devclass, 0, 0);

+#ifndef ARM_INTRNG
 int
 arm_get_next_irq(int last_irq)
 {
@@ -160,7 +452,7 @@

 	/* Check for spurious interrupt */
 	if ((active_irq & 0xffffff80)) {
-		device_printf(sc->sc_dev,
+		device_printf(sc->aintc_dev,
 		    "Spurious interrupt detected (0x%08x)\n", active_irq);
 		aintc_write_4(sc, INTC_SIR_IRQ, 0);
 		return -1;
@@ -188,3 +480,4 @@
 	arm_irq_memory_barrier(nb);
 	aintc_write_4(sc, INTC_MIR_CLEAR(nb >> 5), (1UL << (nb & 0x1F)));
 }
+#endif
-------------- next part --------------
Index: sys/arm/ti/ti_gpio.c
===================================================================
--- sys/arm/ti/ti_gpio.c	(revision 279685)
+++ sys/arm/ti/ti_gpio.c	(working copy)
@@ -33,6 +33,8 @@
 #include <sys/cdefs.h>
 __FBSDID("$FreeBSD$");
 
+#include "opt_platform.h"
+
 #include <sys/param.h>
 #include <sys/systm.h>
 #include <sys/bus.h>
@@ -47,6 +49,7 @@
 
 #include <machine/bus.h>
 #include <machine/resource.h>
+#include <machine/intr.h>
 
 #include <arm/ti/ti_cpuid.h>
 #include <arm/ti/ti_gpio.h>
@@ -59,6 +62,7 @@
 #include <dev/ofw/ofw_bus.h>
 #include <dev/ofw/ofw_bus_subr.h>
 
+#include "pic_if.h"
 #include "gpio_if.h"
 #include "ti_gpio_if.h"
 
@@ -133,6 +137,22 @@
 }
 
 static u_int
+ti_intr_per_bank(void)
+{
+	switch(ti_chip()) {
+#ifdef SOC_OMAP4
+	case CHIP_OMAP_4:
+		return (OMAP4_INTR_PER_BANK);
+#endif
+#ifdef SOC_TI_AM335X
+	case CHIP_AM335X:
+		return (AM335X_INTR_PER_BANK);
+#endif
+	}
+	return (0);
+}
+
+static u_int
 ti_max_gpio_intrs(void)
 {
 	switch(ti_chip()) {
@@ -267,6 +287,28 @@
 }
 
 static inline void
+ti_gpio_cfg_intr_set(struct ti_gpio_softc *sc, unsigned int bank, uint32_t reg,
+    uint32_t mask)
+{
+	uint32_t val;
+
+	val = ti_gpio_read_4(sc, bank, reg);
+	val |= mask;
+	ti_gpio_write_4(sc, bank, reg, val);
+}
+
+static inline void
+ti_gpio_cfg_intr_clr(struct ti_gpio_softc *sc, unsigned int bank, uint32_t reg,
+    uint32_t mask)
+{
+	uint32_t val;
+
+	val = ti_gpio_read_4(sc, bank, reg);
+	val &= ~mask;
+	ti_gpio_write_4(sc, bank, reg, val);
+}
+
+static inline void
 ti_gpio_intr_clr(struct ti_gpio_softc *sc, unsigned int bank, uint32_t mask)
 {
 
@@ -496,7 +538,7 @@
 		oe &= ~TI_GPIO_MASK(pin);
 	ti_gpio_write_4(sc, TI_GPIO_BANK(pin), TI_GPIO_OE, oe);
 	TI_GPIO_UNLOCK(sc);
-	
+
 	return (0);
 }
 
@@ -561,7 +603,7 @@
 		return (EINVAL);
 
 	/*
-	 * Return data from output latch when set as output and from the 
+	 * Return data from output latch when set as output and from the
 	 * input register otherwise.
 	 */
 	TI_GPIO_LOCK(sc);
@@ -621,34 +663,52 @@
  *
  */
 static int
-ti_gpio_intr(void *arg)
+ti_gpio_intr(void *arg,  struct trapframe *tf)
 {
-	int bank_last, irq;
-	struct intr_event *event;
-	struct ti_gpio_softc *sc;
-	uint32_t reg;
+	struct ti_gpio_irq_arg *ia = arg;
+	struct ti_gpio_softc *sc = ia->ia_sc;
+	u_int reg, bit, irq, mask;
+	struct arm_irqsrc *isrc;
 
-	sc = (struct ti_gpio_softc *)arg;
-	bank_last = -1;
-	reg = 0; /* squelch bogus gcc warning */
-	for (irq = 0; irq < sc->sc_maxpin; irq++) {
+	reg = ti_gpio_intr_status(sc, ia->ia_bank);
+	if (reg == 0) {
+		/*
+		 * It could happen especially for active high interrupts
+		 * which could drop level before status is read here.
+		 * In other words, it's GPIO and interrupt sources could be
+		 * very irregular like human driven interface.
+		 */
+		device_printf(sc->sc_dev, "Spurious interrupt detected\n");
+		return (FILTER_HANDLED);
+	}
 
-		/* Read interrupt status only once for each bank. */
-		if (TI_GPIO_BANK(irq) != bank_last) {
-			reg = ti_gpio_intr_status(sc, TI_GPIO_BANK(irq));
-			bank_last = TI_GPIO_BANK(irq);
+dispatch_irq:
+	do {
+		bit = ffs(reg) - 1;
+		mask = (1 << bit);
+
+		irq = ia->ia_bank * PINS_PER_BANK + bit;
+
+		/* Check for registered source */
+		isrc = sc->sc_irqs[irq];
+		if (isrc != NULL) {
+			if (isrc->isrc_trig != INTR_TRIGGER_LEVEL)
+				 ti_gpio_intr_ack(sc, ia->ia_bank, mask);
+			arm_irq_dispatch(isrc, tf);
+		} else {
+			device_printf(sc->sc_dev,
+			    "Stray interrupt %u detected\n", irq);
+			ti_gpio_intr_clr(sc, ia->ia_bank, mask);
+			ti_gpio_intr_ack(sc, ia->ia_bank, mask);
 		}
-		if ((reg & TI_GPIO_MASK(irq)) == 0)
-			continue;
-		event = sc->sc_events[irq];
-		if (event != NULL && !TAILQ_EMPTY(&event->ie_handlers))
-			intr_event_handle(event, NULL);
-		else
-			device_printf(sc->sc_dev, "Stray IRQ %d\n", irq);
-		/* Ack the IRQ Status bit. */
-		ti_gpio_intr_ack(sc, TI_GPIO_BANK(irq), TI_GPIO_MASK(irq));
-	}
 
+		reg &= ~mask;
+	} while (reg != 0);
+
+	reg = ti_gpio_intr_status(sc, ia->ia_bank);
+	if (reg != 0)
+		goto dispatch_irq;
+
 	return (FILTER_HANDLED);
 }
 
@@ -655,20 +715,30 @@
 static int
 ti_gpio_attach_intr(device_t dev)
 {
-	int i;
-	struct ti_gpio_softc *sc;
+	u_int i, bank, idx;
+	struct ti_gpio_softc *sc = device_get_softc(dev);
 
-	sc = device_get_softc(dev);
+	/*
+	 * Prepare arguments for our interrupt routines.
+	 */
+	for (i = 0, bank = 0; bank < ti_max_gpio_banks(); bank++) {
+		for (idx = 0; idx < ti_intr_per_bank(); idx++, i++) {
+			sc->sc_irq_arg[i].ia_sc = sc;
+			sc->sc_irq_arg[i].ia_bank = bank;
+			sc->sc_irq_arg[i].ia_idx = idx;
+		}
+	}
+
+	/*
+	 * Register our interrupt routine for each of the IRQ resources.
+	 */
 	for (i = 0; i < ti_max_gpio_intrs(); i++) {
 		if (sc->sc_irq_res[i] == NULL)
 			break;
 
-		/*
-		 * Register our interrupt filter for each of the IRQ resources.
-		 */
-		if (bus_setup_intr(dev, sc->sc_irq_res[i],
-		    INTR_TYPE_MISC | INTR_MPSAFE, ti_gpio_intr, NULL, sc,
-		    &sc->sc_irq_hdl[i]) != 0) {
+		if (bus_setup_intr(dev, sc->sc_irq_res[i], INTR_TYPE_MISC |
+		    INTR_MPSAFE | INTR_SOLO, (void *)ti_gpio_intr, NULL,
+		    &sc->sc_irq_arg[i], &sc->sc_irq_hdl[i]) != 0) {
 			device_printf(dev,
 			    "WARNING: unable to register interrupt filter\n");
 			return (-1);
@@ -740,6 +810,16 @@
 	return (0);
 }
 
+static inline intptr_t
+ti_gpio_xref(device_t dev)
+{
+#ifdef FDT
+	return (OF_xref_from_node(ofw_bus_get_node(dev)));
+#else
+	return (NULL);
+#endif
+}
+
 /**
  *	ti_gpio_attach - attach function for the driver
  *	@dev: gpio device handle
@@ -786,35 +866,10 @@
 		return (ENXIO);
 	}
 
-	/* Setup the IRQ resources */
-	if (ti_gpio_attach_intr(dev) != 0) {
-		device_printf(dev, "Error: could not setup irq handlers\n");
-		ti_gpio_detach(dev);
-		return (ENXIO);
-	}
-
-	/*
-	 * Initialize the interrupt settings.  The default is active-low
-	 * interrupts.
-	 */
-	sc->sc_irq_trigger = malloc(
-	    sizeof(*sc->sc_irq_trigger) * sc->sc_maxpin,
-	    M_DEVBUF, M_WAITOK | M_ZERO);
-	sc->sc_irq_polarity = malloc(
-	    sizeof(*sc->sc_irq_polarity) * sc->sc_maxpin,
-	    M_DEVBUF, M_WAITOK | M_ZERO);
-	for (i = 0; i < sc->sc_maxpin; i++) {
-		sc->sc_irq_trigger[i] = INTR_TRIGGER_LEVEL;
-		sc->sc_irq_polarity[i] = INTR_POLARITY_LOW;
-	}
-
-	sc->sc_events = malloc(sizeof(struct intr_event *) * sc->sc_maxpin,
-	    M_DEVBUF, M_WAITOK | M_ZERO);
-
 	/* We need to go through each block and ensure the clocks are running and
 	 * the module is enabled.  It might be better to do this only when the
 	 * pins are configured which would result in less power used if the GPIO
-	 * pins weren't used ... 
+	 * pins weren't used ...
 	 */
 	for (i = 0; i < ti_max_gpio_banks(); i++) {
 		if (sc->sc_mem_res[i] != NULL) {
@@ -832,6 +887,24 @@
 		return (ENXIO);
 	}
 
+	/* Initialize stuff for interrupt controller.*/
+	sc->sc_irqs = malloc(sizeof(struct arm_irqsrc *) * sc->sc_maxpin,
+	    M_DEVBUF, M_WAITOK | M_ZERO);
+
+	/* Setup the IRQ resources */
+	if (ti_gpio_attach_intr(dev) != 0) {
+		device_printf(dev, "Error: could not setup irq handlers\n");
+		ti_gpio_detach(dev);
+		return (ENXIO);
+	}
+
+	/* Register us as interrupt controller.*/
+	if (arm_pic_register(dev, ti_gpio_xref(dev)) != 0) {
+		device_printf(dev, "Error: could not register PIC\n");
+		ti_gpio_detach(dev);
+		return (ENXIO);
+	}
+
 	return (0);
 }
 
@@ -862,14 +935,12 @@
 			ti_gpio_intr_clr(sc, i, 0xffffffff);
 	}
 	gpiobus_detach_bus(dev);
-	if (sc->sc_events)
-		free(sc->sc_events, M_DEVBUF);
-	if (sc->sc_irq_polarity)
-		free(sc->sc_irq_polarity, M_DEVBUF);
-	if (sc->sc_irq_trigger)
-		free(sc->sc_irq_trigger, M_DEVBUF);
+	/* Clean up interrupt controller stuff. */
+	ti_gpio_detach_intr(dev);
+	arm_pic_unregister(dev, ti_gpio_xref(dev));
+	if (sc->sc_irqs)
+		free(sc->sc_irqs, M_DEVBUF);
 	/* Release the memory and IRQ resources. */
-	ti_gpio_detach_intr(dev);
 	bus_release_resources(dev, ti_gpio_irq_spec, sc->sc_irq_res);
 	bus_release_resources(dev, ti_gpio_mem_spec, sc->sc_mem_res);
 	TI_GPIO_LOCK_DESTROY(sc);
@@ -877,193 +948,310 @@
 	return (0);
 }
 
-static uint32_t
-ti_gpio_intr_reg(struct ti_gpio_softc *sc, int irq)
+static inline uint32_t
+ti_gpio_irq_config_reg(enum intr_trigger trig, enum intr_polarity pol)
 {
 
-	if (ti_gpio_valid_pin(sc, irq) != 0)
-		return (0);
-
-	if (sc->sc_irq_trigger[irq] == INTR_TRIGGER_LEVEL) {
-		if (sc->sc_irq_polarity[irq] == INTR_POLARITY_LOW)
+	if (trig == INTR_TRIGGER_LEVEL) {
+		if (pol == INTR_POLARITY_LOW)
 			return (TI_GPIO_LEVELDETECT0);
-		else if (sc->sc_irq_polarity[irq] == INTR_POLARITY_HIGH)
+		else if (pol == INTR_POLARITY_HIGH)
 			return (TI_GPIO_LEVELDETECT1);
-	} else if (sc->sc_irq_trigger[irq] == INTR_TRIGGER_EDGE) {
-		if (sc->sc_irq_polarity[irq] == INTR_POLARITY_LOW)
+	} else if (trig == INTR_TRIGGER_EDGE) {
+		if (pol == INTR_POLARITY_LOW)
 			return (TI_GPIO_FALLINGDETECT);
-		else if (sc->sc_irq_polarity[irq] == INTR_POLARITY_HIGH)
+		else if (pol == INTR_POLARITY_HIGH)
 			return (TI_GPIO_RISINGDETECT);
 	}
-
 	return (0);
 }
 
 static void
-ti_gpio_mask_irq(void *source)
+ti_gpio_irq_config(struct ti_gpio_softc *sc, u_int irq, enum intr_trigger trig,
+    enum intr_polarity pol)
 {
-	int irq;
-	uint32_t reg, val;
+	uint32_t reg, bank, mask;
 
-	irq = (int)source;
-	if (ti_gpio_valid_pin(ti_gpio_sc, irq) != 0)
+	if (ti_gpio_valid_pin(sc, irq) != 0)
 		return;
 
-	TI_GPIO_LOCK(ti_gpio_sc);
-	ti_gpio_intr_clr(ti_gpio_sc, TI_GPIO_BANK(irq), TI_GPIO_MASK(irq));
-	reg = ti_gpio_intr_reg(ti_gpio_sc, irq);
-	if (reg != 0) {
-		val = ti_gpio_read_4(ti_gpio_sc, TI_GPIO_BANK(irq), reg);
-		val &= ~TI_GPIO_MASK(irq);
-		ti_gpio_write_4(ti_gpio_sc, TI_GPIO_BANK(irq), reg, val);
-	}
+	if ((reg = ti_gpio_irq_config_reg(trig, pol)) == 0)
+		return;
+
+	bank = TI_GPIO_BANK(irq);
+	mask = TI_GPIO_MASK(irq);
+
+	/*
+	 * Interrupt configuration is not allowed by interrupt framework now
+	 * if the interrupt is enabled. Thus no interrupt could be missed here
+	 * during configuration.
+	 */
+	TI_GPIO_LOCK(sc);
+	ti_gpio_cfg_intr_clr(sc, bank, TI_GPIO_LEVELDETECT0, mask);
+	ti_gpio_cfg_intr_clr(sc, bank, TI_GPIO_LEVELDETECT1, mask);
+	ti_gpio_cfg_intr_clr(sc, bank, TI_GPIO_FALLINGDETECT, mask);
+	ti_gpio_cfg_intr_clr(sc, bank, TI_GPIO_RISINGDETECT, mask);
+
+	ti_gpio_cfg_intr_set(sc, bank, reg, mask);
 	TI_GPIO_UNLOCK(ti_gpio_sc);
 }
 
 static void
-ti_gpio_unmask_irq(void *source)
+ti_gpio_irq_mask(struct ti_gpio_softc *sc, u_int irq, boolean_t do_eoi)
 {
-	int irq;
-	uint32_t reg, val;
+	u_int bank, mask;
 
-	irq = (int)source;
-	if (ti_gpio_valid_pin(ti_gpio_sc, irq) != 0)
+	if (ti_gpio_valid_pin(sc, irq) != 0)
 		return;
 
-	TI_GPIO_LOCK(ti_gpio_sc);
-	reg = ti_gpio_intr_reg(ti_gpio_sc, irq);
-	if (reg != 0) {
-		val = ti_gpio_read_4(ti_gpio_sc, TI_GPIO_BANK(irq), reg);
-		val |= TI_GPIO_MASK(irq);
-		ti_gpio_write_4(ti_gpio_sc, TI_GPIO_BANK(irq), reg, val);
-		ti_gpio_intr_set(ti_gpio_sc, TI_GPIO_BANK(irq),
-		    TI_GPIO_MASK(irq));
-	}
+	bank = TI_GPIO_BANK(irq);
+	mask = TI_GPIO_MASK(irq);
+
+	TI_GPIO_LOCK(sc);
+	ti_gpio_intr_clr(sc, bank, mask);
+	if (do_eoi)
+		ti_gpio_intr_ack(sc, bank, mask);
+	TI_GPIO_UNLOCK(sc);
+}
+
+static void
+ti_gpio_irq_unmask(struct ti_gpio_softc *sc, u_int irq)
+{
+
+	if (ti_gpio_valid_pin(sc, irq) != 0)
+		return;
+
+	TI_GPIO_LOCK(sc);
+	ti_gpio_intr_set(sc, TI_GPIO_BANK(irq), TI_GPIO_MASK(irq));
 	TI_GPIO_UNLOCK(ti_gpio_sc);
 }
 
+static void
+ti_gpio_irq_eoi(struct ti_gpio_softc *sc, u_int irq)
+{
+
+	if (ti_gpio_valid_pin(sc, irq) != 0)
+		return;
+
+	TI_GPIO_LOCK(sc);
+	ti_gpio_intr_ack(sc, TI_GPIO_BANK(irq), TI_GPIO_MASK(irq));
+	TI_GPIO_UNLOCK(ti_gpio_sc);
+}
+
 static int
-ti_gpio_activate_resource(device_t dev, device_t child, int type, int rid,
-	struct resource *res)
+ti_gpio_attach_isrc(struct ti_gpio_softc *sc, struct arm_irqsrc *isrc,
+    u_int irq)
 {
-	int pin;
 
-	if (type != SYS_RES_IRQ)
-		return (ENXIO);
+	/*
+	 * 1. The link between ISRC and controller must be set atomically.
+	 * 2. Just do things only once in rare case when consumers
+	 *    of shared interrupt came here at the same moment.
+	 */
+	TI_GPIO_LOCK(sc);
+	if (sc->sc_irqs[irq] != NULL) {
+		TI_GPIO_UNLOCK(sc);
+		return (sc->sc_irqs[irq] == isrc ? 0 : EEXIST);
+	}
+	sc->sc_irqs[irq] = isrc;
+	isrc->isrc_data = (void *)irq;
+	TI_GPIO_UNLOCK(sc);
 
-	/* Unmask the interrupt. */
-	pin = rman_get_start(res);
-	ti_gpio_unmask_irq((void *)(uintptr_t)pin);
+	arm_irq_set_name(isrc, "%s,s%u", device_get_nameunit(sc->sc_dev), irq);
+	return (0);
+}
 
+static int
+ti_gpio_detach_isrc(struct ti_gpio_softc *sc, struct arm_irqsrc *isrc,
+    u_int irq)
+{
+
+	TI_GPIO_LOCK(sc);
+	if (sc->sc_irqs[irq] != isrc) {
+		TI_GPIO_UNLOCK(sc);
+		return (sc->sc_irqs[irq] == NULL ? 0 : EINVAL);
+	}
+	sc->sc_irqs[irq] = NULL;
+	isrc->isrc_data = NULL;
+	TI_GPIO_UNLOCK(sc);
+
+	arm_irq_set_name(isrc, "");
 	return (0);
 }
 
 static int
-ti_gpio_deactivate_resource(device_t dev, device_t child, int type, int rid,
-	struct resource *res)
+ti_gpio_map_nspc(struct ti_gpio_softc *sc, struct arm_irqsrc *isrc)
 {
-	int pin;
+	u_int irq;
 
-	if (type != SYS_RES_IRQ)
-		return (ENXIO);
+	if (isrc->isrc_nspc_type != ARM_IRQ_NSPC_PLAIN &&
+	    isrc->isrc_nspc_type != ARM_IRQ_NSPC_IRQ)
+		return (EINVAL);
 
-	/* Mask the interrupt. */
-	pin = rman_get_start(res);
-	ti_gpio_mask_irq((void *)(uintptr_t)pin);
+	irq = isrc->isrc_nspc_num;
+	if (irq >= sc->sc_maxpin)
+		return (EINVAL);
 
-	return (0);
+	return (ti_gpio_attach_isrc(sc, isrc, irq));
 }
 
+#ifdef FDT
 static int
-ti_gpio_config_intr(device_t dev, int irq, enum intr_trigger trig,
-	enum intr_polarity pol)
+ti_gpio_map_fdt(struct ti_gpio_softc *sc, struct arm_irqsrc *isrc)
 {
-	struct ti_gpio_softc *sc;
-	uint32_t oldreg, reg, val;
+	u_int irq;
+	enum intr_trigger trig;
+	enum intr_polarity pol;
+	int error;
 
-	sc = device_get_softc(dev);
-	if (ti_gpio_valid_pin(sc, irq) != 0)
+	/* The first cell is the interrupt number.
+	 * The second cell is used to specify flags:
+	 *	bits[3:0] 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.
+	 */
+	if (isrc->isrc_ncells != 2)
 		return (EINVAL);
 
-	/* There is no standard trigger or polarity. */
-	if (trig == INTR_TRIGGER_CONFORM || pol == INTR_POLARITY_CONFORM)
+	irq = isrc->isrc_cells[0];
+	if (irq >= sc->sc_maxpin)
 		return (EINVAL);
 
-	TI_GPIO_LOCK(sc);
 	/*
-	 * TRM recommends add the new event before remove the old one to
-	 * avoid losing interrupts.
+	 * All interrupt types could be set for an interrupt at one moment
+	 * in this controller. At least, the combination of 'low-to-high' and
+	 * 'high-to-low' edge triggered interrupt types can make a sense.
+	 * However it's not possible due to the way how intr_trigger and
+	 * intr_polarity enums are defined.
+	 *
+	 * Anyhow, we could implemented it internally in FDT case if needed.
 	 */
-	oldreg = ti_gpio_intr_reg(sc, irq);
-	sc->sc_irq_trigger[irq] = trig;
-	sc->sc_irq_polarity[irq] = pol;
-	reg = ti_gpio_intr_reg(sc, irq);
-	if (reg != 0) {
-		/* Apply the new settings. */
-		val = ti_gpio_read_4(sc, TI_GPIO_BANK(irq), reg);
-		val |= TI_GPIO_MASK(irq);
-		ti_gpio_write_4(sc, TI_GPIO_BANK(irq), reg, val);
+	switch (isrc->isrc_cells[1]) {
+	case 1:
+		trig = INTR_TRIGGER_EDGE;
+		pol = INTR_POLARITY_HIGH;
+		break;
+	case 2:
+		trig = INTR_TRIGGER_EDGE;
+		pol = INTR_POLARITY_LOW;
+		break;
+	case 4:
+		trig = INTR_TRIGGER_LEVEL;
+		pol = INTR_POLARITY_HIGH;
+		break;
+	case 8:
+		trig = INTR_TRIGGER_LEVEL;
+		pol = INTR_POLARITY_LOW;
+		break;
+	default:
+		return (EINVAL);
 	}
-	if (reg != oldreg && oldreg != 0) {
-		/* Remove the old settings. */
-		val = ti_gpio_read_4(sc, TI_GPIO_BANK(irq), oldreg);
-		val &= ~TI_GPIO_MASK(irq);
-		ti_gpio_write_4(sc, TI_GPIO_BANK(irq), oldreg, val);
-	}
-	TI_GPIO_UNLOCK(sc);
 
+	error = ti_gpio_attach_isrc(sc, isrc, irq);
+	if (error != 0)
+		return (error);
+
+	isrc->isrc_nspc_type = ARM_IRQ_NSPC_PLAIN;
+	isrc->isrc_nspc_num = irq;
+	isrc->isrc_trig = trig;
+	isrc->isrc_pol = pol;
+
 	return (0);
 }
+#endif
 
 static int
-ti_gpio_setup_intr(device_t dev, device_t child, struct resource *ires,
-	int flags, driver_filter_t *filt, driver_intr_t *handler,
-	void *arg, void **cookiep)
+ti_gpio_pic_register(device_t dev, struct arm_irqsrc *isrc,
+    boolean_t *is_percpu)
 {
-	struct ti_gpio_softc *sc;
-	struct intr_event *event;
-	int pin, error;
+	struct ti_gpio_softc *sc = device_get_softc(dev);
 
-	sc = device_get_softc(dev);
-	pin = rman_get_start(ires);
-	if (ti_gpio_valid_pin(sc, pin) != 0)
-		panic("%s: bad pin %d", __func__, pin);
+	*is_percpu = FALSE;
 
-	event = sc->sc_events[pin];
-	if (event == NULL) {
-		error = intr_event_create(&event, (void *)(uintptr_t)pin, 0,
-		    pin, ti_gpio_mask_irq, ti_gpio_unmask_irq, NULL, NULL,
-		    "gpio%d pin%d:", device_get_unit(dev), pin);
-		if (error != 0)
-			return (error);
-		sc->sc_events[pin] = event;
-	}
-	intr_event_add_handler(event, device_get_nameunit(child), filt,
-	    handler, arg, intr_priority(flags), flags, cookiep);
+	if (isrc->isrc_type == ARM_ISRCT_NAMESPACE)
+		return (ti_gpio_map_nspc(sc, isrc));
+#ifdef FDT
+	else if (isrc->isrc_type == ARM_ISRCT_FDT)
+		return (ti_gpio_map_fdt(sc, isrc));
+#endif
+	else
+		return (EINVAL);
+}
 
-	return (0);
+static void
+ti_gpio_pic_enable_intr(device_t dev, struct arm_irqsrc *isrc)
+{
+	struct ti_gpio_softc *sc = device_get_softc(dev);
+	u_int irq = (u_int)isrc->isrc_data;
+
+	/* The default is active-low interrupts. */
+	if (isrc->isrc_trig == INTR_TRIGGER_CONFORM)
+		isrc->isrc_trig = INTR_TRIGGER_LEVEL;
+	if (isrc->isrc_pol == INTR_POLARITY_CONFORM)
+		isrc->isrc_pol = INTR_POLARITY_LOW;
+
+	ti_gpio_irq_config(sc, irq, isrc->isrc_trig, isrc->isrc_pol);
 }
 
+static void
+ti_gpio_pic_enable_source(device_t dev, struct arm_irqsrc *isrc)
+{
+	struct ti_gpio_softc *sc = device_get_softc(dev);
+	u_int irq = (u_int)isrc->isrc_data;
+
+	arm_irq_memory_barrier(irq);
+	ti_gpio_irq_unmask(sc, irq);
+}
+
+static void
+ti_gpio_pic_disable_source(device_t dev, struct arm_irqsrc *isrc)
+{
+	struct ti_gpio_softc *sc = device_get_softc(dev);
+	u_int irq = (u_int)isrc->isrc_data;
+
+	ti_gpio_irq_mask(sc, irq, FALSE);
+}
+
 static int
-ti_gpio_teardown_intr(device_t dev, device_t child, struct resource *ires,
-	void *cookie)
+ti_gpio_pic_unregister(device_t dev, struct arm_irqsrc *isrc)
 {
-	struct ti_gpio_softc *sc;
-	int pin, err;
+	struct ti_gpio_softc *sc = device_get_softc(dev);
+	u_int irq = (u_int)isrc->isrc_data;
 
-	sc = device_get_softc(dev);
-	pin = rman_get_start(ires);
-	if (ti_gpio_valid_pin(sc, pin) != 0)
-		panic("%s: bad pin %d", __func__, pin);
-	if (sc->sc_events[pin] == NULL)
-		panic("Trying to teardown unoccupied IRQ");
-	err = intr_event_remove_handler(cookie);
-	if (!err)
-		sc->sc_events[pin] = NULL;
+	return (ti_gpio_detach_isrc(sc, isrc, irq));
+}
 
-	return (err);
+static void
+ti_gpio_pic_pre_ithread(device_t dev, struct arm_irqsrc *isrc)
+{
+	struct ti_gpio_softc *sc = device_get_softc(dev);
+	u_int irq = (u_int)isrc->isrc_data;
+
+	if (isrc->isrc_trig == INTR_TRIGGER_LEVEL)
+		ti_gpio_irq_mask(sc, irq, TRUE);
+	else
+		ti_gpio_irq_mask(sc, irq, FALSE);
 }
 
+static void
+ti_gpio_pic_post_ithread(device_t dev, struct arm_irqsrc *isrc)
+{
+
+	ti_gpio_pic_enable_source(dev, isrc);
+}
+
+static void
+ti_gpio_pic_post_filter(device_t dev, struct arm_irqsrc *isrc)
+{
+	struct ti_gpio_softc *sc = device_get_softc(dev);
+	u_int irq = (u_int)isrc->isrc_data;
+
+	if (isrc->isrc_trig == INTR_TRIGGER_LEVEL)
+		ti_gpio_irq_eoi(sc, irq);
+}
+
 static phandle_t
 ti_gpio_get_node(device_t bus, device_t dev)
 {
@@ -1087,12 +1275,19 @@
 	DEVMETHOD(gpio_pin_set, ti_gpio_pin_set),
 	DEVMETHOD(gpio_pin_toggle, ti_gpio_pin_toggle),
 
+	/* Interrupt controller interface */
+	DEVMETHOD(pic_disable_source,	ti_gpio_pic_disable_source),
+	DEVMETHOD(pic_enable_intr,	ti_gpio_pic_enable_intr),
+	DEVMETHOD(pic_enable_source,	ti_gpio_pic_enable_source),
+	DEVMETHOD(pic_post_filter,	ti_gpio_pic_post_filter),
+	DEVMETHOD(pic_post_ithread,	ti_gpio_pic_post_ithread),
+	DEVMETHOD(pic_pre_ithread,	ti_gpio_pic_pre_ithread),
+	DEVMETHOD(pic_register,		ti_gpio_pic_register),
+	DEVMETHOD(pic_unregister,	ti_gpio_pic_unregister),
+
 	/* Bus interface */
-	DEVMETHOD(bus_activate_resource, ti_gpio_activate_resource),
-	DEVMETHOD(bus_deactivate_resource, ti_gpio_deactivate_resource),
-	DEVMETHOD(bus_config_intr, ti_gpio_config_intr),
-	DEVMETHOD(bus_setup_intr, ti_gpio_setup_intr),
-	DEVMETHOD(bus_teardown_intr, ti_gpio_teardown_intr),
+	DEVMETHOD(bus_setup_intr, bus_generic_setup_intr),
+	DEVMETHOD(bus_teardown_intr, bus_generic_teardown_intr),
 
 	/* ofw_bus interface */
 	DEVMETHOD(ofw_bus_get_node, ti_gpio_get_node),
@@ -1105,3 +1300,96 @@
 	ti_gpio_methods,
 	sizeof(struct ti_gpio_softc),
 };
+
+
+/* --------------------------------------------------------------------------
+ *
+ *     TEST MODULE
+ *
+ * --------------------------------------------------------------------------
+ */
+struct test_gpio_softc {
+	struct resource	*sc_irq_res;
+	void		*sc_irq_hdl;
+};
+
+static int
+test_gpio_intr(void *arg)
+{
+	//struct test_gpio_softc *sc = arg;
+
+	printf("%s: got interrupt\n", __func__);
+	return (FILTER_HANDLED);
+}
+
+static int
+test_gpio_probe(device_t dev)
+{
+	if (!ofw_bus_is_compatible(dev, "gpio-test"))
+		return (ENXIO);
+
+	device_set_desc(dev, "Test GPIO Module");
+	return(BUS_PROBE_DEFAULT);
+}
+
+static int
+test_gpio_attach(device_t dev)
+{
+	struct test_gpio_softc *sc = device_get_softc(dev);
+	int rid, err;
+
+	rid = 0;
+	sc->sc_irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid,
+	    RF_ACTIVE);
+	if (sc->sc_irq_res == NULL) {
+		device_printf(dev, "Could not allocate irq\n");
+		return (ENXIO);
+	}
+
+#if 0
+#ifdef SOC_TI_AM335X
+	err = BUS_CONFIG_INTR(dev, rman_get_start(sc->sc_irq_res),
+	    INTR_TRIGGER_LEVEL, INTR_POLARITY_HIGH);
+	if (err)
+		device_printf(dev, "Could not config irq, %d\n", err);
+#endif
+#ifdef SOC_OMAP4
+	err = BUS_CONFIG_INTR(dev, rman_get_start(sc->sc_irq_res),
+	    INTR_TRIGGER_EDGE, INTR_POLARITY_LOW);
+	if (err)
+		device_printf(dev, "Could not config irq, %d\n", err);
+#endif
+#endif
+	err = bus_setup_intr(dev, sc->sc_irq_res, INTR_TYPE_MISC | INTR_MPSAFE,
+	    test_gpio_intr, NULL, sc, &sc->sc_irq_hdl);
+	if (err) {
+		device_printf(dev, "Could not setup irq, %d\n", err);
+		bus_release_resource(dev, SYS_RES_IRQ, 0, sc->sc_irq_res);
+		return (ENXIO);
+	}
+
+	err = bus_describe_intr(dev, sc->sc_irq_res, sc->sc_irq_hdl, "XX");
+	if (err)
+		device_printf(dev, "Could not describe irq, %d\n", err);
+
+	return (0);
+}
+
+static device_method_t test_gpio_methods[] = {
+	/* bus interface */
+	DEVMETHOD(device_probe, test_gpio_probe),
+	DEVMETHOD(device_attach, test_gpio_attach),
+
+	{0, 0},
+};
+
+driver_t test_gpio_driver = {
+	"test_gpio",
+	test_gpio_methods,
+	sizeof(struct test_gpio_softc),
+};
+
+static devclass_t test_gpio_devclass;
+
+DRIVER_MODULE(test_gpio, simplebus, test_gpio_driver, test_gpio_devclass,
+    0, 0);
Index: sys/arm/ti/ti_gpio.h
===================================================================
--- sys/arm/ti/ti_gpio.h	(revision 279685)
+++ sys/arm/ti/ti_gpio.h	(working copy)
@@ -39,6 +39,12 @@
  */
 #define	MAX_GPIO_INTRS			8
 
+struct ti_gpio_irq_arg {
+	struct ti_gpio_softc	*ia_sc;
+	uint16_t		ia_bank;	/* GPIO bank */
+	uint16_t		ia_idx;		/* irq on the bank */
+};
+
 /**
  *	Structure that stores the driver context.
  *
@@ -48,10 +54,6 @@
 	device_t		sc_dev;
 	device_t		sc_busdev;
 
-	/* Interrupt trigger type and level. */
-	enum intr_trigger	*sc_irq_trigger;
-	enum intr_polarity	*sc_irq_polarity;
-
 	int			sc_maxpin;
 	struct mtx		sc_mtx;
 
@@ -63,10 +65,11 @@
 	struct resource		*sc_mem_res[MAX_GPIO_BANKS];
 	struct resource		*sc_irq_res[MAX_GPIO_INTRS];
 
-	/* Interrupt events. */
-	struct intr_event	**sc_events;
+	/* Interrupt sources registered in interrupt controller. */
+	struct arm_irqsrc	**sc_irqs;
 
-	/* The handle for the register IRQ handlers. */
+	/* The handle and argument for the register IRQ handlers. */
+	struct ti_gpio_irq_arg	sc_irq_arg[MAX_GPIO_INTRS];
 	void			*sc_irq_hdl[MAX_GPIO_INTRS];
 };
 
Index: sys/boot/fdt/dts/arm/am335x.dtsi
===================================================================
--- sys/boot/fdt/dts/arm/am335x.dtsi	(revision 279685)
+++ sys/boot/fdt/dts/arm/am335x.dtsi	(working copy)
@@ -22,7 +22,7 @@
  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  * SUCH DAMAGE.
- * 
+ *
  * $FreeBSD$
  */
 
@@ -100,7 +100,7 @@
 			interrupts = <91>;
 			interrupt-parent = <&AINTC>;
 		};
- 		
+
 		GPIO: gpio {
 			#gpio-cells = <3>;
 			compatible = "ti,gpio";
@@ -112,7 +112,7 @@
 			interrupts = < 96 97 98 99 32 33 62 63 >;
 			interrupt-parent = <&AINTC>;
 			interrupt-controller;
-			#interrupt-cells = <1>;
+			#interrupt-cells = <2>;
 		};
 
 		uart0: serial at 44E09000 {
@@ -124,7 +124,7 @@
  			clock-frequency = < 48000000 >;
 			uart-device-id = < 0 >;
  		};
- 		
+
  		uart1: serial at 48022000 {
  			compatible = "ti,ns16550";
  			reg = <0x48022000 0x1000>;
@@ -131,11 +131,11 @@
  			reg-shift = <2>;
  			interrupts = < 73 >;
  			interrupt-parent = <&AINTC>;
- 			clock-frequency = < 48000000 >; 
+ 			clock-frequency = < 48000000 >;
 			uart-device-id = < 1 >;
 			status = "disabled";
  		};
- 		
+
  		uart2: serial at 48024000 {
  			compatible = "ti,ns16550";
  			reg = <0x48024000 0x1000>;
@@ -142,11 +142,11 @@
  			reg-shift = <2>;
  			interrupts = < 74 >;
  			interrupt-parent = <&AINTC>;
- 			clock-frequency = < 48000000 >; 
+ 			clock-frequency = < 48000000 >;
 			uart-device-id = < 2 >;
 			status = "disabled";
  		};
- 		
+
  		uart3: serial at 481a6000 {
  			compatible = "ti,ns16550";
  			reg = <0x481A6000 0x1000>;
@@ -153,11 +153,11 @@
  			reg-shift = <2>;
  			interrupts = < 44 >;
  			interrupt-parent = <&AINTC>;
- 			clock-frequency = < 48000000 >; 
+ 			clock-frequency = < 48000000 >;
 			uart-device-id = < 3 >;
 			status = "disabled";
  		};
- 
+
  		uart4: serial at 481a8000 {
  			compatible = "ti,ns16550";
  			reg = <0x481A8000 0x1000>;
@@ -164,11 +164,11 @@
  			reg-shift = <2>;
  			interrupts = < 45 >;
  			interrupt-parent = <&AINTC>;
- 			clock-frequency = < 48000000 >; 
+ 			clock-frequency = < 48000000 >;
 			uart-device-id = < 4 >;
 			status = "disabled";
  		};
- 
+
  		uart5: serial at 481aa000 {
  			compatible = "ti,ns16550";
  			reg = <0x481AA000 0x1000>;
@@ -175,7 +175,7 @@
  			reg-shift = <2>;
  			interrupts = < 46 >;
  			interrupt-parent = <&AINTC>;
- 			clock-frequency = < 48000000 >; 
+ 			clock-frequency = < 48000000 >;
 			uart-device-id = < 5 >;
 			status = "disabled";
   		};
Index: sys/boot/fdt/dts/arm/beaglebone-black.dts
===================================================================
--- sys/boot/fdt/dts/arm/beaglebone-black.dts	(revision 279685)
+++ sys/boot/fdt/dts/arm/beaglebone-black.dts	(working copy)
@@ -22,7 +22,7 @@
  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  * SUCH DAMAGE.
- * 
+ *
  * $FreeBSD$
  */
 
@@ -110,14 +110,14 @@
 				"GPMC_BEn1", "gpio1_28", "input_pulldown",
 				"GPMC_CSn0", "gpio1_29", "input_pulldown",
 				"GPMC_CLK", "gpio2_1", "input_pulldown",
-				"LCD_DATA0", "gpio2_6", "input_pulldown", 
-				"LCD_DATA1", "gpio2_7", "input_pulldown", 
-				"LCD_DATA2", "gpio2_8", "input_pulldown", 
-				"LCD_DATA3", "gpio2_9", "input_pulldown", 
-				"LCD_DATA4", "gpio2_10", "input_pulldown", 
-				"LCD_DATA5", "gpio2_11", "input_pulldown", 
-				"LCD_DATA6", "gpio2_12", "input_pulldown", 
-				"LCD_DATA7", "gpio2_13", "input_pulldown", 
+				"LCD_DATA0", "gpio2_6", "input_pulldown",
+				"LCD_DATA1", "gpio2_7", "input_pulldown",
+				"LCD_DATA2", "gpio2_8", "input_pulldown",
+				"LCD_DATA3", "gpio2_9", "input_pulldown",
+				"LCD_DATA4", "gpio2_10", "input_pulldown",
+				"LCD_DATA5", "gpio2_11", "input_pulldown",
+				"LCD_DATA6", "gpio2_12", "input_pulldown",
+				"LCD_DATA7", "gpio2_13", "input_pulldown",
 				"LCD_VSYNC", "gpio2_22", "input_pulldown",
 				"LCD_HSYNC", "gpio2_23", "input_pulldown",
 				"LCD_PCLK", "gpio2_24", "input_pulldown",
@@ -144,7 +144,7 @@
 			status = "okay";
 			non-removable;
 		};
- 
+
 		i2c at 44e0b000 {
 			pmic at 48 {
 				compatible = "ti,am335x-pmic";
@@ -151,6 +151,13 @@
 				reg = <0x48>;
 			};
 		};
+
+		test at 0 {
+			compatible = "gpio-test";
+			reg = <0x0>;
+			interrupt-parent = <&GPIO>;
+			interrupts = <60 0x4>;
+		};
 	};
 
 	leds {
Index: sys/boot/fdt/dts/arm/pandaboard.dts
===================================================================
--- sys/boot/fdt/dts/arm/pandaboard.dts	(revision 279685)
+++ sys/boot/fdt/dts/arm/pandaboard.dts	(working copy)
@@ -24,7 +24,7 @@
  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  * SUCH DAMAGE.
- * 
+ *
  * $FreeBSD$
  */
 
@@ -70,7 +70,7 @@
 			    0x4a004000 0x1000
 			    0x4a008000 0x8000>;
 		};
-				     
+
 		pl310 at 48242000 {
 			compatible = "arm,pl310";
 			reg = < 0x48242000 0x1000 >;
@@ -101,7 +101,7 @@
 			compatible = "ti,scm";
 			reg =	< 0x4a100000 0x1000 >;
 			/* Set of triplets < padname, muxname, padstate> */
-			scm-pad-config = 
+			scm-pad-config =
 				"ag19", "usbb1_ulpiphy_stp", "output",
 				"ae18", "usbb1_ulpiphy_clk", "input_pulldown",
 				"af19", "usbb1_ulpiphy_dir", "input_pulldown",
@@ -113,7 +113,8 @@
 				"ah17", "usbb1_ulpiphy_dat4", "input_pulldown",
 				"ae16", "usbb1_ulpiphy_dat5", "input_pulldown",
 				"af16", "usbb1_ulpiphy_dat6", "input_pulldown",
-				"ag16", "usbb1_ulpiphy_dat7", "input_pulldown";
+				"ag16", "usbb1_ulpiphy_dat7", "input_pulldown",
+				"ac28", "gpio_113", "input_pullup";
 		};
 
 		GPIO: gpio {
@@ -128,11 +129,13 @@
 				0x4805d000 0x1000>;
 			interrupts = <61 62 63 64 65 66>;
 			interrupt-parent = <&GIC>;
+			interrupt-controller;
+			#interrupt-cells = <2>;
 		};
 
 		ehci {
 			compatible = "ti,usb-ehci", "usb-ehci";
-			/* 
+			/*
 			 * USB port PHY configuration is a tuple: <mode, reset, gpio_pin>
 			 * mode is one of the following values:
 			 *   0 - unknown
@@ -140,7 +143,7 @@
 			 *   2 - TLL
 			 *   3 - HSIC
 			 *
-			 * reset indicates (if non-zero) if port reset is required 
+			 * reset indicates (if non-zero) if port reset is required
 			 * gpio_pin - GPIO pin that is used to perform reset
 			 */
 			phy-config = <  1 0 0
@@ -177,8 +180,15 @@
 			non-removable; /* XXX need real solution */
 		};
 
+		test at 0 {
+			compatible = "gpio-test";
+			reg = <0x0>;
+			interrupt-parent = <&GPIO>;
+			interrupts = <113 0x2>;
 	};
 
+	};
+
 	chosen {
 		stdin = "uart3";
 		stdout = "uart3";
-------------- next part --------------
Index: sys/sys/interrupt.h
===================================================================
--- sys/sys/interrupt.h	(revision 279685)
+++ sys/sys/interrupt.h	(working copy)
@@ -105,7 +105,7 @@
 	TAILQ_ENTRY(intr_event) ie_list;
 	TAILQ_HEAD(, intr_handler) ie_handlers; /* Interrupt handlers. */
 	char		ie_name[MAXCOMLEN + 1]; /* Individual event name. */
-	char		ie_fullname[MAXCOMLEN + 1];
+	char		ie_fullname[2*MAXCOMLEN + 1];
 	struct mtx	ie_lock;
 	void		*ie_source;	/* Cookie used by MD code. */
 	struct intr_thread *ie_thread;	/* Thread we are connected to. */


More information about the freebsd-arm mailing list