Subject: Re: structuring interrupt machine in ppoea-renovation
To: Toru Nishimura <locore64@alkyltechnology.com>
From: Chris Gilbert <chris@dokein.co.uk>
List: port-arm
Date: 05/11/2007 00:56:15
This is a multi-part message in MIME format.
--------------090602050608010106090001
Content-Type: text/plain; charset=ISO-8859-1; format=flowed
Content-Transfer-Encoding: 7bit

Toru Nishimura wrote:
> Attention for cat owners,
(this is also relevant for netwinder, which has the same footbridge & 
ISA issues)

> There is much interesting activities is going in "ppoea-renovation"
> branch and your cats could be benefited by feeding the idea from
> it.
>
> The HW has slots for PCI and ISA which are to mimic standard
> PC-alike configuration with ALi1543C and 21285 footbridge.
> Constructing the whole interrupt machinery has been a hard
> practice since mixing different breed interrupt HW designs into a single
> working one would bring forehead-bang'ng-at-neary-by-wall burdens.
> To solve the issue Macheal Lorenz and Tim Rightnour made a single,
> unified and extensible construct for PPC to house multiple interrupt HW
> sources;
>
> - can cope well with interrupt source > 32.
> - can add and cascade many interrupt HW sources.
> - can do the smart things for spl(9) and softintr(9), e.g., multiple NICs
>  populated separately in PCI/ISA slots (and possiblely ones build
>  inside SoC core) can be handled in the right fashion by IPL_NET.

