NetBSD-5.0.2/sys/arch/arm/omap/omap2430_intr.c

Compare this file to the similar file:
Show the results in this format:

/*	$NetBSD: omap2430_intr.c,v 1.3 2008/08/27 11:03:10 matt Exp $	*/
/*
 * Define the SDP2430 specific information and then include the generic OMAP
 * interrupt header.
 */

/*
 * Copyright (c) 2007 Microsoft
 * 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 Microsoft
 *
 * 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 CONTRIBUTERS 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 "opt_omap.h"

#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: omap2430_intr.c,v 1.3 2008/08/27 11:03:10 matt Exp $");

#include <sys/param.h>
#include <sys/evcnt.h>

#include <uvm/uvm_extern.h>

#include <machine/intr.h>

#include <arm/cpu.h>
#include <arm/armreg.h>
#include <arm/cpufunc.h>
#include <machine/atomic.h>
#include <arm/omap/omap2_reg.h>

#include <machine/bus.h>

#ifdef OMAP_2430
#define	NIGROUPS	8

#define	GPIO1_BASE	GPIO1_BASE_2430
#define	GPIO2_BASE	GPIO2_BASE_2430
#define	GPIO3_BASE	GPIO3_BASE_2430
#define	GPIO4_BASE	GPIO4_BASE_2430
#define	GPIO5_BASE	GPIO5_BASE_2430
#elif defined(OMAP_2420)
#define	NIGROUPS	7

#define	GPIO1_BASE	GPIO1_BASE_2420
#define	GPIO2_BASE	GPIO2_BASE_2420
#define	GPIO3_BASE	GPIO3_BASE_2420
#define	GPIO4_BASE	GPIO4_BASE_2420
#endif

struct intrsource {
	struct evcnt is_ev;
	uint8_t is_ipl;
	uint8_t is_group;
	int (*is_func)(void *);
	void *is_arg;
	uint64_t is_marked;
};

static struct intrgroup {
	uint32_t ig_irqsbyipl[NIPL];
	uint32_t ig_irqs;
	volatile uint32_t ig_pending_irqs;
	uint32_t ig_enabled_irqs;
	uint32_t ig_edge_rising;
	uint32_t ig_edge_falling;
	uint32_t ig_level_low;
	uint32_t ig_level_high;
	struct intrsource ig_sources[32];
	bus_space_tag_t ig_memt;
	bus_space_handle_t ig_memh;
} intrgroups[NIGROUPS] = {
	[0].ig_sources[ 0 ... 31 ].is_group = 0,
	[1].ig_sources[ 0 ... 31 ].is_group = 1,
	[2].ig_sources[ 0 ... 31 ].is_group = 2,
	[3].ig_sources[ 0 ... 31 ].is_group = 3,
	[4].ig_sources[ 0 ... 31 ].is_group = 4,
	[5].ig_sources[ 0 ... 31 ].is_group = 5,
	[6].ig_sources[ 0 ... 31 ].is_group = 6,
#ifdef OMAP_2430
	[7].ig_sources[ 0 ... 31 ].is_group = 7,
#endif
};

volatile uint32_t pending_ipls;
volatile uint32_t pending_igroupsbyipl[NIPL];
void omap2430_intr_init(bus_space_tag_t);

#define	INTC_READ(ig, o)		\
	bus_space_read_4((ig)->ig_memt, (ig)->ig_memh, o)
#define	INTC_WRITE(ig, o, v)	\
	bus_space_write_4((ig)->ig_memt, (ig)->ig_memh, o, v)
#define	GPIO_READ(ig, o)		\
	bus_space_read_4((ig)->ig_memt, (ig)->ig_memh, o)
#define	GPIO_WRITE(ig, o, v)	\
	bus_space_write_4((ig)->ig_memt, (ig)->ig_memh, o, v)

static void
unblock_irq(unsigned int group, int irq_mask)
{
	struct intrgroup * const ig = &intrgroups[group];
	KASSERT((irq_mask & ig->ig_enabled_irqs) == 0);
	ig->ig_enabled_irqs |= irq_mask;
	if (group < 3) {
		INTC_WRITE(ig, INTC_MIR_CLEAR, irq_mask);
	} else {
		GPIO_WRITE(ig, GPIO_SETIRQENABLE1, irq_mask);
		/*
		 * Clear IRQSTATUS of level interrupts, if they are still
		 * asserted, IRQSTATUS will become set again and they will
		 * refire.  This avoids one spurious interrupt for every
		 * real interrupt.
		 */
		if (irq_mask & (ig->ig_level_low|ig->ig_level_high))
			GPIO_WRITE(ig, GPIO_IRQSTATUS1,
			    irq_mask & (ig->ig_level_low|ig->ig_level_high));
	}

	/* Force INTC to recompute IRQ availability */
	INTC_WRITE(&intrgroups[0], INTC_CONTROL, INTC_CONTROL_NEWIRQAGR);
}

