linux/drivers/irqchip/irq-atmel-aic.c
<<
>>
Prefs
   1/*
   2 * Atmel AT91 AIC (Advanced Interrupt Controller) driver
   3 *
   4 *  Copyright (C) 2004 SAN People
   5 *  Copyright (C) 2004 ATMEL
   6 *  Copyright (C) Rick Bronson
   7 *  Copyright (C) 2014 Free Electrons
   8 *
   9 *  Author: Boris BREZILLON <boris.brezillon@free-electrons.com>
  10 *
  11 * This file is licensed under the terms of the GNU General Public
  12 * License version 2.  This program is licensed "as is" without any
  13 * warranty of any kind, whether express or implied.
  14 */
  15
  16#include <linux/init.h>
  17#include <linux/module.h>
  18#include <linux/mm.h>
  19#include <linux/bitmap.h>
  20#include <linux/types.h>
  21#include <linux/irq.h>
  22#include <linux/irqchip.h>
  23#include <linux/of.h>
  24#include <linux/of_address.h>
  25#include <linux/of_irq.h>
  26#include <linux/irqdomain.h>
  27#include <linux/err.h>
  28#include <linux/slab.h>
  29#include <linux/io.h>
  30
  31#include <asm/exception.h>
  32#include <asm/mach/irq.h>
  33
  34#include "irq-atmel-aic-common.h"
  35
  36/* Number of irq lines managed by AIC */
  37#define NR_AIC_IRQS     32
  38
  39#define AT91_AIC_SMR(n)                 ((n) * 4)
  40
  41#define AT91_AIC_SVR(n)                 (0x80 + ((n) * 4))
  42#define AT91_AIC_IVR                    0x100
  43#define AT91_AIC_FVR                    0x104
  44#define AT91_AIC_ISR                    0x108
  45
  46#define AT91_AIC_IPR                    0x10c
  47#define AT91_AIC_IMR                    0x110
  48#define AT91_AIC_CISR                   0x114
  49
  50#define AT91_AIC_IECR                   0x120
  51#define AT91_AIC_IDCR                   0x124
  52#define AT91_AIC_ICCR                   0x128
  53#define AT91_AIC_ISCR                   0x12c
  54#define AT91_AIC_EOICR                  0x130
  55#define AT91_AIC_SPU                    0x134
  56#define AT91_AIC_DCR                    0x138
  57
  58static struct irq_domain *aic_domain;
  59
  60static asmlinkage void __exception_irq_entry
  61aic_handle(struct pt_regs *regs)
  62{
  63        struct irq_domain_chip_generic *dgc = aic_domain->gc;
  64        struct irq_chip_generic *gc = dgc->gc[0];
  65        u32 irqnr;
  66        u32 irqstat;
  67
  68        irqnr = irq_reg_readl(gc, AT91_AIC_IVR);
  69        irqstat = irq_reg_readl(gc, AT91_AIC_ISR);
  70
  71        if (!irqstat)
  72                irq_reg_writel(gc, 0, AT91_AIC_EOICR);
  73        else
  74                handle_domain_irq(aic_domain, irqnr, regs);
  75}
  76
  77static int aic_retrigger(struct irq_data *d)
  78{
  79        struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d);
  80
  81        /* Enable interrupt on AIC5 */
  82        irq_gc_lock(gc);
  83        irq_reg_writel(gc, d->mask, AT91_AIC_ISCR);
  84        irq_gc_unlock(gc);
  85
  86        return 0;
  87}
  88
  89static int aic_set_type(struct irq_data *d, unsigned type)
  90{
  91        struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d);
  92        unsigned int smr;
  93        int ret;
  94
  95        smr = irq_reg_readl(gc, AT91_AIC_SMR(d->hwirq));
  96        ret = aic_common_set_type(d, type, &smr);
  97        if (ret)
  98                return ret;
  99
 100        irq_reg_writel(gc, smr, AT91_AIC_SMR(d->hwirq));
 101
 102        return 0;
 103}
 104
 105#ifdef CONFIG_PM
 106static void aic_suspend(struct irq_data *d)
 107{
 108        struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d);
 109
 110        irq_gc_lock(gc);
 111        irq_reg_writel(gc, gc->mask_cache, AT91_AIC_IDCR);
 112        irq_reg_writel(gc, gc->wake_active, AT91_AIC_IECR);
 113        irq_gc_unlock(gc);
 114}
 115
 116static void aic_resume(struct irq_data *d)
 117{
 118        struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d);
 119
 120        irq_gc_lock(gc);
 121        irq_reg_writel(gc, gc->wake_active, AT91_AIC_IDCR);
 122        irq_reg_writel(gc, gc->mask_cache, AT91_AIC_IECR);
 123        irq_gc_unlock(gc);
 124}
 125
 126static void aic_pm_shutdown(struct irq_data *d)
 127{
 128        struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d);
 129
 130        irq_gc_lock(gc);
 131        irq_reg_writel(gc, 0xffffffff, AT91_AIC_IDCR);
 132        irq_reg_writel(gc, 0xffffffff, AT91_AIC_ICCR);
 133        irq_gc_unlock(gc);
 134}
 135#else
 136#define aic_suspend             NULL
 137#define aic_resume              NULL
 138#define aic_pm_shutdown         NULL
 139#endif /* CONFIG_PM */
 140
 141static void __init aic_hw_init(struct irq_domain *domain)
 142{
 143        struct irq_chip_generic *gc = irq_get_domain_generic_chip(domain, 0);
 144        int i;
 145
 146        /*
 147         * Perform 8 End Of Interrupt Command to make sure AIC
 148         * will not Lock out nIRQ
 149         */
 150        for (i = 0; i < 8; i++)
 151                irq_reg_writel(gc, 0, AT91_AIC_EOICR);
 152
 153        /*
 154         * Spurious Interrupt ID in Spurious Vector Register.
 155         * When there is no current interrupt, the IRQ Vector Register
 156         * reads the value stored in AIC_SPU
 157         */
 158        irq_reg_writel(gc, 0xffffffff, AT91_AIC_SPU);
 159
 160        /* No debugging in AIC: Debug (Protect) Control Register */
 161        irq_reg_writel(gc, 0, AT91_AIC_DCR);
 162
 163        /* Disable and clear all interrupts initially */
 164        irq_reg_writel(gc, 0xffffffff, AT91_AIC_IDCR);
 165        irq_reg_writel(gc, 0xffffffff, AT91_AIC_ICCR);
 166
 167        for (i = 0; i < 32; i++)
 168                irq_reg_writel(gc, i, AT91_AIC_SVR(i));
 169}
 170
 171static int aic_irq_domain_xlate(struct irq_domain *d,
 172                                struct device_node *ctrlr,
 173                                const u32 *intspec, unsigned int intsize,
 174                                irq_hw_number_t *out_hwirq,
 175                                unsigned int *out_type)
 176{
 177        struct irq_domain_chip_generic *dgc = d->gc;
 178        struct irq_chip_generic *gc;
 179        unsigned long flags;
 180        unsigned smr;
 181        int idx;
 182        int ret;
 183
 184        if (!dgc)
 185                return -EINVAL;
 186
 187        ret = aic_common_irq_domain_xlate(d, ctrlr, intspec, intsize,
 188                                          out_hwirq, out_type);
 189        if (ret)
 190                return ret;
 191
 192        idx = intspec[0] / dgc->irqs_per_chip;
 193        if (idx >= dgc->num_chips)
 194                return -EINVAL;
 195
 196        gc = dgc->gc[idx];
 197
 198        irq_gc_lock_irqsave(gc, flags);
 199        smr = irq_reg_readl(gc, AT91_AIC_SMR(*out_hwirq));
 200        aic_common_set_priority(intspec[2], &smr);
 201        irq_reg_writel(gc, smr, AT91_AIC_SMR(*out_hwirq));
 202        irq_gc_unlock_irqrestore(gc, flags);
 203
 204        return ret;
 205}
 206
 207static const struct irq_domain_ops aic_irq_ops = {
 208        .map    = irq_map_generic_chip,
 209        .xlate  = aic_irq_domain_xlate,
 210};
 211
 212static void __init at91rm9200_aic_irq_fixup(void)
 213{
 214        aic_common_rtc_irq_fixup();
 215}
 216
 217static void __init at91sam9260_aic_irq_fixup(void)
 218{
 219        aic_common_rtt_irq_fixup();
 220}
 221
 222static void __init at91sam9g45_aic_irq_fixup(void)
 223{
 224        aic_common_rtc_irq_fixup();
 225        aic_common_rtt_irq_fixup();
 226}
 227
 228static const struct of_device_id aic_irq_fixups[] __initconst = {
 229        { .compatible = "atmel,at91rm9200", .data = at91rm9200_aic_irq_fixup },
 230        { .compatible = "atmel,at91sam9g45", .data = at91sam9g45_aic_irq_fixup },
 231        { .compatible = "atmel,at91sam9n12", .data = at91rm9200_aic_irq_fixup },
 232        { .compatible = "atmel,at91sam9rl", .data = at91sam9g45_aic_irq_fixup },
 233        { .compatible = "atmel,at91sam9x5", .data = at91rm9200_aic_irq_fixup },
 234        { .compatible = "atmel,at91sam9260", .data = at91sam9260_aic_irq_fixup },
 235        { .compatible = "atmel,at91sam9261", .data = at91sam9260_aic_irq_fixup },
 236        { .compatible = "atmel,at91sam9263", .data = at91sam9260_aic_irq_fixup },
 237        { .compatible = "atmel,at91sam9g20", .data = at91sam9260_aic_irq_fixup },
 238        { /* sentinel */ },
 239};
 240
 241static int __init aic_of_init(struct device_node *node,
 242                              struct device_node *parent)
 243{
 244        struct irq_chip_generic *gc;
 245        struct irq_domain *domain;
 246
 247        if (aic_domain)
 248                return -EEXIST;
 249
 250        domain = aic_common_of_init(node, &aic_irq_ops, "atmel-aic",
 251                                    NR_AIC_IRQS, aic_irq_fixups);
 252        if (IS_ERR(domain))
 253                return PTR_ERR(domain);
 254
 255        aic_domain = domain;
 256        gc = irq_get_domain_generic_chip(domain, 0);
 257
 258        gc->chip_types[0].regs.eoi = AT91_AIC_EOICR;
 259        gc->chip_types[0].regs.enable = AT91_AIC_IECR;
 260        gc->chip_types[0].regs.disable = AT91_AIC_IDCR;
 261        gc->chip_types[0].chip.irq_mask = irq_gc_mask_disable_reg;
 262        gc->chip_types[0].chip.irq_unmask = irq_gc_unmask_enable_reg;
 263        gc->chip_types[0].chip.irq_retrigger = aic_retrigger;
 264        gc->chip_types[0].chip.irq_set_type = aic_set_type;
 265        gc->chip_types[0].chip.irq_suspend = aic_suspend;
 266        gc->chip_types[0].chip.irq_resume = aic_resume;
 267        gc->chip_types[0].chip.irq_pm_shutdown = aic_pm_shutdown;
 268
 269        aic_hw_init(domain);
 270        set_handle_irq(aic_handle);
 271
 272        return 0;
 273}
 274IRQCHIP_DECLARE(at91rm9200_aic, "atmel,at91rm9200-aic", aic_of_init);
 275