Back to home page

OSCL-LXR

 
 

    


0001 /*
0002  * linux/arch/arm/mach-omap1/irq.c
0003  *
0004  * Interrupt handler for all OMAP boards
0005  *
0006  * Copyright (C) 2004 Nokia Corporation
0007  * Written by Tony Lindgren <tony@atomide.com>
0008  * Major cleanups by Juha Yrjölä <juha.yrjola@nokia.com>
0009  *
0010  * Completely re-written to support various OMAP chips with bank specific
0011  * interrupt handlers.
0012  *
0013  * Some snippets of the code taken from the older OMAP interrupt handler
0014  * Copyright (C) 2001 RidgeRun, Inc. Greg Lonnon <glonnon@ridgerun.com>
0015  *
0016  * GPIO interrupt handler moved to gpio.c by Juha Yrjola
0017  *
0018  * This program is free software; you can redistribute it and/or modify it
0019  * under the terms of the GNU General Public License as published by the
0020  * Free Software Foundation; either version 2 of the License, or (at your
0021  * option) any later version.
0022  *
0023  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
0024  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
0025  * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
0026  * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
0027  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
0028  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
0029  * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
0030  * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
0031  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
0032  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
0033  *
0034  * You should have received a copy of the  GNU General Public License along
0035  * with this program; if not, write  to the Free Software Foundation, Inc.,
0036  * 675 Mass Ave, Cambridge, MA 02139, USA.
0037  */
0038 #include <linux/gpio.h>
0039 #include <linux/init.h>
0040 #include <linux/module.h>
0041 #include <linux/sched.h>
0042 #include <linux/interrupt.h>
0043 #include <linux/io.h>
0044 
0045 #include <asm/irq.h>
0046 #include <asm/exception.h>
0047 #include <asm/mach/irq.h>
0048 
0049 #include "soc.h"
0050 #include "hardware.h"
0051 #include "common.h"
0052 
0053 #define IRQ_BANK(irq) ((irq) >> 5)
0054 #define IRQ_BIT(irq)  ((irq) & 0x1f)
0055 
0056 struct omap_irq_bank {
0057     unsigned long base_reg;
0058     void __iomem *va;
0059     unsigned long trigger_map;
0060     unsigned long wake_enable;
0061 };
0062 
0063 static u32 omap_l2_irq;
0064 static unsigned int irq_bank_count;
0065 static struct omap_irq_bank *irq_banks;
0066 static struct irq_domain *domain;
0067 
0068 static inline unsigned int irq_bank_readl(int bank, int offset)
0069 {
0070     return readl_relaxed(irq_banks[bank].va + offset);
0071 }
0072 static inline void irq_bank_writel(unsigned long value, int bank, int offset)
0073 {
0074     writel_relaxed(value, irq_banks[bank].va + offset);
0075 }
0076 
0077 static void omap_ack_irq(int irq)
0078 {
0079     if (irq > 31)
0080         writel_relaxed(0x1, irq_banks[1].va + IRQ_CONTROL_REG_OFFSET);
0081 
0082     writel_relaxed(0x1, irq_banks[0].va + IRQ_CONTROL_REG_OFFSET);
0083 }
0084 
0085 static void omap_mask_ack_irq(struct irq_data *d)
0086 {
0087     struct irq_chip_type *ct = irq_data_get_chip_type(d);
0088 
0089     ct->chip.irq_mask(d);
0090     omap_ack_irq(d->irq);
0091 }
0092 
0093 /*
0094  * Allows tuning the IRQ type and priority
0095  *
0096  * NOTE: There is currently no OMAP fiq handler for Linux. Read the
0097  *   mailing list threads on FIQ handlers if you are planning to
0098  *   add a FIQ handler for OMAP.
0099  */
0100 static void omap_irq_set_cfg(int irq, int fiq, int priority, int trigger)
0101 {
0102     signed int bank;
0103     unsigned long val, offset;
0104 
0105     bank = IRQ_BANK(irq);
0106     /* FIQ is only available on bank 0 interrupts */
0107     fiq = bank ? 0 : (fiq & 0x1);
0108     val = fiq | ((priority & 0x1f) << 2) | ((trigger & 0x1) << 1);
0109     offset = IRQ_ILR0_REG_OFFSET + IRQ_BIT(irq) * 0x4;
0110     irq_bank_writel(val, bank, offset);
0111 }
0112 
0113 #if defined (CONFIG_ARCH_OMAP730) || defined (CONFIG_ARCH_OMAP850)
0114 static struct omap_irq_bank omap7xx_irq_banks[] = {
0115     { .base_reg = OMAP_IH1_BASE,        .trigger_map = 0xb3f8e22f },
0116     { .base_reg = OMAP_IH2_BASE,        .trigger_map = 0xfdb9c1f2 },
0117     { .base_reg = OMAP_IH2_BASE + 0x100,    .trigger_map = 0x800040f3 },
0118 };
0119 #endif
0120 
0121 #ifdef CONFIG_ARCH_OMAP15XX
0122 static struct omap_irq_bank omap1510_irq_banks[] = {
0123     { .base_reg = OMAP_IH1_BASE,        .trigger_map = 0xb3febfff },
0124     { .base_reg = OMAP_IH2_BASE,        .trigger_map = 0xffbfffed },
0125 };
0126 static struct omap_irq_bank omap310_irq_banks[] = {
0127     { .base_reg = OMAP_IH1_BASE,        .trigger_map = 0xb3faefc3 },
0128     { .base_reg = OMAP_IH2_BASE,        .trigger_map = 0x65b3c061 },
0129 };
0130 #endif
0131 
0132 #if defined(CONFIG_ARCH_OMAP16XX)
0133 
0134 static struct omap_irq_bank omap1610_irq_banks[] = {
0135     { .base_reg = OMAP_IH1_BASE,        .trigger_map = 0xb3fefe8f },
0136     { .base_reg = OMAP_IH2_BASE,        .trigger_map = 0xfdb7c1fd },
0137     { .base_reg = OMAP_IH2_BASE + 0x100,    .trigger_map = 0xffffb7ff },
0138     { .base_reg = OMAP_IH2_BASE + 0x200,    .trigger_map = 0xffffffff },
0139 };
0140 #endif
0141 
0142 asmlinkage void __exception_irq_entry omap1_handle_irq(struct pt_regs *regs)
0143 {
0144     void __iomem *l1 = irq_banks[0].va;
0145     void __iomem *l2 = irq_banks[1].va;
0146     u32 irqnr;
0147 
0148     do {
0149         irqnr = readl_relaxed(l1 + IRQ_ITR_REG_OFFSET);
0150         irqnr &= ~(readl_relaxed(l1 + IRQ_MIR_REG_OFFSET) & 0xffffffff);
0151         if (!irqnr)
0152             break;
0153 
0154         irqnr = readl_relaxed(l1 + IRQ_SIR_FIQ_REG_OFFSET);
0155         if (irqnr)
0156             goto irq;
0157 
0158         irqnr = readl_relaxed(l1 + IRQ_SIR_IRQ_REG_OFFSET);
0159         if (irqnr == omap_l2_irq) {
0160             irqnr = readl_relaxed(l2 + IRQ_SIR_IRQ_REG_OFFSET);
0161             if (irqnr)
0162                 irqnr += 32;
0163         }
0164 irq:
0165         if (irqnr)
0166             generic_handle_domain_irq(domain, irqnr);
0167         else
0168             break;
0169     } while (irqnr);
0170 }
0171 
0172 static __init void
0173 omap_alloc_gc(void __iomem *base, unsigned int irq_start, unsigned int num)
0174 {
0175     struct irq_chip_generic *gc;
0176     struct irq_chip_type *ct;
0177 
0178     gc = irq_alloc_generic_chip("MPU", 1, irq_start, base,
0179                     handle_level_irq);
0180     ct = gc->chip_types;
0181     ct->chip.irq_ack = omap_mask_ack_irq;
0182     ct->chip.irq_mask = irq_gc_mask_set_bit;
0183     ct->chip.irq_unmask = irq_gc_mask_clr_bit;
0184     ct->chip.irq_set_wake = irq_gc_set_wake;
0185     ct->regs.mask = IRQ_MIR_REG_OFFSET;
0186     irq_setup_generic_chip(gc, IRQ_MSK(num), IRQ_GC_INIT_MASK_CACHE,
0187                    IRQ_NOREQUEST | IRQ_NOPROBE, 0);
0188 }
0189 
0190 void __init omap1_init_irq(void)
0191 {
0192     struct irq_chip_type *ct;
0193     struct irq_data *d = NULL;
0194     int i, j, irq_base;
0195     unsigned long nr_irqs;
0196 
0197 #if defined(CONFIG_ARCH_OMAP730) || defined(CONFIG_ARCH_OMAP850)
0198     if (cpu_is_omap7xx()) {
0199         irq_banks = omap7xx_irq_banks;
0200         irq_bank_count = ARRAY_SIZE(omap7xx_irq_banks);
0201     }
0202 #endif
0203 #ifdef CONFIG_ARCH_OMAP15XX
0204     if (cpu_is_omap1510()) {
0205         irq_banks = omap1510_irq_banks;
0206         irq_bank_count = ARRAY_SIZE(omap1510_irq_banks);
0207     }
0208     if (cpu_is_omap310()) {
0209         irq_banks = omap310_irq_banks;
0210         irq_bank_count = ARRAY_SIZE(omap310_irq_banks);
0211     }
0212 #endif
0213 #if defined(CONFIG_ARCH_OMAP16XX)
0214     if (cpu_is_omap16xx()) {
0215         irq_banks = omap1610_irq_banks;
0216         irq_bank_count = ARRAY_SIZE(omap1610_irq_banks);
0217     }
0218 #endif
0219 
0220     for (i = 0; i < irq_bank_count; i++) {
0221         irq_banks[i].va = ioremap(irq_banks[i].base_reg, 0xff);
0222         if (WARN_ON(!irq_banks[i].va))
0223             return;
0224     }
0225 
0226     nr_irqs = irq_bank_count * 32;
0227 
0228     irq_base = irq_alloc_descs(-1, 0, nr_irqs, 0);
0229     if (irq_base < 0) {
0230         pr_warn("Couldn't allocate IRQ numbers\n");
0231         irq_base = 0;
0232     }
0233     omap_l2_irq = cpu_is_omap7xx() ? irq_base + 1 : irq_base;
0234     omap_l2_irq -= NR_IRQS_LEGACY;
0235 
0236     domain = irq_domain_add_legacy(NULL, nr_irqs, irq_base, 0,
0237                        &irq_domain_simple_ops, NULL);
0238 
0239     pr_info("Total of %lu interrupts in %i interrupt banks\n",
0240         nr_irqs, irq_bank_count);
0241 
0242     /* Mask and clear all interrupts */
0243     for (i = 0; i < irq_bank_count; i++) {
0244         irq_bank_writel(~0x0, i, IRQ_MIR_REG_OFFSET);
0245         irq_bank_writel(0x0, i, IRQ_ITR_REG_OFFSET);
0246     }
0247 
0248     /* Clear any pending interrupts */
0249     irq_bank_writel(0x03, 0, IRQ_CONTROL_REG_OFFSET);
0250     irq_bank_writel(0x03, 1, IRQ_CONTROL_REG_OFFSET);
0251 
0252     /* Enable interrupts in global mask */
0253     if (cpu_is_omap7xx())
0254         irq_bank_writel(0x0, 0, IRQ_GMR_REG_OFFSET);
0255 
0256     /* Install the interrupt handlers for each bank */
0257     for (i = 0; i < irq_bank_count; i++) {
0258         for (j = i * 32; j < (i + 1) * 32; j++) {
0259             int irq_trigger;
0260 
0261             irq_trigger = irq_banks[i].trigger_map >> IRQ_BIT(j);
0262             omap_irq_set_cfg(j, 0, 0, irq_trigger);
0263             irq_clear_status_flags(j, IRQ_NOREQUEST);
0264         }
0265         omap_alloc_gc(irq_banks[i].va, irq_base + i * 32, 32);
0266     }
0267 
0268     /* Unmask level 2 handler */
0269     d = irq_get_irq_data(irq_find_mapping(domain, omap_l2_irq));
0270     if (d) {
0271         ct = irq_data_get_chip_type(d);
0272         ct->chip.irq_unmask(d);
0273     }
0274 }