irqchip: spear_shirq: Use proper irq chips for the different SoCs
[deliverable/linux.git] / drivers / irqchip / spear-shirq.c
CommitLineData
4c18e77f 1/*
4c18e77f 2 * SPEAr platform shared irq layer source file
3 *
df1590d9 4 * Copyright (C) 2009-2012 ST Microelectronics
10d8935f 5 * Viresh Kumar <viresh.linux@gmail.com>
4c18e77f 6 *
df1590d9 7 * Copyright (C) 2012 ST Microelectronics
9cc23682 8 * Shiraz Hashim <shiraz.linux.kernel@gmail.com>
df1590d9 9 *
4c18e77f 10 * 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 */
80515a5a 14#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
4c18e77f 15
16#include <linux/err.h>
80515a5a
SH
17#include <linux/export.h>
18#include <linux/interrupt.h>
4c18e77f 19#include <linux/io.h>
20#include <linux/irq.h>
80515a5a
SH
21#include <linux/irqdomain.h>
22#include <linux/of.h>
23#include <linux/of_address.h>
24#include <linux/of_irq.h>
4c18e77f 25#include <linux/spinlock.h>
4c18e77f 26
e9c51558
RH
27#include "irqchip.h"
28
078bc005
TG
29/*
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
078bc005
TG
36 */
37struct shirq_regs {
38 u32 enb_reg;
39 u32 reset_to_enb;
40 u32 status_reg;
078bc005
TG
41};
42
43/*
44 * struct spear_shirq: shared irq structure
45 *
c5d1d857
TG
46 * base: Base register address
47 * regs: Register configuration for shared irq block
4ecc832f 48 * mask: Mask to apply to the status register
c5d1d857
TG
49 * virq_base: Base virtual interrupt number
50 * nr_irqs: Number of interrupts handled by this block
51 * offset: Bit offset of the first interrupt
f07e42f9
TG
52 * irq_chip: Interrupt controller chip used for this instance,
53 * if NULL group is disabled, but accounted
078bc005
TG
54 */
55struct spear_shirq {
c5d1d857
TG
56 void __iomem *base;
57 struct shirq_regs regs;
4ecc832f 58 u32 mask;
c5d1d857
TG
59 u32 virq_base;
60 u32 nr_irqs;
61 u32 offset;
f07e42f9 62 struct irq_chip *irq_chip;
078bc005
TG
63};
64
80515a5a
SH
65/* spear300 shared irq registers offsets and masks */
66#define SPEAR300_INT_ENB_MASK_REG 0x54
67#define SPEAR300_INT_STS_MASK_REG 0x58
68
f07e42f9
TG
69static DEFINE_RAW_SPINLOCK(shirq_lock);
70
71static void shirq_irq_mask(struct irq_data *d)
72{
73 struct spear_shirq *shirq = irq_data_get_irq_chip_data(d);
74 u32 val, shift = d->irq - shirq->virq_base + shirq->offset;
75 u32 __iomem *reg = shirq->base + shirq->regs.enb_reg;
76
77 raw_spin_lock(&shirq_lock);
78 val = readl(reg) & ~(0x1 << shift);
79 writel(val, reg);
80 raw_spin_unlock(&shirq_lock);
81}
82
83static void shirq_irq_unmask(struct irq_data *d)
84{
85 struct spear_shirq *shirq = irq_data_get_irq_chip_data(d);
86 u32 val, shift = d->irq - shirq->virq_base + shirq->offset;
87 u32 __iomem *reg = shirq->base + shirq->regs.enb_reg;
88
89 raw_spin_lock(&shirq_lock);
90 val = readl(reg) | (0x1 << shift);
91 writel(val, reg);
92 raw_spin_unlock(&shirq_lock);
93}
94
95static struct irq_chip shirq_chip = {
96 .name = "spear-shirq",
97 .irq_mask = shirq_irq_mask,
98 .irq_unmask = shirq_irq_unmask,
99};
100
80515a5a 101static struct spear_shirq spear300_shirq_ras1 = {
c5d1d857
TG
102 .offset = 0,
103 .nr_irqs = 9,
4ecc832f 104 .mask = ((0x1 << 9) - 1) << 0,
f07e42f9 105 .irq_chip = &shirq_chip,
80515a5a
SH
106 .regs = {
107 .enb_reg = SPEAR300_INT_ENB_MASK_REG,
108 .status_reg = SPEAR300_INT_STS_MASK_REG,
80515a5a
SH
109 },
110};
111
112static struct spear_shirq *spear300_shirq_blocks[] = {
113 &spear300_shirq_ras1,
114};
115
116/* spear310 shared irq registers offsets and masks */
117#define SPEAR310_INT_STS_MASK_REG 0x04
118
119static struct spear_shirq spear310_shirq_ras1 = {
c5d1d857
TG
120 .offset = 0,
121 .nr_irqs = 8,
4ecc832f 122 .mask = ((0x1 << 8) - 1) << 0,
f07e42f9 123 .irq_chip = &dummy_irq_chip,
80515a5a 124 .regs = {
80515a5a 125 .status_reg = SPEAR310_INT_STS_MASK_REG,
80515a5a
SH
126 },
127};
128
129static struct spear_shirq spear310_shirq_ras2 = {
c5d1d857
TG
130 .offset = 8,
131 .nr_irqs = 5,
4ecc832f 132 .mask = ((0x1 << 5) - 1) << 8,
f07e42f9 133 .irq_chip = &dummy_irq_chip,
80515a5a
SH
134 .regs = {
135 .enb_reg = -1,
136 .status_reg = SPEAR310_INT_STS_MASK_REG,
80515a5a
SH
137 },
138};
139
140static struct spear_shirq spear310_shirq_ras3 = {
c5d1d857
TG
141 .offset = 13,
142 .nr_irqs = 1,
4ecc832f 143 .mask = ((0x1 << 1) - 1) << 13,
f07e42f9 144 .irq_chip = &dummy_irq_chip,
80515a5a 145 .regs = {
80515a5a 146 .status_reg = SPEAR310_INT_STS_MASK_REG,
80515a5a
SH
147 },
148};
149
150static struct spear_shirq spear310_shirq_intrcomm_ras = {
c5d1d857
TG
151 .offset = 14,
152 .nr_irqs = 3,
4ecc832f 153 .mask = ((0x1 << 3) - 1) << 14,
f07e42f9 154 .irq_chip = &dummy_irq_chip,
80515a5a 155 .regs = {
80515a5a 156 .status_reg = SPEAR310_INT_STS_MASK_REG,
80515a5a
SH
157 },
158};
159
160static struct spear_shirq *spear310_shirq_blocks[] = {
161 &spear310_shirq_ras1,
162 &spear310_shirq_ras2,
163 &spear310_shirq_ras3,
164 &spear310_shirq_intrcomm_ras,
165};
166
167/* spear320 shared irq registers offsets and masks */
168#define SPEAR320_INT_STS_MASK_REG 0x04
169#define SPEAR320_INT_CLR_MASK_REG 0x04
170#define SPEAR320_INT_ENB_MASK_REG 0x08
171
03319a1a
TG
172static struct spear_shirq spear320_shirq_ras3 = {
173 .offset = 0,
174 .nr_irqs = 7,
4ecc832f 175 .mask = ((0x1 << 7) - 1) << 0,
80515a5a
SH
176};
177
03319a1a
TG
178static struct spear_shirq spear320_shirq_ras1 = {
179 .offset = 7,
180 .nr_irqs = 3,
4ecc832f 181 .mask = ((0x1 << 3) - 1) << 7,
f07e42f9 182 .irq_chip = &dummy_irq_chip,
80515a5a 183 .regs = {
80515a5a 184 .status_reg = SPEAR320_INT_STS_MASK_REG,
80515a5a
SH
185 },
186};
187
03319a1a
TG
188static struct spear_shirq spear320_shirq_ras2 = {
189 .offset = 10,
190 .nr_irqs = 1,
4ecc832f 191 .mask = ((0x1 << 1) - 1) << 10,
f07e42f9 192 .irq_chip = &dummy_irq_chip,
80515a5a 193 .regs = {
80515a5a 194 .status_reg = SPEAR320_INT_STS_MASK_REG,
80515a5a
SH
195 },
196};
197
198static struct spear_shirq spear320_shirq_intrcomm_ras = {
c5d1d857
TG
199 .offset = 11,
200 .nr_irqs = 11,
4ecc832f 201 .mask = ((0x1 << 11) - 1) << 11,
f07e42f9 202 .irq_chip = &dummy_irq_chip,
80515a5a 203 .regs = {
80515a5a 204 .status_reg = SPEAR320_INT_STS_MASK_REG,
80515a5a
SH
205 },
206};
207
208static struct spear_shirq *spear320_shirq_blocks[] = {
209 &spear320_shirq_ras3,
210 &spear320_shirq_ras1,
211 &spear320_shirq_ras2,
212 &spear320_shirq_intrcomm_ras,
213};
214
4c18e77f 215static void shirq_handler(unsigned irq, struct irq_desc *desc)
216{
6845664a 217 struct spear_shirq *shirq = irq_get_handler_data(irq);
25dc49e3 218 u32 pend;
4c18e77f 219
25dc49e3
TG
220 pend = readl(shirq->base + shirq->regs.status_reg) & shirq->mask;
221 pend >>= shirq->offset;
80515a5a 222
25dc49e3
TG
223 while (pend) {
224 int irq = __ffs(pend);
80515a5a 225
25dc49e3
TG
226 pend &= ~(0x1 << irq);
227 generic_handle_irq(shirq->virq_base + irq);
4c18e77f 228 }
4c18e77f 229}
230
f37ecbce
TG
231static void __init spear_shirq_register(struct spear_shirq *shirq,
232 int parent_irq)
4c18e77f 233{
234 int i;
235
f07e42f9 236 if (!shirq->irq_chip)
80515a5a 237 return;
4c18e77f 238
f37ecbce
TG
239 irq_set_chained_handler(parent_irq, shirq_handler);
240 irq_set_handler_data(parent_irq, shirq);
241
c5d1d857
TG
242 for (i = 0; i < shirq->nr_irqs; i++) {
243 irq_set_chip_and_handler(shirq->virq_base + i,
f07e42f9 244 shirq->irq_chip, handle_simple_irq);
c5d1d857
TG
245 set_irq_flags(shirq->virq_base + i, IRQF_VALID);
246 irq_set_chip_data(shirq->virq_base + i, shirq);
4c18e77f 247 }
80515a5a
SH
248}
249
250static int __init shirq_init(struct spear_shirq **shirq_blocks, int block_nr,
251 struct device_node *np)
252{
c5d1d857 253 int i, parent_irq, virq_base, hwirq = 0, nr_irqs = 0;
a26c06f9 254 struct irq_domain *shirq_domain;
80515a5a
SH
255 void __iomem *base;
256
257 base = of_iomap(np, 0);
258 if (!base) {
259 pr_err("%s: failed to map shirq registers\n", __func__);
260 return -ENXIO;
261 }
262
263 for (i = 0; i < block_nr; i++)
c5d1d857 264 nr_irqs += shirq_blocks[i]->nr_irqs;
80515a5a 265
c5d1d857
TG
266 virq_base = irq_alloc_descs(-1, 0, nr_irqs, 0);
267 if (IS_ERR_VALUE(virq_base)) {
80515a5a
SH
268 pr_err("%s: irq desc alloc failed\n", __func__);
269 goto err_unmap;
270 }
271
c5d1d857 272 shirq_domain = irq_domain_add_legacy(np, nr_irqs, virq_base, 0,
80515a5a
SH
273 &irq_domain_simple_ops, NULL);
274 if (WARN_ON(!shirq_domain)) {
275 pr_warn("%s: irq domain init failed\n", __func__);
276 goto err_free_desc;
277 }
278
279 for (i = 0; i < block_nr; i++) {
280 shirq_blocks[i]->base = base;
c5d1d857 281 shirq_blocks[i]->virq_base = irq_find_mapping(shirq_domain,
80515a5a 282 hwirq);
80515a5a 283
f37ecbce
TG
284 parent_irq = irq_of_parse_and_map(np, i);
285 spear_shirq_register(shirq_blocks[i], parent_irq);
c5d1d857 286 hwirq += shirq_blocks[i]->nr_irqs;
80515a5a
SH
287 }
288
4c18e77f 289 return 0;
80515a5a
SH
290
291err_free_desc:
c5d1d857 292 irq_free_descs(virq_base, nr_irqs);
80515a5a
SH
293err_unmap:
294 iounmap(base);
295 return -ENXIO;
296}
297
078bc005
TG
298static int __init spear300_shirq_of_init(struct device_node *np,
299 struct device_node *parent)
80515a5a
SH
300{
301 return shirq_init(spear300_shirq_blocks,
302 ARRAY_SIZE(spear300_shirq_blocks), np);
303}
e9c51558 304IRQCHIP_DECLARE(spear300_shirq, "st,spear300-shirq", spear300_shirq_of_init);
80515a5a 305
078bc005
TG
306static int __init spear310_shirq_of_init(struct device_node *np,
307 struct device_node *parent)
80515a5a
SH
308{
309 return shirq_init(spear310_shirq_blocks,
310 ARRAY_SIZE(spear310_shirq_blocks), np);
311}
e9c51558 312IRQCHIP_DECLARE(spear310_shirq, "st,spear310-shirq", spear310_shirq_of_init);
80515a5a 313
078bc005
TG
314static int __init spear320_shirq_of_init(struct device_node *np,
315 struct device_node *parent)
80515a5a
SH
316{
317 return shirq_init(spear320_shirq_blocks,
318 ARRAY_SIZE(spear320_shirq_blocks), np);
4c18e77f 319}
e9c51558 320IRQCHIP_DECLARE(spear320_shirq, "st,spear320-shirq", spear320_shirq_of_init);
This page took 0.348814 seconds and 5 git commands to generate.