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 shirq_regs: shared irq register configuration 31078bc005SThomas Gleixner * 32078bc005SThomas Gleixner * enb_reg: enable register offset 33078bc005SThomas Gleixner * reset_to_enb: val 1 indicates, we need to clear bit for enabling interrupt 34078bc005SThomas Gleixner * status_reg: status register offset 35078bc005SThomas Gleixner * status_reg_mask: status register valid mask 36078bc005SThomas Gleixner * clear_reg: clear register offset 37078bc005SThomas Gleixner * reset_to_clear: val 1 indicates, we need to clear bit for clearing interrupt 38078bc005SThomas Gleixner */ 39078bc005SThomas Gleixner struct shirq_regs { 40078bc005SThomas Gleixner u32 enb_reg; 41078bc005SThomas Gleixner u32 reset_to_enb; 42078bc005SThomas Gleixner u32 status_reg; 43078bc005SThomas Gleixner u32 clear_reg; 44078bc005SThomas Gleixner u32 reset_to_clear; 45078bc005SThomas Gleixner }; 46078bc005SThomas Gleixner 47078bc005SThomas Gleixner /* 48078bc005SThomas Gleixner * struct spear_shirq: shared irq structure 49078bc005SThomas Gleixner * 50c5d1d857SThomas Gleixner * base: Base register address 51c5d1d857SThomas Gleixner * regs: Register configuration for shared irq block 524ecc832fSThomas Gleixner * mask: Mask to apply to the status register 53c5d1d857SThomas Gleixner * virq_base: Base virtual interrupt number 54c5d1d857SThomas Gleixner * nr_irqs: Number of interrupts handled by this block 55c5d1d857SThomas Gleixner * offset: Bit offset of the first interrupt 56c5d1d857SThomas Gleixner * disabled: Group is disabled, but accounted 57078bc005SThomas Gleixner */ 58078bc005SThomas Gleixner struct spear_shirq { 59078bc005SThomas Gleixner void __iomem *base; 60078bc005SThomas Gleixner struct shirq_regs regs; 614ecc832fSThomas Gleixner u32 mask; 62c5d1d857SThomas Gleixner u32 virq_base; 63c5d1d857SThomas Gleixner u32 nr_irqs; 64c5d1d857SThomas Gleixner u32 offset; 65c5d1d857SThomas Gleixner bool disabled; 66078bc005SThomas Gleixner }; 67078bc005SThomas Gleixner 68df1590d9SViresh Kumar static DEFINE_SPINLOCK(lock); 69df1590d9SViresh Kumar 70df1590d9SViresh Kumar /* spear300 shared irq registers offsets and masks */ 71df1590d9SViresh Kumar #define SPEAR300_INT_ENB_MASK_REG 0x54 72df1590d9SViresh Kumar #define SPEAR300_INT_STS_MASK_REG 0x58 73df1590d9SViresh Kumar 74df1590d9SViresh Kumar static struct spear_shirq spear300_shirq_ras1 = { 75c5d1d857SThomas Gleixner .offset = 0, 76c5d1d857SThomas Gleixner .nr_irqs = 9, 774ecc832fSThomas Gleixner .mask = ((0x1 << 9) - 1) << 0, 78df1590d9SViresh Kumar .regs = { 79df1590d9SViresh Kumar .enb_reg = SPEAR300_INT_ENB_MASK_REG, 80df1590d9SViresh Kumar .status_reg = SPEAR300_INT_STS_MASK_REG, 81df1590d9SViresh Kumar .clear_reg = -1, 82df1590d9SViresh Kumar }, 83df1590d9SViresh Kumar }; 84df1590d9SViresh Kumar 85df1590d9SViresh Kumar static struct spear_shirq *spear300_shirq_blocks[] = { 86df1590d9SViresh Kumar &spear300_shirq_ras1, 87df1590d9SViresh Kumar }; 88df1590d9SViresh Kumar 89df1590d9SViresh Kumar /* spear310 shared irq registers offsets and masks */ 90df1590d9SViresh Kumar #define SPEAR310_INT_STS_MASK_REG 0x04 91df1590d9SViresh Kumar 92df1590d9SViresh Kumar static struct spear_shirq spear310_shirq_ras1 = { 93c5d1d857SThomas Gleixner .offset = 0, 94c5d1d857SThomas Gleixner .nr_irqs = 8, 954ecc832fSThomas Gleixner .mask = ((0x1 << 8) - 1) << 0, 96df1590d9SViresh Kumar .regs = { 97df1590d9SViresh Kumar .enb_reg = -1, 98df1590d9SViresh Kumar .status_reg = SPEAR310_INT_STS_MASK_REG, 99df1590d9SViresh Kumar .clear_reg = -1, 100df1590d9SViresh Kumar }, 101df1590d9SViresh Kumar }; 102df1590d9SViresh Kumar 103df1590d9SViresh Kumar static struct spear_shirq spear310_shirq_ras2 = { 104c5d1d857SThomas Gleixner .offset = 8, 105c5d1d857SThomas Gleixner .nr_irqs = 5, 1064ecc832fSThomas Gleixner .mask = ((0x1 << 5) - 1) << 8, 107df1590d9SViresh Kumar .regs = { 108df1590d9SViresh Kumar .enb_reg = -1, 109df1590d9SViresh Kumar .status_reg = SPEAR310_INT_STS_MASK_REG, 110df1590d9SViresh Kumar .clear_reg = -1, 111df1590d9SViresh Kumar }, 112df1590d9SViresh Kumar }; 113df1590d9SViresh Kumar 114df1590d9SViresh Kumar static struct spear_shirq spear310_shirq_ras3 = { 115c5d1d857SThomas Gleixner .offset = 13, 116c5d1d857SThomas Gleixner .nr_irqs = 1, 1174ecc832fSThomas Gleixner .mask = ((0x1 << 1) - 1) << 13, 118df1590d9SViresh Kumar .regs = { 119df1590d9SViresh Kumar .enb_reg = -1, 120df1590d9SViresh Kumar .status_reg = SPEAR310_INT_STS_MASK_REG, 121df1590d9SViresh Kumar .clear_reg = -1, 122df1590d9SViresh Kumar }, 123df1590d9SViresh Kumar }; 124df1590d9SViresh Kumar 125df1590d9SViresh Kumar static struct spear_shirq spear310_shirq_intrcomm_ras = { 126c5d1d857SThomas Gleixner .offset = 14, 127c5d1d857SThomas Gleixner .nr_irqs = 3, 1284ecc832fSThomas Gleixner .mask = ((0x1 << 3) - 1) << 14, 129df1590d9SViresh Kumar .regs = { 130df1590d9SViresh Kumar .enb_reg = -1, 131df1590d9SViresh Kumar .status_reg = SPEAR310_INT_STS_MASK_REG, 132df1590d9SViresh Kumar .clear_reg = -1, 133df1590d9SViresh Kumar }, 134df1590d9SViresh Kumar }; 135df1590d9SViresh Kumar 136df1590d9SViresh Kumar static struct spear_shirq *spear310_shirq_blocks[] = { 137df1590d9SViresh Kumar &spear310_shirq_ras1, 138df1590d9SViresh Kumar &spear310_shirq_ras2, 139df1590d9SViresh Kumar &spear310_shirq_ras3, 140df1590d9SViresh Kumar &spear310_shirq_intrcomm_ras, 141df1590d9SViresh Kumar }; 142df1590d9SViresh Kumar 143df1590d9SViresh Kumar /* spear320 shared irq registers offsets and masks */ 144df1590d9SViresh Kumar #define SPEAR320_INT_STS_MASK_REG 0x04 145df1590d9SViresh Kumar #define SPEAR320_INT_CLR_MASK_REG 0x04 146df1590d9SViresh Kumar #define SPEAR320_INT_ENB_MASK_REG 0x08 147df1590d9SViresh Kumar 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 .disabled = 1, 15303319a1aSThomas Gleixner .regs = { 15403319a1aSThomas Gleixner .enb_reg = SPEAR320_INT_ENB_MASK_REG, 15503319a1aSThomas Gleixner .reset_to_enb = 1, 15603319a1aSThomas Gleixner .status_reg = SPEAR320_INT_STS_MASK_REG, 15703319a1aSThomas Gleixner .clear_reg = SPEAR320_INT_CLR_MASK_REG, 15803319a1aSThomas Gleixner .reset_to_clear = 1, 15903319a1aSThomas Gleixner }, 16003319a1aSThomas Gleixner }; 16103319a1aSThomas Gleixner 162df1590d9SViresh Kumar static struct spear_shirq spear320_shirq_ras1 = { 163c5d1d857SThomas Gleixner .offset = 7, 164c5d1d857SThomas Gleixner .nr_irqs = 3, 1654ecc832fSThomas Gleixner .mask = ((0x1 << 3) - 1) << 7, 166df1590d9SViresh Kumar .regs = { 167df1590d9SViresh Kumar .enb_reg = -1, 168df1590d9SViresh Kumar .status_reg = SPEAR320_INT_STS_MASK_REG, 169df1590d9SViresh Kumar .clear_reg = SPEAR320_INT_CLR_MASK_REG, 170df1590d9SViresh Kumar .reset_to_clear = 1, 171df1590d9SViresh Kumar }, 172df1590d9SViresh Kumar }; 173df1590d9SViresh Kumar 174df1590d9SViresh Kumar static struct spear_shirq spear320_shirq_ras2 = { 175c5d1d857SThomas Gleixner .offset = 10, 176c5d1d857SThomas Gleixner .nr_irqs = 1, 1774ecc832fSThomas Gleixner .mask = ((0x1 << 1) - 1) << 10, 178df1590d9SViresh Kumar .regs = { 179df1590d9SViresh Kumar .enb_reg = -1, 180df1590d9SViresh Kumar .status_reg = SPEAR320_INT_STS_MASK_REG, 181df1590d9SViresh Kumar .clear_reg = SPEAR320_INT_CLR_MASK_REG, 182df1590d9SViresh Kumar .reset_to_clear = 1, 183df1590d9SViresh Kumar }, 184df1590d9SViresh Kumar }; 185df1590d9SViresh Kumar 186df1590d9SViresh Kumar static struct spear_shirq spear320_shirq_intrcomm_ras = { 187c5d1d857SThomas Gleixner .offset = 11, 188c5d1d857SThomas Gleixner .nr_irqs = 11, 1894ecc832fSThomas Gleixner .mask = ((0x1 << 11) - 1) << 11, 190df1590d9SViresh Kumar .regs = { 191df1590d9SViresh Kumar .enb_reg = -1, 192df1590d9SViresh Kumar .status_reg = SPEAR320_INT_STS_MASK_REG, 193df1590d9SViresh Kumar .clear_reg = SPEAR320_INT_CLR_MASK_REG, 194df1590d9SViresh Kumar .reset_to_clear = 1, 195df1590d9SViresh Kumar }, 196df1590d9SViresh Kumar }; 197df1590d9SViresh Kumar 198df1590d9SViresh Kumar static struct spear_shirq *spear320_shirq_blocks[] = { 199df1590d9SViresh Kumar &spear320_shirq_ras3, 200df1590d9SViresh Kumar &spear320_shirq_ras1, 201df1590d9SViresh Kumar &spear320_shirq_ras2, 202df1590d9SViresh Kumar &spear320_shirq_intrcomm_ras, 203df1590d9SViresh Kumar }; 204df1590d9SViresh Kumar 205df1590d9SViresh Kumar static void shirq_irq_mask_unmask(struct irq_data *d, bool mask) 206df1590d9SViresh Kumar { 207df1590d9SViresh Kumar struct spear_shirq *shirq = irq_data_get_irq_chip_data(d); 208c5d1d857SThomas Gleixner u32 val, offset = d->irq - shirq->virq_base; 209df1590d9SViresh Kumar unsigned long flags; 210df1590d9SViresh Kumar 211df1590d9SViresh Kumar if (shirq->regs.enb_reg == -1) 212df1590d9SViresh Kumar return; 213df1590d9SViresh Kumar 214df1590d9SViresh Kumar spin_lock_irqsave(&lock, flags); 215df1590d9SViresh Kumar val = readl(shirq->base + shirq->regs.enb_reg); 216df1590d9SViresh Kumar 217df1590d9SViresh Kumar if (mask ^ shirq->regs.reset_to_enb) 218c5d1d857SThomas Gleixner val &= ~(0x1 << shirq->offset << offset); 219df1590d9SViresh Kumar else 220c5d1d857SThomas Gleixner val |= 0x1 << shirq->offset << offset; 221df1590d9SViresh Kumar 222df1590d9SViresh Kumar writel(val, shirq->base + shirq->regs.enb_reg); 223df1590d9SViresh Kumar spin_unlock_irqrestore(&lock, flags); 224df1590d9SViresh Kumar 225df1590d9SViresh Kumar } 226df1590d9SViresh Kumar 227df1590d9SViresh Kumar static void shirq_irq_mask(struct irq_data *d) 228df1590d9SViresh Kumar { 229df1590d9SViresh Kumar shirq_irq_mask_unmask(d, 1); 230df1590d9SViresh Kumar } 231df1590d9SViresh Kumar 232df1590d9SViresh Kumar static void shirq_irq_unmask(struct irq_data *d) 233df1590d9SViresh Kumar { 234df1590d9SViresh Kumar shirq_irq_mask_unmask(d, 0); 235df1590d9SViresh Kumar } 236df1590d9SViresh Kumar 237df1590d9SViresh Kumar static struct irq_chip shirq_chip = { 238df1590d9SViresh Kumar .name = "spear-shirq", 239df1590d9SViresh Kumar .irq_ack = shirq_irq_mask, 240df1590d9SViresh Kumar .irq_mask = shirq_irq_mask, 241df1590d9SViresh Kumar .irq_unmask = shirq_irq_unmask, 242df1590d9SViresh Kumar }; 243df1590d9SViresh Kumar 244df1590d9SViresh Kumar static void shirq_handler(unsigned irq, struct irq_desc *desc) 245df1590d9SViresh Kumar { 246df1590d9SViresh Kumar struct spear_shirq *shirq = irq_get_handler_data(irq); 247e3c871abSThomas Gleixner struct irq_data *idata = irq_desc_get_irq_data(desc); 248e3c871abSThomas Gleixner struct irq_chip *chip = irq_data_get_irq_chip(idata); 249e3c871abSThomas Gleixner u32 i, j, val, mask, tmp; 250df1590d9SViresh Kumar 251e3c871abSThomas Gleixner chip->irq_ack(idata); 252df1590d9SViresh Kumar 2534ecc832fSThomas Gleixner mask = shirq->mask; 254df1590d9SViresh Kumar while ((val = readl(shirq->base + shirq->regs.status_reg) & 255df1590d9SViresh Kumar mask)) { 256df1590d9SViresh Kumar 257c5d1d857SThomas Gleixner val >>= shirq->offset; 258c5d1d857SThomas Gleixner for (i = 0, j = 1; i < shirq->nr_irqs; i++, j <<= 1) { 259df1590d9SViresh Kumar 260df1590d9SViresh Kumar if (!(j & val)) 261df1590d9SViresh Kumar continue; 262df1590d9SViresh Kumar 263c5d1d857SThomas Gleixner generic_handle_irq(shirq->virq_base + i); 264df1590d9SViresh Kumar 265df1590d9SViresh Kumar /* clear interrupt */ 266df1590d9SViresh Kumar if (shirq->regs.clear_reg == -1) 267df1590d9SViresh Kumar continue; 268df1590d9SViresh Kumar 269df1590d9SViresh Kumar tmp = readl(shirq->base + shirq->regs.clear_reg); 270df1590d9SViresh Kumar if (shirq->regs.reset_to_clear) 271c5d1d857SThomas Gleixner tmp &= ~(j << shirq->offset); 272df1590d9SViresh Kumar else 273c5d1d857SThomas Gleixner tmp |= (j << shirq->offset); 274df1590d9SViresh Kumar writel(tmp, shirq->base + shirq->regs.clear_reg); 275df1590d9SViresh Kumar } 276df1590d9SViresh Kumar } 277e3c871abSThomas Gleixner chip->irq_unmask(idata); 278df1590d9SViresh Kumar } 279df1590d9SViresh Kumar 280f37ecbceSThomas Gleixner static void __init spear_shirq_register(struct spear_shirq *shirq, 281f37ecbceSThomas Gleixner int parent_irq) 282df1590d9SViresh Kumar { 283df1590d9SViresh Kumar int i; 284df1590d9SViresh Kumar 285c5d1d857SThomas Gleixner if (shirq->disabled) 286df1590d9SViresh Kumar return; 287df1590d9SViresh Kumar 288f37ecbceSThomas Gleixner irq_set_chained_handler(parent_irq, shirq_handler); 289f37ecbceSThomas Gleixner irq_set_handler_data(parent_irq, shirq); 290f37ecbceSThomas Gleixner 291c5d1d857SThomas Gleixner for (i = 0; i < shirq->nr_irqs; i++) { 292c5d1d857SThomas Gleixner irq_set_chip_and_handler(shirq->virq_base + i, 293df1590d9SViresh Kumar &shirq_chip, handle_simple_irq); 294c5d1d857SThomas Gleixner set_irq_flags(shirq->virq_base + i, IRQF_VALID); 295c5d1d857SThomas Gleixner irq_set_chip_data(shirq->virq_base + i, shirq); 296df1590d9SViresh Kumar } 297df1590d9SViresh Kumar } 298df1590d9SViresh Kumar 299df1590d9SViresh Kumar static int __init shirq_init(struct spear_shirq **shirq_blocks, int block_nr, 300df1590d9SViresh Kumar struct device_node *np) 301df1590d9SViresh Kumar { 302c5d1d857SThomas Gleixner int i, parent_irq, virq_base, hwirq = 0, nr_irqs = 0; 303a26c06f9SThomas Gleixner struct irq_domain *shirq_domain; 304df1590d9SViresh Kumar void __iomem *base; 305df1590d9SViresh Kumar 306df1590d9SViresh Kumar base = of_iomap(np, 0); 307df1590d9SViresh Kumar if (!base) { 308df1590d9SViresh Kumar pr_err("%s: failed to map shirq registers\n", __func__); 309df1590d9SViresh Kumar return -ENXIO; 310df1590d9SViresh Kumar } 311df1590d9SViresh Kumar 312df1590d9SViresh Kumar for (i = 0; i < block_nr; i++) 313c5d1d857SThomas Gleixner nr_irqs += shirq_blocks[i]->nr_irqs; 314df1590d9SViresh Kumar 315c5d1d857SThomas Gleixner virq_base = irq_alloc_descs(-1, 0, nr_irqs, 0); 316c5d1d857SThomas Gleixner if (IS_ERR_VALUE(virq_base)) { 317df1590d9SViresh Kumar pr_err("%s: irq desc alloc failed\n", __func__); 318df1590d9SViresh Kumar goto err_unmap; 319df1590d9SViresh Kumar } 320df1590d9SViresh Kumar 321c5d1d857SThomas Gleixner shirq_domain = irq_domain_add_legacy(np, nr_irqs, virq_base, 0, 322df1590d9SViresh Kumar &irq_domain_simple_ops, NULL); 323df1590d9SViresh Kumar if (WARN_ON(!shirq_domain)) { 324df1590d9SViresh Kumar pr_warn("%s: irq domain init failed\n", __func__); 325df1590d9SViresh Kumar goto err_free_desc; 326df1590d9SViresh Kumar } 327df1590d9SViresh Kumar 328df1590d9SViresh Kumar for (i = 0; i < block_nr; i++) { 329df1590d9SViresh Kumar shirq_blocks[i]->base = base; 330c5d1d857SThomas Gleixner shirq_blocks[i]->virq_base = irq_find_mapping(shirq_domain, 331df1590d9SViresh Kumar hwirq); 332df1590d9SViresh Kumar 333f37ecbceSThomas Gleixner parent_irq = irq_of_parse_and_map(np, i); 334f37ecbceSThomas Gleixner spear_shirq_register(shirq_blocks[i], parent_irq); 335c5d1d857SThomas Gleixner hwirq += shirq_blocks[i]->nr_irqs; 336df1590d9SViresh Kumar } 337df1590d9SViresh Kumar 338df1590d9SViresh Kumar return 0; 339df1590d9SViresh Kumar 340df1590d9SViresh Kumar err_free_desc: 341c5d1d857SThomas Gleixner irq_free_descs(virq_base, nr_irqs); 342df1590d9SViresh Kumar err_unmap: 343df1590d9SViresh Kumar iounmap(base); 344df1590d9SViresh Kumar return -ENXIO; 345df1590d9SViresh Kumar } 346df1590d9SViresh Kumar 347078bc005SThomas Gleixner static int __init spear300_shirq_of_init(struct device_node *np, 348df1590d9SViresh Kumar struct device_node *parent) 349df1590d9SViresh Kumar { 350df1590d9SViresh Kumar return shirq_init(spear300_shirq_blocks, 351df1590d9SViresh Kumar ARRAY_SIZE(spear300_shirq_blocks), np); 352df1590d9SViresh Kumar } 353e9c51558SRob Herring IRQCHIP_DECLARE(spear300_shirq, "st,spear300-shirq", spear300_shirq_of_init); 354df1590d9SViresh Kumar 355078bc005SThomas Gleixner static int __init spear310_shirq_of_init(struct device_node *np, 356df1590d9SViresh Kumar struct device_node *parent) 357df1590d9SViresh Kumar { 358df1590d9SViresh Kumar return shirq_init(spear310_shirq_blocks, 359df1590d9SViresh Kumar ARRAY_SIZE(spear310_shirq_blocks), np); 360df1590d9SViresh Kumar } 361e9c51558SRob Herring IRQCHIP_DECLARE(spear310_shirq, "st,spear310-shirq", spear310_shirq_of_init); 362df1590d9SViresh Kumar 363078bc005SThomas Gleixner static int __init spear320_shirq_of_init(struct device_node *np, 364df1590d9SViresh Kumar struct device_node *parent) 365df1590d9SViresh Kumar { 366df1590d9SViresh Kumar return shirq_init(spear320_shirq_blocks, 367df1590d9SViresh Kumar ARRAY_SIZE(spear320_shirq_blocks), np); 368df1590d9SViresh Kumar } 369e9c51558SRob Herring IRQCHIP_DECLARE(spear320_shirq, "st,spear320-shirq", spear320_shirq_of_init); 370