linux/arch/avr32/mach-at32ap/intc.c
<<
>>
Prefs
   1/*
   2 * Copyright (C) 2006, 2008 Atmel Corporation
   3 *
   4 * This program is free software; you can redistribute it and/or modify
   5 * it under the terms of the GNU General Public License version 2 as
   6 * published by the Free Software Foundation.
   7 */
   8
   9#include <linux/clk.h>
  10#include <linux/err.h>
  11#include <linux/init.h>
  12#include <linux/interrupt.h>
  13#include <linux/irq.h>
  14#include <linux/platform_device.h>
  15#include <linux/syscore_ops.h>
  16#include <linux/export.h>
  17
  18#include <asm/io.h>
  19
  20#include "intc.h"
  21
  22struct intc {
  23        void __iomem            *regs;
  24        struct irq_chip         chip;
  25#ifdef CONFIG_PM
  26        unsigned long           suspend_ipr;
  27        unsigned long           saved_ipr[64];
  28#endif
  29};
  30
  31extern struct platform_device at32_intc0_device;
  32
  33/*
  34 * TODO: We may be able to implement mask/unmask by setting IxM flags
  35 * in the status register.
  36 */
  37static void intc_mask_irq(struct irq_data *d)
  38{
  39
  40}
  41
  42static void intc_unmask_irq(struct irq_data *d)
  43{
  44
  45}
  46
  47static struct intc intc0 = {
  48        .chip = {
  49                .name           = "intc",
  50                .irq_mask       = intc_mask_irq,
  51                .irq_unmask     = intc_unmask_irq,
  52        },
  53};
  54
  55/*
  56 * All interrupts go via intc at some point.
  57 */
  58asmlinkage void do_IRQ(int level, struct pt_regs *regs)
  59{
  60        struct pt_regs *old_regs;
  61        unsigned int irq;
  62        unsigned long status_reg;
  63
  64        local_irq_disable();
  65
  66        old_regs = set_irq_regs(regs);
  67
  68        irq_enter();
  69
  70        irq = intc_readl(&intc0, INTCAUSE0 - 4 * level);
  71        generic_handle_irq(irq);
  72
  73        /*
  74         * Clear all interrupt level masks so that we may handle
  75         * interrupts during softirq processing.  If this is a nested
  76         * interrupt, interrupts must stay globally disabled until we
  77         * return.
  78         */
  79        status_reg = sysreg_read(SR);
  80        status_reg &= ~(SYSREG_BIT(I0M) | SYSREG_BIT(I1M)
  81                        | SYSREG_BIT(I2M) | SYSREG_BIT(I3M));
  82        sysreg_write(SR, status_reg);
  83
  84        irq_exit();
  85
  86        set_irq_regs(old_regs);
  87}
  88
  89void __init init_IRQ(void)
  90{
  91        extern void _evba(void);
  92        extern void irq_level0(void);
  93        struct resource *regs;
  94        struct clk *pclk;
  95        unsigned int i;
  96        u32 offset, readback;
  97
  98        regs = platform_get_resource(&at32_intc0_device, IORESOURCE_MEM, 0);
  99        if (!regs) {
 100                printk(KERN_EMERG "intc: no mmio resource defined\n");
 101                goto fail;
 102        }
 103        pclk = clk_get(&at32_intc0_device.dev, "pclk");
 104        if (IS_ERR(pclk)) {
 105                printk(KERN_EMERG "intc: no clock defined\n");
 106                goto fail;
 107        }
 108
 109        clk_enable(pclk);
 110
 111        intc0.regs = ioremap(regs->start, resource_size(regs));
 112        if (!intc0.regs) {
 113                printk(KERN_EMERG "intc: failed to map registers (0x%08lx)\n",
 114                       (unsigned long)regs->start);
 115                goto fail;
 116        }
 117
 118        /*
 119         * Initialize all interrupts to level 0 (lowest priority). The
 120         * priority level may be changed by calling
 121         * irq_set_priority().
 122         *
 123         */
 124        offset = (unsigned long)&irq_level0 - (unsigned long)&_evba;
 125        for (i = 0; i < NR_INTERNAL_IRQS; i++) {
 126                intc_writel(&intc0, INTPR0 + 4 * i, offset);
 127                readback = intc_readl(&intc0, INTPR0 + 4 * i);
 128                if (readback == offset)
 129                        irq_set_chip_and_handler(i, &intc0.chip,
 130                                                 handle_simple_irq);
 131        }
 132
 133        /* Unmask all interrupt levels */
 134        sysreg_write(SR, (sysreg_read(SR)
 135                          & ~(SR_I3M | SR_I2M | SR_I1M | SR_I0M)));
 136
 137        return;
 138
 139fail:
 140        panic("Interrupt controller initialization failed!\n");
 141}
 142
 143#ifdef CONFIG_PM
 144void intc_set_suspend_handler(unsigned long offset)
 145{
 146        intc0.suspend_ipr = offset;
 147}
 148
 149static int intc_suspend(void)
 150{
 151        int i;
 152
 153        if (unlikely(!irqs_disabled())) {
 154                pr_err("intc_suspend: called with interrupts enabled\n");
 155                return -EINVAL;
 156        }
 157
 158        if (unlikely(!intc0.suspend_ipr)) {
 159                pr_err("intc_suspend: suspend_ipr not initialized\n");
 160                return -EINVAL;
 161        }
 162
 163        for (i = 0; i < 64; i++) {
 164                intc0.saved_ipr[i] = intc_readl(&intc0, INTPR0 + 4 * i);
 165                intc_writel(&intc0, INTPR0 + 4 * i, intc0.suspend_ipr);
 166        }
 167
 168        return 0;
 169}
 170
 171static void intc_resume(void)
 172{
 173        int i;
 174
 175        for (i = 0; i < 64; i++)
 176                intc_writel(&intc0, INTPR0 + 4 * i, intc0.saved_ipr[i]);
 177}
 178#else
 179#define intc_suspend    NULL
 180#define intc_resume     NULL
 181#endif
 182
 183static struct syscore_ops intc_syscore_ops = {
 184        .suspend        = intc_suspend,
 185        .resume         = intc_resume,
 186};
 187
 188static int __init intc_init_syscore(void)
 189{
 190        register_syscore_ops(&intc_syscore_ops);
 191
 192        return 0;
 193}
 194device_initcall(intc_init_syscore);
 195
 196unsigned long intc_get_pending(unsigned int group)
 197{
 198        return intc_readl(&intc0, INTREQ0 + 4 * group);
 199}
 200EXPORT_SYMBOL_GPL(intc_get_pending);
 201