static void
block_irq(unsigned int group, int irq_mask)
{
	struct intrgroup * const ig = &intrgroups[group];
	ig->ig_enabled_irqs &= ~irq_mask;
	if (group < 3) {
		INTC_WRITE(ig, INTC_MIR_SET, irq_mask);
		return;
	}
	GPIO_WRITE(ig, GPIO_CLEARIRQENABLE1, irq_mask);
	/*
	 * Only clear(reenable) edge interrupts.
	 */
	if (irq_mask & (ig->ig_edge_falling|ig->ig_edge_rising))
		GPIO_WRITE(ig, GPIO_IRQSTATUS1, /* reset int bits */
		    irq_mask & (ig->ig_edge_falling|ig->ig_edge_rising));
}

static void
init_irq(int irq, int spl, int type)
{
	struct intrgroup * const ig = &intrgroups[irq / 32];
	uint32_t irq_mask = __BIT(irq & 31);
	uint32_t v;

	KASSERT(irq >= 0 && irq < 256);
	ig->ig_sources[irq & 31].is_ipl = spl;
	if (irq < 96) {
		KASSERT(type == IST_LEVEL);
		return;
	}

	ig->ig_enabled_irqs &= ~irq_mask;
	GPIO_WRITE(ig, GPIO_CLEARIRQENABLE1, irq_mask);

	v = GPIO_READ(ig, GPIO_OE);
	GPIO_WRITE(ig, GPIO_OE, v | irq_mask);	/* set as input */

	ig->ig_edge_rising &= ~irq_mask;
	ig->ig_edge_falling &= ~irq_mask;
	ig->ig_level_low &= ~irq_mask;
	ig->ig_level_high &= ~irq_mask;

	switch (type) {
	case IST_EDGE_BOTH:
		ig->ig_edge_rising |= irq_mask;
		ig->ig_edge_falling |= irq_mask;
		break;
	case IST_EDGE_RISING:
		ig->ig_edge_rising |= irq_mask;
		break;
	case IST_EDGE_FALLING:
		ig->ig_edge_falling |= irq_mask;
		break;
	case IST_LEVEL_LOW:
		ig->ig_level_low |= irq_mask;
		break;
	case IST_LEVEL_HIGH:
		ig->ig_level_high |= irq_mask;
		break;
	}

	GPIO_WRITE(ig, GPIO_LEVELDETECT0, ig->ig_level_low);
	GPIO_WRITE(ig, GPIO_LEVELDETECT1, ig->ig_level_high);
	GPIO_WRITE(ig, GPIO_RISINGDETECT, ig->ig_edge_rising);
	GPIO_WRITE(ig, GPIO_FALLINGDETECT, ig->ig_edge_falling);
}

/*
 * Called with interrupt disabled
 */
static void
calculate_irq_masks(struct intrgroup *ig)
{
	u_int irq;
	int ipl;
	uint32_t irq_mask;

	memset(ig->ig_irqsbyipl, 0, sizeof(ig->ig_irqsbyipl));
	ig->ig_irqs = 0;

	for (irq_mask = 1, irq = 0; irq < 32; irq_mask <<= 1, irq++) {
		if ((ipl = ig->ig_sources[irq].is_ipl) == IPL_NONE)
			continue;

		ig->ig_irqsbyipl[ipl] |= irq_mask;
		ig->ig_irqs |= irq_mask;
	}
}

