linux/arch/arm/mach-omap2/irq.c
<<
>>
Prefs
   1/*
   2 * linux/arch/arm/mach-omap2/irq.c
   3 *
   4 * Interrupt handler for OMAP2 boards.
   5 *
   6 * Copyright (C) 2005 Nokia Corporation
   7 * Author: Paul Mundt <paul.mundt@nokia.com>
   8 *
   9 * This file is subject to the terms and conditions of the GNU General Public
  10 * License. See the file "COPYING" in the main directory of this archive
  11 * for more details.
  12 */
  13#include <linux/kernel.h>
  14#include <linux/init.h>
  15#include <linux/interrupt.h>
  16#include <linux/io.h>
  17#include <mach/hardware.h>
  18#include <asm/mach/irq.h>
  19
  20
  21/* selected INTC register offsets */
  22
  23#define INTC_REVISION           0x0000
  24#define INTC_SYSCONFIG          0x0010
  25#define INTC_SYSSTATUS          0x0014
  26#define INTC_SIR                0x0040
  27#define INTC_CONTROL            0x0048
  28#define INTC_PROTECTION         0x004C
  29#define INTC_IDLE               0x0050
  30#define INTC_THRESHOLD          0x0068
  31#define INTC_MIR0               0x0084
  32#define INTC_MIR_CLEAR0         0x0088
  33#define INTC_MIR_SET0           0x008c
  34#define INTC_PENDING_IRQ0       0x0098
  35/* Number of IRQ state bits in each MIR register */
  36#define IRQ_BITS_PER_REG        32
  37
  38/*
  39 * OMAP2 has a number of different interrupt controllers, each interrupt
  40 * controller is identified as its own "bank". Register definitions are
  41 * fairly consistent for each bank, but not all registers are implemented
  42 * for each bank.. when in doubt, consult the TRM.
  43 */
  44static struct omap_irq_bank {
  45        void __iomem *base_reg;
  46        unsigned int nr_irqs;
  47} __attribute__ ((aligned(4))) irq_banks[] = {
  48        {
  49                /* MPU INTC */
  50                .nr_irqs        = 96,
  51        },
  52};
  53
  54/* Structure to save interrupt controller context */
  55struct omap3_intc_regs {
  56        u32 sysconfig;
  57        u32 protection;
  58        u32 idle;
  59        u32 threshold;
  60        u32 ilr[INTCPS_NR_IRQS];
  61        u32 mir[INTCPS_NR_MIR_REGS];
  62};
  63
  64/* INTC bank register get/set */
  65
  66static void intc_bank_write_reg(u32 val, struct omap_irq_bank *bank, u16 reg)
  67{
  68        __raw_writel(val, bank->base_reg + reg);
  69}
  70
  71static u32 intc_bank_read_reg(struct omap_irq_bank *bank, u16 reg)
  72{
  73        return __raw_readl(bank->base_reg + reg);
  74}
  75
  76static int previous_irq;
  77
  78/*
  79 * On 34xx we can get occasional spurious interrupts if the ack from
  80 * an interrupt handler does not get posted before we unmask. Warn about
  81 * the interrupt handlers that need to flush posted writes.
  82 */
  83static int omap_check_spurious(unsigned int irq)
  84{
  85        u32 sir, spurious;
  86
  87        sir = intc_bank_read_reg(&irq_banks[0], INTC_SIR);
  88        spurious = sir >> 7;
  89
  90        if (spurious) {
  91                printk(KERN_WARNING "Spurious irq %i: 0x%08x, please flush "
  92                                        "posted write for irq %i\n",
  93                                        irq, sir, previous_irq);
  94                return spurious;
  95        }
  96
  97        return 0;
  98}
  99
 100/* XXX: FIQ and additional INTC support (only MPU at the moment) */
 101static void omap_ack_irq(struct irq_data *d)
 102{
 103        intc_bank_write_reg(0x1, &irq_banks[0], INTC_CONTROL);
 104}
 105
 106static void omap_mask_irq(struct irq_data *d)
 107{
 108        unsigned int irq = d->irq;
 109        int offset = irq & (~(IRQ_BITS_PER_REG - 1));
 110
 111        if (cpu_is_omap34xx() && !cpu_is_ti816x()) {
 112                int spurious = 0;
 113
 114                /*
 115                 * INT_34XX_GPT12_IRQ is also the spurious irq. Maybe because
 116                 * it is the highest irq number?
 117                 */
 118                if (irq == INT_34XX_GPT12_IRQ)
 119                        spurious = omap_check_spurious(irq);
 120
 121                if (!spurious)
 122                        previous_irq = irq;
 123        }
 124
 125        irq &= (IRQ_BITS_PER_REG - 1);
 126
 127        intc_bank_write_reg(1 << irq, &irq_banks[0], INTC_MIR_SET0 + offset);
 128}
 129
 130static void omap_unmask_irq(struct irq_data *d)
 131{
 132        unsigned int irq = d->irq;
 133        int offset = irq & (~(IRQ_BITS_PER_REG - 1));
 134
 135        irq &= (IRQ_BITS_PER_REG - 1);
 136
 137        intc_bank_write_reg(1 << irq, &irq_banks[0], INTC_MIR_CLEAR0 + offset);
 138}
 139
 140static void omap_mask_ack_irq(struct irq_data *d)
 141{
 142        omap_mask_irq(d);
 143        omap_ack_irq(d);
 144}
 145
 146static struct irq_chip omap_irq_chip = {
 147        .name           = "INTC",
 148        .irq_ack        = omap_mask_ack_irq,
 149        .irq_mask       = omap_mask_irq,
 150        .irq_unmask     = omap_unmask_irq,
 151};
 152
 153static void __init omap_irq_bank_init_one(struct omap_irq_bank *bank)
 154{
 155        unsigned long tmp;
 156
 157        tmp = intc_bank_read_reg(bank, INTC_REVISION) & 0xff;
 158        printk(KERN_INFO "IRQ: Found an INTC at 0x%p "
 159                         "(revision %ld.%ld) with %d interrupts\n",
 160                         bank->base_reg, tmp >> 4, tmp & 0xf, bank->nr_irqs);
 161
 162        tmp = intc_bank_read_reg(bank, INTC_SYSCONFIG);
 163        tmp |= 1 << 1;  /* soft reset */
 164        intc_bank_write_reg(tmp, bank, INTC_SYSCONFIG);
 165
 166        while (!(intc_bank_read_reg(bank, INTC_SYSSTATUS) & 0x1))
 167                /* Wait for reset to complete */;
 168
 169        /* Enable autoidle */
 170        intc_bank_write_reg(1 << 0, bank, INTC_SYSCONFIG);
 171}
 172
 173int omap_irq_pending(void)
 174{
 175        int i;
 176
 177        for (i = 0; i < ARRAY_SIZE(irq_banks); i++) {
 178                struct omap_irq_bank *bank = irq_banks + i;
 179                int irq;
 180
 181                for (irq = 0; irq < bank->nr_irqs; irq += 32)
 182                        if (intc_bank_read_reg(bank, INTC_PENDING_IRQ0 +
 183                                               ((irq >> 5) << 5)))
 184                                return 1;
 185        }
 186        return 0;
 187}
 188
 189void __init omap_init_irq(void)
 190{
 191        unsigned long nr_of_irqs = 0;
 192        unsigned int nr_banks = 0;
 193        int i;
 194
 195        for (i = 0; i < ARRAY_SIZE(irq_banks); i++) {
 196                unsigned long base = 0;
 197                struct omap_irq_bank *bank = irq_banks + i;
 198
 199                if (cpu_is_omap24xx())
 200                        base = OMAP24XX_IC_BASE;
 201                else if (cpu_is_omap34xx())
 202                        base = OMAP34XX_IC_BASE;
 203
 204                BUG_ON(!base);
 205
 206                if (cpu_is_ti816x())
 207                        bank->nr_irqs = 128;
 208
 209                /* Static mapping, never released */
 210                bank->base_reg = ioremap(base, SZ_4K);
 211                if (!bank->base_reg) {
 212                        printk(KERN_ERR "Could not ioremap irq bank%i\n", i);
 213                        continue;
 214                }
 215
 216                omap_irq_bank_init_one(bank);
 217
 218                nr_of_irqs += bank->nr_irqs;
 219                nr_banks++;
 220        }
 221
 222        printk(KERN_INFO "Total of %ld interrupts on %d active controller%s\n",
 223               nr_of_irqs, nr_banks, nr_banks > 1 ? "s" : "");
 224
 225        for (i = 0; i < nr_of_irqs; i++) {
 226                irq_set_chip_and_handler(i, &omap_irq_chip, handle_level_irq);
 227                set_irq_flags(i, IRQF_VALID);
 228        }
 229}
 230
 231#ifdef CONFIG_ARCH_OMAP3
 232static struct omap3_intc_regs intc_context[ARRAY_SIZE(irq_banks)];
 233
 234void omap_intc_save_context(void)
 235{
 236        int ind = 0, i = 0;
 237        for (ind = 0; ind < ARRAY_SIZE(irq_banks); ind++) {
 238                struct omap_irq_bank *bank = irq_banks + ind;
 239                intc_context[ind].sysconfig =
 240                        intc_bank_read_reg(bank, INTC_SYSCONFIG);
 241                intc_context[ind].protection =
 242                        intc_bank_read_reg(bank, INTC_PROTECTION);
 243                intc_context[ind].idle =
 244                        intc_bank_read_reg(bank, INTC_IDLE);
 245                intc_context[ind].threshold =
 246                        intc_bank_read_reg(bank, INTC_THRESHOLD);
 247                for (i = 0; i < INTCPS_NR_IRQS; i++)
 248                        intc_context[ind].ilr[i] =
 249                                intc_bank_read_reg(bank, (0x100 + 0x4*i));
 250                for (i = 0; i < INTCPS_NR_MIR_REGS; i++)
 251                        intc_context[ind].mir[i] =
 252                                intc_bank_read_reg(&irq_banks[0], INTC_MIR0 +
 253                                (0x20 * i));
 254        }
 255}
 256
 257void omap_intc_restore_context(void)
 258{
 259        int ind = 0, i = 0;
 260
 261        for (ind = 0; ind < ARRAY_SIZE(irq_banks); ind++) {
 262                struct omap_irq_bank *bank = irq_banks + ind;
 263                intc_bank_write_reg(intc_context[ind].sysconfig,
 264                                        bank, INTC_SYSCONFIG);
 265                intc_bank_write_reg(intc_context[ind].sysconfig,
 266                                        bank, INTC_SYSCONFIG);
 267                intc_bank_write_reg(intc_context[ind].protection,
 268                                        bank, INTC_PROTECTION);
 269                intc_bank_write_reg(intc_context[ind].idle,
 270                                        bank, INTC_IDLE);
 271                intc_bank_write_reg(intc_context[ind].threshold,
 272                                        bank, INTC_THRESHOLD);
 273                for (i = 0; i < INTCPS_NR_IRQS; i++)
 274                        intc_bank_write_reg(intc_context[ind].ilr[i],
 275                                bank, (0x100 + 0x4*i));
 276                for (i = 0; i < INTCPS_NR_MIR_REGS; i++)
 277                        intc_bank_write_reg(intc_context[ind].mir[i],
 278                                 &irq_banks[0], INTC_MIR0 + (0x20 * i));
 279        }
 280        /* MIRs are saved and restore with other PRCM registers */
 281}
 282
 283void omap3_intc_suspend(void)
 284{
 285        /* A pending interrupt would prevent OMAP from entering suspend */
 286        omap_ack_irq(0);
 287}
 288
 289void omap3_intc_prepare_idle(void)
 290{
 291        /*
 292         * Disable autoidle as it can stall interrupt controller,
 293         * cf. errata ID i540 for 3430 (all revisions up to 3.1.x)
 294         */
 295        intc_bank_write_reg(0, &irq_banks[0], INTC_SYSCONFIG);
 296}
 297
 298void omap3_intc_resume_idle(void)
 299{
 300        /* Re-enable autoidle */
 301        intc_bank_write_reg(1, &irq_banks[0], INTC_SYSCONFIG);
 302}
 303#endif /* CONFIG_ARCH_OMAP3 */
 304