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
  64static struct omap3_intc_regs intc_context[ARRAY_SIZE(irq_banks)];
  65
  66/* INTC bank register get/set */
  67
  68static void intc_bank_write_reg(u32 val, struct omap_irq_bank *bank, u16 reg)
  69{
  70        __raw_writel(val, bank->base_reg + reg);
  71}
  72
  73static u32 intc_bank_read_reg(struct omap_irq_bank *bank, u16 reg)
  74{
  75        return __raw_readl(bank->base_reg + reg);
  76}
  77
  78static int previous_irq;
  79
  80/*
  81 * On 34xx we can get occasional spurious interrupts if the ack from
  82 * an interrupt handler does not get posted before we unmask. Warn about
  83 * the interrupt handlers that need to flush posted writes.
  84 */
  85static int omap_check_spurious(unsigned int irq)
  86{
  87        u32 sir, spurious;
  88
  89        sir = intc_bank_read_reg(&irq_banks[0], INTC_SIR);
  90        spurious = sir >> 7;
  91
  92        if (spurious) {
  93                printk(KERN_WARNING "Spurious irq %i: 0x%08x, please flush "
  94                                        "posted write for irq %i\n",
  95                                        irq, sir, previous_irq);
  96                return spurious;
  97        }
  98
  99        return 0;
 100}
 101
 102/* XXX: FIQ and additional INTC support (only MPU at the moment) */
 103static void omap_ack_irq(struct irq_data *d)
 104{
 105        intc_bank_write_reg(0x1, &irq_banks[0], INTC_CONTROL);
 106}
 107
 108static void omap_mask_irq(struct irq_data *d)
 109{
 110        unsigned int irq = d->irq;
 111        int offset = irq & (~(IRQ_BITS_PER_REG - 1));
 112
 113        if (cpu_is_omap34xx()) {
 114                int spurious = 0;
 115
 116                /*
 117                 * INT_34XX_GPT12_IRQ is also the spurious irq. Maybe because
 118                 * it is the highest irq number?
 119                 */
 120                if (irq == INT_34XX_GPT12_IRQ)
 121                        spurious = omap_check_spurious(irq);
 122
 123                if (!spurious)
 124                        previous_irq = irq;
 125        }
 126
 127        irq &= (IRQ_BITS_PER_REG - 1);
 128
 129        intc_bank_write_reg(1 << irq, &irq_banks[0], INTC_MIR_SET0 + offset);
 130}
 131
 132static void omap_unmask_irq(struct irq_data *d)
 133{
 134        unsigned int irq = d->irq;
 135        int offset = irq & (~(IRQ_BITS_PER_REG - 1));
 136
 137        irq &= (IRQ_BITS_PER_REG - 1);
 138
 139        intc_bank_write_reg(1 << irq, &irq_banks[0], INTC_MIR_CLEAR0 + offset);
 140}
 141
 142static void omap_mask_ack_irq(struct irq_data *d)
 143{
 144        omap_mask_irq(d);
 145        omap_ack_irq(d);
 146}
 147
 148static struct irq_chip omap_irq_chip = {
 149        .name           = "INTC",
 150        .irq_ack        = omap_mask_ack_irq,
 151        .irq_mask       = omap_mask_irq,
 152        .irq_unmask     = omap_unmask_irq,
 153};
 154
 155static void __init omap_irq_bank_init_one(struct omap_irq_bank *bank)
 156{
 157        unsigned long tmp;
 158
 159        tmp = intc_bank_read_reg(bank, INTC_REVISION) & 0xff;
 160        printk(KERN_INFO "IRQ: Found an INTC at 0x%p "
 161                         "(revision %ld.%ld) with %d interrupts\n",
 162                         bank->base_reg, tmp >> 4, tmp & 0xf, bank->nr_irqs);
 163
 164        tmp = intc_bank_read_reg(bank, INTC_SYSCONFIG);
 165        tmp |= 1 << 1;  /* soft reset */
 166        intc_bank_write_reg(tmp, bank, INTC_SYSCONFIG);
 167
 168        while (!(intc_bank_read_reg(bank, INTC_SYSSTATUS) & 0x1))
 169                /* Wait for reset to complete */;
 170
 171        /* Enable autoidle */
 172        intc_bank_write_reg(1 << 0, bank, INTC_SYSCONFIG);
 173}
 174
 175int omap_irq_pending(void)
 176{
 177        int i;
 178
 179        for (i = 0; i < ARRAY_SIZE(irq_banks); i++) {
 180                struct omap_irq_bank *bank = irq_banks + i;
 181                int irq;
 182
 183                for (irq = 0; irq < bank->nr_irqs; irq += 32)
 184                        if (intc_bank_read_reg(bank, INTC_PENDING_IRQ0 +
 185                                               ((irq >> 5) << 5)))
 186                                return 1;
 187        }
 188        return 0;
 189}
 190
 191void __init omap_init_irq(void)
 192{
 193        unsigned long nr_of_irqs = 0;
 194        unsigned int nr_banks = 0;
 195        int i;
 196
 197        for (i = 0; i < ARRAY_SIZE(irq_banks); i++) {
 198                unsigned long base = 0;
 199                struct omap_irq_bank *bank = irq_banks + i;
 200
 201                if (cpu_is_omap24xx())
 202                        base = OMAP24XX_IC_BASE;
 203                else if (cpu_is_omap34xx())
 204                        base = OMAP34XX_IC_BASE;
 205
 206                BUG_ON(!base);
 207
 208                /* Static mapping, never released */
 209                bank->base_reg = ioremap(base, SZ_4K);
 210                if (!bank->base_reg) {
 211                        printk(KERN_ERR "Could not ioremap irq bank%i\n", i);
 212                        continue;
 213                }
 214
 215                omap_irq_bank_init_one(bank);
 216
 217                nr_of_irqs += bank->nr_irqs;
 218                nr_banks++;
 219        }
 220
 221        printk(KERN_INFO "Total of %ld interrupts on %d active controller%s\n",
 222               nr_of_irqs, nr_banks, nr_banks > 1 ? "s" : "");
 223
 224        for (i = 0; i < nr_of_irqs; i++) {
 225                set_irq_chip(i, &omap_irq_chip);
 226                set_irq_handler(i, handle_level_irq);
 227                set_irq_flags(i, IRQF_VALID);
 228        }
 229}
 230
 231#ifdef CONFIG_ARCH_OMAP3
 232void omap_intc_save_context(void)
 233{
 234        int ind = 0, i = 0;
 235        for (ind = 0; ind < ARRAY_SIZE(irq_banks); ind++) {
 236                struct omap_irq_bank *bank = irq_banks + ind;
 237                intc_context[ind].sysconfig =
 238                        intc_bank_read_reg(bank, INTC_SYSCONFIG);
 239                intc_context[ind].protection =
 240                        intc_bank_read_reg(bank, INTC_PROTECTION);
 241                intc_context[ind].idle =
 242                        intc_bank_read_reg(bank, INTC_IDLE);
 243                intc_context[ind].threshold =
 244                        intc_bank_read_reg(bank, INTC_THRESHOLD);
 245                for (i = 0; i < INTCPS_NR_IRQS; i++)
 246                        intc_context[ind].ilr[i] =
 247                                intc_bank_read_reg(bank, (0x100 + 0x4*i));
 248                for (i = 0; i < INTCPS_NR_MIR_REGS; i++)
 249                        intc_context[ind].mir[i] =
 250                                intc_bank_read_reg(&irq_banks[0], INTC_MIR0 +
 251                                (0x20 * i));
 252        }
 253}
 254
 255void omap_intc_restore_context(void)
 256{
 257        int ind = 0, i = 0;
 258
 259        for (ind = 0; ind < ARRAY_SIZE(irq_banks); ind++) {
 260                struct omap_irq_bank *bank = irq_banks + ind;
 261                intc_bank_write_reg(intc_context[ind].sysconfig,
 262                                        bank, INTC_SYSCONFIG);
 263                intc_bank_write_reg(intc_context[ind].sysconfig,
 264                                        bank, INTC_SYSCONFIG);
 265                intc_bank_write_reg(intc_context[ind].protection,
 266                                        bank, INTC_PROTECTION);
 267                intc_bank_write_reg(intc_context[ind].idle,
 268                                        bank, INTC_IDLE);
 269                intc_bank_write_reg(intc_context[ind].threshold,
 270                                        bank, INTC_THRESHOLD);
 271                for (i = 0; i < INTCPS_NR_IRQS; i++)
 272                        intc_bank_write_reg(intc_context[ind].ilr[i],
 273                                bank, (0x100 + 0x4*i));
 274                for (i = 0; i < INTCPS_NR_MIR_REGS; i++)
 275                        intc_bank_write_reg(intc_context[ind].mir[i],
 276                                 &irq_banks[0], INTC_MIR0 + (0x20 * i));
 277        }
 278        /* MIRs are saved and restore with other PRCM registers */
 279}
 280
 281void omap3_intc_suspend(void)
 282{
 283        /* A pending interrupt would prevent OMAP from entering suspend */
 284        omap_ack_irq(0);
 285}
 286
 287void omap3_intc_prepare_idle(void)
 288{
 289        /*
 290         * Disable autoidle as it can stall interrupt controller,
 291         * cf. errata ID i540 for 3430 (all revisions up to 3.1.x)
 292         */
 293        intc_bank_write_reg(0, &irq_banks[0], INTC_SYSCONFIG);
 294}
 295
 296void omap3_intc_resume_idle(void)
 297{
 298        /* Re-enable autoidle */
 299        intc_bank_write_reg(1, &irq_banks[0], INTC_SYSCONFIG);
 300}
 301#endif /* CONFIG_ARCH_OMAP3 */
 302