irq.c 3.4 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149
/*
 * linux/arch/arm/mach-omap/omap2/irq.c
 *
 * Interrupt handler for OMAP2 boards.
 *
 * Copyright (C) 2005 Nokia Corporation
 * Author: Paul Mundt <paul.mundt@nokia.com>
 *
 * This file is subject to the terms and conditions of the GNU General Public
 * License. See the file "COPYING" in the main directory of this archive
 * for more details.
 */
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/config.h>
#include <linux/interrupt.h>
#include <asm/hardware.h>
#include <asm/mach/irq.h>
#include <asm/irq.h>
#include <asm/io.h>

#define INTC_REVISION	0x0000
#define INTC_SYSCONFIG	0x0010
#define INTC_SYSSTATUS	0x0014
#define INTC_CONTROL	0x0048
#define INTC_MIR_CLEAR0	0x0088
#define INTC_MIR_SET0	0x008c

/*
 * OMAP2 has a number of different interrupt controllers, each interrupt
 * controller is identified as its own "bank". Register definitions are
 * fairly consistent for each bank, but not all registers are implemented
 * for each bank.. when in doubt, consult the TRM.
 */
static struct omap_irq_bank {
	unsigned long base_reg;
	unsigned int nr_irqs;
} __attribute__ ((aligned(4))) irq_banks[] = {
	{
		/* MPU INTC */
		.base_reg	= OMAP24XX_IC_BASE,
		.nr_irqs	= 96,
	}, {
		/* XXX: DSP INTC */

#if 0
	/*
	 * Commented out for now until we fix the IVA clocking
	 */
#ifdef CONFIG_ARCH_OMAP2420
	}, {
		/* IVA INTC (2420 only) */
		.base_reg	= OMAP24XX_IVA_INTC_BASE,
		.nr_irqs	= 16,	/* Actually 32, but only 16 are used */
#endif
#endif
	}
};

/* XXX: FIQ and additional INTC support (only MPU at the moment) */
static void omap_ack_irq(unsigned int irq)
{
	omap_writel(0x1, irq_banks[0].base_reg + INTC_CONTROL);
}

static void omap_mask_irq(unsigned int irq)
{
	int offset = (irq >> 5) << 5;

	if (irq >= 64) {
		irq %= 64;
	} else if (irq >= 32) {
		irq %= 32;
	}

	omap_writel(1 << irq, irq_banks[0].base_reg + INTC_MIR_SET0 + offset);
}

static void omap_unmask_irq(unsigned int irq)
{
	int offset = (irq >> 5) << 5;

	if (irq >= 64) {
		irq %= 64;
	} else if (irq >= 32) {
		irq %= 32;
	}

	omap_writel(1 << irq, irq_banks[0].base_reg + INTC_MIR_CLEAR0 + offset);
}

static void omap_mask_ack_irq(unsigned int irq)
{
	omap_mask_irq(irq);
	omap_ack_irq(irq);
}

static struct irqchip omap_irq_chip = {
	.ack	= omap_mask_ack_irq,
	.mask	= omap_mask_irq,
	.unmask	= omap_unmask_irq,
};

static void __init omap_irq_bank_init_one(struct omap_irq_bank *bank)
{
	unsigned long tmp;

	tmp = omap_readl(bank->base_reg + INTC_REVISION) & 0xff;
	printk(KERN_INFO "IRQ: Found an INTC at 0x%08lx "
			 "(revision %ld.%ld) with %d interrupts\n",
			 bank->base_reg, tmp >> 4, tmp & 0xf, bank->nr_irqs);

	tmp = omap_readl(bank->base_reg + INTC_SYSCONFIG);
	tmp |= 1 << 1;	/* soft reset */
	omap_writel(tmp, bank->base_reg + INTC_SYSCONFIG);

	while (!(omap_readl(bank->base_reg + INTC_SYSSTATUS) & 0x1))
		/* Wait for reset to complete */;
}

void __init omap_init_irq(void)
{
	unsigned long nr_irqs = 0;
	unsigned int nr_banks = 0;
	int i;

	for (i = 0; i < ARRAY_SIZE(irq_banks); i++) {
		struct omap_irq_bank *bank = irq_banks + i;

		/* XXX */
		if (!bank->base_reg)
			continue;

		omap_irq_bank_init_one(bank);

		nr_irqs += bank->nr_irqs;
		nr_banks++;
	}

	printk(KERN_INFO "Total of %ld interrupts on %d active controller%s\n",
	       nr_irqs, nr_banks, nr_banks > 1 ? "s" : "");

	for (i = 0; i < nr_irqs; i++) {
		set_irq_chip(i, &omap_irq_chip);
		set_irq_handler(i, do_level_IRQ);
		set_irq_flags(i, IRQF_VALID);
	}
}