blob: fc57c35a20b442d365dca152846398cbbc7b6998 [file] [log] [blame]
viresh kumar4c18e772010-05-03 09:24:30 +01001/*
viresh kumar4c18e772010-05-03 09:24:30 +01002 * SPEAr platform shared irq layer source file
3 *
Viresh Kumardf1590d2012-11-12 22:56:03 +05304 * Copyright (C) 2009-2012 ST Microelectronics
Viresh Kumar10d89352012-06-20 12:53:02 -07005 * Viresh Kumar <viresh.linux@gmail.com>
viresh kumar4c18e772010-05-03 09:24:30 +01006 *
Viresh Kumardf1590d2012-11-12 22:56:03 +05307 * Copyright (C) 2012 ST Microelectronics
Viresh Kumar9cc23682014-04-18 15:07:16 -07008 * Shiraz Hashim <shiraz.linux.kernel@gmail.com>
Viresh Kumardf1590d2012-11-12 22:56:03 +05309 *
viresh kumar4c18e772010-05-03 09:24:30 +010010 * 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 */
Shiraz Hashim80515a5a2012-08-03 15:33:10 +053014#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
viresh kumar4c18e772010-05-03 09:24:30 +010015
16#include <linux/err.h>
Shiraz Hashim80515a5a2012-08-03 15:33:10 +053017#include <linux/export.h>
18#include <linux/interrupt.h>
viresh kumar4c18e772010-05-03 09:24:30 +010019#include <linux/io.h>
20#include <linux/irq.h>
Shiraz Hashim80515a5a2012-08-03 15:33:10 +053021#include <linux/irqdomain.h>
22#include <linux/of.h>
23#include <linux/of_address.h>
24#include <linux/of_irq.h>
viresh kumar4c18e772010-05-03 09:24:30 +010025#include <linux/spinlock.h>
viresh kumar4c18e772010-05-03 09:24:30 +010026
Rob Herringe9c51552013-01-02 09:37:56 -060027#include "irqchip.h"
28
Thomas Gleixner078bc002014-06-19 21:34:38 +000029/*
30 * struct shirq_regs: shared irq register configuration
31 *
32 * enb_reg: enable register offset
33 * reset_to_enb: val 1 indicates, we need to clear bit for enabling interrupt
34 * status_reg: status register offset
35 * status_reg_mask: status register valid mask
36 * clear_reg: clear register offset
37 * reset_to_clear: val 1 indicates, we need to clear bit for clearing interrupt
38 */
39struct shirq_regs {
40 u32 enb_reg;
41 u32 reset_to_enb;
42 u32 status_reg;
43 u32 clear_reg;
44 u32 reset_to_clear;
45};
46
47/*
48 * struct spear_shirq: shared irq structure
49 *
Thomas Gleixnerc5d1d852014-06-19 21:34:39 +000050 * base: Base register address
51 * regs: Register configuration for shared irq block
Thomas Gleixner4ecc8322014-06-19 21:34:41 +000052 * mask: Mask to apply to the status register
Thomas Gleixnerc5d1d852014-06-19 21:34:39 +000053 * virq_base: Base virtual interrupt number
54 * nr_irqs: Number of interrupts handled by this block
55 * offset: Bit offset of the first interrupt
56 * disabled: Group is disabled, but accounted
Thomas Gleixner078bc002014-06-19 21:34:38 +000057 */
58struct spear_shirq {
Thomas Gleixnerc5d1d852014-06-19 21:34:39 +000059 void __iomem *base;
60 struct shirq_regs regs;
Thomas Gleixner4ecc8322014-06-19 21:34:41 +000061 u32 mask;
Thomas Gleixnerc5d1d852014-06-19 21:34:39 +000062 u32 virq_base;
63 u32 nr_irqs;
64 u32 offset;
65 bool disabled;
Thomas Gleixner078bc002014-06-19 21:34:38 +000066};
67
viresh kumar4c18e772010-05-03 09:24:30 +010068static DEFINE_SPINLOCK(lock);
69
Shiraz Hashim80515a5a2012-08-03 15:33:10 +053070/* spear300 shared irq registers offsets and masks */
71#define SPEAR300_INT_ENB_MASK_REG 0x54
72#define SPEAR300_INT_STS_MASK_REG 0x58
73
74static struct spear_shirq spear300_shirq_ras1 = {
Thomas Gleixnerc5d1d852014-06-19 21:34:39 +000075 .offset = 0,
76 .nr_irqs = 9,
Thomas Gleixner4ecc8322014-06-19 21:34:41 +000077 .mask = ((0x1 << 9) - 1) << 0,
Shiraz Hashim80515a5a2012-08-03 15:33:10 +053078 .regs = {
79 .enb_reg = SPEAR300_INT_ENB_MASK_REG,
80 .status_reg = SPEAR300_INT_STS_MASK_REG,
81 .clear_reg = -1,
82 },
83};
84
85static struct spear_shirq *spear300_shirq_blocks[] = {
86 &spear300_shirq_ras1,
87};
88
89/* spear310 shared irq registers offsets and masks */
90#define SPEAR310_INT_STS_MASK_REG 0x04
91
92static struct spear_shirq spear310_shirq_ras1 = {
Thomas Gleixnerc5d1d852014-06-19 21:34:39 +000093 .offset = 0,
94 .nr_irqs = 8,
Thomas Gleixner4ecc8322014-06-19 21:34:41 +000095 .mask = ((0x1 << 8) - 1) << 0,
Shiraz Hashim80515a5a2012-08-03 15:33:10 +053096 .regs = {
97 .enb_reg = -1,
98 .status_reg = SPEAR310_INT_STS_MASK_REG,
99 .clear_reg = -1,
100 },
101};
102
103static struct spear_shirq spear310_shirq_ras2 = {
Thomas Gleixnerc5d1d852014-06-19 21:34:39 +0000104 .offset = 8,
105 .nr_irqs = 5,
Thomas Gleixner4ecc8322014-06-19 21:34:41 +0000106 .mask = ((0x1 << 5) - 1) << 8,
Shiraz Hashim80515a5a2012-08-03 15:33:10 +0530107 .regs = {
108 .enb_reg = -1,
109 .status_reg = SPEAR310_INT_STS_MASK_REG,
110 .clear_reg = -1,
111 },
112};
113
114static struct spear_shirq spear310_shirq_ras3 = {
Thomas Gleixnerc5d1d852014-06-19 21:34:39 +0000115 .offset = 13,
116 .nr_irqs = 1,
Thomas Gleixner4ecc8322014-06-19 21:34:41 +0000117 .mask = ((0x1 << 1) - 1) << 13,
Shiraz Hashim80515a5a2012-08-03 15:33:10 +0530118 .regs = {
119 .enb_reg = -1,
120 .status_reg = SPEAR310_INT_STS_MASK_REG,
121 .clear_reg = -1,
122 },
123};
124
125static struct spear_shirq spear310_shirq_intrcomm_ras = {
Thomas Gleixnerc5d1d852014-06-19 21:34:39 +0000126 .offset = 14,
127 .nr_irqs = 3,
Thomas Gleixner4ecc8322014-06-19 21:34:41 +0000128 .mask = ((0x1 << 3) - 1) << 14,
Shiraz Hashim80515a5a2012-08-03 15:33:10 +0530129 .regs = {
130 .enb_reg = -1,
131 .status_reg = SPEAR310_INT_STS_MASK_REG,
132 .clear_reg = -1,
133 },
134};
135
136static struct spear_shirq *spear310_shirq_blocks[] = {
137 &spear310_shirq_ras1,
138 &spear310_shirq_ras2,
139 &spear310_shirq_ras3,
140 &spear310_shirq_intrcomm_ras,
141};
142
143/* spear320 shared irq registers offsets and masks */
144#define SPEAR320_INT_STS_MASK_REG 0x04
145#define SPEAR320_INT_CLR_MASK_REG 0x04
146#define SPEAR320_INT_ENB_MASK_REG 0x08
147
Thomas Gleixner03319a12014-06-19 21:34:40 +0000148static struct spear_shirq spear320_shirq_ras3 = {
149 .offset = 0,
150 .nr_irqs = 7,
Thomas Gleixner4ecc8322014-06-19 21:34:41 +0000151 .mask = ((0x1 << 7) - 1) << 0,
Thomas Gleixner03319a12014-06-19 21:34:40 +0000152 .disabled = 1,
153 .regs = {
154 .enb_reg = SPEAR320_INT_ENB_MASK_REG,
155 .reset_to_enb = 1,
156 .status_reg = SPEAR320_INT_STS_MASK_REG,
157 .clear_reg = SPEAR320_INT_CLR_MASK_REG,
158 .reset_to_clear = 1,
159 },
160};
161
Shiraz Hashim80515a5a2012-08-03 15:33:10 +0530162static struct spear_shirq spear320_shirq_ras1 = {
Thomas Gleixnerc5d1d852014-06-19 21:34:39 +0000163 .offset = 7,
164 .nr_irqs = 3,
Thomas Gleixner4ecc8322014-06-19 21:34:41 +0000165 .mask = ((0x1 << 3) - 1) << 7,
Shiraz Hashim80515a5a2012-08-03 15:33:10 +0530166 .regs = {
167 .enb_reg = -1,
168 .status_reg = SPEAR320_INT_STS_MASK_REG,
169 .clear_reg = SPEAR320_INT_CLR_MASK_REG,
170 .reset_to_clear = 1,
171 },
172};
173
174static struct spear_shirq spear320_shirq_ras2 = {
Thomas Gleixnerc5d1d852014-06-19 21:34:39 +0000175 .offset = 10,
176 .nr_irqs = 1,
Thomas Gleixner4ecc8322014-06-19 21:34:41 +0000177 .mask = ((0x1 << 1) - 1) << 10,
Shiraz Hashim80515a5a2012-08-03 15:33:10 +0530178 .regs = {
179 .enb_reg = -1,
180 .status_reg = SPEAR320_INT_STS_MASK_REG,
181 .clear_reg = SPEAR320_INT_CLR_MASK_REG,
182 .reset_to_clear = 1,
183 },
184};
185
Shiraz Hashim80515a5a2012-08-03 15:33:10 +0530186static struct spear_shirq spear320_shirq_intrcomm_ras = {
Thomas Gleixnerc5d1d852014-06-19 21:34:39 +0000187 .offset = 11,
188 .nr_irqs = 11,
Thomas Gleixner4ecc8322014-06-19 21:34:41 +0000189 .mask = ((0x1 << 11) - 1) << 11,
Shiraz Hashim80515a5a2012-08-03 15:33:10 +0530190 .regs = {
191 .enb_reg = -1,
192 .status_reg = SPEAR320_INT_STS_MASK_REG,
193 .clear_reg = SPEAR320_INT_CLR_MASK_REG,
194 .reset_to_clear = 1,
195 },
196};
197
198static struct spear_shirq *spear320_shirq_blocks[] = {
199 &spear320_shirq_ras3,
200 &spear320_shirq_ras1,
201 &spear320_shirq_ras2,
202 &spear320_shirq_intrcomm_ras,
203};
204
205static void shirq_irq_mask_unmask(struct irq_data *d, bool mask)
viresh kumar4c18e772010-05-03 09:24:30 +0100206{
Lennert Buytenhek0e60e112010-11-29 11:22:33 +0100207 struct spear_shirq *shirq = irq_data_get_irq_chip_data(d);
Thomas Gleixnerc5d1d852014-06-19 21:34:39 +0000208 u32 val, offset = d->irq - shirq->virq_base;
viresh kumar4c18e772010-05-03 09:24:30 +0100209 unsigned long flags;
210
Shiraz Hashim80515a5a2012-08-03 15:33:10 +0530211 if (shirq->regs.enb_reg == -1)
viresh kumar4c18e772010-05-03 09:24:30 +0100212 return;
213
214 spin_lock_irqsave(&lock, flags);
Shiraz Hashim80515a5a2012-08-03 15:33:10 +0530215 val = readl(shirq->base + shirq->regs.enb_reg);
216
217 if (mask ^ shirq->regs.reset_to_enb)
Thomas Gleixnerc5d1d852014-06-19 21:34:39 +0000218 val &= ~(0x1 << shirq->offset << offset);
viresh kumar4c18e772010-05-03 09:24:30 +0100219 else
Thomas Gleixnerc5d1d852014-06-19 21:34:39 +0000220 val |= 0x1 << shirq->offset << offset;
Shiraz Hashim80515a5a2012-08-03 15:33:10 +0530221
222 writel(val, shirq->base + shirq->regs.enb_reg);
viresh kumar4c18e772010-05-03 09:24:30 +0100223 spin_unlock_irqrestore(&lock, flags);
Shiraz Hashim80515a5a2012-08-03 15:33:10 +0530224
225}
226
227static void shirq_irq_mask(struct irq_data *d)
228{
229 shirq_irq_mask_unmask(d, 1);
viresh kumar4c18e772010-05-03 09:24:30 +0100230}
231
Lennert Buytenhek0e60e112010-11-29 11:22:33 +0100232static void shirq_irq_unmask(struct irq_data *d)
viresh kumar4c18e772010-05-03 09:24:30 +0100233{
Shiraz Hashim80515a5a2012-08-03 15:33:10 +0530234 shirq_irq_mask_unmask(d, 0);
viresh kumar4c18e772010-05-03 09:24:30 +0100235}
236
237static struct irq_chip shirq_chip = {
Shiraz Hashim80515a5a2012-08-03 15:33:10 +0530238 .name = "spear-shirq",
Lennert Buytenhek0e60e112010-11-29 11:22:33 +0100239 .irq_ack = shirq_irq_mask,
240 .irq_mask = shirq_irq_mask,
241 .irq_unmask = shirq_irq_unmask,
viresh kumar4c18e772010-05-03 09:24:30 +0100242};
243
244static void shirq_handler(unsigned irq, struct irq_desc *desc)
245{
Thomas Gleixner6845664a2011-03-24 13:25:22 +0100246 struct spear_shirq *shirq = irq_get_handler_data(irq);
Thomas Gleixnere3c871a2014-06-19 21:34:40 +0000247 struct irq_data *idata = irq_desc_get_irq_data(desc);
248 struct irq_chip *chip = irq_data_get_irq_chip(idata);
249 u32 i, j, val, mask, tmp;
viresh kumar4c18e772010-05-03 09:24:30 +0100250
Thomas Gleixnere3c871a2014-06-19 21:34:40 +0000251 chip->irq_ack(idata);
Shiraz Hashim80515a5a2012-08-03 15:33:10 +0530252
Thomas Gleixner4ecc8322014-06-19 21:34:41 +0000253 mask = shirq->mask;
Shiraz Hashim80515a5a2012-08-03 15:33:10 +0530254 while ((val = readl(shirq->base + shirq->regs.status_reg) &
255 mask)) {
256
Thomas Gleixnerc5d1d852014-06-19 21:34:39 +0000257 val >>= shirq->offset;
258 for (i = 0, j = 1; i < shirq->nr_irqs; i++, j <<= 1) {
Shiraz Hashim80515a5a2012-08-03 15:33:10 +0530259
260 if (!(j & val))
viresh kumar4c18e772010-05-03 09:24:30 +0100261 continue;
262
Thomas Gleixnerc5d1d852014-06-19 21:34:39 +0000263 generic_handle_irq(shirq->virq_base + i);
viresh kumar4c18e772010-05-03 09:24:30 +0100264
265 /* clear interrupt */
Shiraz Hashim80515a5a2012-08-03 15:33:10 +0530266 if (shirq->regs.clear_reg == -1)
viresh kumar4c18e772010-05-03 09:24:30 +0100267 continue;
Shiraz Hashim80515a5a2012-08-03 15:33:10 +0530268
269 tmp = readl(shirq->base + shirq->regs.clear_reg);
viresh kumar4c18e772010-05-03 09:24:30 +0100270 if (shirq->regs.reset_to_clear)
Thomas Gleixnerc5d1d852014-06-19 21:34:39 +0000271 tmp &= ~(j << shirq->offset);
viresh kumar4c18e772010-05-03 09:24:30 +0100272 else
Thomas Gleixnerc5d1d852014-06-19 21:34:39 +0000273 tmp |= (j << shirq->offset);
Shiraz Hashim80515a5a2012-08-03 15:33:10 +0530274 writel(tmp, shirq->base + shirq->regs.clear_reg);
viresh kumar4c18e772010-05-03 09:24:30 +0100275 }
276 }
Thomas Gleixnere3c871a2014-06-19 21:34:40 +0000277 chip->irq_unmask(idata);
viresh kumar4c18e772010-05-03 09:24:30 +0100278}
279
Thomas Gleixnerf37ecbc2014-06-19 21:34:39 +0000280static void __init spear_shirq_register(struct spear_shirq *shirq,
281 int parent_irq)
viresh kumar4c18e772010-05-03 09:24:30 +0100282{
283 int i;
284
Thomas Gleixnerc5d1d852014-06-19 21:34:39 +0000285 if (shirq->disabled)
Shiraz Hashim80515a5a2012-08-03 15:33:10 +0530286 return;
viresh kumar4c18e772010-05-03 09:24:30 +0100287
Thomas Gleixnerf37ecbc2014-06-19 21:34:39 +0000288 irq_set_chained_handler(parent_irq, shirq_handler);
289 irq_set_handler_data(parent_irq, shirq);
290
Thomas Gleixnerc5d1d852014-06-19 21:34:39 +0000291 for (i = 0; i < shirq->nr_irqs; i++) {
292 irq_set_chip_and_handler(shirq->virq_base + i,
Thomas Gleixnerf38c02f2011-03-24 13:35:09 +0100293 &shirq_chip, handle_simple_irq);
Thomas Gleixnerc5d1d852014-06-19 21:34:39 +0000294 set_irq_flags(shirq->virq_base + i, IRQF_VALID);
295 irq_set_chip_data(shirq->virq_base + i, shirq);
viresh kumar4c18e772010-05-03 09:24:30 +0100296 }
Shiraz Hashim80515a5a2012-08-03 15:33:10 +0530297}
298
299static int __init shirq_init(struct spear_shirq **shirq_blocks, int block_nr,
300 struct device_node *np)
301{
Thomas Gleixnerc5d1d852014-06-19 21:34:39 +0000302 int i, parent_irq, virq_base, hwirq = 0, nr_irqs = 0;
Thomas Gleixnera26c06f2014-06-19 21:34:37 +0000303 struct irq_domain *shirq_domain;
Shiraz Hashim80515a5a2012-08-03 15:33:10 +0530304 void __iomem *base;
305
306 base = of_iomap(np, 0);
307 if (!base) {
308 pr_err("%s: failed to map shirq registers\n", __func__);
309 return -ENXIO;
310 }
311
312 for (i = 0; i < block_nr; i++)
Thomas Gleixnerc5d1d852014-06-19 21:34:39 +0000313 nr_irqs += shirq_blocks[i]->nr_irqs;
Shiraz Hashim80515a5a2012-08-03 15:33:10 +0530314
Thomas Gleixnerc5d1d852014-06-19 21:34:39 +0000315 virq_base = irq_alloc_descs(-1, 0, nr_irqs, 0);
316 if (IS_ERR_VALUE(virq_base)) {
Shiraz Hashim80515a5a2012-08-03 15:33:10 +0530317 pr_err("%s: irq desc alloc failed\n", __func__);
318 goto err_unmap;
319 }
320
Thomas Gleixnerc5d1d852014-06-19 21:34:39 +0000321 shirq_domain = irq_domain_add_legacy(np, nr_irqs, virq_base, 0,
Shiraz Hashim80515a5a2012-08-03 15:33:10 +0530322 &irq_domain_simple_ops, NULL);
323 if (WARN_ON(!shirq_domain)) {
324 pr_warn("%s: irq domain init failed\n", __func__);
325 goto err_free_desc;
326 }
327
328 for (i = 0; i < block_nr; i++) {
329 shirq_blocks[i]->base = base;
Thomas Gleixnerc5d1d852014-06-19 21:34:39 +0000330 shirq_blocks[i]->virq_base = irq_find_mapping(shirq_domain,
Shiraz Hashim80515a5a2012-08-03 15:33:10 +0530331 hwirq);
Shiraz Hashim80515a5a2012-08-03 15:33:10 +0530332
Thomas Gleixnerf37ecbc2014-06-19 21:34:39 +0000333 parent_irq = irq_of_parse_and_map(np, i);
334 spear_shirq_register(shirq_blocks[i], parent_irq);
Thomas Gleixnerc5d1d852014-06-19 21:34:39 +0000335 hwirq += shirq_blocks[i]->nr_irqs;
Shiraz Hashim80515a5a2012-08-03 15:33:10 +0530336 }
337
viresh kumar4c18e772010-05-03 09:24:30 +0100338 return 0;
Shiraz Hashim80515a5a2012-08-03 15:33:10 +0530339
340err_free_desc:
Thomas Gleixnerc5d1d852014-06-19 21:34:39 +0000341 irq_free_descs(virq_base, nr_irqs);
Shiraz Hashim80515a5a2012-08-03 15:33:10 +0530342err_unmap:
343 iounmap(base);
344 return -ENXIO;
345}
346
Thomas Gleixner078bc002014-06-19 21:34:38 +0000347static int __init spear300_shirq_of_init(struct device_node *np,
348 struct device_node *parent)
Shiraz Hashim80515a5a2012-08-03 15:33:10 +0530349{
350 return shirq_init(spear300_shirq_blocks,
351 ARRAY_SIZE(spear300_shirq_blocks), np);
352}
Rob Herringe9c51552013-01-02 09:37:56 -0600353IRQCHIP_DECLARE(spear300_shirq, "st,spear300-shirq", spear300_shirq_of_init);
Shiraz Hashim80515a5a2012-08-03 15:33:10 +0530354
Thomas Gleixner078bc002014-06-19 21:34:38 +0000355static int __init spear310_shirq_of_init(struct device_node *np,
356 struct device_node *parent)
Shiraz Hashim80515a5a2012-08-03 15:33:10 +0530357{
358 return shirq_init(spear310_shirq_blocks,
359 ARRAY_SIZE(spear310_shirq_blocks), np);
360}
Rob Herringe9c51552013-01-02 09:37:56 -0600361IRQCHIP_DECLARE(spear310_shirq, "st,spear310-shirq", spear310_shirq_of_init);
Shiraz Hashim80515a5a2012-08-03 15:33:10 +0530362
Thomas Gleixner078bc002014-06-19 21:34:38 +0000363static int __init spear320_shirq_of_init(struct device_node *np,
364 struct device_node *parent)
Shiraz Hashim80515a5a2012-08-03 15:33:10 +0530365{
366 return shirq_init(spear320_shirq_blocks,
367 ARRAY_SIZE(spear320_shirq_blocks), np);
viresh kumar4c18e772010-05-03 09:24:30 +0100368}
Rob Herringe9c51552013-01-02 09:37:56 -0600369IRQCHIP_DECLARE(spear320_shirq, "st,spear320-shirq", spear320_shirq_of_init);