162306a36Sopenharmony_ci/* 262306a36Sopenharmony_ci * linux/arch/arm/mach-omap1/irq.c 362306a36Sopenharmony_ci * 462306a36Sopenharmony_ci * Interrupt handler for all OMAP boards 562306a36Sopenharmony_ci * 662306a36Sopenharmony_ci * Copyright (C) 2004 Nokia Corporation 762306a36Sopenharmony_ci * Written by Tony Lindgren <tony@atomide.com> 862306a36Sopenharmony_ci * Major cleanups by Juha Yrjölä <juha.yrjola@nokia.com> 962306a36Sopenharmony_ci * 1062306a36Sopenharmony_ci * Completely re-written to support various OMAP chips with bank specific 1162306a36Sopenharmony_ci * interrupt handlers. 1262306a36Sopenharmony_ci * 1362306a36Sopenharmony_ci * Some snippets of the code taken from the older OMAP interrupt handler 1462306a36Sopenharmony_ci * Copyright (C) 2001 RidgeRun, Inc. Greg Lonnon <glonnon@ridgerun.com> 1562306a36Sopenharmony_ci * 1662306a36Sopenharmony_ci * GPIO interrupt handler moved to gpio.c by Juha Yrjola 1762306a36Sopenharmony_ci * 1862306a36Sopenharmony_ci * This program is free software; you can redistribute it and/or modify it 1962306a36Sopenharmony_ci * under the terms of the GNU General Public License as published by the 2062306a36Sopenharmony_ci * Free Software Foundation; either version 2 of the License, or (at your 2162306a36Sopenharmony_ci * option) any later version. 2262306a36Sopenharmony_ci * 2362306a36Sopenharmony_ci * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED 2462306a36Sopenharmony_ci * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 2562306a36Sopenharmony_ci * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN 2662306a36Sopenharmony_ci * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 2762306a36Sopenharmony_ci * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 2862306a36Sopenharmony_ci * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF 2962306a36Sopenharmony_ci * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 3062306a36Sopenharmony_ci * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 3162306a36Sopenharmony_ci * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 3262306a36Sopenharmony_ci * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 3362306a36Sopenharmony_ci * 3462306a36Sopenharmony_ci * You should have received a copy of the GNU General Public License along 3562306a36Sopenharmony_ci * with this program; if not, write to the Free Software Foundation, Inc., 3662306a36Sopenharmony_ci * 675 Mass Ave, Cambridge, MA 02139, USA. 3762306a36Sopenharmony_ci */ 3862306a36Sopenharmony_ci#include <linux/init.h> 3962306a36Sopenharmony_ci#include <linux/irq.h> 4062306a36Sopenharmony_ci#include <linux/module.h> 4162306a36Sopenharmony_ci#include <linux/sched.h> 4262306a36Sopenharmony_ci#include <linux/interrupt.h> 4362306a36Sopenharmony_ci#include <linux/io.h> 4462306a36Sopenharmony_ci#include <linux/irqdomain.h> 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_ci#include <asm/irq.h> 4762306a36Sopenharmony_ci#include <asm/exception.h> 4862306a36Sopenharmony_ci#include <asm/mach/irq.h> 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_ci#include "soc.h" 5162306a36Sopenharmony_ci#include "hardware.h" 5262306a36Sopenharmony_ci#include "common.h" 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_ci#define IRQ_BANK(irq) ((irq) >> 5) 5562306a36Sopenharmony_ci#define IRQ_BIT(irq) ((irq) & 0x1f) 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_cistruct omap_irq_bank { 5862306a36Sopenharmony_ci unsigned long base_reg; 5962306a36Sopenharmony_ci void __iomem *va; 6062306a36Sopenharmony_ci unsigned long trigger_map; 6162306a36Sopenharmony_ci unsigned long wake_enable; 6262306a36Sopenharmony_ci}; 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_cistatic u32 omap_l2_irq; 6562306a36Sopenharmony_cistatic unsigned int irq_bank_count; 6662306a36Sopenharmony_cistatic struct omap_irq_bank *irq_banks; 6762306a36Sopenharmony_cistatic struct irq_domain *domain; 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_cistatic inline unsigned int irq_bank_readl(int bank, int offset) 7062306a36Sopenharmony_ci{ 7162306a36Sopenharmony_ci return readl_relaxed(irq_banks[bank].va + offset); 7262306a36Sopenharmony_ci} 7362306a36Sopenharmony_cistatic inline void irq_bank_writel(unsigned long value, int bank, int offset) 7462306a36Sopenharmony_ci{ 7562306a36Sopenharmony_ci writel_relaxed(value, irq_banks[bank].va + offset); 7662306a36Sopenharmony_ci} 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_cistatic void omap_ack_irq(int irq) 7962306a36Sopenharmony_ci{ 8062306a36Sopenharmony_ci if (irq > 31) 8162306a36Sopenharmony_ci writel_relaxed(0x1, irq_banks[1].va + IRQ_CONTROL_REG_OFFSET); 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_ci writel_relaxed(0x1, irq_banks[0].va + IRQ_CONTROL_REG_OFFSET); 8462306a36Sopenharmony_ci} 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_cistatic void omap_mask_ack_irq(struct irq_data *d) 8762306a36Sopenharmony_ci{ 8862306a36Sopenharmony_ci struct irq_chip_type *ct = irq_data_get_chip_type(d); 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_ci ct->chip.irq_mask(d); 9162306a36Sopenharmony_ci omap_ack_irq(d->irq); 9262306a36Sopenharmony_ci} 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_ci/* 9562306a36Sopenharmony_ci * Allows tuning the IRQ type and priority 9662306a36Sopenharmony_ci * 9762306a36Sopenharmony_ci * NOTE: There is currently no OMAP fiq handler for Linux. Read the 9862306a36Sopenharmony_ci * mailing list threads on FIQ handlers if you are planning to 9962306a36Sopenharmony_ci * add a FIQ handler for OMAP. 10062306a36Sopenharmony_ci */ 10162306a36Sopenharmony_cistatic void omap_irq_set_cfg(int irq, int fiq, int priority, int trigger) 10262306a36Sopenharmony_ci{ 10362306a36Sopenharmony_ci signed int bank; 10462306a36Sopenharmony_ci unsigned long val, offset; 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_ci bank = IRQ_BANK(irq); 10762306a36Sopenharmony_ci /* FIQ is only available on bank 0 interrupts */ 10862306a36Sopenharmony_ci fiq = bank ? 0 : (fiq & 0x1); 10962306a36Sopenharmony_ci val = fiq | ((priority & 0x1f) << 2) | ((trigger & 0x1) << 1); 11062306a36Sopenharmony_ci offset = IRQ_ILR0_REG_OFFSET + IRQ_BIT(irq) * 0x4; 11162306a36Sopenharmony_ci irq_bank_writel(val, bank, offset); 11262306a36Sopenharmony_ci} 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_ci#ifdef CONFIG_ARCH_OMAP15XX 11562306a36Sopenharmony_cistatic struct omap_irq_bank omap1510_irq_banks[] = { 11662306a36Sopenharmony_ci { .base_reg = OMAP_IH1_BASE, .trigger_map = 0xb3febfff }, 11762306a36Sopenharmony_ci { .base_reg = OMAP_IH2_BASE, .trigger_map = 0xffbfffed }, 11862306a36Sopenharmony_ci}; 11962306a36Sopenharmony_cistatic struct omap_irq_bank omap310_irq_banks[] = { 12062306a36Sopenharmony_ci { .base_reg = OMAP_IH1_BASE, .trigger_map = 0xb3faefc3 }, 12162306a36Sopenharmony_ci { .base_reg = OMAP_IH2_BASE, .trigger_map = 0x65b3c061 }, 12262306a36Sopenharmony_ci}; 12362306a36Sopenharmony_ci#endif 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_ci#if defined(CONFIG_ARCH_OMAP16XX) 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_cistatic struct omap_irq_bank omap1610_irq_banks[] = { 12862306a36Sopenharmony_ci { .base_reg = OMAP_IH1_BASE, .trigger_map = 0xb3fefe8f }, 12962306a36Sopenharmony_ci { .base_reg = OMAP_IH2_BASE, .trigger_map = 0xfdb7c1fd }, 13062306a36Sopenharmony_ci { .base_reg = OMAP_IH2_BASE + 0x100, .trigger_map = 0xffffb7ff }, 13162306a36Sopenharmony_ci { .base_reg = OMAP_IH2_BASE + 0x200, .trigger_map = 0xffffffff }, 13262306a36Sopenharmony_ci}; 13362306a36Sopenharmony_ci#endif 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_ciasmlinkage void __exception_irq_entry omap1_handle_irq(struct pt_regs *regs) 13662306a36Sopenharmony_ci{ 13762306a36Sopenharmony_ci void __iomem *l1 = irq_banks[0].va; 13862306a36Sopenharmony_ci void __iomem *l2 = irq_banks[1].va; 13962306a36Sopenharmony_ci u32 irqnr; 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_ci do { 14262306a36Sopenharmony_ci irqnr = readl_relaxed(l1 + IRQ_ITR_REG_OFFSET); 14362306a36Sopenharmony_ci irqnr &= ~(readl_relaxed(l1 + IRQ_MIR_REG_OFFSET) & 0xffffffff); 14462306a36Sopenharmony_ci if (!irqnr) 14562306a36Sopenharmony_ci break; 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_ci irqnr = readl_relaxed(l1 + IRQ_SIR_FIQ_REG_OFFSET); 14862306a36Sopenharmony_ci if (irqnr) 14962306a36Sopenharmony_ci goto irq; 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_ci irqnr = readl_relaxed(l1 + IRQ_SIR_IRQ_REG_OFFSET); 15262306a36Sopenharmony_ci if (irqnr == omap_l2_irq) { 15362306a36Sopenharmony_ci irqnr = readl_relaxed(l2 + IRQ_SIR_IRQ_REG_OFFSET); 15462306a36Sopenharmony_ci if (irqnr) 15562306a36Sopenharmony_ci irqnr += 32; 15662306a36Sopenharmony_ci } 15762306a36Sopenharmony_ciirq: 15862306a36Sopenharmony_ci if (irqnr) 15962306a36Sopenharmony_ci generic_handle_domain_irq(domain, irqnr); 16062306a36Sopenharmony_ci else 16162306a36Sopenharmony_ci break; 16262306a36Sopenharmony_ci } while (irqnr); 16362306a36Sopenharmony_ci} 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_cistatic __init void 16662306a36Sopenharmony_ciomap_alloc_gc(void __iomem *base, unsigned int irq_start, unsigned int num) 16762306a36Sopenharmony_ci{ 16862306a36Sopenharmony_ci struct irq_chip_generic *gc; 16962306a36Sopenharmony_ci struct irq_chip_type *ct; 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_ci gc = irq_alloc_generic_chip("MPU", 1, irq_start, base, 17262306a36Sopenharmony_ci handle_level_irq); 17362306a36Sopenharmony_ci ct = gc->chip_types; 17462306a36Sopenharmony_ci ct->chip.irq_ack = omap_mask_ack_irq; 17562306a36Sopenharmony_ci ct->chip.irq_mask = irq_gc_mask_set_bit; 17662306a36Sopenharmony_ci ct->chip.irq_unmask = irq_gc_mask_clr_bit; 17762306a36Sopenharmony_ci ct->chip.irq_set_wake = irq_gc_set_wake; 17862306a36Sopenharmony_ci ct->regs.mask = IRQ_MIR_REG_OFFSET; 17962306a36Sopenharmony_ci irq_setup_generic_chip(gc, IRQ_MSK(num), IRQ_GC_INIT_MASK_CACHE, 18062306a36Sopenharmony_ci IRQ_NOREQUEST | IRQ_NOPROBE, 0); 18162306a36Sopenharmony_ci} 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_civoid __init omap1_init_irq(void) 18462306a36Sopenharmony_ci{ 18562306a36Sopenharmony_ci struct irq_chip_type *ct; 18662306a36Sopenharmony_ci struct irq_data *d = NULL; 18762306a36Sopenharmony_ci int i, j, irq_base; 18862306a36Sopenharmony_ci unsigned long nr_irqs; 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_ci#ifdef CONFIG_ARCH_OMAP15XX 19162306a36Sopenharmony_ci if (cpu_is_omap1510()) { 19262306a36Sopenharmony_ci irq_banks = omap1510_irq_banks; 19362306a36Sopenharmony_ci irq_bank_count = ARRAY_SIZE(omap1510_irq_banks); 19462306a36Sopenharmony_ci } 19562306a36Sopenharmony_ci if (cpu_is_omap310()) { 19662306a36Sopenharmony_ci irq_banks = omap310_irq_banks; 19762306a36Sopenharmony_ci irq_bank_count = ARRAY_SIZE(omap310_irq_banks); 19862306a36Sopenharmony_ci } 19962306a36Sopenharmony_ci#endif 20062306a36Sopenharmony_ci#if defined(CONFIG_ARCH_OMAP16XX) 20162306a36Sopenharmony_ci if (cpu_is_omap16xx()) { 20262306a36Sopenharmony_ci irq_banks = omap1610_irq_banks; 20362306a36Sopenharmony_ci irq_bank_count = ARRAY_SIZE(omap1610_irq_banks); 20462306a36Sopenharmony_ci } 20562306a36Sopenharmony_ci#endif 20662306a36Sopenharmony_ci 20762306a36Sopenharmony_ci for (i = 0; i < irq_bank_count; i++) { 20862306a36Sopenharmony_ci irq_banks[i].va = ioremap(irq_banks[i].base_reg, 0xff); 20962306a36Sopenharmony_ci if (WARN_ON(!irq_banks[i].va)) 21062306a36Sopenharmony_ci return; 21162306a36Sopenharmony_ci } 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_ci nr_irqs = irq_bank_count * 32; 21462306a36Sopenharmony_ci 21562306a36Sopenharmony_ci irq_base = irq_alloc_descs(-1, 0, nr_irqs, 0); 21662306a36Sopenharmony_ci if (irq_base < 0) { 21762306a36Sopenharmony_ci pr_warn("Couldn't allocate IRQ numbers\n"); 21862306a36Sopenharmony_ci irq_base = 0; 21962306a36Sopenharmony_ci } 22062306a36Sopenharmony_ci omap_l2_irq = irq_base; 22162306a36Sopenharmony_ci omap_l2_irq -= NR_IRQS_LEGACY; 22262306a36Sopenharmony_ci 22362306a36Sopenharmony_ci domain = irq_domain_add_legacy(NULL, nr_irqs, irq_base, 0, 22462306a36Sopenharmony_ci &irq_domain_simple_ops, NULL); 22562306a36Sopenharmony_ci 22662306a36Sopenharmony_ci pr_info("Total of %lu interrupts in %i interrupt banks\n", 22762306a36Sopenharmony_ci nr_irqs, irq_bank_count); 22862306a36Sopenharmony_ci 22962306a36Sopenharmony_ci /* Mask and clear all interrupts */ 23062306a36Sopenharmony_ci for (i = 0; i < irq_bank_count; i++) { 23162306a36Sopenharmony_ci irq_bank_writel(~0x0, i, IRQ_MIR_REG_OFFSET); 23262306a36Sopenharmony_ci irq_bank_writel(0x0, i, IRQ_ITR_REG_OFFSET); 23362306a36Sopenharmony_ci } 23462306a36Sopenharmony_ci 23562306a36Sopenharmony_ci /* Clear any pending interrupts */ 23662306a36Sopenharmony_ci irq_bank_writel(0x03, 0, IRQ_CONTROL_REG_OFFSET); 23762306a36Sopenharmony_ci irq_bank_writel(0x03, 1, IRQ_CONTROL_REG_OFFSET); 23862306a36Sopenharmony_ci 23962306a36Sopenharmony_ci /* Install the interrupt handlers for each bank */ 24062306a36Sopenharmony_ci for (i = 0; i < irq_bank_count; i++) { 24162306a36Sopenharmony_ci for (j = i * 32; j < (i + 1) * 32; j++) { 24262306a36Sopenharmony_ci int irq_trigger; 24362306a36Sopenharmony_ci 24462306a36Sopenharmony_ci irq_trigger = irq_banks[i].trigger_map >> IRQ_BIT(j); 24562306a36Sopenharmony_ci omap_irq_set_cfg(j, 0, 0, irq_trigger); 24662306a36Sopenharmony_ci irq_clear_status_flags(j, IRQ_NOREQUEST); 24762306a36Sopenharmony_ci } 24862306a36Sopenharmony_ci omap_alloc_gc(irq_banks[i].va, irq_base + i * 32, 32); 24962306a36Sopenharmony_ci } 25062306a36Sopenharmony_ci 25162306a36Sopenharmony_ci /* Unmask level 2 handler */ 25262306a36Sopenharmony_ci d = irq_get_irq_data(irq_find_mapping(domain, omap_l2_irq)); 25362306a36Sopenharmony_ci if (d) { 25462306a36Sopenharmony_ci ct = irq_data_get_chip_type(d); 25562306a36Sopenharmony_ci ct->chip.irq_unmask(d); 25662306a36Sopenharmony_ci } 25762306a36Sopenharmony_ci 25862306a36Sopenharmony_ci set_handle_irq(omap1_handle_irq); 25962306a36Sopenharmony_ci} 260