1df1590d9SViresh Kumar /* 2df1590d9SViresh Kumar * SPEAr platform shared irq layer source file 3df1590d9SViresh Kumar * 4df1590d9SViresh Kumar * Copyright (C) 2009-2012 ST Microelectronics 5df1590d9SViresh Kumar * Viresh Kumar <viresh.linux@gmail.com> 6df1590d9SViresh Kumar * 7df1590d9SViresh Kumar * Copyright (C) 2012 ST Microelectronics 89cc23682SViresh Kumar * Shiraz Hashim <shiraz.linux.kernel@gmail.com> 9df1590d9SViresh Kumar * 10df1590d9SViresh Kumar * This file is licensed under the terms of the GNU General Public 11df1590d9SViresh Kumar * License version 2. This program is licensed "as is" without any 12df1590d9SViresh Kumar * warranty of any kind, whether express or implied. 13df1590d9SViresh Kumar */ 14df1590d9SViresh Kumar #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 15df1590d9SViresh Kumar 16df1590d9SViresh Kumar #include <linux/err.h> 17df1590d9SViresh Kumar #include <linux/export.h> 18df1590d9SViresh Kumar #include <linux/interrupt.h> 19df1590d9SViresh Kumar #include <linux/io.h> 20df1590d9SViresh Kumar #include <linux/irq.h> 21df1590d9SViresh Kumar #include <linux/irqdomain.h> 22df1590d9SViresh Kumar #include <linux/of.h> 23df1590d9SViresh Kumar #include <linux/of_address.h> 24df1590d9SViresh Kumar #include <linux/of_irq.h> 25df1590d9SViresh Kumar #include <linux/spinlock.h> 26df1590d9SViresh Kumar 27e9c51558SRob Herring #include "irqchip.h" 28e9c51558SRob Herring 29078bc005SThomas Gleixner /* 30078bc005SThomas Gleixner * struct spear_shirq: shared irq structure 31078bc005SThomas Gleixner * 32c5d1d857SThomas Gleixner * base: Base register address 331b0a76c1SThomas Gleixner * status_reg: Status register offset for chained interrupt handler 341b0a76c1SThomas Gleixner * mask_reg: Mask register offset for irq chip 354ecc832fSThomas Gleixner * mask: Mask to apply to the status register 36c5d1d857SThomas Gleixner * virq_base: Base virtual interrupt number 37c5d1d857SThomas Gleixner * nr_irqs: Number of interrupts handled by this block 38c5d1d857SThomas Gleixner * offset: Bit offset of the first interrupt 39f07e42f9SThomas Gleixner * irq_chip: Interrupt controller chip used for this instance, 40f07e42f9SThomas Gleixner * if NULL group is disabled, but accounted 41078bc005SThomas Gleixner */ 42078bc005SThomas Gleixner struct spear_shirq { 43078bc005SThomas Gleixner void __iomem *base; 441b0a76c1SThomas Gleixner u32 status_reg; 451b0a76c1SThomas Gleixner u32 mask_reg; 464ecc832fSThomas Gleixner u32 mask; 47c5d1d857SThomas Gleixner u32 virq_base; 48c5d1d857SThomas Gleixner u32 nr_irqs; 49c5d1d857SThomas Gleixner u32 offset; 50f07e42f9SThomas Gleixner struct irq_chip *irq_chip; 51078bc005SThomas Gleixner }; 52078bc005SThomas Gleixner 53df1590d9SViresh Kumar /* spear300 shared irq registers offsets and masks */ 54df1590d9SViresh Kumar #define SPEAR300_INT_ENB_MASK_REG 0x54 55df1590d9SViresh Kumar #define SPEAR300_INT_STS_MASK_REG 0x58 56df1590d9SViresh Kumar 57f07e42f9SThomas Gleixner static DEFINE_RAW_SPINLOCK(shirq_lock); 58f07e42f9SThomas Gleixner 59f07e42f9SThomas Gleixner static void shirq_irq_mask(struct irq_data *d) 60f07e42f9SThomas Gleixner { 61f07e42f9SThomas Gleixner struct spear_shirq *shirq = irq_data_get_irq_chip_data(d); 62f07e42f9SThomas Gleixner u32 val, shift = d->irq - shirq->virq_base + shirq->offset; 631b0a76c1SThomas Gleixner u32 __iomem *reg = shirq->base + shirq->mask_reg; 64f07e42f9SThomas Gleixner 65f07e42f9SThomas Gleixner raw_spin_lock(&shirq_lock); 66f07e42f9SThomas Gleixner val = readl(reg) & ~(0x1 << shift); 67f07e42f9SThomas Gleixner writel(val, reg); 68f07e42f9SThomas Gleixner raw_spin_unlock(&shirq_lock); 69f07e42f9SThomas Gleixner } 70f07e42f9SThomas Gleixner 71f07e42f9SThomas Gleixner static void shirq_irq_unmask(struct irq_data *d) 72f07e42f9SThomas Gleixner { 73f07e42f9SThomas Gleixner struct spear_shirq *shirq = irq_data_get_irq_chip_data(d); 74f07e42f9SThomas Gleixner u32 val, shift = d->irq - shirq->virq_base + shirq->offset; 751b0a76c1SThomas Gleixner u32 __iomem *reg = shirq->base + shirq->mask_reg; 76f07e42f9SThomas Gleixner 77f07e42f9SThomas Gleixner raw_spin_lock(&shirq_lock); 78f07e42f9SThomas Gleixner val = readl(reg) | (0x1 << shift); 79f07e42f9SThomas Gleixner writel(val, reg); 80f07e42f9SThomas Gleixner raw_spin_unlock(&shirq_lock); 81f07e42f9SThomas Gleixner } 82f07e42f9SThomas Gleixner 83f07e42f9SThomas Gleixner static struct irq_chip shirq_chip = { 84f07e42f9SThomas Gleixner .name = "spear-shirq", 85f07e42f9SThomas Gleixner .irq_mask = shirq_irq_mask, 86f07e42f9SThomas Gleixner .irq_unmask = shirq_irq_unmask, 87f07e42f9SThomas Gleixner }; 88f07e42f9SThomas Gleixner 89df1590d9SViresh Kumar static struct spear_shirq spear300_shirq_ras1 = { 90c5d1d857SThomas Gleixner .offset = 0, 91c5d1d857SThomas Gleixner .nr_irqs = 9, 924ecc832fSThomas Gleixner .mask = ((0x1 << 9) - 1) << 0, 93f07e42f9SThomas Gleixner .irq_chip = &shirq_chip, 94df1590d9SViresh Kumar .status_reg = SPEAR300_INT_STS_MASK_REG, 951b0a76c1SThomas Gleixner .mask_reg = SPEAR300_INT_ENB_MASK_REG, 96df1590d9SViresh Kumar }; 97df1590d9SViresh Kumar 98df1590d9SViresh Kumar static struct spear_shirq *spear300_shirq_blocks[] = { 99df1590d9SViresh Kumar &spear300_shirq_ras1, 100df1590d9SViresh Kumar }; 101df1590d9SViresh Kumar 102df1590d9SViresh Kumar /* spear310 shared irq registers offsets and masks */ 103df1590d9SViresh Kumar #define SPEAR310_INT_STS_MASK_REG 0x04 104df1590d9SViresh Kumar 105df1590d9SViresh Kumar static struct spear_shirq spear310_shirq_ras1 = { 106c5d1d857SThomas Gleixner .offset = 0, 107c5d1d857SThomas Gleixner .nr_irqs = 8, 1084ecc832fSThomas Gleixner .mask = ((0x1 << 8) - 1) << 0, 109f07e42f9SThomas Gleixner .irq_chip = &dummy_irq_chip, 110df1590d9SViresh Kumar .status_reg = SPEAR310_INT_STS_MASK_REG, 111df1590d9SViresh Kumar }; 112df1590d9SViresh Kumar 113df1590d9SViresh Kumar static struct spear_shirq spear310_shirq_ras2 = { 114c5d1d857SThomas Gleixner .offset = 8, 115c5d1d857SThomas Gleixner .nr_irqs = 5, 1164ecc832fSThomas Gleixner .mask = ((0x1 << 5) - 1) << 8, 117f07e42f9SThomas Gleixner .irq_chip = &dummy_irq_chip, 118df1590d9SViresh Kumar .status_reg = SPEAR310_INT_STS_MASK_REG, 119df1590d9SViresh Kumar }; 120df1590d9SViresh Kumar 121df1590d9SViresh Kumar static struct spear_shirq spear310_shirq_ras3 = { 122c5d1d857SThomas Gleixner .offset = 13, 123c5d1d857SThomas Gleixner .nr_irqs = 1, 1244ecc832fSThomas Gleixner .mask = ((0x1 << 1) - 1) << 13, 125f07e42f9SThomas Gleixner .irq_chip = &dummy_irq_chip, 126df1590d9SViresh Kumar .status_reg = SPEAR310_INT_STS_MASK_REG, 127df1590d9SViresh Kumar }; 128df1590d9SViresh Kumar 129df1590d9SViresh Kumar static struct spear_shirq spear310_shirq_intrcomm_ras = { 130c5d1d857SThomas Gleixner .offset = 14, 131c5d1d857SThomas Gleixner .nr_irqs = 3, 1324ecc832fSThomas Gleixner .mask = ((0x1 << 3) - 1) << 14, 133f07e42f9SThomas Gleixner .irq_chip = &dummy_irq_chip, 134df1590d9SViresh Kumar .status_reg = SPEAR310_INT_STS_MASK_REG, 135df1590d9SViresh Kumar }; 136df1590d9SViresh Kumar 137df1590d9SViresh Kumar static struct spear_shirq *spear310_shirq_blocks[] = { 138df1590d9SViresh Kumar &spear310_shirq_ras1, 139df1590d9SViresh Kumar &spear310_shirq_ras2, 140df1590d9SViresh Kumar &spear310_shirq_ras3, 141df1590d9SViresh Kumar &spear310_shirq_intrcomm_ras, 142df1590d9SViresh Kumar }; 143df1590d9SViresh Kumar 144df1590d9SViresh Kumar /* spear320 shared irq registers offsets and masks */ 145df1590d9SViresh Kumar #define SPEAR320_INT_STS_MASK_REG 0x04 146df1590d9SViresh Kumar #define SPEAR320_INT_CLR_MASK_REG 0x04 147df1590d9SViresh Kumar #define SPEAR320_INT_ENB_MASK_REG 0x08 148df1590d9SViresh Kumar 14903319a1aSThomas Gleixner static struct spear_shirq spear320_shirq_ras3 = { 15003319a1aSThomas Gleixner .offset = 0, 15103319a1aSThomas Gleixner .nr_irqs = 7, 1524ecc832fSThomas Gleixner .mask = ((0x1 << 7) - 1) << 0, 15303319a1aSThomas Gleixner }; 15403319a1aSThomas Gleixner 155df1590d9SViresh Kumar static struct spear_shirq spear320_shirq_ras1 = { 156c5d1d857SThomas Gleixner .offset = 7, 157c5d1d857SThomas Gleixner .nr_irqs = 3, 1584ecc832fSThomas Gleixner .mask = ((0x1 << 3) - 1) << 7, 159f07e42f9SThomas Gleixner .irq_chip = &dummy_irq_chip, 160df1590d9SViresh Kumar .status_reg = SPEAR320_INT_STS_MASK_REG, 161df1590d9SViresh Kumar }; 162df1590d9SViresh Kumar 163df1590d9SViresh Kumar static struct spear_shirq spear320_shirq_ras2 = { 164c5d1d857SThomas Gleixner .offset = 10, 165c5d1d857SThomas Gleixner .nr_irqs = 1, 1664ecc832fSThomas Gleixner .mask = ((0x1 << 1) - 1) << 10, 167f07e42f9SThomas Gleixner .irq_chip = &dummy_irq_chip, 168df1590d9SViresh Kumar .status_reg = SPEAR320_INT_STS_MASK_REG, 169df1590d9SViresh Kumar }; 170df1590d9SViresh Kumar 171df1590d9SViresh Kumar static struct spear_shirq spear320_shirq_intrcomm_ras = { 172c5d1d857SThomas Gleixner .offset = 11, 173c5d1d857SThomas Gleixner .nr_irqs = 11, 1744ecc832fSThomas Gleixner .mask = ((0x1 << 11) - 1) << 11, 175f07e42f9SThomas Gleixner .irq_chip = &dummy_irq_chip, 176df1590d9SViresh Kumar .status_reg = SPEAR320_INT_STS_MASK_REG, 177df1590d9SViresh Kumar }; 178df1590d9SViresh Kumar 179df1590d9SViresh Kumar static struct spear_shirq *spear320_shirq_blocks[] = { 180df1590d9SViresh Kumar &spear320_shirq_ras3, 181df1590d9SViresh Kumar &spear320_shirq_ras1, 182df1590d9SViresh Kumar &spear320_shirq_ras2, 183df1590d9SViresh Kumar &spear320_shirq_intrcomm_ras, 184df1590d9SViresh Kumar }; 185df1590d9SViresh Kumar 186df1590d9SViresh Kumar static void shirq_handler(unsigned irq, struct irq_desc *desc) 187df1590d9SViresh Kumar { 188df1590d9SViresh Kumar struct spear_shirq *shirq = irq_get_handler_data(irq); 18925dc49e3SThomas Gleixner u32 pend; 190df1590d9SViresh Kumar 1911b0a76c1SThomas Gleixner pend = readl(shirq->base + shirq->status_reg) & shirq->mask; 19225dc49e3SThomas Gleixner pend >>= shirq->offset; 193df1590d9SViresh Kumar 19425dc49e3SThomas Gleixner while (pend) { 19525dc49e3SThomas Gleixner int irq = __ffs(pend); 196df1590d9SViresh Kumar 19725dc49e3SThomas Gleixner pend &= ~(0x1 << irq); 19825dc49e3SThomas Gleixner generic_handle_irq(shirq->virq_base + irq); 199df1590d9SViresh Kumar } 200df1590d9SViresh Kumar } 201df1590d9SViresh Kumar 202f37ecbceSThomas Gleixner static void __init spear_shirq_register(struct spear_shirq *shirq, 203f37ecbceSThomas Gleixner int parent_irq) 204df1590d9SViresh Kumar { 205df1590d9SViresh Kumar int i; 206df1590d9SViresh Kumar 207f07e42f9SThomas Gleixner if (!shirq->irq_chip) 208df1590d9SViresh Kumar return; 209df1590d9SViresh Kumar 210f37ecbceSThomas Gleixner irq_set_chained_handler(parent_irq, shirq_handler); 211f37ecbceSThomas Gleixner irq_set_handler_data(parent_irq, shirq); 212f37ecbceSThomas Gleixner 213c5d1d857SThomas Gleixner for (i = 0; i < shirq->nr_irqs; i++) { 214c5d1d857SThomas Gleixner irq_set_chip_and_handler(shirq->virq_base + i, 215f07e42f9SThomas Gleixner shirq->irq_chip, handle_simple_irq); 216c5d1d857SThomas Gleixner set_irq_flags(shirq->virq_base + i, IRQF_VALID); 217c5d1d857SThomas Gleixner irq_set_chip_data(shirq->virq_base + i, shirq); 218df1590d9SViresh Kumar } 219df1590d9SViresh Kumar } 220df1590d9SViresh Kumar 221df1590d9SViresh Kumar static int __init shirq_init(struct spear_shirq **shirq_blocks, int block_nr, 222df1590d9SViresh Kumar struct device_node *np) 223df1590d9SViresh Kumar { 224c5d1d857SThomas Gleixner int i, parent_irq, virq_base, hwirq = 0, nr_irqs = 0; 225a26c06f9SThomas Gleixner struct irq_domain *shirq_domain; 226df1590d9SViresh Kumar void __iomem *base; 227df1590d9SViresh Kumar 228df1590d9SViresh Kumar base = of_iomap(np, 0); 229df1590d9SViresh Kumar if (!base) { 230df1590d9SViresh Kumar pr_err("%s: failed to map shirq registers\n", __func__); 231df1590d9SViresh Kumar return -ENXIO; 232df1590d9SViresh Kumar } 233df1590d9SViresh Kumar 234df1590d9SViresh Kumar for (i = 0; i < block_nr; i++) 235c5d1d857SThomas Gleixner nr_irqs += shirq_blocks[i]->nr_irqs; 236df1590d9SViresh Kumar 237c5d1d857SThomas Gleixner virq_base = irq_alloc_descs(-1, 0, nr_irqs, 0); 238c5d1d857SThomas Gleixner if (IS_ERR_VALUE(virq_base)) { 239df1590d9SViresh Kumar pr_err("%s: irq desc alloc failed\n", __func__); 240df1590d9SViresh Kumar goto err_unmap; 241df1590d9SViresh Kumar } 242df1590d9SViresh Kumar 243c5d1d857SThomas Gleixner shirq_domain = irq_domain_add_legacy(np, nr_irqs, virq_base, 0, 244df1590d9SViresh Kumar &irq_domain_simple_ops, NULL); 245df1590d9SViresh Kumar if (WARN_ON(!shirq_domain)) { 246df1590d9SViresh Kumar pr_warn("%s: irq domain init failed\n", __func__); 247df1590d9SViresh Kumar goto err_free_desc; 248df1590d9SViresh Kumar } 249df1590d9SViresh Kumar 250df1590d9SViresh Kumar for (i = 0; i < block_nr; i++) { 251df1590d9SViresh Kumar shirq_blocks[i]->base = base; 252c5d1d857SThomas Gleixner shirq_blocks[i]->virq_base = irq_find_mapping(shirq_domain, 253df1590d9SViresh Kumar hwirq); 254df1590d9SViresh Kumar 255f37ecbceSThomas Gleixner parent_irq = irq_of_parse_and_map(np, i); 256f37ecbceSThomas Gleixner spear_shirq_register(shirq_blocks[i], parent_irq); 257c5d1d857SThomas Gleixner hwirq += shirq_blocks[i]->nr_irqs; 258df1590d9SViresh Kumar } 259df1590d9SViresh Kumar 260df1590d9SViresh Kumar return 0; 261df1590d9SViresh Kumar 262df1590d9SViresh Kumar err_free_desc: 263c5d1d857SThomas Gleixner irq_free_descs(virq_base, nr_irqs); 264df1590d9SViresh Kumar err_unmap: 265df1590d9SViresh Kumar iounmap(base); 266df1590d9SViresh Kumar return -ENXIO; 267df1590d9SViresh Kumar } 268df1590d9SViresh Kumar 269078bc005SThomas Gleixner static int __init spear300_shirq_of_init(struct device_node *np, 270df1590d9SViresh Kumar struct device_node *parent) 271df1590d9SViresh Kumar { 272df1590d9SViresh Kumar return shirq_init(spear300_shirq_blocks, 273df1590d9SViresh Kumar ARRAY_SIZE(spear300_shirq_blocks), np); 274df1590d9SViresh Kumar } 275e9c51558SRob Herring IRQCHIP_DECLARE(spear300_shirq, "st,spear300-shirq", spear300_shirq_of_init); 276df1590d9SViresh Kumar 277078bc005SThomas Gleixner static int __init spear310_shirq_of_init(struct device_node *np, 278df1590d9SViresh Kumar struct device_node *parent) 279df1590d9SViresh Kumar { 280df1590d9SViresh Kumar return shirq_init(spear310_shirq_blocks, 281df1590d9SViresh Kumar ARRAY_SIZE(spear310_shirq_blocks), np); 282df1590d9SViresh Kumar } 283e9c51558SRob Herring IRQCHIP_DECLARE(spear310_shirq, "st,spear310-shirq", spear310_shirq_of_init); 284df1590d9SViresh Kumar 285078bc005SThomas Gleixner static int __init spear320_shirq_of_init(struct device_node *np, 286df1590d9SViresh Kumar struct device_node *parent) 287df1590d9SViresh Kumar { 288df1590d9SViresh Kumar return shirq_init(spear320_shirq_blocks, 289df1590d9SViresh Kumar ARRAY_SIZE(spear320_shirq_blocks), np); 290df1590d9SViresh Kumar } 291e9c51558SRob Herring IRQCHIP_DECLARE(spear320_shirq, "st,spear320-shirq", spear320_shirq_of_init); 292