linux/drivers/irqchip/spear-shirq.c
<<
>>
Prefs
   1/*
   2 * SPEAr platform shared irq layer source file
   3 *
   4 * Copyright (C) 2009-2012 ST Microelectronics
   5 * Viresh Kumar <viresh.linux@gmail.com>
   6 *
   7 * Copyright (C) 2012 ST Microelectronics
   8 * Shiraz Hashim <shiraz.hashim@st.com>
   9 *
  10 * This file is licensed under the terms of the GNU General Public
  11 * License version 2. This program is licensed "as is" without any
  12 * warranty of any kind, whether express or implied.
  13 */
  14#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
  15
  16#include <linux/err.h>
  17#include <linux/export.h>
  18#include <linux/interrupt.h>
  19#include <linux/io.h>
  20#include <linux/irq.h>
  21#include <linux/irqdomain.h>
  22#include <linux/irqchip/spear-shirq.h>
  23#include <linux/of.h>
  24#include <linux/of_address.h>
  25#include <linux/of_irq.h>
  26#include <linux/spinlock.h>
  27
  28static DEFINE_SPINLOCK(lock);
  29
  30/* spear300 shared irq registers offsets and masks */
  31#define SPEAR300_INT_ENB_MASK_REG       0x54
  32#define SPEAR300_INT_STS_MASK_REG       0x58
  33
  34static struct spear_shirq spear300_shirq_ras1 = {
  35        .irq_nr = 9,
  36        .irq_bit_off = 0,
  37        .regs = {
  38                .enb_reg = SPEAR300_INT_ENB_MASK_REG,
  39                .status_reg = SPEAR300_INT_STS_MASK_REG,
  40                .clear_reg = -1,
  41        },
  42};
  43
  44static struct spear_shirq *spear300_shirq_blocks[] = {
  45        &spear300_shirq_ras1,
  46};
  47
  48/* spear310 shared irq registers offsets and masks */
  49#define SPEAR310_INT_STS_MASK_REG       0x04
  50
  51static struct spear_shirq spear310_shirq_ras1 = {
  52        .irq_nr = 8,
  53        .irq_bit_off = 0,
  54        .regs = {
  55                .enb_reg = -1,
  56                .status_reg = SPEAR310_INT_STS_MASK_REG,
  57                .clear_reg = -1,
  58        },
  59};
  60
  61static struct spear_shirq spear310_shirq_ras2 = {
  62        .irq_nr = 5,
  63        .irq_bit_off = 8,
  64        .regs = {
  65                .enb_reg = -1,
  66                .status_reg = SPEAR310_INT_STS_MASK_REG,
  67                .clear_reg = -1,
  68        },
  69};
  70
  71static struct spear_shirq spear310_shirq_ras3 = {
  72        .irq_nr = 1,
  73        .irq_bit_off = 13,
  74        .regs = {
  75                .enb_reg = -1,
  76                .status_reg = SPEAR310_INT_STS_MASK_REG,
  77                .clear_reg = -1,
  78        },
  79};
  80
  81static struct spear_shirq spear310_shirq_intrcomm_ras = {
  82        .irq_nr = 3,
  83        .irq_bit_off = 14,
  84        .regs = {
  85                .enb_reg = -1,
  86                .status_reg = SPEAR310_INT_STS_MASK_REG,
  87                .clear_reg = -1,
  88        },
  89};
  90
  91static struct spear_shirq *spear310_shirq_blocks[] = {
  92        &spear310_shirq_ras1,
  93        &spear310_shirq_ras2,
  94        &spear310_shirq_ras3,
  95        &spear310_shirq_intrcomm_ras,
  96};
  97
  98/* spear320 shared irq registers offsets and masks */
  99#define SPEAR320_INT_STS_MASK_REG               0x04
 100#define SPEAR320_INT_CLR_MASK_REG               0x04
 101#define SPEAR320_INT_ENB_MASK_REG               0x08
 102
 103static struct spear_shirq spear320_shirq_ras1 = {
 104        .irq_nr = 3,
 105        .irq_bit_off = 7,
 106        .regs = {
 107                .enb_reg = -1,
 108                .status_reg = SPEAR320_INT_STS_MASK_REG,
 109                .clear_reg = SPEAR320_INT_CLR_MASK_REG,
 110                .reset_to_clear = 1,
 111        },
 112};
 113
 114static struct spear_shirq spear320_shirq_ras2 = {
 115        .irq_nr = 1,
 116        .irq_bit_off = 10,
 117        .regs = {
 118                .enb_reg = -1,
 119                .status_reg = SPEAR320_INT_STS_MASK_REG,
 120                .clear_reg = SPEAR320_INT_CLR_MASK_REG,
 121                .reset_to_clear = 1,
 122        },
 123};
 124
 125static struct spear_shirq spear320_shirq_ras3 = {
 126        .irq_nr = 3,
 127        .irq_bit_off = 0,
 128        .invalid_irq = 1,
 129        .regs = {
 130                .enb_reg = SPEAR320_INT_ENB_MASK_REG,
 131                .reset_to_enb = 1,
 132                .status_reg = SPEAR320_INT_STS_MASK_REG,
 133                .clear_reg = SPEAR320_INT_CLR_MASK_REG,
 134                .reset_to_clear = 1,
 135        },
 136};
 137
 138static struct spear_shirq spear320_shirq_intrcomm_ras = {
 139        .irq_nr = 11,
 140        .irq_bit_off = 11,
 141        .regs = {
 142                .enb_reg = -1,
 143                .status_reg = SPEAR320_INT_STS_MASK_REG,
 144                .clear_reg = SPEAR320_INT_CLR_MASK_REG,
 145                .reset_to_clear = 1,
 146        },
 147};
 148
 149static struct spear_shirq *spear320_shirq_blocks[] = {
 150        &spear320_shirq_ras3,
 151        &spear320_shirq_ras1,
 152        &spear320_shirq_ras2,
 153        &spear320_shirq_intrcomm_ras,
 154};
 155
 156static void shirq_irq_mask_unmask(struct irq_data *d, bool mask)
 157{
 158        struct spear_shirq *shirq = irq_data_get_irq_chip_data(d);
 159        u32 val, offset = d->irq - shirq->irq_base;
 160        unsigned long flags;
 161
 162        if (shirq->regs.enb_reg == -1)
 163                return;
 164
 165        spin_lock_irqsave(&lock, flags);
 166        val = readl(shirq->base + shirq->regs.enb_reg);
 167
 168        if (mask ^ shirq->regs.reset_to_enb)
 169                val &= ~(0x1 << shirq->irq_bit_off << offset);
 170        else
 171                val |= 0x1 << shirq->irq_bit_off << offset;
 172
 173        writel(val, shirq->base + shirq->regs.enb_reg);
 174        spin_unlock_irqrestore(&lock, flags);
 175
 176}
 177
 178static void shirq_irq_mask(struct irq_data *d)
 179{
 180        shirq_irq_mask_unmask(d, 1);
 181}
 182
 183static void shirq_irq_unmask(struct irq_data *d)
 184{
 185        shirq_irq_mask_unmask(d, 0);
 186}
 187
 188static struct irq_chip shirq_chip = {
 189        .name           = "spear-shirq",
 190        .irq_ack        = shirq_irq_mask,
 191        .irq_mask       = shirq_irq_mask,
 192        .irq_unmask     = shirq_irq_unmask,
 193};
 194
 195static void shirq_handler(unsigned irq, struct irq_desc *desc)
 196{
 197        u32 i, j, val, mask, tmp;
 198        struct irq_chip *chip;
 199        struct spear_shirq *shirq = irq_get_handler_data(irq);
 200
 201        chip = irq_get_chip(irq);
 202        chip->irq_ack(&desc->irq_data);
 203
 204        mask = ((0x1 << shirq->irq_nr) - 1) << shirq->irq_bit_off;
 205        while ((val = readl(shirq->base + shirq->regs.status_reg) &
 206                                mask)) {
 207
 208                val >>= shirq->irq_bit_off;
 209                for (i = 0, j = 1; i < shirq->irq_nr; i++, j <<= 1) {
 210
 211                        if (!(j & val))
 212                                continue;
 213
 214                        generic_handle_irq(shirq->irq_base + i);
 215
 216                        /* clear interrupt */
 217                        if (shirq->regs.clear_reg == -1)
 218                                continue;
 219
 220                        tmp = readl(shirq->base + shirq->regs.clear_reg);
 221                        if (shirq->regs.reset_to_clear)
 222                                tmp &= ~(j << shirq->irq_bit_off);
 223                        else
 224                                tmp |= (j << shirq->irq_bit_off);
 225                        writel(tmp, shirq->base + shirq->regs.clear_reg);
 226                }
 227        }
 228        chip->irq_unmask(&desc->irq_data);
 229}
 230
 231static void __init spear_shirq_register(struct spear_shirq *shirq)
 232{
 233        int i;
 234
 235        if (shirq->invalid_irq)
 236                return;
 237
 238        irq_set_chained_handler(shirq->irq, shirq_handler);
 239        for (i = 0; i < shirq->irq_nr; i++) {
 240                irq_set_chip_and_handler(shirq->irq_base + i,
 241                                         &shirq_chip, handle_simple_irq);
 242                set_irq_flags(shirq->irq_base + i, IRQF_VALID);
 243                irq_set_chip_data(shirq->irq_base + i, shirq);
 244        }
 245
 246        irq_set_handler_data(shirq->irq, shirq);
 247}
 248
 249static int __init shirq_init(struct spear_shirq **shirq_blocks, int block_nr,
 250                struct device_node *np)
 251{
 252        int i, irq_base, hwirq = 0, irq_nr = 0;
 253        static struct irq_domain *shirq_domain;
 254        void __iomem *base;
 255
 256        base = of_iomap(np, 0);
 257        if (!base) {
 258                pr_err("%s: failed to map shirq registers\n", __func__);
 259                return -ENXIO;
 260        }
 261
 262        for (i = 0; i < block_nr; i++)
 263                irq_nr += shirq_blocks[i]->irq_nr;
 264
 265        irq_base = irq_alloc_descs(-1, 0, irq_nr, 0);
 266        if (IS_ERR_VALUE(irq_base)) {
 267                pr_err("%s: irq desc alloc failed\n", __func__);
 268                goto err_unmap;
 269        }
 270
 271        shirq_domain = irq_domain_add_legacy(np, irq_nr, irq_base, 0,
 272                        &irq_domain_simple_ops, NULL);
 273        if (WARN_ON(!shirq_domain)) {
 274                pr_warn("%s: irq domain init failed\n", __func__);
 275                goto err_free_desc;
 276        }
 277
 278        for (i = 0; i < block_nr; i++) {
 279                shirq_blocks[i]->base = base;
 280                shirq_blocks[i]->irq_base = irq_find_mapping(shirq_domain,
 281                                hwirq);
 282                shirq_blocks[i]->irq = irq_of_parse_and_map(np, i);
 283
 284                spear_shirq_register(shirq_blocks[i]);
 285                hwirq += shirq_blocks[i]->irq_nr;
 286        }
 287
 288        return 0;
 289
 290err_free_desc:
 291        irq_free_descs(irq_base, irq_nr);
 292err_unmap:
 293        iounmap(base);
 294        return -ENXIO;
 295}
 296
 297int __init spear300_shirq_of_init(struct device_node *np,
 298                struct device_node *parent)
 299{
 300        return shirq_init(spear300_shirq_blocks,
 301                        ARRAY_SIZE(spear300_shirq_blocks), np);
 302}
 303
 304int __init spear310_shirq_of_init(struct device_node *np,
 305                struct device_node *parent)
 306{
 307        return shirq_init(spear310_shirq_blocks,
 308                        ARRAY_SIZE(spear310_shirq_blocks), np);
 309}
 310
 311int __init spear320_shirq_of_init(struct device_node *np,
 312                struct device_node *parent)
 313{
 314        return shirq_init(spear320_shirq_blocks,
 315                        ARRAY_SIZE(spear320_shirq_blocks), np);
 316}
 317
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.