Blame view
drivers/irqchip/spear-shirq.c
7.36 KB
4c18e77f7 ARM: 6091/1: ST S... |
1 |
/* |
4c18e77f7 ARM: 6091/1: ST S... |
2 3 |
* SPEAr platform shared irq layer source file * |
df1590d9a ARM: SPEAr3xx: Sh... |
4 |
* Copyright (C) 2009-2012 ST Microelectronics |
10d8935f4 Viresh has moved |
5 |
* Viresh Kumar <viresh.linux@gmail.com> |
4c18e77f7 ARM: 6091/1: ST S... |
6 |
* |
df1590d9a ARM: SPEAr3xx: Sh... |
7 |
* Copyright (C) 2012 ST Microelectronics |
9cc236827 Shiraz has moved |
8 |
* Shiraz Hashim <shiraz.linux.kernel@gmail.com> |
df1590d9a ARM: SPEAr3xx: Sh... |
9 |
* |
4c18e77f7 ARM: 6091/1: ST S... |
10 11 12 13 |
* This file is licensed under the terms of the GNU General Public * License version 2. This program is licensed "as is" without any * warranty of any kind, whether express or implied. */ |
80515a5a2 ARM: SPEAr3xx: sh... |
14 |
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt |
4c18e77f7 ARM: 6091/1: ST S... |
15 16 |
#include <linux/err.h> |
80515a5a2 ARM: SPEAr3xx: sh... |
17 18 |
#include <linux/export.h> #include <linux/interrupt.h> |
4c18e77f7 ARM: 6091/1: ST S... |
19 20 |
#include <linux/io.h> #include <linux/irq.h> |
80515a5a2 ARM: SPEAr3xx: sh... |
21 22 23 24 |
#include <linux/irqdomain.h> #include <linux/of.h> #include <linux/of_address.h> #include <linux/of_irq.h> |
4c18e77f7 ARM: 6091/1: ST S... |
25 |
#include <linux/spinlock.h> |
4c18e77f7 ARM: 6091/1: ST S... |
26 |
|
e9c515589 ARM: spear: use c... |
27 |
#include "irqchip.h" |
078bc0056 irqchip: spear_sh... |
28 |
/* |
078bc0056 irqchip: spear_sh... |
29 30 |
* struct spear_shirq: shared irq structure * |
c5d1d8574 irqchip: spear_sh... |
31 |
* base: Base register address |
1b0a76c14 irqchip: spear_sh... |
32 33 |
* status_reg: Status register offset for chained interrupt handler * mask_reg: Mask register offset for irq chip |
4ecc832f4 irqchip: spear_sh... |
34 |
* mask: Mask to apply to the status register |
c5d1d8574 irqchip: spear_sh... |
35 36 37 |
* virq_base: Base virtual interrupt number * nr_irqs: Number of interrupts handled by this block * offset: Bit offset of the first interrupt |
f07e42f96 irqchip: spear_sh... |
38 39 |
* irq_chip: Interrupt controller chip used for this instance, * if NULL group is disabled, but accounted |
078bc0056 irqchip: spear_sh... |
40 41 |
*/ struct spear_shirq { |
c5d1d8574 irqchip: spear_sh... |
42 |
void __iomem *base; |
1b0a76c14 irqchip: spear_sh... |
43 44 |
u32 status_reg; u32 mask_reg; |
4ecc832f4 irqchip: spear_sh... |
45 |
u32 mask; |
c5d1d8574 irqchip: spear_sh... |
46 47 48 |
u32 virq_base; u32 nr_irqs; u32 offset; |
f07e42f96 irqchip: spear_sh... |
49 |
struct irq_chip *irq_chip; |
078bc0056 irqchip: spear_sh... |
50 |
}; |
80515a5a2 ARM: SPEAr3xx: sh... |
51 52 53 |
/* spear300 shared irq registers offsets and masks */ #define SPEAR300_INT_ENB_MASK_REG 0x54 #define SPEAR300_INT_STS_MASK_REG 0x58 |
f07e42f96 irqchip: spear_sh... |
54 55 56 57 58 59 |
static DEFINE_RAW_SPINLOCK(shirq_lock); static void shirq_irq_mask(struct irq_data *d) { struct spear_shirq *shirq = irq_data_get_irq_chip_data(d); u32 val, shift = d->irq - shirq->virq_base + shirq->offset; |
1b0a76c14 irqchip: spear_sh... |
60 |
u32 __iomem *reg = shirq->base + shirq->mask_reg; |
f07e42f96 irqchip: spear_sh... |
61 62 63 64 65 66 67 68 69 70 71 |
raw_spin_lock(&shirq_lock); val = readl(reg) & ~(0x1 << shift); writel(val, reg); raw_spin_unlock(&shirq_lock); } static void shirq_irq_unmask(struct irq_data *d) { struct spear_shirq *shirq = irq_data_get_irq_chip_data(d); u32 val, shift = d->irq - shirq->virq_base + shirq->offset; |
1b0a76c14 irqchip: spear_sh... |
72 |
u32 __iomem *reg = shirq->base + shirq->mask_reg; |
f07e42f96 irqchip: spear_sh... |
73 74 75 76 77 78 79 80 81 82 83 84 |
raw_spin_lock(&shirq_lock); val = readl(reg) | (0x1 << shift); writel(val, reg); raw_spin_unlock(&shirq_lock); } static struct irq_chip shirq_chip = { .name = "spear-shirq", .irq_mask = shirq_irq_mask, .irq_unmask = shirq_irq_unmask, }; |
80515a5a2 ARM: SPEAr3xx: sh... |
85 |
static struct spear_shirq spear300_shirq_ras1 = { |
c5d1d8574 irqchip: spear_sh... |
86 87 |
.offset = 0, .nr_irqs = 9, |
4ecc832f4 irqchip: spear_sh... |
88 |
.mask = ((0x1 << 9) - 1) << 0, |
f07e42f96 irqchip: spear_sh... |
89 |
.irq_chip = &shirq_chip, |
1b0a76c14 irqchip: spear_sh... |
90 91 |
.status_reg = SPEAR300_INT_STS_MASK_REG, .mask_reg = SPEAR300_INT_ENB_MASK_REG, |
80515a5a2 ARM: SPEAr3xx: sh... |
92 93 94 95 96 97 98 99 100 101 |
}; static struct spear_shirq *spear300_shirq_blocks[] = { &spear300_shirq_ras1, }; /* spear310 shared irq registers offsets and masks */ #define SPEAR310_INT_STS_MASK_REG 0x04 static struct spear_shirq spear310_shirq_ras1 = { |
c5d1d8574 irqchip: spear_sh... |
102 103 |
.offset = 0, .nr_irqs = 8, |
4ecc832f4 irqchip: spear_sh... |
104 |
.mask = ((0x1 << 8) - 1) << 0, |
f07e42f96 irqchip: spear_sh... |
105 |
.irq_chip = &dummy_irq_chip, |
1b0a76c14 irqchip: spear_sh... |
106 |
.status_reg = SPEAR310_INT_STS_MASK_REG, |
80515a5a2 ARM: SPEAr3xx: sh... |
107 108 109 |
}; static struct spear_shirq spear310_shirq_ras2 = { |
c5d1d8574 irqchip: spear_sh... |
110 111 |
.offset = 8, .nr_irqs = 5, |
4ecc832f4 irqchip: spear_sh... |
112 |
.mask = ((0x1 << 5) - 1) << 8, |
f07e42f96 irqchip: spear_sh... |
113 |
.irq_chip = &dummy_irq_chip, |
1b0a76c14 irqchip: spear_sh... |
114 |
.status_reg = SPEAR310_INT_STS_MASK_REG, |
80515a5a2 ARM: SPEAr3xx: sh... |
115 116 117 |
}; static struct spear_shirq spear310_shirq_ras3 = { |
c5d1d8574 irqchip: spear_sh... |
118 119 |
.offset = 13, .nr_irqs = 1, |
4ecc832f4 irqchip: spear_sh... |
120 |
.mask = ((0x1 << 1) - 1) << 13, |
f07e42f96 irqchip: spear_sh... |
121 |
.irq_chip = &dummy_irq_chip, |
1b0a76c14 irqchip: spear_sh... |
122 |
.status_reg = SPEAR310_INT_STS_MASK_REG, |
80515a5a2 ARM: SPEAr3xx: sh... |
123 124 125 |
}; static struct spear_shirq spear310_shirq_intrcomm_ras = { |
c5d1d8574 irqchip: spear_sh... |
126 127 |
.offset = 14, .nr_irqs = 3, |
4ecc832f4 irqchip: spear_sh... |
128 |
.mask = ((0x1 << 3) - 1) << 14, |
f07e42f96 irqchip: spear_sh... |
129 |
.irq_chip = &dummy_irq_chip, |
1b0a76c14 irqchip: spear_sh... |
130 |
.status_reg = SPEAR310_INT_STS_MASK_REG, |
80515a5a2 ARM: SPEAr3xx: sh... |
131 132 133 134 135 136 137 138 139 140 141 142 143 |
}; static struct spear_shirq *spear310_shirq_blocks[] = { &spear310_shirq_ras1, &spear310_shirq_ras2, &spear310_shirq_ras3, &spear310_shirq_intrcomm_ras, }; /* spear320 shared irq registers offsets and masks */ #define SPEAR320_INT_STS_MASK_REG 0x04 #define SPEAR320_INT_CLR_MASK_REG 0x04 #define SPEAR320_INT_ENB_MASK_REG 0x08 |
03319a1a2 irqchip: spear_sh... |
144 145 146 |
static struct spear_shirq spear320_shirq_ras3 = { .offset = 0, .nr_irqs = 7, |
4ecc832f4 irqchip: spear_sh... |
147 |
.mask = ((0x1 << 7) - 1) << 0, |
80515a5a2 ARM: SPEAr3xx: sh... |
148 |
}; |
03319a1a2 irqchip: spear_sh... |
149 150 151 |
static struct spear_shirq spear320_shirq_ras1 = { .offset = 7, .nr_irqs = 3, |
4ecc832f4 irqchip: spear_sh... |
152 |
.mask = ((0x1 << 3) - 1) << 7, |
f07e42f96 irqchip: spear_sh... |
153 |
.irq_chip = &dummy_irq_chip, |
1b0a76c14 irqchip: spear_sh... |
154 |
.status_reg = SPEAR320_INT_STS_MASK_REG, |
80515a5a2 ARM: SPEAr3xx: sh... |
155 |
}; |
03319a1a2 irqchip: spear_sh... |
156 157 158 |
static struct spear_shirq spear320_shirq_ras2 = { .offset = 10, .nr_irqs = 1, |
4ecc832f4 irqchip: spear_sh... |
159 |
.mask = ((0x1 << 1) - 1) << 10, |
f07e42f96 irqchip: spear_sh... |
160 |
.irq_chip = &dummy_irq_chip, |
1b0a76c14 irqchip: spear_sh... |
161 |
.status_reg = SPEAR320_INT_STS_MASK_REG, |
80515a5a2 ARM: SPEAr3xx: sh... |
162 163 164 |
}; static struct spear_shirq spear320_shirq_intrcomm_ras = { |
c5d1d8574 irqchip: spear_sh... |
165 166 |
.offset = 11, .nr_irqs = 11, |
4ecc832f4 irqchip: spear_sh... |
167 |
.mask = ((0x1 << 11) - 1) << 11, |
f07e42f96 irqchip: spear_sh... |
168 |
.irq_chip = &dummy_irq_chip, |
1b0a76c14 irqchip: spear_sh... |
169 |
.status_reg = SPEAR320_INT_STS_MASK_REG, |
80515a5a2 ARM: SPEAr3xx: sh... |
170 171 172 173 174 175 176 177 |
}; static struct spear_shirq *spear320_shirq_blocks[] = { &spear320_shirq_ras3, &spear320_shirq_ras1, &spear320_shirq_ras2, &spear320_shirq_intrcomm_ras, }; |
4c18e77f7 ARM: 6091/1: ST S... |
178 179 |
static void shirq_handler(unsigned irq, struct irq_desc *desc) { |
6845664a6 arm: Cleanup the ... |
180 |
struct spear_shirq *shirq = irq_get_handler_data(irq); |
25dc49e33 irqchip: spear_sh... |
181 |
u32 pend; |
4c18e77f7 ARM: 6091/1: ST S... |
182 |
|
1b0a76c14 irqchip: spear_sh... |
183 |
pend = readl(shirq->base + shirq->status_reg) & shirq->mask; |
25dc49e33 irqchip: spear_sh... |
184 |
pend >>= shirq->offset; |
80515a5a2 ARM: SPEAr3xx: sh... |
185 |
|
25dc49e33 irqchip: spear_sh... |
186 187 |
while (pend) { int irq = __ffs(pend); |
80515a5a2 ARM: SPEAr3xx: sh... |
188 |
|
25dc49e33 irqchip: spear_sh... |
189 190 |
pend &= ~(0x1 << irq); generic_handle_irq(shirq->virq_base + irq); |
4c18e77f7 ARM: 6091/1: ST S... |
191 |
} |
4c18e77f7 ARM: 6091/1: ST S... |
192 |
} |
f37ecbce8 irqchip: spear_sh... |
193 194 |
static void __init spear_shirq_register(struct spear_shirq *shirq, int parent_irq) |
4c18e77f7 ARM: 6091/1: ST S... |
195 196 |
{ int i; |
f07e42f96 irqchip: spear_sh... |
197 |
if (!shirq->irq_chip) |
80515a5a2 ARM: SPEAr3xx: sh... |
198 |
return; |
4c18e77f7 ARM: 6091/1: ST S... |
199 |
|
f37ecbce8 irqchip: spear_sh... |
200 201 |
irq_set_chained_handler(parent_irq, shirq_handler); irq_set_handler_data(parent_irq, shirq); |
c5d1d8574 irqchip: spear_sh... |
202 203 |
for (i = 0; i < shirq->nr_irqs; i++) { irq_set_chip_and_handler(shirq->virq_base + i, |
f07e42f96 irqchip: spear_sh... |
204 |
shirq->irq_chip, handle_simple_irq); |
c5d1d8574 irqchip: spear_sh... |
205 206 |
set_irq_flags(shirq->virq_base + i, IRQF_VALID); irq_set_chip_data(shirq->virq_base + i, shirq); |
4c18e77f7 ARM: 6091/1: ST S... |
207 |
} |
80515a5a2 ARM: SPEAr3xx: sh... |
208 209 210 211 212 |
} static int __init shirq_init(struct spear_shirq **shirq_blocks, int block_nr, struct device_node *np) { |
c5d1d8574 irqchip: spear_sh... |
213 |
int i, parent_irq, virq_base, hwirq = 0, nr_irqs = 0; |
a26c06f96 irqchip: spear_sh... |
214 |
struct irq_domain *shirq_domain; |
80515a5a2 ARM: SPEAr3xx: sh... |
215 216 217 218 219 220 221 222 223 224 |
void __iomem *base; base = of_iomap(np, 0); if (!base) { pr_err("%s: failed to map shirq registers ", __func__); return -ENXIO; } for (i = 0; i < block_nr; i++) |
c5d1d8574 irqchip: spear_sh... |
225 |
nr_irqs += shirq_blocks[i]->nr_irqs; |
80515a5a2 ARM: SPEAr3xx: sh... |
226 |
|
c5d1d8574 irqchip: spear_sh... |
227 228 |
virq_base = irq_alloc_descs(-1, 0, nr_irqs, 0); if (IS_ERR_VALUE(virq_base)) { |
80515a5a2 ARM: SPEAr3xx: sh... |
229 230 231 232 |
pr_err("%s: irq desc alloc failed ", __func__); goto err_unmap; } |
c5d1d8574 irqchip: spear_sh... |
233 |
shirq_domain = irq_domain_add_legacy(np, nr_irqs, virq_base, 0, |
80515a5a2 ARM: SPEAr3xx: sh... |
234 235 236 237 238 239 240 241 242 |
&irq_domain_simple_ops, NULL); if (WARN_ON(!shirq_domain)) { pr_warn("%s: irq domain init failed ", __func__); goto err_free_desc; } for (i = 0; i < block_nr; i++) { shirq_blocks[i]->base = base; |
c5d1d8574 irqchip: spear_sh... |
243 |
shirq_blocks[i]->virq_base = irq_find_mapping(shirq_domain, |
80515a5a2 ARM: SPEAr3xx: sh... |
244 |
hwirq); |
80515a5a2 ARM: SPEAr3xx: sh... |
245 |
|
f37ecbce8 irqchip: spear_sh... |
246 247 |
parent_irq = irq_of_parse_and_map(np, i); spear_shirq_register(shirq_blocks[i], parent_irq); |
c5d1d8574 irqchip: spear_sh... |
248 |
hwirq += shirq_blocks[i]->nr_irqs; |
80515a5a2 ARM: SPEAr3xx: sh... |
249 |
} |
4c18e77f7 ARM: 6091/1: ST S... |
250 |
return 0; |
80515a5a2 ARM: SPEAr3xx: sh... |
251 252 |
err_free_desc: |
c5d1d8574 irqchip: spear_sh... |
253 |
irq_free_descs(virq_base, nr_irqs); |
80515a5a2 ARM: SPEAr3xx: sh... |
254 255 256 257 |
err_unmap: iounmap(base); return -ENXIO; } |
078bc0056 irqchip: spear_sh... |
258 259 |
static int __init spear300_shirq_of_init(struct device_node *np, struct device_node *parent) |
80515a5a2 ARM: SPEAr3xx: sh... |
260 261 262 263 |
{ return shirq_init(spear300_shirq_blocks, ARRAY_SIZE(spear300_shirq_blocks), np); } |
e9c515589 ARM: spear: use c... |
264 |
IRQCHIP_DECLARE(spear300_shirq, "st,spear300-shirq", spear300_shirq_of_init); |
80515a5a2 ARM: SPEAr3xx: sh... |
265 |
|
078bc0056 irqchip: spear_sh... |
266 267 |
static int __init spear310_shirq_of_init(struct device_node *np, struct device_node *parent) |
80515a5a2 ARM: SPEAr3xx: sh... |
268 269 270 271 |
{ return shirq_init(spear310_shirq_blocks, ARRAY_SIZE(spear310_shirq_blocks), np); } |
e9c515589 ARM: spear: use c... |
272 |
IRQCHIP_DECLARE(spear310_shirq, "st,spear310-shirq", spear310_shirq_of_init); |
80515a5a2 ARM: SPEAr3xx: sh... |
273 |
|
078bc0056 irqchip: spear_sh... |
274 275 |
static int __init spear320_shirq_of_init(struct device_node *np, struct device_node *parent) |
80515a5a2 ARM: SPEAr3xx: sh... |
276 277 278 |
{ return shirq_init(spear320_shirq_blocks, ARRAY_SIZE(spear320_shirq_blocks), np); |
4c18e77f7 ARM: 6091/1: ST S... |
279 |
} |
e9c515589 ARM: spear: use c... |
280 |
IRQCHIP_DECLARE(spear320_shirq, "st,spear320-shirq", spear320_shirq_of_init); |