linux/drivers/irqchip/irq-atmel-aic5.c
<<
>>
Prefs
   1/*
   2 * Atmel AT91 AIC5 (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_AIC5_IRQS    128
  38
  39#define AT91_AIC5_SSR           0x0
  40#define AT91_AIC5_INTSEL_MSK    (0x7f << 0)
  41
  42#define AT91_AIC5_SMR                   0x4
  43
  44#define AT91_AIC5_SVR                   0x8
  45#define AT91_AIC5_IVR                   0x10
  46#define AT91_AIC5_FVR                   0x14
  47#define AT91_AIC5_ISR                   0x18
  48
  49#define AT91_AIC5_IPR0                  0x20
  50#define AT91_AIC5_IPR1                  0x24
  51#define AT91_AIC5_IPR2                  0x28
  52#define AT91_AIC5_IPR3                  0x2c
  53#define AT91_AIC5_IMR                   0x30
  54#define AT91_AIC5_CISR                  0x34
  55
  56#define AT91_AIC5_IECR                  0x40
  57#define AT91_AIC5_IDCR                  0x44
  58#define AT91_AIC5_ICCR                  0x48
  59#define AT91_AIC5_ISCR                  0x4c
  60#define AT91_AIC5_EOICR                 0x38
  61#define AT91_AIC5_SPU                   0x3c
  62#define AT91_AIC5_DCR                   0x6c
  63
  64#define AT91_AIC5_FFER                  0x50
  65#define AT91_AIC5_FFDR                  0x54
  66#define AT91_AIC5_FFSR                  0x58
  67
  68static struct irq_domain *aic5_domain;
  69
  70static asmlinkage void __exception_irq_entry
  71aic5_handle(struct pt_regs *regs)
  72{
  73        struct irq_chip_generic *bgc = irq_get_domain_generic_chip(aic5_domain, 0);
  74        u32 irqnr;
  75        u32 irqstat;
  76
  77        irqnr = irq_reg_readl(bgc, AT91_AIC5_IVR);
  78        irqstat = irq_reg_readl(bgc, AT91_AIC5_ISR);
  79
  80        if (!irqstat)
  81                irq_reg_writel(bgc, 0, AT91_AIC5_EOICR);
  82        else
  83                handle_domain_irq(aic5_domain, irqnr, regs);
  84}
  85
  86static void aic5_mask(struct irq_data *d)
  87{
  88        struct irq_domain *domain = d->domain;
  89        struct irq_chip_generic *bgc = irq_get_domain_generic_chip(domain, 0);
  90        struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d);
  91
  92        /*
  93         * Disable interrupt on AIC5. We always take the lock of the
  94         * first irq chip as all chips share the same registers.
  95         */
  96        irq_gc_lock(bgc);
  97        irq_reg_writel(gc, d->hwirq, AT91_AIC5_SSR);
  98        irq_reg_writel(gc, 1, AT91_AIC5_IDCR);
  99        gc->mask_cache &= ~d->mask;
 100        irq_gc_unlock(bgc);
 101}
 102
 103static void aic5_unmask(struct irq_data *d)
 104{
 105        struct irq_domain *domain = d->domain;
 106        struct irq_chip_generic *bgc = irq_get_domain_generic_chip(domain, 0);
 107        struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d);
 108
 109        /*
 110         * Enable interrupt on AIC5. We always take the lock of the
 111         * first irq chip as all chips share the same registers.
 112         */
 113        irq_gc_lock(bgc);
 114        irq_reg_writel(gc, d->hwirq, AT91_AIC5_SSR);
 115        irq_reg_writel(gc, 1, AT91_AIC5_IECR);
 116        gc->mask_cache |= d->mask;
 117        irq_gc_unlock(bgc);
 118}
 119
 120static int aic5_retrigger(struct irq_data *d)
 121{
 122        struct irq_domain *domain = d->domain;
 123        struct irq_chip_generic *bgc = irq_get_domain_generic_chip(domain, 0);
 124
 125        /* Enable interrupt on AIC5 */
 126        irq_gc_lock(bgc);
 127        irq_reg_writel(bgc, d->hwirq, AT91_AIC5_SSR);
 128        irq_reg_writel(bgc, 1, AT91_AIC5_ISCR);
 129        irq_gc_unlock(bgc);
 130
 131        return 0;
 132}
 133
 134static int aic5_set_type(struct irq_data *d, unsigned type)
 135{
 136        struct irq_domain *domain = d->domain;
 137        struct irq_chip_generic *bgc = irq_get_domain_generic_chip(domain, 0);
 138        unsigned int smr;
 139        int ret;
 140
 141        irq_gc_lock(bgc);
 142        irq_reg_writel(bgc, d->hwirq, AT91_AIC5_SSR);
 143        smr = irq_reg_readl(bgc, AT91_AIC5_SMR);
 144        ret = aic_common_set_type(d, type, &smr);
 145        if (!ret)
 146                irq_reg_writel(bgc, smr, AT91_AIC5_SMR);
 147        irq_gc_unlock(bgc);
 148
 149        return ret;
 150}
 151
 152#ifdef CONFIG_PM
 153static void aic5_suspend(struct irq_data *d)
 154{
 155        struct irq_domain *domain = d->domain;
 156        struct irq_domain_chip_generic *dgc = domain->gc;
 157        struct irq_chip_generic *bgc = irq_get_domain_generic_chip(domain, 0);
 158        struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d);
 159        int i;
 160        u32 mask;
 161
 162        irq_gc_lock(bgc);
 163        for (i = 0; i < dgc->irqs_per_chip; i++) {
 164                mask = 1 << i;
 165                if ((mask & gc->mask_cache) == (mask & gc->wake_active))
 166                        continue;
 167
 168                irq_reg_writel(bgc, i + gc->irq_base, AT91_AIC5_SSR);
 169                if (mask & gc->wake_active)
 170                        irq_reg_writel(bgc, 1, AT91_AIC5_IECR);
 171                else
 172                        irq_reg_writel(bgc, 1, AT91_AIC5_IDCR);
 173        }
 174        irq_gc_unlock(bgc);
 175}
 176
 177static void aic5_resume(struct irq_data *d)
 178{
 179        struct irq_domain *domain = d->domain;
 180        struct irq_domain_chip_generic *dgc = domain->gc;
 181        struct irq_chip_generic *bgc = irq_get_domain_generic_chip(domain, 0);
 182        struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d);
 183        int i;
 184        u32 mask;
 185
 186        irq_gc_lock(bgc);
 187        for (i = 0; i < dgc->irqs_per_chip; i++) {
 188                mask = 1 << i;
 189                if ((mask & gc->mask_cache) == (mask & gc->wake_active))
 190                        continue;
 191
 192                irq_reg_writel(bgc, i + gc->irq_base, AT91_AIC5_SSR);
 193                if (mask & gc->mask_cache)
 194                        irq_reg_writel(bgc, 1, AT91_AIC5_IECR);
 195                else
 196                        irq_reg_writel(bgc, 1, AT91_AIC5_IDCR);
 197        }
 198        irq_gc_unlock(bgc);
 199}
 200
 201static void aic5_pm_shutdown(struct irq_data *d)
 202{
 203        struct irq_domain *domain = d->domain;
 204        struct irq_domain_chip_generic *dgc = domain->gc;
 205        struct irq_chip_generic *bgc = irq_get_domain_generic_chip(domain, 0);
 206        struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d);
 207        int i;
 208
 209        irq_gc_lock(bgc);
 210        for (i = 0; i < dgc->irqs_per_chip; i++) {
 211                irq_reg_writel(bgc, i + gc->irq_base, AT91_AIC5_SSR);
 212                irq_reg_writel(bgc, 1, AT91_AIC5_IDCR);
 213                irq_reg_writel(bgc, 1, AT91_AIC5_ICCR);
 214        }
 215        irq_gc_unlock(bgc);
 216}
 217#else
 218#define aic5_suspend            NULL
 219#define aic5_resume             NULL
 220#define aic5_pm_shutdown        NULL
 221#endif /* CONFIG_PM */
 222
 223static void __init aic5_hw_init(struct irq_domain *domain)
 224{
 225        struct irq_chip_generic *gc = irq_get_domain_generic_chip(domain, 0);
 226        int i;
 227
 228        /*
 229         * Perform 8 End Of Interrupt Command to make sure AIC
 230         * will not Lock out nIRQ
 231         */
 232        for (i = 0; i < 8; i++)
 233                irq_reg_writel(gc, 0, AT91_AIC5_EOICR);
 234
 235        /*
 236         * Spurious Interrupt ID in Spurious Vector Register.
 237         * When there is no current interrupt, the IRQ Vector Register
 238         * reads the value stored in AIC_SPU
 239         */
 240        irq_reg_writel(gc, 0xffffffff, AT91_AIC5_SPU);
 241
 242        /* No debugging in AIC: Debug (Protect) Control Register */
 243        irq_reg_writel(gc, 0, AT91_AIC5_DCR);
 244
 245        /* Disable and clear all interrupts initially */
 246        for (i = 0; i < domain->revmap_size; i++) {
 247                irq_reg_writel(gc, i, AT91_AIC5_SSR);
 248                irq_reg_writel(gc, i, AT91_AIC5_SVR);
 249                irq_reg_writel(gc, 1, AT91_AIC5_IDCR);
 250                irq_reg_writel(gc, 1, AT91_AIC5_ICCR);
 251        }
 252}
 253
 254static int aic5_irq_domain_xlate(struct irq_domain *d,
 255                                 struct device_node *ctrlr,
 256                                 const u32 *intspec, unsigned int intsize,
 257                                 irq_hw_number_t *out_hwirq,
 258                                 unsigned int *out_type)
 259{
 260        struct irq_chip_generic *bgc = irq_get_domain_generic_chip(d, 0);
 261        unsigned smr;
 262        int ret;
 263
 264        if (!bgc)
 265                return -EINVAL;
 266
 267        ret = aic_common_irq_domain_xlate(d, ctrlr, intspec, intsize,
 268                                          out_hwirq, out_type);
 269        if (ret)
 270                return ret;
 271
 272        irq_gc_lock(bgc);
 273        irq_reg_writel(bgc, *out_hwirq, AT91_AIC5_SSR);
 274        smr = irq_reg_readl(bgc, AT91_AIC5_SMR);
 275        aic_common_set_priority(intspec[2], &smr);
 276        irq_reg_writel(bgc, smr, AT91_AIC5_SMR);
 277        irq_gc_unlock(bgc);
 278
 279        return ret;
 280}
 281
 282static const struct irq_domain_ops aic5_irq_ops = {
 283        .map    = irq_map_generic_chip,
 284        .xlate  = aic5_irq_domain_xlate,
 285};
 286
 287static void __init sama5d3_aic_irq_fixup(struct device_node *root)
 288{
 289        aic_common_rtc_irq_fixup(root);
 290}
 291
 292static const struct of_device_id aic5_irq_fixups[] __initconst = {
 293        { .compatible = "atmel,sama5d3", .data = sama5d3_aic_irq_fixup },
 294        { .compatible = "atmel,sama5d4", .data = sama5d3_aic_irq_fixup },
 295        { /* sentinel */ },
 296};
 297
 298static int __init aic5_of_init(struct device_node *node,
 299                               struct device_node *parent,
 300                               int nirqs)
 301{
 302        struct irq_chip_generic *gc;
 303        struct irq_domain *domain;
 304        int nchips;
 305        int i;
 306
 307        if (nirqs > NR_AIC5_IRQS)
 308                return -EINVAL;
 309
 310        if (aic5_domain)
 311                return -EEXIST;
 312
 313        domain = aic_common_of_init(node, &aic5_irq_ops, "atmel-aic5",
 314                                    nirqs, aic5_irq_fixups);
 315        if (IS_ERR(domain))
 316                return PTR_ERR(domain);
 317
 318        aic5_domain = domain;
 319        nchips = aic5_domain->revmap_size / 32;
 320        for (i = 0; i < nchips; i++) {
 321                gc = irq_get_domain_generic_chip(domain, i * 32);
 322
 323                gc->chip_types[0].regs.eoi = AT91_AIC5_EOICR;
 324                gc->chip_types[0].chip.irq_mask = aic5_mask;
 325                gc->chip_types[0].chip.irq_unmask = aic5_unmask;
 326                gc->chip_types[0].chip.irq_retrigger = aic5_retrigger;
 327                gc->chip_types[0].chip.irq_set_type = aic5_set_type;
 328                gc->chip_types[0].chip.irq_suspend = aic5_suspend;
 329                gc->chip_types[0].chip.irq_resume = aic5_resume;
 330                gc->chip_types[0].chip.irq_pm_shutdown = aic5_pm_shutdown;
 331        }
 332
 333        aic5_hw_init(domain);
 334        set_handle_irq(aic5_handle);
 335
 336        return 0;
 337}
 338
 339#define NR_SAMA5D2_IRQS         77
 340
 341static int __init sama5d2_aic5_of_init(struct device_node *node,
 342                                       struct device_node *parent)
 343{
 344        return aic5_of_init(node, parent, NR_SAMA5D2_IRQS);
 345}
 346IRQCHIP_DECLARE(sama5d2_aic5, "atmel,sama5d2-aic", sama5d2_aic5_of_init);
 347
 348#define NR_SAMA5D3_IRQS         48
 349
 350static int __init sama5d3_aic5_of_init(struct device_node *node,
 351                                       struct device_node *parent)
 352{
 353        return aic5_of_init(node, parent, NR_SAMA5D3_IRQS);
 354}
 355IRQCHIP_DECLARE(sama5d3_aic5, "atmel,sama5d3-aic", sama5d3_aic5_of_init);
 356
 357#define NR_SAMA5D4_IRQS         68
 358
 359static int __init sama5d4_aic5_of_init(struct device_node *node,
 360                                       struct device_node *parent)
 361{
 362        return aic5_of_init(node, parent, NR_SAMA5D4_IRQS);
 363}
 364IRQCHIP_DECLARE(sama5d4_aic5, "atmel,sama5d4-aic", sama5d4_aic5_of_init);
 365