On an initial look over the code, I can see some areas that have 
improved.  Mainly around pic handling.  Certainly the loop in 
pic_do_pending_int does the same as the cats one, it works through irqs 
in bitwise order changing the spl to match, so if BIT 1 is IPL_NET, bit 
2 is IPL_BIO and bit 3 is IPL_NET, bit 3 risks being starved  (note that 
I'm not familiar with the powerpc code so may not be understanding it 
fully)  (or does this relate

Also the code looks to regularly write and set the irq masks in the 
hardware, which is something we try to avoid on arm, as any time you 
touch hardware is slow, especially if it's in every spl(9) routine.

I've separately been working on changing the interrupt mechanism around 
for cats and acorn32.   I mentioned this recently on the port-arm list.  
Attached is my latest diff, I've not looked at it for a while as 
diagnostic kernels won't go multi-user dues to locking bugs in -current, 
which are fixed in the yamt-idlelwp branch which I had expected to have 
been merged by now.

I've rather changed how it views interrupts, rather than rely on spl 
masks etc, there are linked lists of interrupts handlers for each IPL 
level, and an ipls_pending variable.  An interrupt is masked when it 
fires, and cleared when it's handled.  I never change the hardware mask 
unless it's necessary.

I've also moved the initialisation of irq handling into arm common code 
(arm_intr_init) and hardware specific code (footbridge_intr_init)  The 
hardware code registers a couple of functions with the arm_intr code, 
and then maps the common interrupt routines to the arm_intr versions.

To handle interrupts in ipl order is a two stage process.  When 
interrupts occur in hardware their interrupt handler is flagged as 
pending, as is ipl_pending bit (and the interrupt bit masked in 
hardware)  Then the code looks at the bits in ipl_pending, and handles 
each list of irq handlers in priority order.

An spl change just updates a software value, if the spl drops and there 
are ipls pending, we jump into the interrupt handling code and deal with 
them. This makes spl(9) functions very small and sweet, see 
arm_intr_splx in the attached diff.

I also made soft interrupts another source of interrupts, so they're 
just seen as another interrupt handler in the ipl lists, they get no 
special treatment and are handled at the appropriate point (eg 
IPL_SOFTNET will be handled before IPL_BIO, which isn't always the case 
at the moment)

For cascading irqs I was thinking of an IPL_IRQBRIDGE just below 
IPL_HIGH, and set up and futher IRQs that need to be handled.  However 
I'd not read enough about the 8259 pic to understand how it really works 
so I could hook it in that way.  This could also apply for anything that 
bridges to other devices.  eg can USB detect which device interrupted it 
and ignore it, eg if we're able to detect the device wanting attention 
is a speaker (IPL_AUDIO), it can be handled at the correct level

However it does have a flaw, which is that it will take longer to 
dispatch an irq than it used to (mainly due to scanning the hardware 
irqs and then working through the ipls in order), which is something 
that I have worked on optimising (eg arm_intr_fls)  but probably has 
room for further improvement.   In it's favour is that spl(9) routines 
are so much smaller and quicker, that my GENERIC cats kernel lost 300KB 
by changing to this code.

I'd like to think other arm hardware can switch to it quickly, eg ep93xx 
springs to mind as being able to switch over quickly, as it's in the 
position of having 2 banks of 32 irqs.

Obviously I've a bias towards the code I've been working on and familiar 
with, but my initial feeling is that ppc code may have a gain for 
handling the ISA pics better, which I could see being useful, but I'm 
not sure about the rest of it (and this is possibly lack of familiarity 
with it)

> PLS take a glance at powerpc/pic/ directory and how prep/macppc
> and other less-complicated ppc ports ultilize the construct, and learn
> the way how it can be modelled for cats.
>
> I'm planning to build 2nd-stage netboot loader to make cats possible
> to do DHCP/NFS netboot.  The goal is to provide true ELF kernel
> loading with DDB symbol table and no size limit.

Is tftp not enough?  I think that ABLE ELF kernels should be able to use 
symbols from the ELF image, I've not looked closely enough to see if the 
ABLE bootloader includes or excludes the symbol table when loading the 
kernel.

Thanks,
Chris


--------------090602050608010106090001
Content-Type: text/plain;
 name="arm.generic.irq.diff"
Content-Transfer-Encoding: 7bit
Content-Disposition: inline;
 filename="arm.generic.irq.diff"

Index: arm/arm/soft_irqhandler.c
===================================================================
RCS file: arm/arm/soft_irqhandler.c
diff -N arm/arm/soft_irqhandler.c
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ arm/arm/soft_irqhandler.c	10 May 2007 23:11:42 -0000
@@ -0,0 +1,238 @@
+/*	$NetBSD: softintr.c,v 1.4 2007/02/19 21:45:23 matt Exp $	*/
+
+/*
+ * Copyright (c) 2001 Wasabi Systems, Inc.
+ * All rights reserved.
+ *
+ * Written by Jason R. Thorpe for Wasabi Systems, Inc.
+ *
+ * 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 for the NetBSD Project by
+ *	Wasabi Systems, Inc.
+ * 4. The name of Wasabi Systems, Inc. may not be used to endorse
+ *    or promote products derived from this software without specific prior
+ *    written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY WASABI SYSTEMS, INC. ``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 WASABI SYSTEMS, INC
+ * 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>
+__KERNEL_RCSID(0, "$NetBSD: softintr.c,v 1.4 2007/02/19 21:45:23 matt Exp $");
+
+#include <sys/param.h>
+#include <sys/malloc.h>
+
+/* XXX Network interrupts should be converted to new softintrs. */
+#include <net/netisr.h>
+
+#include <uvm/uvm_extern.h>
+
+#include <machine/intr.h>
+
+struct soft_intrq soft_intrq[SI_NQUEUES];
+
+struct soft_intrhand *softnet_intrhand;
+
+void	netintr(void);
+
+static irqhandler_t softintr_irq_handlers[SI_NQUEUES];
+
+static const u_int32_t si_to_ipl[SI_NQUEUES] = {
+	IPL_SOFT,			/* SI_SOFT */
+	IPL_SOFTCLOCK,		/* SI_SOFTCLOCK */
+	IPL_SOFTNET,		/* SI_SOFTNET */
+	IPL_SOFTSERIAL,		/* SI_SOFTSERIAL */
+};
+
+static irqgroup_t softintr_irq_group;
+
+static const char * const softintr_names[] = SI_QUEUENAMES;
+
+static void softintr_set_irq_mask(irq_hardware_cookie_t cookie, uint32_t intr_enabled) { }
+static uint32_t softintr_irq_status(irq_hardware_cookie_t cookie) {	return 0; }
+
+static int softintr_dispatch(void *);
+
+
+/*
+ * softintr_init:
+ *
+ *	Initialize the software interrupt system.
+ */
+void
+softintr_init(void)
+{
+	int i;
+	softintr_irq_group = arm_intr_register_irq_provider("softintr", SI_NQUEUES,
+		   	softintr_set_irq_mask,
+			softintr_irq_status,
+			NULL, false);
+
+	for (i = 0; i < SI_NQUEUES; i++)
+	{
+		struct soft_intrq *siq = &soft_intrq[i];
+		TAILQ_INIT(&siq->siq_list);
+		evcnt_attach_dynamic(&siq->siq_evcnt, EVCNT_TYPE_INTR,
+		    NULL, "soft", softintr_names[i]);
+		siq->siq_si = i;
+
+		softintr_irq_handlers[i] = arm_intr_claim(softintr_irq_group, i, si_to_ipl[i],
+				softintr_names[i], softintr_dispatch,
+				&soft_intrq[i]);
+		if (softintr_irq_handlers[i] == NULL)
+		{
+			panic("Unable to allocate soft irq handler");
+		}
+	}
+	
+	/* XXX Establish legacy software interrupt handlers. */
+	softnet_intrhand = softintr_establish(IPL_SOFTNET,
+	    (void (*)(void *))netintr, NULL);
+
+	assert(softnet_intrhand != NULL);
+}
+
+/*
+ * softintr_dispatch:
+ *
+ *	Process pending software interrupts on the specified queue.
+ *
+ *	NOTE: We must already be at the correct interrupt priority level.
+ */
+static int
+softintr_dispatch(void *arg)
+{
+	struct soft_intrq *siq = arg;
+	struct soft_intrhand *sih;
+	int oldirqstate;
+
+	siq->siq_evcnt.ev_count++;
+	for (;;) {
+		oldirqstate = disable_interrupts(I32_bit);
+		sih = TAILQ_FIRST(&siq->siq_list);
+		if (sih == NULL) {
+			restore_interrupts(oldirqstate);
+			break;
+		}
+
+		TAILQ_REMOVE(&siq->siq_list, sih, sih_list);
+		sih->sih_pending = 0;
+
+		uvmexp.softs++;
+
+		restore_interrupts(oldirqstate);
+
+		(*sih->sih_func)(sih->sih_arg);
+	}
+	return 1;
+}
+
+
+/*
+ * softintr_establish:		[interface]
+ *
+ *	Register a software interrupt handler.
+ */
+void *
+softintr_establish(int ipl, void (*func)(void *), void *arg)
+{
+	struct soft_intrhand *sih;
+	int si;
+
+	switch (ipl) {
+	case IPL_SOFT:
+		si = SI_SOFT;
+		break;
+
+	case IPL_SOFTCLOCK:
+		si = SI_SOFTCLOCK;
+		break;
+
+	case IPL_SOFTNET:
+		si = SI_SOFTNET;
+		break;
+
+	case IPL_SOFTSERIAL:
+		si = SI_SOFTSERIAL;
+		break;
+
+	default:
+		panic("softintr_establish: unknown soft IPL %d", ipl);
+	}
+
+	sih = malloc(sizeof(*sih), M_DEVBUF, M_NOWAIT);
+	if (__predict_true(sih != NULL)) {
+		sih->sih_func = func;
+		sih->sih_arg = arg;
+		sih->sih_siq = &soft_intrq[si];
+		sih->sih_pending = 0;
+	}
+	return (sih);
+}
+
+/*
+ * softintr_disestablish:	[interface]
+ *
+ *	Unregister a software interrupt handler.
+ */
+void
+softintr_disestablish(void *arg)
+{
+	struct soft_intrhand *sih = arg;
+	struct soft_intrq *siq = sih->sih_siq;
+	int oldirqstate;
+
+	oldirqstate = disable_interrupts(I32_bit);
+	if (sih->sih_pending) {
+		TAILQ_REMOVE(&siq->siq_list, sih, sih_list);
+		sih->sih_pending = 0;
+	}
+	restore_interrupts(oldirqstate);
+}
+
+void
+_setsoftintr(int si)
+{
+	return arm_intr_schedule(softintr_irq_group, softintr_irq_handlers[si]);
+}
+
+
+void
+netintr(void)
+{
+	int n, s;
+
+	s = splhigh();
+	n = netisr;
+	netisr = 0;
+	splx(s);
+
+#define	DONETISR(bit, fn)						\
+	do {								\
+		if (n & (1 << (bit)))					\
+			fn();						\
+	} while (/*CONSTCOND*/0)
+
+#include <net/netisr_dispatch.h>
+
+#undef DONETISR
+}
Index: arm/arm32/arm_irqhandler.c
===================================================================
RCS file: arm/arm32/arm_irqhandler.c
diff -N arm/arm32/arm_irqhandler.c
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ arm/arm32/arm_irqhandler.c	10 May 2007 23:11:43 -0000
@@ -0,0 +1,641 @@
+/*	$NetBSD: footbridge_irqhandler.c,v 1.17 2006/12/25 18:39:48 wiz Exp $	*/
+
+/*
+ * Copyright (c) 2001, 2002 Wasabi Systems, Inc.
+ * All rights reserved.
+ *
+ * Written by Jason R. Thorpe for Wasabi Systems, Inc.
+ *
+ * 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 for the NetBSD Project by
+ *	Wasabi Systems, Inc.
+ * 4. The name of Wasabi Systems, Inc. may not be used to endorse
+ *    or promote products derived from this software without specific prior
+ *    written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY WASABI SYSTEMS, INC. ``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 WASABI SYSTEMS, INC
+ * 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.
+ */
+
+#ifndef ARM_SPL_NOINLINE
+#define	ARM_SPL_NOINLINE
+#endif
+
+#include <sys/cdefs.h>
+__KERNEL_RCSID(0,"$NetBSD: footbridge_irqhandler.c,v 1.17 2006/12/25 18:39:48 wiz Exp $");
+
+#include "opt_irqstats.h"
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/malloc.h>
+#include <uvm/uvm_extern.h>
+
+#include <arm/arm_intr.h>
+#include <machine/intr.h>
+#include <machine/cpu.h>
+
+/*
+ * This generic code provides for multiple interrupt sources all merging into one place
+ * eg pci, isa and soft interrupts.  They're all treated as an irq source,
+ * each registers with a few simple functions
+ */
+
+
+/* ipl queues */
+static struct iplq arm_iplq[NIPL];
+
+
+TAILQ_HEAD(, irq_group) irq_groups_list;	/* groups list */
+
+static void arm_intr_run_handlers_for_ipl(int ipl_level, struct clockframe *frame);
+static int arm_intr_fls(uint32_t n);
+
+void arm_intr_dispatch(struct clockframe *frame);
+
+void arm_do_pending_soft_intr(void);
+
+const struct evcnt *
+arm_intr_evcnt(irqgroup_t cookie, int irq)
+{
+	struct irq_group *irqs = cookie;
+	if (irq < 0 || irq > irqs->nirqs)
+		return NULL;
+	else
+		return &(irqs->irqs[irq].iq_ev);
+}
+
+/* XXX should this be a define so it's always inlined? */
+static inline void
+arm_intr_set_hardware_mask(struct irq_group *irqs)
+{
+	irqs->set_irq_hardware_mask(irqs->irq_hardware_cookie, irqs->intr_enabled & (irqs->intr_soft_enabled));
+}
+
+static inline void
+arm_intr_enable_irq(struct irq_group *irqs, int irq)
+{
+	irqs->intr_enabled |= (1U << irq);
+	arm_intr_set_hardware_mask(irqs);
+}
+
+static inline void
+arm_intr_disable_irq(struct irq_group *irqs, int irq)
+{
+	irqs->intr_enabled &= ~(1U << irq);
+	arm_intr_set_hardware_mask(irqs);
+}
+
+void
+arm_intr_soft_enable_irq(irqgroup_t cookie, int irq)
+{
+	struct irq_group *irqs = cookie;	
+	uint32_t oldirqstate;
+
+	oldirqstate = disable_interrupts(I32_bit);
+
+	irqs->intr_soft_enabled |= (1U << irq);
+	arm_intr_set_hardware_mask(irqs);
+	
+	restore_interrupts(oldirqstate);
+}
+
+void
+arm_intr_soft_disable_irq(irqgroup_t cookie, int irq)
+{
+	struct irq_group *irqs = cookie;
+	uint32_t oldirqstate;
+
+	oldirqstate = disable_interrupts(I32_bit);
+
+	irqs->intr_soft_enabled &= ~(1U << irq);
+	arm_intr_set_hardware_mask(irqs);
+	
+	restore_interrupts(oldirqstate);
+}
+
+/* 
+ * fake up a frame, and handle pending interrupts
+ * 
+ * This should be quicker than turning on interrupts, and going the normal route
+ */
+void arm_intr_splx_lifter(int newspl)
+{
+	int oldirqstate;
+	int oldintrdepth;
+	struct clockframe frame;
+	oldirqstate	= disable_interrupts(I32_bit);
+
+	/* fake up the clockframe parts we take interest in */
+	frame.cf_if.if_spsr = PSR_MODE & PSR_SVC32_MODE;
+	frame.cf_if.if_pc = (unsigned int)&arm_intr_splx_lifter;
+	oldintrdepth = current_intr_depth++;
+
+	arm_intr_process_pending_ipls(&frame, newspl);
+
+	current_intr_depth = oldintrdepth;
+	restore_interrupts(oldirqstate);
+}
+
+/*
+ * NOTE: This routine must be called with interrupts disabled in the CPSR.
+ */
+static void
+arm_intr_calculate_masks(struct irq_group *irqs)
+{
+	struct intrq *iq;
+	struct intrhand *ih;
+	int irq;
+
+	/* First, figure out which IPLs each IRQ has. */
+	for (irq = 0; irq < irqs->nirqs; irq++) {
+		iq = &(irqs->irqs[irq]);
+		arm_intr_disable_irq(irqs, irq);
+		
+		TAILQ_REMOVE(&(arm_iplq[iq->iq_ipl].ipl_list), iq, ipl_list);
+
+		iq->iq_ipl = IPL_NONE;
+		for (ih = TAILQ_FIRST(&iq->iq_list); ih != NULL;
+		     ih = TAILQ_NEXT(ih, ih_list))
+		{
+			/* check if this is the highest ipl for this irq */
+			if (iq->iq_ipl < ih->ih_ipl)
+				iq->iq_ipl = ih->ih_ipl;
+		}
+	}
+
+	/*
+	 * flag enable all active lines in the hardware
+	 */
+	for (irq = 0; irq < irqs->nirqs; irq++) {
+		iq = &(irqs->irqs[irq]);
+		TAILQ_INSERT_TAIL(&(arm_iplq[iq->iq_ipl].ipl_list), iq, ipl_list);
+		if (TAILQ_FIRST(&iq->iq_list) != NULL)
+		{
+			arm_intr_enable_irq(irqs, irq);
+		}
+	}
+}
+
+int
+_splraise(int ipl)
+{
+    return (arm_intr_splraise(ipl));
+}
+
+void
+splx(int new)
+{
+    arm_intr_splx(new);
+}
+
+int
+_spllower(int ipl)
+{
+    return (arm_intr_spllower(ipl));
+}
+
+#if 0
+/* called from splhigh, so the matching splx will set the interrupt up.*/
+void
+_setsoftintr(int si)
+{
+	int oldirqstate;
+
+	oldirqstate = disable_interrupts(I32_bit);
+	arm_soft_intr_pending |= SI_TO_BIT(si);
+	restore_interrupts(oldirqstate);
+
+	/* Process unmasked pending soft interrupts. */
+	if (arm_soft_intr_pending & ~arm_simask[current_spl_level])
+		arm_do_pending_soft_intr();
+}
+#endif
+void
+arm_intr_init(void)
+{
+	struct iplq *ipl;
+	int i;
+
+	/*
+	 * clear down the global items, once irqs are registered we enable irqs
+	 * doing so now would cause issues as this code couldn't disable any of them
+	 */
+	current_ipl_level = NIPL-1;
+	ipls_pending = 0;
+	
+	for (i = 0; i < NIPL; i++) {
+		ipl = &arm_iplq[i];
+		TAILQ_INIT(&ipl->ipl_list);
+
+		/* XXX: this should be a hard coded array */
+		sprintf(ipl->ipl_name, "ipl %d", i);
+		evcnt_attach_dynamic(&ipl->ipl_ev, EVCNT_TYPE_MISC,
+		    NULL, "arm_intr", ipl->ipl_name);
+	}
+	TAILQ_INIT(&irq_groups_list);	/* groups list */
+	disable_interrupts(I32_bit|F32_bit);
+
+}
+
+void
+arm_intr_enable_irqs(void)
+{
+	enable_interrupts(I32_bit);
+	spl0();
+}
+
+
+irqgroup_t
+arm_intr_register_irq_provider(const char *name, int nirqs, 
+		void (*set_irq_hardware_mask)(irq_hardware_cookie_t, uint32_t intr_enabled),
+		uint32_t (*fetch_irq_hardware_status)(irq_hardware_cookie_t),
+		irq_hardware_cookie_t cookie, bool primary_irq_group)
+{
+	struct irq_group *irqg;
+	int i;
+	
+	/* grab all the memory we need */
+	irqg = malloc(sizeof(*irqg), M_DEVBUF, M_NOWAIT);
+	if (irqg == NULL)
+	{
+		panic("No memory for irqgroup %s", name);
+		return NULL;
+	}
+
+	irqg->irqs = malloc(sizeof(struct intrq) * nirqs, M_DEVBUF, M_NOWAIT);
+	if (irqg->irqs == NULL)
+	{
+		panic("No memory for irq handlers for group %s", name);
+		free(irqg, M_DEVBUF);
+		return NULL;
+	}
+
+	/* initialise the structure */
+	irqg->nirqs = nirqs;
+	irqg->intr_enabled = 0;
+	irqg->intr_soft_enabled = 0xffffffff;
+	irqg->set_irq_hardware_mask = set_irq_hardware_mask;
+	irqg->fetch_irq_hardware_status = fetch_irq_hardware_status;
+	irqg->irq_hardware_cookie = cookie;
+	irqg->group_name = name;
+
+    for (i = 0; i < nirqs; i++) {
+            struct intrq *iq = &(irqg->irqs[i]);
+            TAILQ_INIT(&iq->iq_list);
+
+            sprintf(iq->iq_name, "irq %d", i);
+            evcnt_attach_dynamic(&iq->iq_ev, EVCNT_TYPE_INTR,
+                                     NULL, irqg->group_name, iq->iq_name);
+			iq->iq_ipl = IPL_NONE;
+			iq->iq_ist = 0;
+			iq->iq_irq = i;
+			iq->iq_mask = 0;
+			iq->iq_group = irqg;
+			iq->iq_pending = false;
+			TAILQ_INSERT_TAIL(&(arm_iplq[IPL_NONE].ipl_list), iq, ipl_list);
+    }
+	
+	if (primary_irq_group)
+		TAILQ_INSERT_HEAD(&irq_groups_list, irqg, irq_groups_list);
+	else
+		TAILQ_INSERT_TAIL(&irq_groups_list, irqg, irq_groups_list);
+
+	/* clear the masks etc */
+	arm_intr_calculate_masks(irqg);
+	return irqg;
+}	
+
+
+
+irqhandler_t
+arm_intr_claim(irqgroup_t cookie, int irq, int ipl, const char *name,
+		int (*func)(void *), void *arg)
+{
+	struct irq_group *irqg = cookie;
+	struct intrq *iq;
+	struct intrhand *ih;
+	u_int oldirqstate;
+
+	if (irq < 0 || irq > irqg->nirqs)
+		panic("arm_intr_establish: IRQ %d out of range", irq);
+
+	ih = malloc(sizeof(*ih), M_DEVBUF, M_NOWAIT);
+	if (ih == NULL)
+	{
+		printf("No memory for irq %d, group %s", irq, irqg->group_name);
+		return (NULL);
+	}
+		
+	ih->ih_func = func;
+	ih->ih_arg = arg;
+	ih->ih_ipl = ipl;
+	ih->ih_irq = irq;
+
+	iq = &(irqg->irqs[irq]);
+
+	oldirqstate = disable_interrupts(I32_bit);
+
+	TAILQ_INSERT_TAIL(&iq->iq_list, ih, ih_list);
+
+	arm_intr_calculate_masks(irqg);
+
+	/* detach the existing event counter and add the new name */
+	evcnt_detach(&iq->iq_ev);
+	evcnt_attach_dynamic(&iq->iq_ev, EVCNT_TYPE_INTR,
+			NULL, irqg->group_name, name);
+	
+	restore_interrupts(oldirqstate);
+	
+	return(ih);
+}
+
+void
+arm_intr_disestablish(irqgroup_t group_cookie, irqhandler_t cookie)
+{
+	struct irq_group *irqg = group_cookie;
+	struct intrhand *ih = cookie;
+	struct intrq *iq = &(irqg->irqs[ih->ih_irq]);
+	int oldirqstate;
+
+	oldirqstate = disable_interrupts(I32_bit);
+
+	TAILQ_REMOVE(&iq->iq_list, ih, ih_list);
+
+	arm_intr_calculate_masks(irqg);
+
+	restore_interrupts(oldirqstate);
+	free(ih, M_DEVBUF);
+}
+
+void arm_intr_schedule(irqgroup_t group_cookie, irqhandler_t cookie)
+{
+	struct irq_group *irqg = group_cookie;
+	struct intrhand *ih = cookie;
+	struct intrq *iq = &(irqg->irqs[ih->ih_irq]);
+	int oldirqstate;
+
+	oldirqstate = disable_interrupts(I32_bit);
+
+	/* flag irq line as pending */
+	iq->iq_pending = true;
+	/* flag ipl level as pending */
+	ipls_pending |= 1 << iq->iq_ipl;
+
+	restore_interrupts(oldirqstate);
+
+	if (current_ipl_level > iq->iq_ipl)
+		arm_intr_splx_lifter(iq->iq_ipl);
+}
+
+void
+arm_intr_print_all_masks()
+{
+	int ipl;
+
+	for (ipl = NIPL-1; ipl > 0; ipl--)
+	{
+		struct intrq *iplq;
+		printf("%02d: ", ipl);
+
+		/* process the interrupt queues */
+		for (iplq = TAILQ_FIRST(&(arm_iplq[ipl].ipl_list));
+				(iplq != NULL);
+				iplq = TAILQ_NEXT(iplq, ipl_list))
+		{
+			printf("%s (irq %d) ->", iplq->iq_name, iplq->iq_irq);
+		}
+		printf("\n");
+	}
+}
+
+void
+arm_intr_print_pending_irq_details(void);
+
+
+void
+arm_intr_print_pending_irq_details()
+{
+	int ipl;
+
+	printf("ipls_pending: 0x%0x\n", ipls_pending);
+
+	for (ipl = NIPL-1; ipl > 0; ipl--)
+	{
+		struct intrq *iplq;
+		printf("%02d: ", ipl);
+
+		/* process the interrupt queues */
+		for (iplq = TAILQ_FIRST(&(arm_iplq[ipl].ipl_list));
+				(iplq != NULL);
+				iplq = TAILQ_NEXT(iplq, ipl_list))
+		{
+			if (iplq->iq_pending)
+			{
+				struct irq_group *irqg = iplq->iq_group;
+				printf("%s (%s irq %d) ->", iplq->iq_name, irqg->group_name, iplq->iq_irq);
+			}
+		}
+		printf("\n");
+	}
+}
+
+
+static void
+arm_intr_run_handlers_for_ipl(int ipl_level, struct clockframe *frame)
+{
+	struct intrq *iplq;
+	int oldirqstate;
+
+	arm_iplq[ipl_level].ipl_ev.ev_count++;
+	
+	/* process the interrupt queues */
+	for (iplq = TAILQ_FIRST(&(arm_iplq[ipl_level].ipl_list));
+			(iplq != NULL);
+		     iplq = TAILQ_NEXT(iplq, ipl_list))
+	{
+		struct intrhand *ih;
+		int intr_rc = 0;
+		/* process the handler list queue */
+		/* skip the intrq if it's not pending */
+		if (__predict_false(!iplq->iq_pending))
+			continue;
+
+		/* 
+		 * we're handling this item, so clear the pending flag
+		 * Note it may get set again while we're running the irq handlers
+		 */
+		iplq->iq_pending = false;
+		
+		/* bump stats */
+		iplq->iq_ev.ev_count++;
+		uvmexp.intrs++;
+		
+		oldirqstate = enable_interrupts(I32_bit);
+		for (ih = TAILQ_FIRST(&iplq->iq_list);
+			((ih != NULL) && (intr_rc != 1));
+		     ih = TAILQ_NEXT(ih, ih_list)) {
+			intr_rc = (*ih->ih_func)(ih->ih_arg ? ih->ih_arg : frame);
+		}
+		restore_interrupts(oldirqstate);
+
+		/* Re-enable this interrupt now that it has been handled */
+		arm_intr_enable_irq(iplq->iq_group, iplq->iq_irq);
+	}
+}
+
+
+void
+arm_intr_process_pending_ipls(struct clockframe *frame, int target_ipl_level)
+{
+	int ipl_level;
+
+	while ((ipl_level = arm_intr_fls(ipls_pending)) > (target_ipl_level + 1))
+	{
+		--ipl_level;
+		current_ipl_level = ipl_level;
+		ipls_pending &= ~(1<<ipl_level);
+		arm_intr_run_handlers_for_ipl(ipl_level, frame);
+	}
+	current_ipl_level = target_ipl_level;
+}
+
+/* called with external interrupts disabled */
+void
+arm_intr_dispatch(struct clockframe *frame)
+{
+	struct irq_group *irqg;
+	int ipl_level_at_entry = current_ipl_level;
+
+	irqg = TAILQ_FIRST(&irq_groups_list);
+#ifdef DIAGNOSTIC
+	if (irqg == NULL)
+		panic("dispatch interrupts with no interrupt groups");
+#endif
+	arm_intr_queue_irqs(irqg);
+
+	/*
+	 * we only need to process ipls if they are of a higher priority than our current level
+	 */
+	if (ipls_pending > (1 << ipl_level_at_entry))
+		arm_intr_process_pending_ipls(frame, ipl_level_at_entry);
+
+}
+
+void
+arm_intr_queue_irqs(irqgroup_t group_cookie)
+{
+	struct irq_group *irqg = group_cookie;
+	uint32_t hwpend;
+
+	hwpend = irqg->fetch_irq_hardware_status(irqg->irq_hardware_cookie);
+
+	if (__predict_true(hwpend != 0))
+	{
+		/*
+		 * Disable all the interrupts that are pending.  We will
+		 * reenable them once they are processed and not masked.
+		 */
+		irqg->intr_enabled &= ~hwpend;
+		arm_intr_set_hardware_mask(irqg);
+		
+		/* run through the pending bits */
+		while (hwpend != 0) {
+			int irq;
+			struct intrq *iq;
+			
+			irq = ffs(hwpend) - 1;
+			iq = &(irqg->irqs[irq]);
+			hwpend &= ~(1<<irq);
+			
+			/* flag irq line as pending */
+			iq->iq_pending = true;
+			/* flag ipl level as pending */
+			ipls_pending |= 1 << iq->iq_ipl;
+		}
+	}
+}
+
+
+/* 
+ * an optimised find last set bit routine (basically which bit is the top most
+ * set bit)  Like ffs except we're after the top bit not the bottom bit.
+ *
+ * Note this returns 0 if passed 0.  bits are numbered 1-32.
+ *
+ * a more optimal routine may find a way to normalize the top bit and call the
+ * ffs routines lookup
+ * 
+ * on Arch v5, this could be a clz call
+ *
+ * Note that the compiler inlines this in an optimised build.  The if's actually end up as
+ * a couple of instructions, inlined this totals 10 instructions.
+ */
+static uint8_t fls_table[16];
+static int
+arm_intr_fls(uint32_t n)
+{
+	int offset = 0;
+
+	if (n & 0x00f0)
+		offset = 4;
+	if (n & 0x0f00)
+		offset = 8;
+	if (n & 0xf000)
+		offset = 12;
+#if NIPL > 16
+#error fls code needs to be examined for larger IPL counts
+#endif
+
+	/* note that the table lookup can be done by the following instructions
+	 * it's not clear which is quicker
+	 */
+	return fls_table[n>>offset]+offset;
+
+#if 0
+	int result = 0;
+	int shifted;
+	
+	shifted = n >> offset;
+	if (n & 0x1)
+		result = 1;
+	if (n & 0x2)
+		result = 2;
+	if (n & 0x4)
+		result = 3;
+	if (n & 0x8)
+		result = 4;
+	return result + offset;
+#endif
+}
+
+static uint8_t fls_table[16] = {
+	0,
+/* 1 */
+	1,
+/* 2 */
+	2, 2,
+/* 4 */
+	3, 3, 3, 3,
+/* 8 */
+	4, 4, 4, 4, 4, 4, 4, 4
+};
+
Index: arm/arm32/cpuswitch.S
===================================================================
RCS file: /cvsroot/src/sys/arch/arm/arm32/cpuswitch.S,v
retrieving revision 1.46
diff -u -p -r1.46 cpuswitch.S
--- arm/arm32/cpuswitch.S	19 Feb 2007 01:59:23 -0000	1.46
+++ arm/arm32/cpuswitch.S	10 May 2007 23:11:43 -0000
@@ -241,7 +241,7 @@ ASENTRY_NP(idle)
 	 * Restore the original interrupt priority level, grab the
 	 * scheduler lock if necessary, and jump back into cpu_switch.
 	 */
-2:	mov	r0, r4
+2:	mov	r0, #(IPL_HIGH)
 	bl	_C_LABEL(splx)
 	adr	lr, .Lswitch_search
 	b	_C_LABEL(sched_lock_idle)
@@ -816,13 +816,17 @@ ENTRY(cpu_switch)
 ENTRY(cpu_switchto)
 	stmfd	sp!, {r4-r7, lr}
 
+	/* disable irqs before unlocking the scheduler, otherwise
+	 * there's a risk of an interrupt rescheduling
+     */
+	IRQdisable
+
 	mov	r6, r1		/* save new lwp */
 
 	mov	r5, r0		/* save old lwp */
 	bl	_C_LABEL(sched_unlock_idle)
 	mov	r1, r5
 
-	IRQdisable
 
 	/*
 	 * Okay, set up registers the way cpu_switch() wants them,
@@ -964,7 +968,9 @@ ENTRY(switch_exit)
 	mov	lr, pc
 	mov	pc, r6
 
+	IRQdisableALL
 	bl	_C_LABEL(sched_lock_idle)
+	IRQenableALL
 
 	ldr	r7, .Lwhichqs		/* r7 = &whichqs */
 	mov	r5, #0x00000000		/* r5 = old lwp = NULL */
Index: arm/arm32/fault.c
===================================================================
RCS file: /cvsroot/src/sys/arch/arm/arm32/fault.c,v
retrieving revision 1.64
diff -u -p -r1.64 fault.c
--- arm/arm32/fault.c	18 Feb 2007 07:25:35 -0000	1.64
+++ arm/arm32/fault.c	10 May 2007 23:11:44 -0000
@@ -198,8 +198,8 @@ data_abort_fixup(trapframe_t *tf, u_int 
 #ifdef THUMB_CODE
 	if (tf->tf_spsr & PSR_T_bit) {
 		printf("pc = 0x%08x, opcode 0x%04x, 0x%04x, insn = ",
-		    tf->tf_pc, *((u_int16 *)(tf->tf_pc & ~1),
-		    *((u_int16 *)((tf->tf_pc + 2) & ~1));
+		    tf->tf_pc, *((u_int16 *)(tf->tf_pc & ~1)),
+		    *((u_int16 *)((tf->tf_pc + 2) & ~1)));
 	}
 	else
 #endif
Index: arm/conf/files.footbridge
===================================================================
RCS file: /cvsroot/src/sys/arch/arm/conf/files.footbridge,v
retrieving revision 1.12
diff -u -p -r1.12 files.footbridge
--- arm/conf/files.footbridge	19 Sep 2006 10:05:32 -0000	1.12
+++ arm/conf/files.footbridge	10 May 2007 23:11:44 -0000
@@ -12,7 +12,8 @@ file	arch/arm/footbridge/footbridge_pci.
 file	arch/arm/arm32/irq_dispatch.S
 file	arch/arm/footbridge/footbridge_irqhandler.c	footbridge
 file	arch/arm/footbridge/footbridge_clock.c		footbridge
-file	arch/arm/arm/softintr.c				footbridge
+file	arch/arm/arm/soft_irqhandler.c				footbridge
+file	arch/arm/arm32/arm_irqhandler.c			footbridge
 
 # DC21285 "Footbridge" serial port
 device	fcom: tty, bus_space_generic
Index: arm/footbridge/footbridge.c
===================================================================
RCS file: /cvsroot/src/sys/arch/arm/footbridge/footbridge.c,v
retrieving revision 1.17
diff -u -p -r1.17 footbridge.c
--- arm/footbridge/footbridge.c	6 Jan 2007 16:18:18 -0000	1.17
+++ arm/footbridge/footbridge.c	10 May 2007 23:11:44 -0000
@@ -181,6 +181,9 @@ footbridge_attach(parent, self, aux)
 	bus_space_write_4(sc->sc_iot, sc->sc_ioh, IRQ_ENABLE_CLEAR, 0xffffffff);
 	bus_space_write_4(sc->sc_iot, sc->sc_ioh, FIQ_ENABLE_CLEAR, 0xffffffff);
 
+	/* setup footbridge irqs */
+	footbridge_intr_init();
+	
 /*	bus_space_write_4(sc->sc_iot, sc->sc_ioh, 0x18, 0x40000000);*/
 
 	/* Install a generic handler to catch a load of system interrupts */
Index: arm/footbridge/footbridge_intr.h
===================================================================
RCS file: /cvsroot/src/sys/arch/arm/footbridge/footbridge_intr.h,v
retrieving revision 1.10
diff -u -p -r1.10 footbridge_intr.h
--- arm/footbridge/footbridge_intr.h	9 Mar 2007 06:45:20 -0000	1.10
+++ arm/footbridge/footbridge_intr.h	10 May 2007 23:11:44 -0000
@@ -38,190 +38,14 @@
 #ifndef _FOOTBRIDGE_INTR_H_
 #define _FOOTBRIDGE_INTR_H_
 
-#include <arm/armreg.h>
+#include <arm/arm_intr.h>
 
-/* Define the various Interrupt Priority Levels */
+#define        IST_UNUSABLE    -1      /* interrupt cannot be used */
+#define        IST_NONE        0       /* none (dummy) */
+#define        IST_PULSE       1       /* pulsed */
+#define        IST_EDGE        2       /* edge-triggered */
+#define        IST_LEVEL       3       /* level-triggered */
 
-/* Hardware Interrupt Priority Levels are not mutually exclusive. */
-
-#define IPL_NONE	0	/* nothing */
-#define IPL_SOFT	1	/* generic soft interrupts */
-#define IPL_SOFTCLOCK	2	/* clock software interrupts */
-#define IPL_SOFTNET	3	/* network software interrupts */
-#define IPL_BIO		4	/* block I/O */
-#define IPL_NET		5	/* network */
-#define IPL_SOFTSERIAL	6	/* serial software interrupts */
-#define IPL_TTY		7	/* terminal */
-#define	IPL_LPT		IPL_TTY
-#define IPL_VM		8	/* memory allocation */
-#define IPL_AUDIO	9	/* audio */
-#define IPL_CLOCK	10	/* clock */
-#define IPL_STATCLOCK	11	/* statclock */
-#define IPL_HIGH	12	/* everything */
-#define	IPL_SCHED	IPL_HIGH
-#define	IPL_LOCK	IPL_HIGH
-#define IPL_SERIAL	13	/* serial */
-
-#define NIPL		14
-
-#define	IST_UNUSABLE	-1	/* interrupt cannot be used */
-#define	IST_NONE	0	/* none (dummy) */
-#define	IST_PULSE	1	/* pulsed */
-#define	IST_EDGE	2	/* edge-triggered */
-#define	IST_LEVEL	3	/* level-triggered */
-
-#define	__NEWINTR	/* enables new hooks in cpu_fork()/cpu_switch() */
-
-#define	ARM_IRQ_HANDLER	_C_LABEL(footbridge_intr_dispatch)
-
-#ifndef _LOCORE
-#include <arm/cpufunc.h>
-
-#include <arm/footbridge/dc21285mem.h>
-#include <arm/footbridge/dc21285reg.h>
-
-#define INT_SWMASK							\
-	((1U << IRQ_SOFTINT) | (1U << IRQ_RESERVED0) |			\
-	 (1U << IRQ_RESERVED1) | (1U << IRQ_RESERVED2))
-#define ICU_INT_HWMASK	(0xffffffff & ~(INT_SWMASK |  (1U << IRQ_RESERVED3)))
-
-/* only call this with interrupts off */
-static inline void __attribute__((__unused__))
-    footbridge_set_intrmask(void)
-{
-    extern volatile uint32_t intr_enabled;
-    /* fetch once so we write the same number to both registers */
-    uint32_t tmp = intr_enabled & ICU_INT_HWMASK;
-
-    ((volatile uint32_t*)(DC21285_ARMCSR_VBASE))[IRQ_ENABLE_SET>>2] = tmp;
-    ((volatile uint32_t*)(DC21285_ARMCSR_VBASE))[IRQ_ENABLE_CLEAR>>2] = ~tmp;
-}
-    
-static inline void __attribute__((__unused__))
-footbridge_splx(int newspl)
-{
-	extern volatile uint32_t intr_enabled;
-	extern volatile int current_spl_level;
-	extern volatile int footbridge_ipending;
-	extern void footbridge_do_pending(void);
-	int oldirqstate, hwpend;
-
-	/* Don't let the compiler re-order this code with preceding code */
-	__insn_barrier();
-
-	current_spl_level = newspl;
-
-	hwpend = (footbridge_ipending & ICU_INT_HWMASK) & ~newspl;
-	if (hwpend != 0) {
-		oldirqstate = disable_interrupts(I32_bit);
-		intr_enabled |= hwpend;
-		footbridge_set_intrmask();
-		restore_interrupts(oldirqstate);
-	}
-
-	if ((footbridge_ipending & INT_SWMASK) & ~newspl)
-		footbridge_do_pending();
-}
-
-static inline int __attribute__((__unused__))
-footbridge_splraise(int ipl)
-{
-	extern volatile int current_spl_level;
-	extern int footbridge_imask[];
-	int	old;
-
-	old = current_spl_level;
-	current_spl_level |= footbridge_imask[ipl];
-
-	/* Don't let the compiler re-order this code with subsequent code */
-	__insn_barrier();
-
-	return (old);
-}
-
-static inline int __attribute__((__unused__))
-footbridge_spllower(int ipl)
-{
-	extern volatile int current_spl_level;
-	extern int footbridge_imask[];
-	int old = current_spl_level;
-
-	footbridge_splx(footbridge_imask[ipl]);
-	return(old);
-}
-
-/* should only be defined in footbridge_intr.c */
-#if !defined(ARM_SPL_NOINLINE)
-
-#define splx(newspl)		footbridge_splx(newspl)
-#define	_spllower(ipl)		footbridge_spllower(ipl)
-#define	_splraise(ipl)		footbridge_splraise(ipl)
-void	_setsoftintr(int);
-
-#else
-
-int	_splraise(int);
-int	_spllower(int);
-void	splx(int);
-void	_setsoftintr(int);
-
-#endif /* ! ARM_SPL_NOINLINE */
-
-#include <sys/device.h>
-#include <sys/queue.h>
-#include <machine/irqhandler.h>
-
-#define	splsoft()	_splraise(IPL_SOFT)
-
-#define	spl0()		(void)_spllower(IPL_NONE)
-#define	spllowersoftclock() (void)_spllower(IPL_SOFTCLOCK)
-
-typedef uint8_t ipl_t;
-typedef struct {
-	ipl_t _ipl;
-} ipl_cookie_t;
-
-static inline ipl_cookie_t
-makeiplcookie(ipl_t ipl)
-{
-
-	return (ipl_cookie_t){._ipl = ipl};
-}
-
-static inline int
-splraiseipl(ipl_cookie_t icookie)
-{
-
-	return _splraise(icookie._ipl);
-}
-
-#include <sys/spl.h>
-
-/* Use generic software interrupt support. */
-#include <arm/softintr.h>
-
-/* footbridge has 32 interrupt lines */
-#define	NIRQ		32
-
-struct intrhand {
-	TAILQ_ENTRY(intrhand) ih_list;	/* link on intrq list */
-	int (*ih_func)(void *);		/* handler */
-	void *ih_arg;			/* arg for handler */
-	int ih_ipl;			/* IPL_* */
-	int ih_irq;			/* IRQ number */
-};
-
-#define	IRQNAMESIZE	sizeof("footbridge irq 31")
-
-struct intrq {
-	TAILQ_HEAD(, intrhand) iq_list;	/* handler list */
-	struct evcnt iq_ev;		/* event counter */
-	int iq_mask;			/* IRQs to mask while handling */
-	int iq_levels;			/* IPL_*'s this IRQ has */
-	int iq_ist;			/* share type */
-	char iq_name[IRQNAMESIZE];	/* interrupt name */
-};
-
-#endif /* _LOCORE */
+#define		FOOTBRIDGE_NIRQ	32	/* 32bits in the irq mask */
 
 #endif	/* _FOOTBRIDGE_INTR_H */
Index: arm/footbridge/footbridge_irqhandler.c
===================================================================
RCS file: /cvsroot/src/sys/arch/arm/footbridge/footbridge_irqhandler.c,v
retrieving revision 1.17
diff -u -p -r1.17 footbridge_irqhandler.c
--- arm/footbridge/footbridge_irqhandler.c	25 Dec 2006 18:39:48 -0000	1.17
+++ arm/footbridge/footbridge_irqhandler.c	10 May 2007 23:11:44 -0000
@@ -51,59 +51,24 @@ __KERNEL_RCSID(0,"$NetBSD: footbridge_ir
 
 #include <machine/intr.h>
 #include <machine/cpu.h>
-#include <arm/footbridge/dc21285mem.h>
-#include <arm/footbridge/dc21285reg.h>
-
+#include <arm/arm_intr.h>
+#include <arm/footbridge/footbridge_irqhandler.h>
 #include <dev/pci/pcivar.h>
 
-#include "isa.h"
-#if NISA > 0
-#include <dev/isa/isavar.h>
-#endif
-
-/* Interrupt handler queues. */
-static struct intrq footbridge_intrq[NIRQ];
-
-/* Interrupts to mask at each level. */
-int footbridge_imask[NIPL];
-
-/* Software copy of the IRQs we have enabled. */
-volatile uint32_t intr_enabled;
+#include <arm/footbridge/dc21285mem.h>
+#include <arm/footbridge/dc21285reg.h>
 
-/* Current interrupt priority level */
-volatile int current_spl_level;
+extern void isa_intr_init(void);
 
-/* Interrupts pending */
-volatile int footbridge_ipending;
+static irqgroup_t footbridge_irq_group;
 
-void footbridge_intr_dispatch(struct clockframe *frame);
+static void footbridge_set_irq_mask(irq_hardware_cookie_t, uint32_t intr_enabled);
+static uint32_t footbridge_irq_status(irq_hardware_cookie_t);
 
 const struct evcnt *footbridge_pci_intr_evcnt __P((void *, pci_intr_handle_t));
 
-void footbridge_do_pending(void);
-
-static const uint32_t si_to_irqbit[SI_NQUEUES] =
-	{ IRQ_SOFTINT,
-	  IRQ_RESERVED0,
-	  IRQ_RESERVED1,
-	  IRQ_RESERVED2 };
-
-#define	SI_TO_IRQBIT(si)	(1U << si_to_irqbit[(si)])
-
-/*
- * Map a software interrupt queue to an interrupt priority level.
- */
-static const int si_to_ipl[SI_NQUEUES] = {
-	IPL_SOFT,		/* SI_SOFT */
-	IPL_SOFTCLOCK,		/* SI_SOFTCLOCK */
-	IPL_SOFTNET,		/* SI_SOFTNET */
-	IPL_SOFTSERIAL,		/* SI_SOFTSERIAL */
-};
-
 const struct evcnt *
-footbridge_pci_intr_evcnt(pcv, ih)
-	void *pcv;
-	pci_intr_handle_t ih;
+footbridge_pci_intr_evcnt(void *pcv, pci_intr_handle_t ih)
 {
 	/* XXX check range is valid */
 #if NISA > 0
@@ -111,356 +76,51 @@ footbridge_pci_intr_evcnt(pcv, ih)
 		return isa_intr_evcnt(NULL, (ih & 0x0f));
 	}
 #endif
-	return &footbridge_intrq[ih].iq_ev;
+	return arm_intr_evcnt(footbridge_irq_group, ih);
 }
 
-static inline void
-footbridge_enable_irq(int irq)
-{
-	intr_enabled |= (1U << irq);
-	
-	footbridge_set_intrmask();
-}
-
-static inline void
-footbridge_disable_irq(int irq)
-{
-	intr_enabled &= ~(1U << irq);
-	footbridge_set_intrmask();
-}
-
-/*
- * NOTE: This routine must be called with interrupts disabled in the CPSR.
- */
-static void
-footbridge_intr_calculate_masks(void)
+void
+footbridge_intr_init(void)
 {
-	struct intrq *iq;
-	struct intrhand *ih;
-	int irq, ipl;
-
-	/* First, figure out which IPLs each IRQ has. */
-	for (irq = 0; irq < NIRQ; irq++) {
-		int levels = 0;
-		iq = &footbridge_intrq[irq];
-		footbridge_disable_irq(irq);
-		for (ih = TAILQ_FIRST(&iq->iq_list); ih != NULL;
-		     ih = TAILQ_NEXT(ih, ih_list))
-			levels |= (1U << ih->ih_ipl);
-		iq->iq_levels = levels;
-	}
-
-	/* Next, figure out which IRQs are used by each IPL. */
-	for (ipl = 0; ipl < NIPL; ipl++) {
-		int irqs = 0;
-		for (irq = 0; irq < NIRQ; irq++) {
-			if (footbridge_intrq[irq].iq_levels & (1U << ipl))
-				irqs |= (1U << irq);
-		}
-		footbridge_imask[ipl] = irqs;
-	}
-
-	/* IPL_NONE must open up all interrupts */
-	footbridge_imask[IPL_NONE] = 0;
-
-	/*
-	 * Initialize the soft interrupt masks to block themselves.
-	 */
-	footbridge_imask[IPL_SOFT] = SI_TO_IRQBIT(SI_SOFT);
-	footbridge_imask[IPL_SOFTCLOCK] = SI_TO_IRQBIT(SI_SOFTCLOCK);
-	footbridge_imask[IPL_SOFTNET] = SI_TO_IRQBIT(SI_SOFTNET);
-	footbridge_imask[IPL_SOFTSERIAL] = SI_TO_IRQBIT(SI_SOFTSERIAL);
-
-	footbridge_imask[IPL_SOFTCLOCK] |= footbridge_imask[IPL_SOFT];
-	footbridge_imask[IPL_SOFTNET] |= footbridge_imask[IPL_SOFTCLOCK];
-
-	/*
-	 * Enforce a hierarchy that gives "slow" device (or devices with
-	 * limited input buffer space/"real-time" requirements) a better
-	 * chance at not dropping data.
-	 */
-	footbridge_imask[IPL_BIO] |= footbridge_imask[IPL_SOFTNET];
-	footbridge_imask[IPL_NET] |= footbridge_imask[IPL_BIO];
-	footbridge_imask[IPL_SOFTSERIAL] |= footbridge_imask[IPL_NET];
-
-	footbridge_imask[IPL_TTY] |= footbridge_imask[IPL_SOFTSERIAL];
-
-	/*
-	 * splvm() blocks all interrupts that use the kernel memory
-	 * allocation facilities.
-	 */
-	footbridge_imask[IPL_VM] |= footbridge_imask[IPL_TTY];
-
-	/*
-	 * Audio devices are not allowed to perform memory allocation
-	 * in their interrupt routines, and they have fairly "real-time"
-	 * requirements, so give them a high interrupt priority.
-	 */
-	footbridge_imask[IPL_AUDIO] |= footbridge_imask[IPL_VM];
-
-	/*
-	 * splclock() must block anything that uses the scheduler.
-	 */
-	footbridge_imask[IPL_CLOCK] |= footbridge_imask[IPL_AUDIO];
-
-	/*
-	 * footbridge has separate statclock.
-	 */
-	footbridge_imask[IPL_STATCLOCK] |= footbridge_imask[IPL_CLOCK];
-
-	/*
-	 * splhigh() must block "everything".
-	 */
-	footbridge_imask[IPL_HIGH] |= footbridge_imask[IPL_STATCLOCK];
-
-	/*
-	 * XXX We need serial drivers to run at the absolute highest priority
-	 * in order to avoid overruns, so serial > high.
-	 */
-	footbridge_imask[IPL_SERIAL] |= footbridge_imask[IPL_HIGH];
+	footbridge_irq_group = arm_intr_register_irq_provider("footbridge", FOOTBRIDGE_NIRQ,
+		   	footbridge_set_irq_mask,
+			footbridge_irq_status,
+			NULL, true);
 
 	/*
-	 * Calculate the ipl level to go to when handling this interrupt
+	 * Since various PCI interrupts could be routed via the ICU
+	 * (for PCI devices in the bridge) we need to set up the ICU
+	 * now so that these interrupts can be established correctly
+	 * i.e. This is a hack.
 	 */
-	for (irq = 0; irq < NIRQ; irq++) {
-		int irqs = (1U << irq);
-		iq = &footbridge_intrq[irq];
-		if (TAILQ_FIRST(&iq->iq_list) != NULL)
-			footbridge_enable_irq(irq);
-		for (ih = TAILQ_FIRST(&iq->iq_list); ih != NULL;
-		     ih = TAILQ_NEXT(ih, ih_list))
-			irqs |= footbridge_imask[ih->ih_ipl];
-		iq->iq_mask = irqs;
-	}
-}
+	isa_intr_init();
 
-int
-_splraise(int ipl)
-{
-    return (footbridge_splraise(ipl));
 }
 
-/* this will always take us to the ipl passed in */
-void
-splx(int new)
-{
-    footbridge_splx(new);
-}
-
-int
-_spllower(int ipl)
-{
-    return (footbridge_spllower(ipl));
-}
-
-void
-footbridge_do_pending(void)
-{
-	static __cpu_simple_lock_t processing = __SIMPLELOCK_UNLOCKED;
-	uint32_t new, oldirqstate;
-
-	if (__cpu_simple_lock_try(&processing) == 0)
-		return;
-
-	new = current_spl_level;
-	
-	oldirqstate = disable_interrupts(I32_bit);
-
-#define	DO_SOFTINT(si)							\
-	if ((footbridge_ipending & ~new) & SI_TO_IRQBIT(si)) {		\
-		footbridge_ipending &= ~SI_TO_IRQBIT(si);		\
-		current_spl_level |= footbridge_imask[si_to_ipl[(si)]];	\
-		restore_interrupts(oldirqstate);			\
-		softintr_dispatch(si);					\
-		oldirqstate = disable_interrupts(I32_bit);		\
-		current_spl_level = new;				\
-	}
-	DO_SOFTINT(SI_SOFTSERIAL);
-	DO_SOFTINT(SI_SOFTNET);
-	DO_SOFTINT(SI_SOFTCLOCK);
-	DO_SOFTINT(SI_SOFT);
-	
-	__cpu_simple_unlock(&processing);
-
-	restore_interrupts(oldirqstate);
-}
-
-
-/* called from splhigh, so the matching splx will set the interrupt up.*/
-void
-_setsoftintr(int si)
+static void
+footbridge_set_irq_mask(irq_hardware_cookie_t cookie, uint32_t intr_enabled)
 {
-	int oldirqstate;
-
-	oldirqstate = disable_interrupts(I32_bit);
-	footbridge_ipending |= SI_TO_IRQBIT(si);
-	restore_interrupts(oldirqstate);
-
-	/* Process unmasked pending soft interrupts. */
-	if ((footbridge_ipending & INT_SWMASK) & ~current_spl_level)
-		footbridge_do_pending();
+    ((volatile uint32_t*)(DC21285_ARMCSR_VBASE))[IRQ_ENABLE_SET>>2] = intr_enabled;
+    ((volatile uint32_t*)(DC21285_ARMCSR_VBASE))[IRQ_ENABLE_CLEAR>>2] = ~intr_enabled;
 }
 
-void
-footbridge_intr_init(void)
+static uint32_t
+footbridge_irq_status(irq_hardware_cookie_t cookie)
 {
-	struct intrq *iq;
-	int i;
-
-	intr_enabled = 0;
-	current_spl_level = 0xffffffff;
-	footbridge_ipending = 0;
-	footbridge_set_intrmask();
-	
-	for (i = 0; i < NIRQ; i++) {
-		iq = &footbridge_intrq[i];
-		TAILQ_INIT(&iq->iq_list);
-
-		sprintf(iq->iq_name, "irq %d", i);
-		evcnt_attach_dynamic(&iq->iq_ev, EVCNT_TYPE_INTR,
-		    NULL, "footbridge", iq->iq_name);
-	}
-	
-	footbridge_intr_calculate_masks();
-
-	/* Enable IRQ's, we don't have any FIQ's*/
-	enable_interrupts(I32_bit);
+	    return ((volatile uint32_t*)(DC21285_ARMCSR_VBASE))[IRQ_STATUS>>2];
 }
 
 void *
 footbridge_intr_claim(int irq, int ipl, const char *name, int (*func)(void *), void *arg)
 {
-	struct intrq *iq;
-	struct intrhand *ih;
-	u_int oldirqstate;
-
-	if (irq < 0 || irq > NIRQ)
+	if (irq < 0 || irq > FOOTBRIDGE_NIRQ)
 		panic("footbridge_intr_establish: IRQ %d out of range", irq);
 
-	ih = malloc(sizeof(*ih), M_DEVBUF, M_NOWAIT);
-	if (ih == NULL)
-	{
-		printf("No memory");
-		return (NULL);
-	}
-		
-	ih->ih_func = func;
-	ih->ih_arg = arg;
-	ih->ih_ipl = ipl;
-	ih->ih_irq = irq;
-
-	iq = &footbridge_intrq[irq];
-
-	iq->iq_ist = IST_LEVEL;
-
-	oldirqstate = disable_interrupts(I32_bit);
-
-	TAILQ_INSERT_TAIL(&iq->iq_list, ih, ih_list);
-
-	footbridge_intr_calculate_masks();
-
-	/* detach the existing event counter and add the new name */
-	evcnt_detach(&iq->iq_ev);
-	evcnt_attach_dynamic(&iq->iq_ev, EVCNT_TYPE_INTR,
-			NULL, "footbridge", name);
-	
-	restore_interrupts(oldirqstate);
-	
-	return(ih);
+	return arm_intr_claim(footbridge_irq_group, irq, ipl, name, func, arg);
 }
 
 void
 footbridge_intr_disestablish(void *cookie)
 {
-	struct intrhand *ih = cookie;
-	struct intrq *iq = &footbridge_intrq[ih->ih_irq];
-	int oldirqstate;
-
-	/* XXX need to free ih ? */
-	oldirqstate = disable_interrupts(I32_bit);
-
-	TAILQ_REMOVE(&iq->iq_list, ih, ih_list);
-
-	footbridge_intr_calculate_masks();
-
-	restore_interrupts(oldirqstate);
-}
-
-static uint32_t footbridge_intstatus(void);
-
-static inline uint32_t footbridge_intstatus()
-{
-    return ((volatile uint32_t*)(DC21285_ARMCSR_VBASE))[IRQ_STATUS>>2];
-}
-
-/* called with external interrupts disabled */
-void
-footbridge_intr_dispatch(struct clockframe *frame)
-{
-	struct intrq *iq;
-	struct intrhand *ih;
-	int oldirqstate, pcpl, irq, ibit, hwpend;
-
-	pcpl = current_spl_level;
-
-	hwpend = footbridge_intstatus();
-
-	/*
-	 * Disable all the interrupts that are pending.  We will
-	 * reenable them once they are processed and not masked.
-	 */
-	intr_enabled &= ~hwpend;
-	footbridge_set_intrmask();
-
-	while (hwpend != 0) {
-		int intr_rc = 0;
-		irq = ffs(hwpend) - 1;
-		ibit = (1U << irq);
-
-		hwpend &= ~ibit;
-
-		if (pcpl & ibit) {
-			/*
-			 * IRQ is masked; mark it as pending and check
-			 * the next one.  Note: the IRQ is already disabled.
-			 */
-			footbridge_ipending |= ibit;
-			continue;
-		}
-
-		footbridge_ipending &= ~ibit;
-
-		iq = &footbridge_intrq[irq];
-		iq->iq_ev.ev_count++;
-		uvmexp.intrs++;
-		current_spl_level |= iq->iq_mask;
-		oldirqstate = enable_interrupts(I32_bit);
-		for (ih = TAILQ_FIRST(&iq->iq_list);
-			((ih != NULL) && (intr_rc != 1));
-		     ih = TAILQ_NEXT(ih, ih_list)) {
-			intr_rc = (*ih->ih_func)(ih->ih_arg ? ih->ih_arg : frame);
-		}
-		restore_interrupts(oldirqstate);
-
-		current_spl_level = pcpl;
-
-		/* Re-enable this interrupt now that's it's cleared. */
-		intr_enabled |= ibit;
-		footbridge_set_intrmask();
-
-		/* also check for any new interrupts that may have occurred,
-		 * that we can handle at this spl level */
-		hwpend |= (footbridge_ipending & ICU_INT_HWMASK) & ~pcpl;
-	}
-
-	/* Check for pendings soft intrs. */
-        if ((footbridge_ipending & INT_SWMASK) & ~current_spl_level) {
-	    /* 
-	     * XXX this feels the wrong place to enable irqs, as some
-	     * soft ints are higher priority than hardware irqs
-	     */
-                oldirqstate = enable_interrupts(I32_bit);
-                footbridge_do_pending();
-                restore_interrupts(oldirqstate);
-        }
+	return arm_intr_disestablish(footbridge_irq_group, cookie);
 }
Index: arm/include/Makefile
===================================================================
RCS file: /cvsroot/src/sys/arch/arm/include/Makefile,v
retrieving revision 1.34
diff -u -p -r1.34 Makefile
--- arm/include/Makefile	18 Feb 2007 15:53:55 -0000	1.34
+++ arm/include/Makefile	10 May 2007 23:11:44 -0000
@@ -2,7 +2,7 @@
 
 INCSDIR= /usr/include/arm
 
-INCS=	ansi.h aout_machdep.h armreg.h asm.h atomic.h \
+INCS=	ansi.h aout_machdep.h arm_intr.h armreg.h asm.h atomic.h \
 	bswap.h byte_swap.h bus.h \
 	cdefs.h cpu.h \
 	disklabel.h \
Index: arm/include/arm_intr.h
===================================================================
RCS file: arm/include/arm_intr.h
diff -N arm/include/arm_intr.h
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ arm/include/arm_intr.h	10 May 2007 23:11:44 -0000
@@ -0,0 +1,264 @@
+/* 	$NetBSD: footbridge_intr.h,v 1.10 2007/03/09 06:45:20 thorpej Exp $	*/
+
+/*
+ * Copyright (c) 2001, 2002 Wasabi Systems, Inc.
+ * All rights reserved.
+ *
+ * Written by Jason R. Thorpe for Wasabi Systems, Inc.
+ *
+ * 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 for the NetBSD Project by
+ *	Wasabi Systems, Inc.
+ * 4. The name of Wasabi Systems, Inc. may not be used to endorse
+ *    or promote products derived from this software without specific prior
+ *    written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY WASABI SYSTEMS, INC. ``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 WASABI SYSTEMS, INC
+ * 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.
+ */
+
+#ifndef _ARM_INTR_H_
+#define _ARM_INTR_H_
+
+#include <arm/armreg.h>
+
+/* Define the various Interrupt Priority Levels */
+
+/* Hardware Interrupt Priority Levels are not mutually exclusive. */
+
+#define IPL_NONE	0	/* nothing */
+#define IPL_SOFT	1	/* generic soft interrupts */
+#define IPL_SOFTCLOCK	2	/* clock software interrupts */
+#define IPL_SOFTNET	3	/* network software interrupts */
+#define IPL_BIO		4	/* block I/O */
+#define IPL_NET		5	/* network */
+#define IPL_SOFTSERIAL	6	/* serial software interrupts */
+#define IPL_TTY		7	/* terminal */
+#define	IPL_LPT		IPL_TTY
+#define IPL_VM		8	/* memory allocation */
+#define IPL_AUDIO	9	/* audio */
+#define IPL_IRQBUS  10	/* this irq collates other irq */
+#define IPL_CLOCK	11	/* clock */
+#define IPL_STATCLOCK	12	/* statclock */
+#define IPL_HIGH	13	/* everything */
+#define	IPL_SCHED	IPL_HIGH
+#define	IPL_LOCK	IPL_HIGH
+#define IPL_SERIAL	14	/* serial */
+
+#define NIPL		15
+
+#define	__NEWINTR	/* enables new hooks in cpu_fork()/cpu_switch() */
+
+#define	ARM_IRQ_HANDLER	_C_LABEL(arm_intr_dispatch)
+
+#ifndef _LOCORE
+#include <arm/cpufunc.h>
+#include <arm/cpu.h>
+
+#define current_ipl_level (curcpu()->ci_current_ipl_level)
+/* 
+ * note that ipls_pending must be altered with irqs disabled
+ * as it's normally modified as a read, modify, write
+ */
+#define ipls_pending (curcpu()->ci_ipls_pending)
+extern void arm_intr_splx_lifter(int newspl);
+
+static inline void __attribute__((__unused__))
+arm_intr_splx(int newspl)
+{
+    uint32_t iplvalue = (1 << newspl);
+
+    /* Don't let the compiler re-order this code with preceding code */
+    __insn_barrier();
+    current_ipl_level = newspl;
+
+    if (ipls_pending > iplvalue)
+        arm_intr_splx_lifter(newspl);
+
+    return;
+}
+
+static inline int __attribute__((__unused__))
+arm_intr_splraise(int ipl)
+{
+	int	old;
+
+	old = current_ipl_level;
+	current_ipl_level = ipl;
+
+	/* Don't let the compiler re-order this code with subsequent code */
+	__insn_barrier();
+
+	return (old);
+}
+
+static inline int __attribute__((__unused__))
+arm_intr_spllower(int ipl)
+{
+	int old = current_ipl_level;
+
+	arm_intr_splx(ipl);
+	return(old);
+}
+
+/* should only be defined in footbridge_intr.c */
+#if !defined(ARM_SPL_NOINLINE)
+
+#define splx(newspl)		arm_intr_splx(newspl)
+#define	_spllower(ipl)		arm_intr_spllower(ipl)
+#define	_splraise(ipl)		arm_intr_splraise(ipl)
+void	_setsoftintr(int);
+
+#else
+
+int	_splraise(int);
+int	_spllower(int);
+void	splx(int);
+void	_setsoftintr(int);
+
+#endif /* ! ARM_SPL_NOINLINE */
+
+/* public apis in arm_irqhandler.c*/
+typedef void *irqhandler_t;
+typedef struct irq_group *irqgroup_t;
+typedef void *irq_hardware_cookie_t;
+void arm_intr_init(void);
+void arm_intr_enable_irqs(void);
+
+
+irqgroup_t
+arm_intr_register_irq_provider(const char *name, int nirqs, 
+		void (*set_irq_hardware_mask)(irq_hardware_cookie_t, uint32_t intr_enabled),
+		uint32_t (*fetch_irq_hardware_status)(irq_hardware_cookie_t),
+		irq_hardware_cookie_t, bool primary_irq_group);
+
+irqhandler_t
+arm_intr_claim(irqgroup_t, int irq, int ipl, const char *name, int (*func)(void *), void *arg);
+void arm_intr_disestablish(irqgroup_t, irqhandler_t cookie);
+
+void arm_intr_schedule(irqgroup_t, irqhandler_t);
+
+const struct evcnt * arm_intr_evcnt(irqgroup_t, int ih);
+
+void arm_intr_soft_enable_irq(irqgroup_t, int irq);
+void arm_intr_soft_disable_irq(irqgroup_t, int irq);
+void arm_intr_queue_irqs(irqgroup_t);
+void arm_intr_process_pending_ipls(struct clockframe *frame, int target_ipl_level);
+
+
+/* autoconf's like to print out the IPL masks */
+extern void arm_intr_print_all_masks(void);
+
+#include <sys/device.h>
+#include <sys/queue.h>
+#include <machine/irqhandler.h>
+
+#define	splsoft()	_splraise(IPL_SOFT)
+
+#define	spl0()		(void)_spllower(IPL_NONE)
+#define	spllowersoftclock() (void)_spllower(IPL_SOFTCLOCK)
+
+typedef uint8_t ipl_t;
+typedef struct {
+	ipl_t _ipl;
+} ipl_cookie_t;
+
+static inline ipl_cookie_t
+makeiplcookie(ipl_t ipl)
+{
+
+	return (ipl_cookie_t){._ipl = ipl};
+}
+
+static inline int
+splraiseipl(ipl_cookie_t icookie)
+{
+
+	return _splraise(icookie._ipl);
+}
+
+#include <sys/spl.h>
+
+/* Use generic software interrupt support. */
+#include <arm/softintr.h>
+
+struct intrhand {
+	TAILQ_ENTRY(intrhand) ih_list;	/* link on intrq list */
+	int (*ih_func)(void *);		/* handler */
+	void *ih_arg;			/* arg for handler */
+	int ih_ipl;			/* IPL_* */
+	int ih_irq;			/* IRQ number */
+};
+
+#define	IRQNAMESIZE	sizeof("abcdefghijklmnopqrstuvwxyz irq 31")
+
+struct intrq {
+	TAILQ_HEAD(, intrhand) iq_list;	/* handler list */
+	TAILQ_ENTRY(intrq) ipl_list;	/* link on iplq list */
+	struct evcnt iq_ev;		/* event counter */
+	int iq_levels;			/* IPL_*'s this IRQ has */
+	int iq_ipl;				/* highest ipl this IRQ has */
+	int iq_ist;				/* share type */
+	int iq_irq;				/* irq line */
+	int iq_mask;
+	irqgroup_t iq_group;	/* which irq group owns this irq */
+	bool iq_pending;		/* is there an irq pending */
+	char iq_name[IRQNAMESIZE];	/* interrupt name */
+};
+
+struct iplq {
+	TAILQ_HEAD(, intrq) ipl_list;	/* irqs list */
+	struct evcnt ipl_ev;				/* event counter */
+	char ipl_name[IRQNAMESIZE];		/* ipl name */
+};
+
+/* an irq group consists of the items that are needed to make an irq provider generic
+ * Originally this was hoped to be hidden from the users, but that provided to be more pain
+ * that it's worth, eg footbridge/isa needs access to the iqs so it can find free ones and
+ * handle ists
+ */
+struct irq_group
+{
+	struct intrq *irqs;		/* array of irq handlers, one for each line */
+	int nirqs;				/* number of irq lines */
+
+	TAILQ_ENTRY(irq_group) irq_groups_list;	/* link on irq group list */
+
+	/* Software copy of the IRQs we have enabled in hardware */
+	uint32_t intr_enabled;
+
+	/* 
+	 * Interrupt lines that have been enabled from software, by default everything
+	 */
+	uint32_t intr_soft_enabled;
+
+	/* used to set and retrieve the hardware irq status */
+	void (*set_irq_hardware_mask)(irq_hardware_cookie_t, uint32_t intr_enabled);
+	uint32_t (*fetch_irq_hardware_status)(irq_hardware_cookie_t);
+	irq_hardware_cookie_t irq_hardware_cookie;
+
+	const char *group_name;
+};
+
+
+#endif /* _LOCORE */
+
+#endif	/* _ARM_INTR_H */
Index: arm/include/cpu.h
===================================================================
RCS file: /cvsroot/src/sys/arch/arm/include/cpu.h,v
retrieving revision 1.42
diff -u -p -r1.42 cpu.h
--- arm/include/cpu.h	18 Feb 2007 07:25:35 -0000	1.42
+++ arm/include/cpu.h	10 May 2007 23:11:45 -0000
@@ -82,7 +82,6 @@
 
 #include <arm/cpuconf.h>
 
-#include <machine/intr.h>
 #ifndef _LOCORE
 #include <sys/user.h>
 #include <machine/frame.h>
@@ -218,6 +217,10 @@ struct cpu_info {
 #ifdef MULTIPROCESSOR
 	MP_CPU_INFO_MEMBERS
 #endif
+#ifdef __HAVE_GENERIC_ARM_INTERRUPTS
+	uint32_t ci_ipls_pending;
+	volatile int ci_current_ipl_level;
+#endif
 };
 
 #ifndef MULTIPROCESSOR
@@ -226,6 +229,13 @@ extern struct cpu_info cpu_info_store;
 #define cpu_number()	0
 #endif
 
+#endif	/* !_LOCORE */
+
+/* intr.h depends on cpu_info in some cases */
+#include <machine/intr.h>
+
+#ifndef _LOCORE
+
 #ifdef __PROG32
 void	cpu_proc_fork(struct proc *, struct proc *);
 #else
Index: arm/include/isa_machdep.h
===================================================================
RCS file: /cvsroot/src/sys/arch/arm/include/isa_machdep.h,v
retrieving revision 1.4
diff -u -p -r1.4 isa_machdep.h
--- arm/include/isa_machdep.h	9 May 2003 23:51:26 -0000	1.4
+++ arm/include/isa_machdep.h	10 May 2007 23:11:45 -0000
@@ -48,6 +48,9 @@
  */
 struct arm32_isa_chipset {
 	struct isa_dma_state ic_dmastate;
+#ifdef __HAVE_GENERIC_ARM_INTERRUPTS
+	irqgroup_t	ic_irqgroup;
+#endif
 };
 
 typedef struct arm32_isa_chipset *isa_chipset_tag_t;
Index: arm/include/softintr.h
===================================================================
RCS file: /cvsroot/src/sys/arch/arm/include/softintr.h,v
retrieving revision 1.3
diff -u -p -r1.3 softintr.h
--- arm/include/softintr.h	22 Feb 2007 04:38:03 -0000	1.3
+++ arm/include/softintr.h	10 May 2007 23:11:45 -0000
@@ -79,7 +79,9 @@ struct soft_intrq {
 void	*softintr_establish(int, void (*)(void *), void *);
 void	softintr_disestablish(void *);
 void	softintr_init(void);
+#ifndef __HAVE_GENERIC_ARM_INTERRUPTS
 void	softintr_dispatch(int);
+#endif
 
 #define	softintr_schedule(arg)						\
 do {									\
Index: arm/iomd/files.iomd
===================================================================
RCS file: /cvsroot/src/sys/arch/arm/iomd/files.iomd,v
retrieving revision 1.18
diff -u -p -r1.18 files.iomd
--- arm/iomd/files.iomd	19 Aug 2006 13:34:15 -0000	1.18
+++ arm/iomd/files.iomd	10 May 2007 23:11:45 -0000
@@ -9,10 +9,13 @@ attach	iomd at mainbus
 file	arch/arm/iomd/iomd.c			iomd		needs-flag
 file	arch/arm/iomd/iomd_io.c			iomd
 file	arch/arm/iomd/iomd_io_asm.S		iomd
-file	arch/arm/iomd/iomd_irq.S		iomd
+#file	arch/arm/iomd/iomd_irq.S		iomd
 file	arch/arm/iomd/iomd_irqhandler.c		iomd
 file	arch/arm/iomd/iomd_fiq.S		iomd
 file	arch/arm/iomd/iomd_dma.c		iomd
+file	arch/arm/arm32/arm_irqhandler.c	iomd
+file	arch/arm/arm/soft_irqhandler.c			iomd
+file	arch/arm/arm32/irq_dispatch.S	iomd
 
 # I^2C bus (bit-banged through IOMD control register)
 device	iomdiic: i2cbus, i2c_bitbang
Index: arm/iomd/iomd.c
===================================================================
RCS file: /cvsroot/src/sys/arch/arm/iomd/iomd.c,v
retrieving revision 1.15
diff -u -p -r1.15 iomd.c
--- arm/iomd/iomd.c	5 Aug 2006 18:22:57 -0000	1.15
+++ arm/iomd/iomd.c	10 May 2007 23:11:45 -0000
@@ -95,7 +95,7 @@ int       iomd_found;
 u_int32_t iomd_base = IOMD_BASE;
 
 /* following flag is used in iomd_irq.s ... has to be cleaned up one day ! */
-u_int32_t arm7500_ioc_found = 0;
+uint32_t arm7500_ioc_found = 0;
 
 
 /* Declare prototypes */
Index: arm/iomd/iomd_clock.c
===================================================================
RCS file: /cvsroot/src/sys/arch/arm/iomd/iomd_clock.c,v
retrieving revision 1.22
diff -u -p -r1.22 iomd_clock.c
--- arm/iomd/iomd_clock.c	22 Feb 2007 05:14:05 -0000	1.22
+++ arm/iomd/iomd_clock.c	10 May 2007 23:11:45 -0000
@@ -283,7 +283,7 @@ cpu_initclocks(void)
 	bus_space_write_1(clock_sc->sc_iot, clock_sc->sc_ioh,
 	    IOMD_T0GO, 0);
 
-	clockirq = intr_claim(IRQ_TIMER0, IPL_CLOCK, "tmr0 hard clk",
+	clockirq = iomd_intr_establish(IRQ_TIMER0, IPL_CLOCK, "tmr0 hard clk",
 	    clockhandler, 0);
 
 	if (clockirq == NULL)
@@ -292,7 +292,7 @@ cpu_initclocks(void)
 
 	if (stathz) {
 		setstatclockrate(stathz);
-       		statclockirq = intr_claim(IRQ_TIMER1, IPL_CLOCK,
+       		statclockirq = iomd_intr_establish(IRQ_TIMER1, IPL_CLOCK,
        		    "tmr1 stat clk", statclockhandler, 0);
 		if (statclockirq == NULL)
 			panic("%s: Cannot installer timer 1 IRQ handler",
Index: arm/iomd/iomd_dma.c
===================================================================
RCS file: /cvsroot/src/sys/arch/arm/iomd/iomd_dma.c,v
retrieving revision 1.10
diff -u -p -r1.10 iomd_dma.c
--- arm/iomd/iomd_dma.c	5 Aug 2006 18:22:57 -0000	1.10
+++ arm/iomd/iomd_dma.c	10 May 2007 23:11:45 -0000
@@ -71,7 +71,7 @@ dma_go(struct dma_ctrl *dp)
 #endif
 	if (dp->dc_flags & DMA_FL_READY) {
 		dp->dc_flags = DMA_FL_ACTIVE;
-		enable_irq(IRQ_DMACH0 + dp->dc_channel);
+		iomd_enable_irq(IRQ_DMACH0 + dp->dc_channel);
 	} else
 		panic("dma not ready");
 }
@@ -86,7 +86,7 @@ dma_reset(struct dma_ctrl *dp)
 #endif
 	*dp->dc_cr = DMA_CR_CLEAR;
 	dp->dc_flags = 0;
-	disable_irq(IRQ_DMACH0 + dp->dc_channel);
+	iomd_disable_irq(IRQ_DMACH0 + dp->dc_channel);
 	return 0;
 }
 
@@ -254,7 +254,7 @@ done:
 #endif
 	dp->dc_flags = 0;
 	*dp->dc_cr = 0;
-	disable_irq(IRQ_DMACH0 + dp->dc_channel);
+	iomd_disable_irq(IRQ_DMACH0 + dp->dc_channel);
 #ifdef DMA_DEBUG
 	printf("about to return\n");
 #endif
@@ -308,14 +308,9 @@ dma_init(int ch, int extp, int dmasize, 
 
 	printf("about to claim interrupt\n");
 
-	dp->dc_ih.ih_func = dma_intr;
-	dp->dc_ih.ih_arg = dp;
-	dp->dc_ih.ih_level = ipl;
-	dp->dc_ih.ih_name = "dma";
-	dp->dc_ih.ih_maskaddr = (u_int) IOMD_ADDRESS(IOMD_DMARQ);
-	dp->dc_ih.ih_maskbits = (1 << ch);
+	dp->dc_ih = iomd_intr_establish( IRQ_DMACH0 + ch, ipl, "dma", dma_intr, dp);
 
-	if (irq_claim(IRQ_DMACH0 + ch, &dp->dc_ih))
+	if (dp->dc_ih == NULL)
 		panic("Cannot install DMA IRQ handler");
 
 	return dp;
Index: arm/iomd/iomd_dma.h
===================================================================
RCS file: /cvsroot/src/sys/arch/arm/iomd/iomd_dma.h,v
retrieving revision 1.2
diff -u -p -r1.2 iomd_dma.h
--- arm/iomd/iomd_dma.h	18 Feb 2002 11:41:18 -0000	1.2
+++ arm/iomd/iomd_dma.h	10 May 2007 23:11:45 -0000
@@ -51,7 +51,7 @@ struct dma_ctrl {
 	u_char			*dc_nextaddr;
 	int			dc_len;
 
-	irqhandler_t		dc_ih;
+	void		*dc_ih;
 };
 
 #define DMA_FL_ACTIVE	0x01
Index: arm/iomd/iomd_irqhandler.c
===================================================================
RCS file: /cvsroot/src/sys/arch/arm/iomd/iomd_irqhandler.c,v
retrieving revision 1.13
diff -u -p -r1.13 iomd_irqhandler.c
--- arm/iomd/iomd_irqhandler.c	19 Feb 2007 21:46:32 -0000	1.13
+++ arm/iomd/iomd_irqhandler.c	10 May 2007 23:11:46 -0000
@@ -55,418 +55,154 @@ __KERNEL_RCSID(0, "$NetBSD: iomd_irqhand
 
 #include <machine/intr.h>
 #include <machine/cpu.h>
-#include <arm/arm32/katelib.h>
 
-irqhandler_t *irqhandlers[NIRQS];
+static irqgroup_t iomd_irq_group;
 
-int current_intr_depth;
-u_int current_mask;
-u_int actual_mask;
-u_int disabled_mask;
-u_int spl_mask;
-u_int irqmasks[IPL_LEVELS];
+static void iomd_set_irq_mask(irq_hardware_cookie_t, uint32_t intr_enabled);
+static uint32_t iomd_irq_status(irq_hardware_cookie_t);
 
-extern char *_intrnames;
-
-/* Prototypes */
-
-extern void set_spl_masks(void);
+static void iomd7500_set_irq_mask(irq_hardware_cookie_t, uint32_t intr_enabled);
+static uint32_t iomd7500_irq_status(irq_hardware_cookie_t);
 
 /*
- * void irq_init(void)
+ * void intr_init(void)
  *
  * Initialise the IRQ/FIQ sub system
  */
 
 void
-irq_init(void)
+iomd_intr_init(void)
 {
-	int loop;
-
-	/* Clear all the IRQ handlers and the irq block masks */
-	for (loop = 0; loop < NIRQS; ++loop)
-		irqhandlers[loop] = NULL;
-
-	/* Clear the IRQ/FIQ masks in the IOMD */
-	IOMD_WRITE_BYTE(IOMD_IRQMSKA, 0x00);
-	IOMD_WRITE_BYTE(IOMD_IRQMSKB, 0x00);
-
 	switch (IOMD_ID) {
 	case RPC600_IOMD_ID:
-		break;
 	case ARM7500_IOC_ID:
 	case ARM7500FE_IOC_ID:
-		IOMD_WRITE_BYTE(IOMD_IRQMSKC, 0x00);
-		IOMD_WRITE_BYTE(IOMD_IRQMSKD, 0x00);
 		break;
 	default:
-		printf("Unknown IOMD id (%d) found in irq_init()\n", IOMD_ID);
+		printf("Unknown IOMD id (%d) found in iomd_irq_init()\n", IOMD_ID);
 	}
-
+	/* clear FIQ register */
 	IOMD_WRITE_BYTE(IOMD_FIQMSK, 0x00);
-	IOMD_WRITE_BYTE(IOMD_DMAMSK, 0x00);
-
-	/*
-	 * Setup the irqmasks for the different Interrupt Priority Levels
-	 * We will start with no bits set and these will be updated as handlers
-	 * are installed at different IPL's.
-	 */
-	for (loop = 0; loop < IPL_LEVELS; ++loop)
-		irqmasks[loop] = 0;
-
-	current_intr_depth = 0;
-	current_mask = 0x00000000;
-	disabled_mask = 0x00000000;
-	actual_mask = 0x00000000;
-	spl_mask = 0x00000000;
-
-	set_spl_masks();
-
-	/* Enable IRQ's and FIQ's */
-	enable_interrupts(I32_bit | F32_bit); 
-}
-
-
-/*
- * int irq_claim(int irq, irqhandler_t *handler)
- *
- * Enable an IRQ and install a handler for it.
- */
-
-int
-irq_claim(int irq, irqhandler_t *handler)
-{
-	int level;
-	u_int oldirqstate;
-
-#ifdef DIAGNOSTIC
-	/* Sanity check */
-	if (handler == NULL)
-		panic("NULL interrupt handler");
-	if (handler->ih_func == NULL)
-		panic("Interrupt handler does not have a function");
-#endif	/* DIAGNOSTIC */
-
-	/*
-	 * IRQ_INSTRUCT indicates that we should get the irq number
-	 * from the irq structure
-	 */
-	if (irq == IRQ_INSTRUCT)
-		irq = handler->ih_num;
-    
-	/* Make sure the irq number is valid */
-	if (irq < 0 || irq >= NIRQS)
-		return -1;
-
-	/* Make sure the level is valid */
-	if (handler->ih_level < 0 || handler->ih_level >= IPL_LEVELS)
-    	        return -1;
-
-	oldirqstate = disable_interrupts(I32_bit);
-
-	/* Attach handler at top of chain */
-	handler->ih_next = irqhandlers[irq];
-	irqhandlers[irq] = handler;
-
-	/*
-	 * Reset the flags for this handler.
-	 * As the handler is now in the chain mark it as active.
-	 */
-	handler->ih_flags = 0 | IRQ_FLAG_ACTIVE;
-
-	/*
-	 * Record the interrupt number for accounting.
-	 * Done here as the accounting number may not be the same as the
-	 * IRQ number though for the moment they are
-	 */
-	handler->ih_num = irq;
-
-#ifdef IRQSTATS
-	/* Get the interrupt name from the head of the list */
-	if (handler->ih_name) {
-		char *ptr = _intrnames + (irq * 14);
-		strcpy(ptr, "             ");
-		strncpy(ptr, handler->ih_name,
-		    min(strlen(handler->ih_name), 13));
-	} else {
-		char *ptr = _intrnames + (irq * 14);
-		sprintf(ptr, "irq %2d     ", irq);
-	}
-#endif	/* IRQSTATS */
 
-	/*
-	 * Update the irq masks.
-	 * Find the lowest interrupt priority on the irq chain.
-	 * Interrupt is allowable at priorities lower than this.
-	 * If ih_level is out of range then don't bother to update
-	 * the masks.
-	 */
-	if (handler->ih_level >= 0 && handler->ih_level < IPL_LEVELS) {
-		irqhandler_t *ptr;
-
-		/*
-		 * Find the lowest interrupt priority on the irq chain.
-		 * Interrupt is allowable at priorities lower than this.
-		 */
-		ptr = irqhandlers[irq];
-		if (ptr) {
-			int max_level;
-
-			level = ptr->ih_level - 1;
-			max_level = ptr->ih_level - 1;
-			while (ptr) {
-				if (ptr->ih_level - 1 < level)
-					level = ptr->ih_level - 1;
-				else if (ptr->ih_level - 1 > max_level)
-					max_level = ptr->ih_level - 1;
-				ptr = ptr->ih_next;
-			}
-			/* Clear out any levels that we cannot now allow */
-			while (max_level >=0 && max_level > level) {
-				irqmasks[max_level] &= ~(1 << irq);
-				--max_level;
-			}
-			while (level >= 0) {
-				irqmasks[level] |= (1 << irq);
-				--level;
-			}
-		}
-
-#include "sl.h"
-#include "ppp.h"
-#if NSL > 0 || NPPP > 0
-		/* In the presence of SLIP or PPP, splimp > spltty. */
-		irqmasks[IPL_NET] &= irqmasks[IPL_TTY];
-#endif
-	}
-
-	enable_irq(irq);
-	set_spl_masks();
-	restore_interrupts(oldirqstate);
-
-	return 0;
+	iomd_irq_group = NULL;
+	switch (IOMD_ID) {
+		case RPC600_IOMD_ID:
+			iomd_irq_group = arm_intr_register_irq_provider("iomd", 24,
+					iomd_set_irq_mask,
+					iomd_irq_status,
+					NULL, true);
+			break;
+		case ARM7500_IOC_ID:
+		case ARM7500FE_IOC_ID:
+			iomd_irq_group = arm_intr_register_irq_provider("iomd_7500", 32,
+					iomd7500_set_irq_mask,
+					iomd7500_irq_status,
+					NULL, true);
+			break;
+	}
+
+	if (iomd_irq_group == NULL)
+		panic("Unable to allocate irq group for iomd");
+}
+
+static void
+iomd_set_irq_mask(irq_hardware_cookie_t cookie, uint32_t intr_enabled)
+{
+	IOMD_WRITE_BYTE(IOMD_IRQMSKA, intr_enabled & 0xff);
+	IOMD_WRITE_BYTE(IOMD_IRQMSKB, (intr_enabled >> 8) & 0xff);
+	IOMD_WRITE_BYTE(IOMD_DMAMSK, (intr_enabled >> 16));
+}
+
+static uint32_t
+iomd_irq_status(irq_hardware_cookie_t cookie)
+{
+	uint32_t all_irqs;
+	uint8_t irqs;
+	
+	irqs = IOMD_READ_BYTE(IOMD_IRQRQA);
+	all_irqs = irqs;
+	
+	/* the A IRQs need to be cleared after reading */
+	IOMD_WRITE_BYTE(IOMD_IRQRQA, irqs);
+	
+	irqs = IOMD_READ_BYTE(IOMD_IRQRQB);
+	all_irqs |= (irqs << 8);
+	
+	irqs = IOMD_READ_BYTE(IOMD_DMARQ);
+	all_irqs |= (irqs << 16);
+
+	return all_irqs;
+}
+
+static void
+iomd7500_set_irq_mask(irq_hardware_cookie_t cookie, uint32_t intr_enabled)
+{
+	extern uint32_t arm7500_ioc_found;
+	IOMD_WRITE_BYTE(IOMD_IRQMSKA, intr_enabled & 0xff);
+	IOMD_WRITE_BYTE(IOMD_IRQMSKB, (intr_enabled >> 8) & 0xff);
+	
+	IOMD_WRITE_BYTE(IOMD_IRQMSKC, (intr_enabled >> 16));
+	IOMD_WRITE_BYTE(IOMD_IRQMSKD, (intr_enabled >> 24) & 0x7f);
+		
+	IOMD_WRITE_BYTE(IOMD_DMAMSK, (intr_enabled >> 27) & 0x10);
+}
+
+static uint32_t
+iomd7500_irq_status(irq_hardware_cookie_t cookie)
+{
+ 	extern uint32_t arm7500_ioc_found;
+	uint32_t all_irqs;
+	uint8_t irqs;
+	
+	irqs = IOMD_READ_BYTE(IOMD_IRQRQA);
+	all_irqs = irqs;
+	
+	/* the A IRQs need to be cleared after reading */
+	IOMD_WRITE_BYTE(IOMD_IRQRQA, irqs);
+	
+	irqs = IOMD_READ_BYTE(IOMD_IRQRQB);
+	all_irqs |= (irqs << 8);
+	
+	irqs = IOMD_READ_BYTE(IOMD_IRQRQC);
+	all_irqs |= (irqs << 16);
+	
+	irqs = IOMD_READ_BYTE(IOMD_IRQRQD);
+	all_irqs |= (irqs << 24);
+	
+	/* note that DMARQ only uses the 5th bit (0x10),
+	 * so we shift it into the top bit */
+	irqs = IOMD_READ_BYTE(IOMD_DMARQ);
+	all_irqs |= (irqs << 27);
+	
+	return all_irqs;
 }
 
 
-/*
- * int irq_release(int irq, irqhandler_t *handler)
- *
- * Disable an IRQ and remove a handler for it.
- */
-
-int
-irq_release(int irq, irqhandler_t *handler)
-{
-	int level;
-	irqhandler_t *irqhand;
-	irqhandler_t **prehand;
-#ifdef IRQSTATS
-	extern char *_intrnames;
-#endif
-
-	/*
-	 * IRQ_INSTRUCT indicates that we should get the irq number
-	 * from the irq structure
-	 */
-	if (irq == IRQ_INSTRUCT)
-		irq = handler->ih_num;
-
-	/* Make sure the irq number is valid */
-	if (irq < 0 || irq >= NIRQS)
-		return(-1);
-
-	/* Locate the handler */
-	irqhand = irqhandlers[irq];
-	prehand = &irqhandlers[irq];
-    
-	while (irqhand && handler != irqhand) {
-		prehand = &irqhand->ih_next;
-		irqhand = irqhand->ih_next;
-	}
-
-	/* Remove the handler if located */
-	if (irqhand)
-		*prehand = irqhand->ih_next;
-	else
-		return -1;
-
-	/* Now the handler has been removed from the chain mark is as inactive */
-	irqhand->ih_flags &= ~IRQ_FLAG_ACTIVE;
-
-	/* Make sure the head of the handler list is active */
-	if (irqhandlers[irq])
-		irqhandlers[irq]->ih_flags |= IRQ_FLAG_ACTIVE;
-
-#ifdef IRQSTATS
-	/* Get the interrupt name from the head of the list */
-	if (irqhandlers[irq] && irqhandlers[irq]->ih_name) {
-		char *ptr = _intrnames + (irq * 14);
-		strcpy(ptr, "             ");
-		strncpy(ptr, irqhandlers[irq]->ih_name,
-		    min(strlen(irqhandlers[irq]->ih_name), 13));
-	} else {
-		char *ptr = _intrnames + (irq * 14);
-		sprintf(ptr, "irq %2d     ", irq);
-	}
-#endif	/* IRQSTATS */
-
-	/*
-	 * Update the irq masks.
-	 * If ih_level is out of range then don't bother to update
-	 * the masks.
-	 */
-	if (handler->ih_level >= 0 && handler->ih_level < IPL_LEVELS) {
-		irqhandler_t *ptr;
-
-		/* Clean the bit from all the masks */
-		for (level = 0; level < IPL_LEVELS; ++level)
-			irqmasks[level] &= ~(1 << irq);
-
-		/*
-		 * Find the lowest interrupt priority on the irq chain.
-		 * Interrupt is allowable at priorities lower than this.
-		 */
-		ptr = irqhandlers[irq];
-		if (ptr) {
-			level = ptr->ih_level - 1;
-			while (ptr) {
-				if (ptr->ih_level - 1 < level)
-					level = ptr->ih_level - 1;
-				ptr = ptr->ih_next;
-			}
-			while (level >= 0) {
-				irqmasks[level] |= (1 << irq);
-				--level;
-			}
-		}
-	}
-
-	/*
-	 * Disable the appropriate mask bit if there are no handlers left for
-	 * this IRQ.
-	 */
-	if (irqhandlers[irq] == NULL)
-		disable_irq(irq);
-
-	set_spl_masks();
-      
-	return 0;
-}
-
 
 void *
-intr_claim(int irq, int level, const char *name, int (*ih_func)(void *),
-    void *ih_arg)
+iomd_intr_establish(int irq, int level, const char *name, int (*func)(void *),
+    void *arg)
 {
-	irqhandler_t *ih;
-
-	ih = malloc(sizeof(*ih), M_DEVBUF, M_NOWAIT);
-	if (!ih)
-		panic("intr_claim(): Cannot malloc handler memory");
-
-	ih->ih_level = level;
-	ih->ih_name = name;
-	ih->ih_func = ih_func;
-	ih->ih_arg = ih_arg;
-	ih->ih_flags = 0;
-
-	if (irq_claim(irq, ih) != 0)
-		return NULL;
-	return ih;
+	return arm_intr_claim(iomd_irq_group, irq, level, name, func, arg);
 }
 
-
-int
-intr_release(void *arg)
-{
-	irqhandler_t *ih = (irqhandler_t *)arg;
-
-	if (irq_release(ih->ih_num, ih) == 0) {
-		free(ih, M_DEVBUF);
-		return 0 ;
-	}
-	return 1;
-}
-
-#if 0
-u_int
-disable_interrupts(u_int mask)
-{
-	u_int cpsr;
-
-	cpsr = SetCPSR(mask, mask);
-	return cpsr;
-}
-
-
-u_int
-restore_interrupts(u_int old_cpsr)
-{
-	int mask = I32_bit | F32_bit;
-
-	return SetCPSR(mask, old_cpsr & mask);
-}
-
-
-u_int
-enable_interrupts(u_int mask)
+void
+iomd_intr_disestablish(void *arg)
 {
-
-	return SetCPSR(mask, 0);
+	arm_intr_disestablish(iomd_irq_group, arg);
 }
-#endif
-
-/*
- * void disable_irq(int irq)
- *
- * Disables a specific irq. The irq is removed from the master irq mask
- */
 
 void
-disable_irq(int irq)
+iomd_disable_irq(int irq)
 {
-	u_int oldirqstate; 
-
-	oldirqstate = disable_interrupts(I32_bit);
-	current_mask &= ~(1 << irq);
-	irq_setmasks();
-	restore_interrupts(oldirqstate);
-}  
-
-
-/*
- * void enable_irq(int irq)
- *
- * Enables a specific irq. The irq is added to the master irq mask
- * This routine should be used with caution. A handler should already
- * be installed.
- */
+	return arm_intr_soft_disable_irq(iomd_irq_group, irq);
+}
 
 void
-enable_irq(int irq)
+iomd_enable_irq(int irq)
 {
-	u_int oldirqstate; 
-
-	oldirqstate = disable_interrupts(I32_bit);
-	current_mask |= (1 << irq);
-	irq_setmasks();
-	restore_interrupts(oldirqstate);
-}  
+	return arm_intr_soft_enable_irq(iomd_irq_group, irq);
+}
 
 
-/*
- * void stray_irqhandler(u_int mask)
- *
- * Handler for stray interrupts. This gets called if a handler cannot be
- * found for an interrupt.
- */
 
-void
-stray_irqhandler(u_int mask)
-{
-	static u_int stray_irqs = 0;
-
-	if (++stray_irqs <= 8)
-		log(LOG_ERR, "Stray interrupt %08x%s\n", mask,
-		    stray_irqs >= 8 ? ": stopped logging" : "");
-}
Index: arm/iomd/iomdkbc.c
===================================================================
RCS file: /cvsroot/src/sys/arch/arm/iomd/iomdkbc.c,v
retrieving revision 1.3
diff -u -p -r1.3 iomdkbc.c
--- arm/iomd/iomdkbc.c	21 Oct 2006 22:45:03 -0000	1.3
+++ arm/iomd/iomdkbc.c	10 May 2007 23:11:46 -0000
@@ -142,10 +142,8 @@ iomdkbc_attach(struct device *parent, st
 			t->t_iot = ka->ka_iot;
 			t->t_ioh[PCKBPORT_KBD_SLOT] = ka->ka_ioh;
 		}
-		t->t_rxih[PCKBPORT_KBD_SLOT] = intr_claim(ka->ka_rxirq,
-		    IPL_TTY, sc->sc_dev.dv_xname, iomdkbc_intr, t);
+		t->t_rxih[PCKBPORT_KBD_SLOT] = NULL;
 		t->t_rxirq[PCKBPORT_KBD_SLOT] = ka->ka_rxirq;
-		disable_irq(t->t_rxirq[PCKBPORT_KBD_SLOT]);
 		sc->sc_id = t;
 		t->t_sc = sc;
 		t->t_pt = pckbport_attach(t, &iomdkbc_ops);
@@ -164,10 +162,8 @@ iomdkbc_attach(struct device *parent, st
 		t->t_haveport[PCKBPORT_AUX_SLOT] = 1;
 		t->t_iot = pa->pa_iot;
 		t->t_ioh[PCKBPORT_AUX_SLOT] = pa->pa_ioh;
-		t->t_rxih[PCKBPORT_AUX_SLOT] = intr_claim(pa->pa_irq,
-		    IPL_TTY, sc->sc_dev.dv_xname, iomdkbc_intr, t);
+		t->t_rxih[PCKBPORT_AUX_SLOT] = NULL;
 		t->t_rxirq[PCKBPORT_AUX_SLOT] = pa->pa_irq;
-		disable_irq(t->t_rxirq[PCKBPORT_AUX_SLOT]);
 		sc->sc_id = t;
 		t->t_sc = sc;
 		if (t->t_pt == NULL)
@@ -243,7 +239,11 @@ iomdkbc_intr_establish(void *cookie, pck
 {
 	struct iomdkbc_internal *t = cookie;
 
-	enable_irq(t->t_rxirq[slot]);
+	if (t->t_rxih[slot] == NULL)
+	{
+		t->t_rxih[slot] = iomd_intr_establish(t->t_rxirq[slot],
+			    IPL_TTY, t->t_sc->sc_dev.dv_xname, iomdkbc_intr, t);
+	}
 }
 
 static void
@@ -252,9 +252,14 @@ iomdkbc_set_poll(void *cookie, pckbport_
 	struct iomdkbc_internal *t = cookie;
 
 	if (on)
-		disable_irq(t->t_rxirq[slot]);
+	{
+		iomd_intr_disestablish(t->t_rxih[slot]);
+		t->t_rxih[slot] = NULL;
+	}
 	else
-		enable_irq(t->t_rxirq[slot]);
+	{
+		iomdkbc_intr_establish(cookie, slot);
+	}
 }
 
 static int
Index: arm/iomd/iomdvar.h
===================================================================
RCS file: /cvsroot/src/sys/arch/arm/iomd/iomdvar.h,v
retrieving revision 1.2
diff -u -p -r1.2 iomdvar.h
--- arm/iomd/iomdvar.h	19 Apr 2002 01:04:39 -0000	1.2
+++ arm/iomd/iomdvar.h	10 May 2007 23:11:46 -0000
@@ -39,6 +39,9 @@
  * Created      : 02/02/97
  */
 
+#ifndef _IOMD_IOMDVAR_H_
+#define _IOMD_IOMDVAR_H_
+
 #include <machine/bus.h>
 
 /*
@@ -121,4 +124,6 @@ union iomd_attach_args {
  */
 extern u_int32_t iomd_base;
 
+#endif /* _IOMD_IOMDVAR_H_ */
+
 /* End of iomdvar.h */
Index: arm/iomd/vidcaudio.c
===================================================================
RCS file: /cvsroot/src/sys/arch/arm/iomd/vidcaudio.c,v
retrieving revision 1.45
diff -u -p -r1.45 vidcaudio.c
--- arm/iomd/vidcaudio.c	22 Feb 2007 05:14:05 -0000	1.45
+++ arm/iomd/vidcaudio.c	10 May 2007 23:11:46 -0000
@@ -102,7 +102,7 @@ extern int *vidc_base;
 #ifdef VIDCAUDIO_DEBUG
 #define DPRINTF(x)	printf x
 #else
-#define DPRINTF(x)
+#define DPRINTF(x) printf x
 #endif
 
 struct vidcaudio_softc {
@@ -241,18 +241,14 @@ vidcaudio_attach(struct device *parent, 
 		aprint_normal(": 8-bit internal DAC\n");
 
 	/* Install the irq handler for the DMA interrupt */
-	sc->sc_ih.ih_func = vidcaudio_intr;
-	sc->sc_ih.ih_arg = sc;
-	sc->sc_ih.ih_level = IPL_AUDIO;
-	sc->sc_ih.ih_name = self->dv_xname;
-
-	if (irq_claim(sc->sc_dma_intr, &sc->sc_ih) != 0) {
+	sc->sc_ih = iomd_intr_establish(sc->sc_dma_intr, IPL_AUDIO, self->dv_xname, vidcaudio_intr, sc);
+	if (sc->sc_ih == NULL) {
 		aprint_error("%s: couldn't claim IRQ %d\n",
 		    self->dv_xname, sc->sc_dma_intr);
 		return;
 	}
 
-	disable_irq(sc->sc_dma_intr);
+	iomd_disable_irq(sc->sc_dma_intr);
 
 	beepdev = audio_attach_mi(&vidcaudio_hw_if, sc, self);
 #if NPCKBD > 0
@@ -486,7 +482,7 @@ vidcaudio_trigger_output(void *addr, voi
 	 */
 	sc->sc_pcountdown = 2;
 
-	enable_irq(sc->sc_dma_intr);
+	iomd_enable_irq(sc->sc_dma_intr);
 
 	return 0;
 }
@@ -506,7 +502,7 @@ vidcaudio_halt_output(void *addr)
 
 	DPRINTF(("vidcaudio_halt_output\n"));
 	sc = addr;
-	disable_irq(sc->sc_dma_intr);
+	iomd_disable_irq(sc->sc_dma_intr);
 	IOMD_WRITE_WORD(IOMD_SD0CR, IOMD_DMACR_CLEAR | IOMD_DMACR_QUADWORD);
 	return 0;
 }
@@ -577,7 +573,7 @@ vidcaudio_stereo(int channel, int positi
 	WriteWord(vidc_base, channel | position);
 }
 
-static int
+int
 vidcaudio_intr(void *arg)
 {
 	struct vidcaudio_softc *sc;
Index: arm/iomd/vidcvideo.c
===================================================================
RCS file: /cvsroot/src/sys/arch/arm/iomd/vidcvideo.c,v
retrieving revision 1.31
diff -u -p -r1.31 vidcvideo.c
--- arm/iomd/vidcvideo.c	4 Mar 2007 05:59:38 -0000	1.31
+++ arm/iomd/vidcvideo.c	10 May 2007 23:11:47 -0000
@@ -380,7 +380,7 @@ vidcvideo_attach(struct device *parent, 
 	dc->rinfo.ri_hw = sc->sc_dc;
 
 	/* Establish an interrupt handler, and clear any pending interrupts */
-	intr_claim(IRQ_FLYBACK, IPL_TTY, "vblank", vidcvideointr, dc);
+	iomd_intr_establish(IRQ_FLYBACK, IPL_TTY, "vblank", vidcvideointr, dc);
 
 	waa.console = (vidcvideo_is_console ? 1 : 0);
 	waa.scrdata = &vidcvideo_screenlist;
@@ -544,7 +544,7 @@ vidcvideo_cnattach(vaddr_t addr)
 }
 
 
-static int
+int
 vidcvideointr(void *arg)
 {
 	struct fb_devconfig *dc = arg;
Index: cats/cats/autoconf.c
===================================================================
RCS file: /cvsroot/src/sys/arch/cats/cats/autoconf.c,v
retrieving revision 1.10
diff -u -p -r1.10 autoconf.c
--- cats/cats/autoconf.c	11 Dec 2005 12:17:04 -0000	1.10
+++ cats/cats/autoconf.c	10 May 2007 23:11:47 -0000
@@ -58,8 +58,6 @@ __KERNEL_RCSID(0, "$NetBSD: autoconf.c,v
 
 #include "isa.h"
 
-void isa_intr_init(void);
-
 static void get_device(const char *name);
 static void set_root_device(void);
 
@@ -133,35 +131,22 @@ cpu_rootconf(void)
  * Configure all the root devices
  * The root devices are expected to configure their own children
  */
-extern int footbridge_imask[NIPL];
 
 void
 cpu_configure(void)
 {
 	softintr_init();
-	/*
-	 * Since various PCI interrupts could be routed via the ICU
-	 * (for PCI devices in the bridge) we need to set up the ICU
-	 * now so that these interrupts can be established correctly
-	 * i.e. This is a hack.
-	 */
-	isa_intr_init();
 
 
 	config_rootfound("mainbus", NULL);
 
 	/* Debugging information */
 #ifndef TERSE
-	printf("ipl_bio=%08x ipl_net=%08x ipl_tty=%08x ipl_vm=%08x\n",
-	    footbridge_imask[IPL_BIO], footbridge_imask[IPL_NET],
-	    footbridge_imask[IPL_TTY], footbridge_imask[IPL_VM]);
-	printf("ipl_audio=%08x ipl_imp=%08x ipl_high=%08x ipl_serial=%08x\n",
-	    footbridge_imask[IPL_AUDIO], footbridge_imask[IPL_CLOCK],
-	    footbridge_imask[IPL_HIGH], footbridge_imask[IPL_SERIAL]);
+	arm_intr_print_all_masks();
 #endif
 
 	/* Time to start taking interrupts so lets open the flood gates .... */
-	(void)spl0();
+	arm_intr_enable_irqs();
 }
 
 void
Index: cats/cats/cats_machdep.c
===================================================================
RCS file: /cvsroot/src/sys/arch/cats/cats/cats_machdep.c,v
retrieving revision 1.58
diff -u -p -r1.58 cats_machdep.c
--- cats/cats/cats_machdep.c	24 Nov 2006 22:04:21 -0000	1.58
+++ cats/cats/cats_machdep.c	10 May 2007 23:11:48 -0000
@@ -884,7 +884,7 @@ initarm(void *arm_bootargs)
 
 	/* Setup the IRQ system */
 	printf("irq ");
-	footbridge_intr_init();
+	arm_intr_init();
 	printf("done.\n");
 
 #ifdef IPKDB
Index: cats/conf/GENERIC
===================================================================
RCS file: /cvsroot/src/sys/arch/cats/conf/GENERIC,v
retrieving revision 1.114
diff -u -p -r1.114 GENERIC
--- cats/conf/GENERIC	14 Mar 2007 12:27:21 -0000	1.114
+++ cats/conf/GENERIC	10 May 2007 23:11:48 -0000
@@ -196,8 +196,8 @@ options 	WSDISPLAY_COMPAT_RAWKBD		# can 
 #options 	IPKDB		# remote kernel debugging
 options 	DDB		# in-kernel debugger
 options 	DDB_HISTORY_SIZE=100	# Enable history editing in DDB
-#makeoptions	DEBUG="-g"	# compile full symbol table
-#options		SYMTAB_SPACE=380000
+makeoptions	DEBUG="-g"	# compile full symbol table
+options		SYMTAB_SPACE=450000
 
 config		netbsd	root on ? type ?
 
Index: cats/include/types.h
===================================================================
RCS file: /cvsroot/src/sys/arch/cats/include/types.h,v
retrieving revision 1.8
diff -u -p -r1.8 types.h
--- cats/include/types.h	19 Sep 2006 10:05:32 -0000	1.8
+++ cats/include/types.h	10 May 2007 23:11:48 -0000
@@ -8,5 +8,6 @@
 #define __HAVE_DEVICE_REGISTER
 #define	__HAVE_TIMECOUNTER
 #define __HAVE_GENERIC_TODR
+#define __HAVE_GENERIC_ARM_INTERRUPTS
 
 #endif

--------------090602050608010106090001--