/*
 * Called with interrupts disabled
 */
static uint32_t
mark_pending_irqs(int group, uint32_t pending)
{
	struct intrgroup * const ig = &intrgroups[group];
	struct intrsource *is;
	int n;
	int ipl_mask = 0;

	if (pending == 0)
		return ipl_mask;

	KASSERT((ig->ig_enabled_irqs & pending) == pending);
	KASSERT((ig->ig_pending_irqs & pending) == 0);

	ig->ig_pending_irqs |= pending;
	block_irq(group, pending);
	for (;;) {
		n = ffs(pending);
		if (n-- == 0)
			break;
		is = &ig->ig_sources[n];
		KASSERT(ig->ig_irqsbyipl[is->is_ipl] & pending);
		pending &= ~ig->ig_irqsbyipl[is->is_ipl];
		ipl_mask |= __BIT(is->is_ipl);
		KASSERT(ipl_mask < __BIT(NIPL));
		pending_igroupsbyipl[is->is_ipl] |= __BIT(group);
		is->is_marked++;
	}
	KASSERT(ipl_mask < __BIT(NIPL));
	return ipl_mask;
}

/*
 * Called with interrupts disabled
 */
static uint32_t
get_pending_irqs(void)
{
	uint32_t pending[3];
	uint32_t ipl_mask = 0;
	uint32_t xpending;

	pending[0] = INTC_READ(&intrgroups[0], INTC_PENDING_IRQ);
	pending[1] = INTC_READ(&intrgroups[1], INTC_PENDING_IRQ);
	pending[2] = INTC_READ(&intrgroups[2], INTC_PENDING_IRQ);

	/* Get interrupt status of GPIO1 */
	if (pending[GPIO1_MPU_IRQ / 32] & __BIT(GPIO1_MPU_IRQ & 31)) {
		KASSERT(intrgroups[3].ig_enabled_irqs);
		xpending = GPIO_READ(&intrgroups[3], GPIO_IRQSTATUS1);
		xpending &= intrgroups[3].ig_enabled_irqs;
		ipl_mask |= mark_pending_irqs(3, xpending);
	}

	/* Get interrupt status of GPIO2 */
	if (pending[GPIO2_MPU_IRQ / 32] & __BIT(GPIO2_MPU_IRQ & 31)) {
		KASSERT(intrgroups[4].ig_enabled_irqs);
		xpending = GPIO_READ(&intrgroups[4], GPIO_IRQSTATUS1);
		xpending &= intrgroups[4].ig_enabled_irqs;
		ipl_mask |= mark_pending_irqs(4, xpending);
	}

	/* Get interrupt status of GPIO3 */
	if (pending[GPIO3_MPU_IRQ / 32] & __BIT(GPIO3_MPU_IRQ & 31)) {
		KASSERT(intrgroups[5].ig_enabled_irqs);
		xpending = GPIO_READ(&intrgroups[5], GPIO_IRQSTATUS1);
		xpending &= intrgroups[5].ig_enabled_irqs;
		ipl_mask |= mark_pending_irqs(5, xpending);
	}

	/* Get interrupt status of GPIO4 */
	if (pending[GPIO4_MPU_IRQ / 32] & __BIT(GPIO4_MPU_IRQ & 31)) {
		KASSERT(intrgroups[6].ig_enabled_irqs);
		xpending = GPIO_READ(&intrgroups[6], GPIO_IRQSTATUS1);
		xpending &= intrgroups[6].ig_enabled_irqs;
		ipl_mask |= mark_pending_irqs(6, xpending);
	}

#ifdef OMAP_2430
	/* Get interrupt status of GPIO5 */
	if (pending[GPIO5_MPU_IRQ / 32] & __BIT(GPIO5_MPU_IRQ & 31)) {
		KASSERT(intrgroups[7].ig_enabled_irqs);
		xpending = GPIO_READ(&intrgroups[7], GPIO_IRQSTATUS1);
		xpending = GPIO_READ(&intrgroups[7], GPIO_IRQSTATUS1);
		xpending &= intrgroups[7].ig_enabled_irqs;
		ipl_mask |= mark_pending_irqs(7, xpending);
	}
#endif

	/* Clear GPIO indication from summaries */
	pending[GPIO1_MPU_IRQ / 32] &= ~__BIT(GPIO1_MPU_IRQ & 31);
	pending[GPIO2_MPU_IRQ / 32] &= ~__BIT(GPIO2_MPU_IRQ & 31);
	pending[GPIO3_MPU_IRQ / 32] &= ~__BIT(GPIO3_MPU_IRQ & 31);
	pending[GPIO4_MPU_IRQ / 32] &= ~__BIT(GPIO4_MPU_IRQ & 31);
#ifdef OMAP_2430
	pending[GPIO5_MPU_IRQ / 32] &= ~__BIT(GPIO5_MPU_IRQ & 31);
#endif

	/* Now handle the primaries interrupt summaries */
	ipl_mask |= mark_pending_irqs(0, pending[0]);
	ipl_mask |= mark_pending_irqs(1, pending[1]);
	ipl_mask |= mark_pending_irqs(2, pending[2]);

	/* Force INTC to recompute IRQ availability */
	INTC_WRITE(&intrgroups[0], INTC_CONTROL, INTC_CONTROL_NEWIRQAGR);

	return ipl_mask;
}

