14c18e77fSviresh kumar /* 24c18e77fSviresh kumar * SPEAr platform shared irq layer source file 34c18e77fSviresh kumar * 4df1590d9SViresh Kumar * Copyright (C) 2009-2012 ST Microelectronics 5da89947bSViresh Kumar * Viresh Kumar <vireshk@kernel.org> 64c18e77fSviresh kumar * 7df1590d9SViresh Kumar * Copyright (C) 2012 ST Microelectronics 89cc23682SViresh Kumar * Shiraz Hashim <shiraz.linux.kernel@gmail.com> 9df1590d9SViresh Kumar * 104c18e77fSviresh kumar * This file is licensed under the terms of the GNU General Public 114c18e77fSviresh kumar * License version 2. This program is licensed "as is" without any 124c18e77fSviresh kumar * warranty of any kind, whether express or implied. 134c18e77fSviresh kumar */ 1480515a5aSShiraz Hashim #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 154c18e77fSviresh kumar 164c18e77fSviresh kumar #include <linux/err.h> 1780515a5aSShiraz Hashim #include <linux/export.h> 1880515a5aSShiraz Hashim #include <linux/interrupt.h> 194c18e77fSviresh kumar #include <linux/io.h> 204c18e77fSviresh kumar #include <linux/irq.h> 2141a83e06SJoel Porquet #include <linux/irqchip.h> 2280515a5aSShiraz Hashim #include <linux/irqdomain.h> 2380515a5aSShiraz Hashim #include <linux/of.h> 2480515a5aSShiraz Hashim #include <linux/of_address.h> 2580515a5aSShiraz Hashim #include <linux/of_irq.h> 264c18e77fSviresh kumar #include <linux/spinlock.h> 274c18e77fSviresh kumar 28078bc005SThomas Gleixner /* 29078bc005SThomas Gleixner * struct spear_shirq: shared irq structure 30078bc005SThomas Gleixner * 31c5d1d857SThomas Gleixner * base: Base register address 321b0a76c1SThomas Gleixner * status_reg: Status register offset for chained interrupt handler 331b0a76c1SThomas Gleixner * mask_reg: Mask register offset for irq chip 344ecc832fSThomas Gleixner * mask: Mask to apply to the status register 35c5d1d857SThomas Gleixner * virq_base: Base virtual interrupt number 36c5d1d857SThomas Gleixner * nr_irqs: Number of interrupts handled by this block 37c5d1d857SThomas Gleixner * offset: Bit offset of the first interrupt 38f07e42f9SThomas Gleixner * irq_chip: Interrupt controller chip used for this instance, 39f07e42f9SThomas Gleixner * if NULL group is disabled, but accounted 40078bc005SThomas Gleixner */ 41078bc005SThomas Gleixner struct spear_shirq { 42078bc005SThomas Gleixner void __iomem *base; 431b0a76c1SThomas Gleixner u32 status_reg; 441b0a76c1SThomas Gleixner u32 mask_reg; 454ecc832fSThomas Gleixner u32 mask; 46c5d1d857SThomas Gleixner u32 virq_base; 47c5d1d857SThomas Gleixner u32 nr_irqs; 48c5d1d857SThomas Gleixner u32 offset; 49f07e42f9SThomas Gleixner struct irq_chip *irq_chip; 50078bc005SThomas Gleixner }; 51078bc005SThomas Gleixner 5280515a5aSShiraz Hashim /* spear300 shared irq registers offsets and masks */ 5380515a5aSShiraz Hashim #define SPEAR300_INT_ENB_MASK_REG 0x54 5480515a5aSShiraz Hashim #define SPEAR300_INT_STS_MASK_REG 0x58 5580515a5aSShiraz Hashim 56f07e42f9SThomas Gleixner static DEFINE_RAW_SPINLOCK(shirq_lock); 57f07e42f9SThomas Gleixner 58f07e42f9SThomas Gleixner static void shirq_irq_mask(struct irq_data *d) 59f07e42f9SThomas Gleixner { 60f07e42f9SThomas Gleixner struct spear_shirq *shirq = irq_data_get_irq_chip_data(d); 61f07e42f9SThomas Gleixner u32 val, shift = d->irq - shirq->virq_base + shirq->offset; 621b0a76c1SThomas Gleixner u32 __iomem *reg = shirq->base + shirq->mask_reg; 63f07e42f9SThomas Gleixner 64f07e42f9SThomas Gleixner raw_spin_lock(&shirq_lock); 65f07e42f9SThomas Gleixner val = readl(reg) & ~(0x1 << shift); 66f07e42f9SThomas Gleixner writel(val, reg); 67f07e42f9SThomas Gleixner raw_spin_unlock(&shirq_lock); 68f07e42f9SThomas Gleixner } 69f07e42f9SThomas Gleixner 70f07e42f9SThomas Gleixner static void shirq_irq_unmask(struct irq_data *d) 71f07e42f9SThomas Gleixner { 72f07e42f9SThomas Gleixner struct spear_shirq *shirq = irq_data_get_irq_chip_data(d); 73f07e42f9SThomas Gleixner u32 val, shift = d->irq - shirq->virq_base + shirq->offset; 741b0a76c1SThomas Gleixner u32 __iomem *reg = shirq->base + shirq->mask_reg; 75f07e42f9SThomas Gleixner 76f07e42f9SThomas Gleixner raw_spin_lock(&shirq_lock); 77f07e42f9SThomas Gleixner val = readl(reg) | (0x1 << shift); 78f07e42f9SThomas Gleixner writel(val, reg); 79f07e42f9SThomas Gleixner raw_spin_unlock(&shirq_lock); 80f07e42f9SThomas Gleixner } 81f07e42f9SThomas Gleixner 82f07e42f9SThomas Gleixner static struct irq_chip shirq_chip = { 83f07e42f9SThomas Gleixner .name = "spear-shirq", 84f07e42f9SThomas Gleixner .irq_mask = shirq_irq_mask, 85f07e42f9SThomas Gleixner .irq_unmask = shirq_irq_unmask, 86f07e42f9SThomas Gleixner }; 87f07e42f9SThomas Gleixner 8880515a5aSShiraz Hashim static struct spear_shirq spear300_shirq_ras1 = { 89c5d1d857SThomas Gleixner .offset = 0, 90c5d1d857SThomas Gleixner .nr_irqs = 9, 914ecc832fSThomas Gleixner .mask = ((0x1 << 9) - 1) << 0, 92f07e42f9SThomas Gleixner .irq_chip = &shirq_chip, 9380515a5aSShiraz Hashim .status_reg = SPEAR300_INT_STS_MASK_REG, 941b0a76c1SThomas Gleixner .mask_reg = SPEAR300_INT_ENB_MASK_REG, 9580515a5aSShiraz Hashim }; 9680515a5aSShiraz Hashim 9780515a5aSShiraz Hashim static struct spear_shirq *spear300_shirq_blocks[] = { 9880515a5aSShiraz Hashim &spear300_shirq_ras1, 9980515a5aSShiraz Hashim }; 10080515a5aSShiraz Hashim 10180515a5aSShiraz Hashim /* spear310 shared irq registers offsets and masks */ 10280515a5aSShiraz Hashim #define SPEAR310_INT_STS_MASK_REG 0x04 10380515a5aSShiraz Hashim 10480515a5aSShiraz Hashim static struct spear_shirq spear310_shirq_ras1 = { 105c5d1d857SThomas Gleixner .offset = 0, 106c5d1d857SThomas Gleixner .nr_irqs = 8, 1074ecc832fSThomas Gleixner .mask = ((0x1 << 8) - 1) << 0, 108f07e42f9SThomas Gleixner .irq_chip = &dummy_irq_chip, 10980515a5aSShiraz Hashim .status_reg = SPEAR310_INT_STS_MASK_REG, 11080515a5aSShiraz Hashim }; 11180515a5aSShiraz Hashim 11280515a5aSShiraz Hashim static struct spear_shirq spear310_shirq_ras2 = { 113c5d1d857SThomas Gleixner .offset = 8, 114c5d1d857SThomas Gleixner .nr_irqs = 5, 1154ecc832fSThomas Gleixner .mask = ((0x1 << 5) - 1) << 8, 116f07e42f9SThomas Gleixner .irq_chip = &dummy_irq_chip, 11780515a5aSShiraz Hashim .status_reg = SPEAR310_INT_STS_MASK_REG, 11880515a5aSShiraz Hashim }; 11980515a5aSShiraz Hashim 12080515a5aSShiraz Hashim static struct spear_shirq spear310_shirq_ras3 = { 121c5d1d857SThomas Gleixner .offset = 13, 122c5d1d857SThomas Gleixner .nr_irqs = 1, 1234ecc832fSThomas Gleixner .mask = ((0x1 << 1) - 1) << 13, 124f07e42f9SThomas Gleixner .irq_chip = &dummy_irq_chip, 12580515a5aSShiraz Hashim .status_reg = SPEAR310_INT_STS_MASK_REG, 12680515a5aSShiraz Hashim }; 12780515a5aSShiraz Hashim 12880515a5aSShiraz Hashim static struct spear_shirq spear310_shirq_intrcomm_ras = { 129c5d1d857SThomas Gleixner .offset = 14, 130c5d1d857SThomas Gleixner .nr_irqs = 3, 1314ecc832fSThomas Gleixner .mask = ((0x1 << 3) - 1) << 14, 132f07e42f9SThomas Gleixner .irq_chip = &dummy_irq_chip, 13380515a5aSShiraz Hashim .status_reg = SPEAR310_INT_STS_MASK_REG, 13480515a5aSShiraz Hashim }; 13580515a5aSShiraz Hashim 13680515a5aSShiraz Hashim static struct spear_shirq *spear310_shirq_blocks[] = { 13780515a5aSShiraz Hashim &spear310_shirq_ras1, 13880515a5aSShiraz Hashim &spear310_shirq_ras2, 13980515a5aSShiraz Hashim &spear310_shirq_ras3, 14080515a5aSShiraz Hashim &spear310_shirq_intrcomm_ras, 14180515a5aSShiraz Hashim }; 14280515a5aSShiraz Hashim 14380515a5aSShiraz Hashim /* spear320 shared irq registers offsets and masks */ 14480515a5aSShiraz Hashim #define SPEAR320_INT_STS_MASK_REG 0x04 14580515a5aSShiraz Hashim #define SPEAR320_INT_CLR_MASK_REG 0x04 14680515a5aSShiraz Hashim #define SPEAR320_INT_ENB_MASK_REG 0x08 14780515a5aSShiraz Hashim 14803319a1aSThomas Gleixner static struct spear_shirq spear320_shirq_ras3 = { 14903319a1aSThomas Gleixner .offset = 0, 15003319a1aSThomas Gleixner .nr_irqs = 7, 1514ecc832fSThomas Gleixner .mask = ((0x1 << 7) - 1) << 0, 15203319a1aSThomas Gleixner }; 15303319a1aSThomas Gleixner 15480515a5aSShiraz Hashim static struct spear_shirq spear320_shirq_ras1 = { 155c5d1d857SThomas Gleixner .offset = 7, 156c5d1d857SThomas Gleixner .nr_irqs = 3, 1574ecc832fSThomas Gleixner .mask = ((0x1 << 3) - 1) << 7, 158f07e42f9SThomas Gleixner .irq_chip = &dummy_irq_chip, 15980515a5aSShiraz Hashim .status_reg = SPEAR320_INT_STS_MASK_REG, 16080515a5aSShiraz Hashim }; 16180515a5aSShiraz Hashim 16280515a5aSShiraz Hashim static struct spear_shirq spear320_shirq_ras2 = { 163c5d1d857SThomas Gleixner .offset = 10, 164c5d1d857SThomas Gleixner .nr_irqs = 1, 1654ecc832fSThomas Gleixner .mask = ((0x1 << 1) - 1) << 10, 166f07e42f9SThomas Gleixner .irq_chip = &dummy_irq_chip, 16780515a5aSShiraz Hashim .status_reg = SPEAR320_INT_STS_MASK_REG, 16880515a5aSShiraz Hashim }; 16980515a5aSShiraz Hashim 17080515a5aSShiraz Hashim static struct spear_shirq spear320_shirq_intrcomm_ras = { 171c5d1d857SThomas Gleixner .offset = 11, 172c5d1d857SThomas Gleixner .nr_irqs = 11, 1734ecc832fSThomas Gleixner .mask = ((0x1 << 11) - 1) << 11, 174f07e42f9SThomas Gleixner .irq_chip = &dummy_irq_chip, 17580515a5aSShiraz Hashim .status_reg = SPEAR320_INT_STS_MASK_REG, 17680515a5aSShiraz Hashim }; 17780515a5aSShiraz Hashim 17880515a5aSShiraz Hashim static struct spear_shirq *spear320_shirq_blocks[] = { 17980515a5aSShiraz Hashim &spear320_shirq_ras3, 18080515a5aSShiraz Hashim &spear320_shirq_ras1, 18180515a5aSShiraz Hashim &spear320_shirq_ras2, 18280515a5aSShiraz Hashim &spear320_shirq_intrcomm_ras, 18380515a5aSShiraz Hashim }; 18480515a5aSShiraz Hashim 185*bd0b9ac4SThomas Gleixner static void shirq_handler(struct irq_desc *desc) 1864c18e77fSviresh kumar { 1875b29264cSJiang Liu struct spear_shirq *shirq = irq_desc_get_handler_data(desc); 18825dc49e3SThomas Gleixner u32 pend; 1894c18e77fSviresh kumar 1901b0a76c1SThomas Gleixner pend = readl(shirq->base + shirq->status_reg) & shirq->mask; 19125dc49e3SThomas Gleixner pend >>= shirq->offset; 19280515a5aSShiraz Hashim 19325dc49e3SThomas Gleixner while (pend) { 19425dc49e3SThomas Gleixner int irq = __ffs(pend); 19580515a5aSShiraz Hashim 19625dc49e3SThomas Gleixner pend &= ~(0x1 << irq); 19725dc49e3SThomas Gleixner generic_handle_irq(shirq->virq_base + irq); 1984c18e77fSviresh kumar } 1994c18e77fSviresh kumar } 2004c18e77fSviresh kumar 201f37ecbceSThomas Gleixner static void __init spear_shirq_register(struct spear_shirq *shirq, 202f37ecbceSThomas Gleixner int parent_irq) 2034c18e77fSviresh kumar { 2044c18e77fSviresh kumar int i; 2054c18e77fSviresh kumar 206f07e42f9SThomas Gleixner if (!shirq->irq_chip) 20780515a5aSShiraz Hashim return; 2084c18e77fSviresh kumar 2092aedd0fdSRussell King irq_set_chained_handler_and_data(parent_irq, shirq_handler, shirq); 210f37ecbceSThomas Gleixner 211c5d1d857SThomas Gleixner for (i = 0; i < shirq->nr_irqs; i++) { 212c5d1d857SThomas Gleixner irq_set_chip_and_handler(shirq->virq_base + i, 213f07e42f9SThomas Gleixner shirq->irq_chip, handle_simple_irq); 214c5d1d857SThomas Gleixner set_irq_flags(shirq->virq_base + i, IRQF_VALID); 215c5d1d857SThomas Gleixner irq_set_chip_data(shirq->virq_base + i, shirq); 2164c18e77fSviresh kumar } 21780515a5aSShiraz Hashim } 21880515a5aSShiraz Hashim 21980515a5aSShiraz Hashim static int __init shirq_init(struct spear_shirq **shirq_blocks, int block_nr, 22080515a5aSShiraz Hashim struct device_node *np) 22180515a5aSShiraz Hashim { 222c5d1d857SThomas Gleixner int i, parent_irq, virq_base, hwirq = 0, nr_irqs = 0; 223a26c06f9SThomas Gleixner struct irq_domain *shirq_domain; 22480515a5aSShiraz Hashim void __iomem *base; 22580515a5aSShiraz Hashim 22680515a5aSShiraz Hashim base = of_iomap(np, 0); 22780515a5aSShiraz Hashim if (!base) { 22880515a5aSShiraz Hashim pr_err("%s: failed to map shirq registers\n", __func__); 22980515a5aSShiraz Hashim return -ENXIO; 23080515a5aSShiraz Hashim } 23180515a5aSShiraz Hashim 23280515a5aSShiraz Hashim for (i = 0; i < block_nr; i++) 233c5d1d857SThomas Gleixner nr_irqs += shirq_blocks[i]->nr_irqs; 23480515a5aSShiraz Hashim 235c5d1d857SThomas Gleixner virq_base = irq_alloc_descs(-1, 0, nr_irqs, 0); 236c5d1d857SThomas Gleixner if (IS_ERR_VALUE(virq_base)) { 23780515a5aSShiraz Hashim pr_err("%s: irq desc alloc failed\n", __func__); 23880515a5aSShiraz Hashim goto err_unmap; 23980515a5aSShiraz Hashim } 24080515a5aSShiraz Hashim 241c5d1d857SThomas Gleixner shirq_domain = irq_domain_add_legacy(np, nr_irqs, virq_base, 0, 24280515a5aSShiraz Hashim &irq_domain_simple_ops, NULL); 24380515a5aSShiraz Hashim if (WARN_ON(!shirq_domain)) { 24480515a5aSShiraz Hashim pr_warn("%s: irq domain init failed\n", __func__); 24580515a5aSShiraz Hashim goto err_free_desc; 24680515a5aSShiraz Hashim } 24780515a5aSShiraz Hashim 24880515a5aSShiraz Hashim for (i = 0; i < block_nr; i++) { 24980515a5aSShiraz Hashim shirq_blocks[i]->base = base; 250c5d1d857SThomas Gleixner shirq_blocks[i]->virq_base = irq_find_mapping(shirq_domain, 25180515a5aSShiraz Hashim hwirq); 25280515a5aSShiraz Hashim 253f37ecbceSThomas Gleixner parent_irq = irq_of_parse_and_map(np, i); 254f37ecbceSThomas Gleixner spear_shirq_register(shirq_blocks[i], parent_irq); 255c5d1d857SThomas Gleixner hwirq += shirq_blocks[i]->nr_irqs; 25680515a5aSShiraz Hashim } 25780515a5aSShiraz Hashim 2584c18e77fSviresh kumar return 0; 25980515a5aSShiraz Hashim 26080515a5aSShiraz Hashim err_free_desc: 261c5d1d857SThomas Gleixner irq_free_descs(virq_base, nr_irqs); 26280515a5aSShiraz Hashim err_unmap: 26380515a5aSShiraz Hashim iounmap(base); 26480515a5aSShiraz Hashim return -ENXIO; 26580515a5aSShiraz Hashim } 26680515a5aSShiraz Hashim 267078bc005SThomas Gleixner static int __init spear300_shirq_of_init(struct device_node *np, 26880515a5aSShiraz Hashim struct device_node *parent) 26980515a5aSShiraz Hashim { 27080515a5aSShiraz Hashim return shirq_init(spear300_shirq_blocks, 27180515a5aSShiraz Hashim ARRAY_SIZE(spear300_shirq_blocks), np); 27280515a5aSShiraz Hashim } 273e9c51558SRob Herring IRQCHIP_DECLARE(spear300_shirq, "st,spear300-shirq", spear300_shirq_of_init); 27480515a5aSShiraz Hashim 275078bc005SThomas Gleixner static int __init spear310_shirq_of_init(struct device_node *np, 27680515a5aSShiraz Hashim struct device_node *parent) 27780515a5aSShiraz Hashim { 27880515a5aSShiraz Hashim return shirq_init(spear310_shirq_blocks, 27980515a5aSShiraz Hashim ARRAY_SIZE(spear310_shirq_blocks), np); 28080515a5aSShiraz Hashim } 281e9c51558SRob Herring IRQCHIP_DECLARE(spear310_shirq, "st,spear310-shirq", spear310_shirq_of_init); 28280515a5aSShiraz Hashim 283078bc005SThomas Gleixner static int __init spear320_shirq_of_init(struct device_node *np, 28480515a5aSShiraz Hashim struct device_node *parent) 28580515a5aSShiraz Hashim { 28680515a5aSShiraz Hashim return shirq_init(spear320_shirq_blocks, 28780515a5aSShiraz Hashim ARRAY_SIZE(spear320_shirq_blocks), np); 2884c18e77fSviresh kumar } 289e9c51558SRob Herring IRQCHIP_DECLARE(spear320_shirq, "st,spear320-shirq", spear320_shirq_of_init); 290