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