static int last_delivered_ipl;
static u_long no_pending_irqs[NIPL][NIGROUPS];

static void
deliver_irqs(register_t psw, int ipl, void *frame)
{
	struct intrgroup *ig;
	struct intrsource *is;
	uint32_t pending_irqs;
	uint32_t irq_mask;
	uint32_t blocked_irqs;
	volatile uint32_t * const pending_igroups = &pending_igroupsbyipl[ipl];
	const uint32_t ipl_mask = __BIT(ipl);
	int n;
	int saved_ipl = IPL_NONE;	/* XXX stupid GCC */
	unsigned int group;
	int rv;

	if (frame == NULL) {
		saved_ipl = last_delivered_ipl;
		KASSERT(saved_ipl < ipl);
		last_delivered_ipl = ipl;
	}
	/*
	 * We only must be called is there is this IPL has pending interrupts
	 * and therefore there must be at least one intrgroup with a pending
	 * interrupt.
	 */
	KASSERT(pending_ipls & ipl_mask);
	KASSERT(*pending_igroups);

	/*
	 * We loop until there are no more intrgroups with pending interrupts.
	 */
	do {
		group = 31 - __builtin_clz(*pending_igroups);
		KASSERT(group < NIGROUPS);

		ig = &intrgroups[group];
		irq_mask = ig->ig_irqsbyipl[ipl];
		pending_irqs = ig->ig_pending_irqs & irq_mask;
		blocked_irqs = pending_irqs;
		if ((*pending_igroups &= ~__BIT(group)) == 0)
			pending_ipls &= ~ipl_mask;
#if 0
		KASSERT(group < 3 || (GPIO_READ(ig, GPIO_IRQSTATUS1) & blocked_irqs) == 0);
#endif
		/*
		 * We couldn't gotten here unless there was at least one
		 * pending interrupt in this intrgroup.
		 */
		if (pending_irqs == 0) {
			no_pending_irqs[ipl][group]++;
			continue;
		}
#if 0
		KASSERT(pending_irqs != 0);
#endif
		do {
			n = 31 - __builtin_clz(pending_irqs);
			KASSERT(ig->ig_irqs & __BIT(n));
			KASSERT(irq_mask & __BIT(n));

			/*
			 * If this was the last bit cleared for this IRQ,
			 * we need to clear this group's bit in 
			 * pending_igroupsbyipl[ipl].  Now if that's now 0,
			 * we need to clear pending_ipls for this IPL.
			 */
			ig->ig_pending_irqs &= ~__BIT(n);
			if (irq_mask == __BIT(n))
				KASSERT((ig->ig_pending_irqs & irq_mask) == 0);
			is = &ig->ig_sources[n];
			if (__predict_false(frame != NULL)) {
				(*is->is_func)(frame);
			} else {
				restore_interrupts(psw);
				rv = (*is->is_func)(is->is_arg);
				disable_interrupts(I32_bit);
			}
#if 0
			if (rv && group >= 3) /* XXX */
				GPIO_WRITE(ig, GPIO_IRQSTATUS1, __BIT(n));
#endif
#if 0
			if (ig->ig_irqsbyipl[ipl] == __BIT(n))
				KASSERT((ig->ig_pending_irqs & irq_mask) == 0);
#endif
			is->is_ev.ev_count++;
			pending_irqs = ig->ig_pending_irqs & irq_mask;
		} while (pending_irqs);
		/*
		 * We don't block the interrupts individually because even if
		 * one was unblocked it couldn't be delivered since our
		 * current IPL would prevent it.  So we wait until we can do
		 * them all at once.
		 */
#if 0
		KASSERT(group < 3 || (GPIO_READ(ig, GPIO_IRQSTATUS1) & blocked_irqs) == 0);
#endif
		unblock_irq(group, blocked_irqs);
	} while (*pending_igroups);
	/*
	 * Since there are no more pending interrupts for this IPL,
	 * this IPL must not be present in the pending IPLs.
	 */
	KASSERT((pending_ipls & ipl_mask) == 0);
	KASSERT((intrgroups[0].ig_pending_irqs & intrgroups[0].ig_irqsbyipl[ipl]) == 0);
	KASSERT((intrgroups[1].ig_pending_irqs & intrgroups[1].ig_irqsbyipl[ipl]) == 0);
	KASSERT((intrgroups[2].ig_pending_irqs & intrgroups[2].ig_irqsbyipl[ipl]) == 0);
	KASSERT((intrgroups[3].ig_pending_irqs & intrgroups[3].ig_irqsbyipl[ipl]) == 0);
	KASSERT((intrgroups[4].ig_pending_irqs & intrgroups[4].ig_irqsbyipl[ipl]) == 0);
	KASSERT((intrgroups[5].ig_pending_irqs & intrgroups[5].ig_irqsbyipl[ipl]) == 0);
	KASSERT((intrgroups[6].ig_pending_irqs & intrgroups[6].ig_irqsbyipl[ipl]) == 0);
	KASSERT((intrgroups[7].ig_pending_irqs & intrgroups[7].ig_irqsbyipl[ipl]) == 0);
	if (frame == NULL)
		last_delivered_ipl = saved_ipl;
}

static inline void
do_pending_ints(register_t psw, int newipl)
{
	while ((pending_ipls & ~__BIT(newipl)) > __BIT(newipl)) {
		KASSERT(pending_ipls < __BIT(NIPL));
		for (;;) {
			int ipl = 31 - __builtin_clz(pending_ipls);
			KASSERT(ipl < NIPL);
			if (ipl <= newipl)
				break;
		
			curcpu()->ci_cpl = ipl;
			deliver_irqs(psw, ipl, NULL);
		}
	}
	curcpu()->ci_cpl = newipl;
}

int
_splraise(int newipl)
{
	const int oldipl = curcpu()->ci_cpl;
	KASSERT(newipl < NIPL);
	if (newipl > curcpu()->ci_cpl)
		curcpu()->ci_cpl = newipl;
	return oldipl;
}
int
_spllower(int newipl)
{
	const int oldipl = curcpu()->ci_cpl;
	KASSERT(panicstr || newipl <= curcpu()->ci_cpl);
	if (newipl < curcpu()->ci_cpl) {
		register_t psw = disable_interrupts(I32_bit);
		do_pending_ints(psw, newipl);
		restore_interrupts(psw);
	}
	return oldipl;
}

void
splx(int savedipl)
{
	KASSERT(savedipl < NIPL);
	if (savedipl < curcpu()->ci_cpl) {
		register_t psw = disable_interrupts(I32_bit);
		do_pending_ints(psw, savedipl);
		restore_interrupts(psw);
	}
	curcpu()->ci_cpl = savedipl;
}

void
omap_irq_handler(void *frame)
{
	const int oldipl = curcpu()->ci_cpl;
	const uint32_t oldipl_mask = __BIT(oldipl);

	/*
	 * When we enter there must be no pending IRQs for IPL greater than
	 * the current IPL.  There might be pending IRQs for the current IPL
	 * if we are servicing interrupts.
	 */
	KASSERT((pending_ipls & ~oldipl_mask) < oldipl_mask);
	pending_ipls |= get_pending_irqs();

	uvmexp.intrs++;
	/*
	 * We assume this isn't a clock intr.  But if it is, deliver it 
	 * unconditionally so it will always have the interrupted frame.
	 * The clock intr will handle being called at IPLs != IPL_CLOCK.
	 */
	if (__predict_false(pending_ipls & __BIT(IPL_STATCLOCK))) {
		deliver_irqs(0, IPL_STATCLOCK, frame);
		pending_ipls &= ~__BIT(IPL_STATCLOCK);
	}
	if (__predict_false(pending_ipls & __BIT(IPL_CLOCK))) {
		deliver_irqs(0, IPL_CLOCK, frame);
		pending_ipls &= ~__BIT(IPL_CLOCK);
	}

	/*
	 * Record the pending_ipls and deliver them if we can.
	 */
	if ((pending_ipls & ~oldipl_mask) > oldipl_mask)
		do_pending_ints(I32_bit, oldipl);
}

void *
omap_intr_establish(int irq, int ipl, const char *name,
	int (*func)(void *), void *arg)
{
	struct intrgroup *ig = &intrgroups[irq / 32];
	struct intrsource *is;
	register_t psw;

	KASSERT(irq >= 0 && irq < 256);
	is = &ig->ig_sources[irq & 0x1f];
	KASSERT(irq != GPIO1_MPU_IRQ);
	KASSERT(irq != GPIO2_MPU_IRQ);
	KASSERT(irq != GPIO3_MPU_IRQ);
	KASSERT(irq != GPIO4_MPU_IRQ);
	KASSERT(irq != GPIO5_MPU_IRQ);
	KASSERT(is->is_ipl == IPL_NONE);

	is->is_func = func;
	is->is_arg = arg;
	psw = disable_interrupts(I32_bit);
	evcnt_attach_dynamic(&is->is_ev, EVCNT_TYPE_INTR, NULL, name, "intr");
	init_irq(irq, ipl, IST_LEVEL);

	calculate_irq_masks(ig);
	unblock_irq(is->is_group, __BIT(irq & 31));
	restore_interrupts(psw);
	return is;
}

void
omap_intr_disestablish(void *ih)
{
	struct intrsource * const is = ih;
	struct intrgroup *ig;
	register_t psw;
	uint32_t mask;

	KASSERT(ih != NULL);

	ig = &intrgroups[is->is_group];
	psw = disable_interrupts(I32_bit);
	mask = __BIT(is - ig->ig_sources);
	block_irq(is->is_group, mask);
	ig->ig_pending_irqs &= ~mask;
	calculate_irq_masks(ig);
	evcnt_detach(&is->is_ev);
	restore_interrupts(psw);
}

#ifdef GPIO5_BASE
static void
gpio5_clkinit(bus_space_tag_t memt)
{
	bus_space_handle_t memh;
	uint32_t r;
	int error;

	error = bus_space_map(memt, OMAP2430_CM_BASE,
	    OMAP2430_CM_SIZE, 0, &memh);
	if (error != 0)
		panic("%s: cannot map OMAP2430_CM_BASE at %#x: %d\n",
			__func__, OMAP2430_CM_BASE, error);

	r = bus_space_read_4(memt, memh, OMAP2430_CM_FCLKEN2_CORE);
	r |= OMAP2430_CM_FCLKEN2_CORE_EN_GPIO5;
	bus_space_write_4(memt, memh, OMAP2430_CM_FCLKEN2_CORE, r);

	r = bus_space_read_4(memt, memh, OMAP2430_CM_ICLKEN2_CORE);
	r |= OMAP2430_CM_ICLKEN2_CORE_EN_GPIO5;
	bus_space_write_4(memt, memh, OMAP2430_CM_ICLKEN2_CORE, r);

	bus_space_unmap(memt, memh, OMAP2430_CM_SIZE);
}
#endif

void
omap2430_intr_init(bus_space_tag_t memt)
{
	int error;
	int group;

	for (group = 0; group < NIGROUPS; group++)
		intrgroups[group].ig_memt = memt;
	error = bus_space_map(memt, INTC_BASE, 0x1000, 0,
	    &intrgroups[0].ig_memh);
	if (error)
		panic("failed to map interrupt registers: %d", error);
	error = bus_space_subregion(memt, intrgroups[0].ig_memh, 0x20, 0x20,
	    &intrgroups[1].ig_memh);
	if (error)
		panic("failed to region interrupt registers: %d", error);
	error = bus_space_subregion(memt, intrgroups[0].ig_memh, 0x40, 0x20,
	    &intrgroups[2].ig_memh);
	if (error)
		panic("failed to subregion interrupt registers: %d", error);
	error = bus_space_map(memt, GPIO1_BASE, 0x400, 0,
	    &intrgroups[3].ig_memh);
	if (error)
		panic("failed to map gpio #1 registers: %d", error);
	error = bus_space_map(memt, GPIO2_BASE, 0x400, 0,
	    &intrgroups[4].ig_memh);
	if (error)
		panic("failed to map gpio #2 registers: %d", error);
	error = bus_space_map(memt, GPIO3_BASE, 0x400, 0,
	    &intrgroups[5].ig_memh);
	if (error)
		panic("failed to map gpio #3 registers: %d", error);
	error = bus_space_map(memt, GPIO4_BASE, 0x400, 0,
	    &intrgroups[6].ig_memh);
	if (error)
		panic("failed to map gpio #4 registers: %d", error);

#ifdef GPIO5_BASE
	gpio5_clkinit(memt);
	error = bus_space_map(memt, GPIO5_BASE, 0x400, 0,
	    &intrgroups[7].ig_memh);
	if (error)
		panic("failed to map gpio #5 registers: %d", error);
#endif

	INTC_WRITE(&intrgroups[0], INTC_MIR_SET, 0xffffffff);
	INTC_WRITE(&intrgroups[1], INTC_MIR_SET, 0xffffffff);
	INTC_WRITE(&intrgroups[2], INTC_MIR_SET, 0xffffffff);
	INTC_WRITE(&intrgroups[GPIO1_MPU_IRQ / 32], INTC_MIR_CLEAR,
	    __BIT(GPIO1_MPU_IRQ & 31));
	INTC_WRITE(&intrgroups[GPIO2_MPU_IRQ / 32], INTC_MIR_CLEAR,
	    __BIT(GPIO2_MPU_IRQ & 31));
	INTC_WRITE(&intrgroups[GPIO3_MPU_IRQ / 32], INTC_MIR_CLEAR,
	    __BIT(GPIO3_MPU_IRQ & 31));
	INTC_WRITE(&intrgroups[GPIO4_MPU_IRQ / 32], INTC_MIR_CLEAR,
	    __BIT(GPIO4_MPU_IRQ & 31));
#ifdef GPIO5_BASE
	INTC_WRITE(&intrgroups[GPIO5_MPU_IRQ / 32], INTC_MIR_CLEAR,
	    __BIT(GPIO5_MPU_IRQ & 31));
#endif

	/*
	 * Setup the primary intrgroups.
	 */
	calculate_irq_masks(&intrgroups[0]);
	calculate_irq_masks(&intrgroups[1]);
	calculate_irq_masks(&intrgroups[2]);
}