diff options
author | Felipe Balbi <balbi@ti.com> | 2014-09-15 17:15:02 -0400 |
---|---|---|
committer | Tony Lindgren <tony@atomide.com> | 2014-09-16 17:44:59 -0400 |
commit | 8598066cddd186809c4edf5aae5f018c00079e8c (patch) | |
tree | fa1eee6f1f4a4e2c7feb3905c655f70ff7461fc5 /drivers/irqchip | |
parent | eaacabc0d9b637c82788c66955b4ba0efebd5500 (diff) |
arm: omap: irq: move irq.c to drivers/irqchip/
Just move the code over as it has no dependencies
on arch/arm/ anymore.
Signed-off-by: Felipe Balbi <balbi@ti.com>
Signed-off-by: Tony Lindgren <tony@atomide.com>
Diffstat (limited to 'drivers/irqchip')
-rw-r--r-- | drivers/irqchip/Kconfig | 5 | ||||
-rw-r--r-- | drivers/irqchip/Makefile | 1 | ||||
-rw-r--r-- | drivers/irqchip/irq-omap-intc.c | 394 |
3 files changed, 400 insertions, 0 deletions
diff --git a/drivers/irqchip/Kconfig b/drivers/irqchip/Kconfig index b8632bf9a7f3..9d539decf864 100644 --- a/drivers/irqchip/Kconfig +++ b/drivers/irqchip/Kconfig | |||
@@ -75,6 +75,11 @@ config OR1K_PIC | |||
75 | bool | 75 | bool |
76 | select IRQ_DOMAIN | 76 | select IRQ_DOMAIN |
77 | 77 | ||
78 | config OMAP_IRQCHIP | ||
79 | bool | ||
80 | select GENERIC_IRQ_CHIP | ||
81 | select IRQ_DOMAIN | ||
82 | |||
78 | config ORION_IRQCHIP | 83 | config ORION_IRQCHIP |
79 | bool | 84 | bool |
80 | select IRQ_DOMAIN | 85 | select IRQ_DOMAIN |
diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile index 73052ba9ca62..d0a2613c73bc 100644 --- a/drivers/irqchip/Makefile +++ b/drivers/irqchip/Makefile | |||
@@ -13,6 +13,7 @@ obj-$(CONFIG_ARCH_MOXART) += irq-moxart.o | |||
13 | obj-$(CONFIG_CLPS711X_IRQCHIP) += irq-clps711x.o | 13 | obj-$(CONFIG_CLPS711X_IRQCHIP) += irq-clps711x.o |
14 | obj-$(CONFIG_OR1K_PIC) += irq-or1k-pic.o | 14 | obj-$(CONFIG_OR1K_PIC) += irq-or1k-pic.o |
15 | obj-$(CONFIG_ORION_IRQCHIP) += irq-orion.o | 15 | obj-$(CONFIG_ORION_IRQCHIP) += irq-orion.o |
16 | obj-$(CONFIG_OMAP_IRQCHIP) += irq-omap-intc.o | ||
16 | obj-$(CONFIG_ARCH_SUNXI) += irq-sun4i.o | 17 | obj-$(CONFIG_ARCH_SUNXI) += irq-sun4i.o |
17 | obj-$(CONFIG_ARCH_SUNXI) += irq-sunxi-nmi.o | 18 | obj-$(CONFIG_ARCH_SUNXI) += irq-sunxi-nmi.o |
18 | obj-$(CONFIG_ARCH_SPEAR3XX) += spear-shirq.o | 19 | obj-$(CONFIG_ARCH_SPEAR3XX) += spear-shirq.o |
diff --git a/drivers/irqchip/irq-omap-intc.c b/drivers/irqchip/irq-omap-intc.c new file mode 100644 index 000000000000..1478f1a3c400 --- /dev/null +++ b/drivers/irqchip/irq-omap-intc.c | |||
@@ -0,0 +1,394 @@ | |||
1 | /* | ||
2 | * linux/arch/arm/mach-omap2/irq.c | ||
3 | * | ||
4 | * Interrupt handler for OMAP2 boards. | ||
5 | * | ||
6 | * Copyright (C) 2005 Nokia Corporation | ||
7 | * Author: Paul Mundt <paul.mundt@nokia.com> | ||
8 | * | ||
9 | * This file is subject to the terms and conditions of the GNU General Public | ||
10 | * License. See the file "COPYING" in the main directory of this archive | ||
11 | * for more details. | ||
12 | */ | ||
13 | #include <linux/kernel.h> | ||
14 | #include <linux/module.h> | ||
15 | #include <linux/init.h> | ||
16 | #include <linux/interrupt.h> | ||
17 | #include <linux/io.h> | ||
18 | |||
19 | #include <asm/exception.h> | ||
20 | #include <linux/irqdomain.h> | ||
21 | #include <linux/of.h> | ||
22 | #include <linux/of_address.h> | ||
23 | #include <linux/of_irq.h> | ||
24 | |||
25 | #include "irqchip.h" | ||
26 | |||
27 | /* Define these here for now until we drop all board-files */ | ||
28 | #define OMAP24XX_IC_BASE 0x480fe000 | ||
29 | #define OMAP34XX_IC_BASE 0x48200000 | ||
30 | |||
31 | /* selected INTC register offsets */ | ||
32 | |||
33 | #define INTC_REVISION 0x0000 | ||
34 | #define INTC_SYSCONFIG 0x0010 | ||
35 | #define INTC_SYSSTATUS 0x0014 | ||
36 | #define INTC_SIR 0x0040 | ||
37 | #define INTC_CONTROL 0x0048 | ||
38 | #define INTC_PROTECTION 0x004C | ||
39 | #define INTC_IDLE 0x0050 | ||
40 | #define INTC_THRESHOLD 0x0068 | ||
41 | #define INTC_MIR0 0x0084 | ||
42 | #define INTC_MIR_CLEAR0 0x0088 | ||
43 | #define INTC_MIR_SET0 0x008c | ||
44 | #define INTC_PENDING_IRQ0 0x0098 | ||
45 | #define INTC_PENDING_IRQ1 0x00b8 | ||
46 | #define INTC_PENDING_IRQ2 0x00d8 | ||
47 | #define INTC_PENDING_IRQ3 0x00f8 | ||
48 | #define INTC_ILR0 0x0100 | ||
49 | |||
50 | #define ACTIVEIRQ_MASK 0x7f /* omap2/3 active interrupt bits */ | ||
51 | #define INTCPS_NR_ILR_REGS 128 | ||
52 | #define INTCPS_NR_MIR_REGS 3 | ||
53 | |||
54 | /* | ||
55 | * OMAP2 has a number of different interrupt controllers, each interrupt | ||
56 | * controller is identified as its own "bank". Register definitions are | ||
57 | * fairly consistent for each bank, but not all registers are implemented | ||
58 | * for each bank.. when in doubt, consult the TRM. | ||
59 | */ | ||
60 | |||
61 | /* Structure to save interrupt controller context */ | ||
62 | struct omap_intc_regs { | ||
63 | u32 sysconfig; | ||
64 | u32 protection; | ||
65 | u32 idle; | ||
66 | u32 threshold; | ||
67 | u32 ilr[INTCPS_NR_ILR_REGS]; | ||
68 | u32 mir[INTCPS_NR_MIR_REGS]; | ||
69 | }; | ||
70 | static struct omap_intc_regs intc_context; | ||
71 | |||
72 | static struct irq_domain *domain; | ||
73 | static void __iomem *omap_irq_base; | ||
74 | static int omap_nr_pending = 3; | ||
75 | static int omap_nr_irqs = 96; | ||
76 | |||
77 | /* INTC bank register get/set */ | ||
78 | static void intc_writel(u32 reg, u32 val) | ||
79 | { | ||
80 | writel_relaxed(val, omap_irq_base + reg); | ||
81 | } | ||
82 | |||
83 | static u32 intc_readl(u32 reg) | ||
84 | { | ||
85 | return readl_relaxed(omap_irq_base + reg); | ||
86 | } | ||
87 | |||
88 | void omap_intc_save_context(void) | ||
89 | { | ||
90 | int i; | ||
91 | |||
92 | intc_context.sysconfig = | ||
93 | intc_readl(INTC_SYSCONFIG); | ||
94 | intc_context.protection = | ||
95 | intc_readl(INTC_PROTECTION); | ||
96 | intc_context.idle = | ||
97 | intc_readl(INTC_IDLE); | ||
98 | intc_context.threshold = | ||
99 | intc_readl(INTC_THRESHOLD); | ||
100 | |||
101 | for (i = 0; i < omap_nr_irqs; i++) | ||
102 | intc_context.ilr[i] = | ||
103 | intc_readl((INTC_ILR0 + 0x4 * i)); | ||
104 | for (i = 0; i < INTCPS_NR_MIR_REGS; i++) | ||
105 | intc_context.mir[i] = | ||
106 | intc_readl(INTC_MIR0 + (0x20 * i)); | ||
107 | } | ||
108 | |||
109 | void omap_intc_restore_context(void) | ||
110 | { | ||
111 | int i; | ||
112 | |||
113 | intc_writel(INTC_SYSCONFIG, intc_context.sysconfig); | ||
114 | intc_writel(INTC_PROTECTION, intc_context.protection); | ||
115 | intc_writel(INTC_IDLE, intc_context.idle); | ||
116 | intc_writel(INTC_THRESHOLD, intc_context.threshold); | ||
117 | |||
118 | for (i = 0; i < omap_nr_irqs; i++) | ||
119 | intc_writel(INTC_ILR0 + 0x4 * i, | ||
120 | intc_context.ilr[i]); | ||
121 | |||
122 | for (i = 0; i < INTCPS_NR_MIR_REGS; i++) | ||
123 | intc_writel(INTC_MIR0 + 0x20 * i, | ||
124 | intc_context.mir[i]); | ||
125 | /* MIRs are saved and restore with other PRCM registers */ | ||
126 | } | ||
127 | |||
128 | void omap3_intc_prepare_idle(void) | ||
129 | { | ||
130 | /* | ||
131 | * Disable autoidle as it can stall interrupt controller, | ||
132 | * cf. errata ID i540 for 3430 (all revisions up to 3.1.x) | ||
133 | */ | ||
134 | intc_writel(INTC_SYSCONFIG, 0); | ||
135 | } | ||
136 | |||
137 | void omap3_intc_resume_idle(void) | ||
138 | { | ||
139 | /* Re-enable autoidle */ | ||
140 | intc_writel(INTC_SYSCONFIG, 1); | ||
141 | } | ||
142 | |||
143 | /* XXX: FIQ and additional INTC support (only MPU at the moment) */ | ||
144 | static void omap_ack_irq(struct irq_data *d) | ||
145 | { | ||
146 | intc_writel(INTC_CONTROL, 0x1); | ||
147 | } | ||
148 | |||
149 | static void omap_mask_ack_irq(struct irq_data *d) | ||
150 | { | ||
151 | irq_gc_mask_disable_reg(d); | ||
152 | omap_ack_irq(d); | ||
153 | } | ||
154 | |||
155 | static void __init omap_irq_soft_reset(void) | ||
156 | { | ||
157 | unsigned long tmp; | ||
158 | |||
159 | tmp = intc_readl(INTC_REVISION) & 0xff; | ||
160 | |||
161 | pr_info("IRQ: Found an INTC at 0x%p (revision %ld.%ld) with %d interrupts\n", | ||
162 | omap_irq_base, tmp >> 4, tmp & 0xf, omap_nr_irqs); | ||
163 | |||
164 | tmp = intc_readl(INTC_SYSCONFIG); | ||
165 | tmp |= 1 << 1; /* soft reset */ | ||
166 | intc_writel(INTC_SYSCONFIG, tmp); | ||
167 | |||
168 | while (!(intc_readl(INTC_SYSSTATUS) & 0x1)) | ||
169 | /* Wait for reset to complete */; | ||
170 | |||
171 | /* Enable autoidle */ | ||
172 | intc_writel(INTC_SYSCONFIG, 1 << 0); | ||
173 | } | ||
174 | |||
175 | int omap_irq_pending(void) | ||
176 | { | ||
177 | int irq; | ||
178 | |||
179 | for (irq = 0; irq < omap_nr_irqs; irq += 32) | ||
180 | if (intc_readl(INTC_PENDING_IRQ0 + | ||
181 | ((irq >> 5) << 5))) | ||
182 | return 1; | ||
183 | return 0; | ||
184 | } | ||
185 | |||
186 | void omap3_intc_suspend(void) | ||
187 | { | ||
188 | /* A pending interrupt would prevent OMAP from entering suspend */ | ||
189 | omap_ack_irq(NULL); | ||
190 | } | ||
191 | |||
192 | static int __init omap_alloc_gc_of(struct irq_domain *d, void __iomem *base) | ||
193 | { | ||
194 | int ret; | ||
195 | int i; | ||
196 | |||
197 | ret = irq_alloc_domain_generic_chips(d, 32, 1, "INTC", | ||
198 | handle_level_irq, IRQ_NOREQUEST | IRQ_NOPROBE, | ||
199 | IRQ_LEVEL, 0); | ||
200 | if (ret) { | ||
201 | pr_warn("Failed to allocate irq chips\n"); | ||
202 | return ret; | ||
203 | } | ||
204 | |||
205 | for (i = 0; i < omap_nr_pending; i++) { | ||
206 | struct irq_chip_generic *gc; | ||
207 | struct irq_chip_type *ct; | ||
208 | |||
209 | gc = irq_get_domain_generic_chip(d, 32 * i); | ||
210 | gc->reg_base = base; | ||
211 | ct = gc->chip_types; | ||
212 | |||
213 | ct->type = IRQ_TYPE_LEVEL_MASK; | ||
214 | ct->handler = handle_level_irq; | ||
215 | |||
216 | ct->chip.irq_ack = omap_mask_ack_irq; | ||
217 | ct->chip.irq_mask = irq_gc_mask_disable_reg; | ||
218 | ct->chip.irq_unmask = irq_gc_unmask_enable_reg; | ||
219 | |||
220 | ct->chip.flags |= IRQCHIP_SKIP_SET_WAKE; | ||
221 | |||
222 | ct->regs.enable = INTC_MIR_CLEAR0 + 32 * i; | ||
223 | ct->regs.disable = INTC_MIR_SET0 + 32 * i; | ||
224 | } | ||
225 | |||
226 | return 0; | ||
227 | } | ||
228 | |||
229 | static void __init omap_alloc_gc_legacy(void __iomem *base, | ||
230 | unsigned int irq_start, unsigned int num) | ||
231 | { | ||
232 | struct irq_chip_generic *gc; | ||
233 | struct irq_chip_type *ct; | ||
234 | |||
235 | gc = irq_alloc_generic_chip("INTC", 1, irq_start, base, | ||
236 | handle_level_irq); | ||
237 | ct = gc->chip_types; | ||
238 | ct->chip.irq_ack = omap_mask_ack_irq; | ||
239 | ct->chip.irq_mask = irq_gc_mask_disable_reg; | ||
240 | ct->chip.irq_unmask = irq_gc_unmask_enable_reg; | ||
241 | ct->chip.flags |= IRQCHIP_SKIP_SET_WAKE; | ||
242 | |||
243 | ct->regs.enable = INTC_MIR_CLEAR0; | ||
244 | ct->regs.disable = INTC_MIR_SET0; | ||
245 | irq_setup_generic_chip(gc, IRQ_MSK(num), IRQ_GC_INIT_MASK_CACHE, | ||
246 | IRQ_NOREQUEST | IRQ_NOPROBE, 0); | ||
247 | } | ||
248 | |||
249 | static int __init omap_init_irq_of(struct device_node *node) | ||
250 | { | ||
251 | int ret; | ||
252 | |||
253 | omap_irq_base = of_iomap(node, 0); | ||
254 | if (WARN_ON(!omap_irq_base)) | ||
255 | return -ENOMEM; | ||
256 | |||
257 | domain = irq_domain_add_linear(node, omap_nr_irqs, | ||
258 | &irq_generic_chip_ops, NULL); | ||
259 | |||
260 | omap_irq_soft_reset(); | ||
261 | |||
262 | ret = omap_alloc_gc_of(domain, omap_irq_base); | ||
263 | if (ret < 0) | ||
264 | irq_domain_remove(domain); | ||
265 | |||
266 | return ret; | ||
267 | } | ||
268 | |||
269 | static int __init omap_init_irq_legacy(u32 base) | ||
270 | { | ||
271 | int j, irq_base; | ||
272 | |||
273 | omap_irq_base = ioremap(base, SZ_4K); | ||
274 | if (WARN_ON(!omap_irq_base)) | ||
275 | return -ENOMEM; | ||
276 | |||
277 | irq_base = irq_alloc_descs(-1, 0, omap_nr_irqs, 0); | ||
278 | if (irq_base < 0) { | ||
279 | pr_warn("Couldn't allocate IRQ numbers\n"); | ||
280 | irq_base = 0; | ||
281 | } | ||
282 | |||
283 | domain = irq_domain_add_legacy(NULL, omap_nr_irqs, irq_base, 0, | ||
284 | &irq_domain_simple_ops, NULL); | ||
285 | |||
286 | omap_irq_soft_reset(); | ||
287 | |||
288 | for (j = 0; j < omap_nr_irqs; j += 32) | ||
289 | omap_alloc_gc_legacy(omap_irq_base + j, j + irq_base, 32); | ||
290 | |||
291 | return 0; | ||
292 | } | ||
293 | |||
294 | static int __init omap_init_irq(u32 base, struct device_node *node) | ||
295 | { | ||
296 | if (node) | ||
297 | return omap_init_irq_of(node); | ||
298 | else | ||
299 | return omap_init_irq_legacy(base); | ||
300 | } | ||
301 | |||
302 | static asmlinkage void __exception_irq_entry | ||
303 | omap_intc_handle_irq(struct pt_regs *regs) | ||
304 | { | ||
305 | u32 irqnr = 0; | ||
306 | int handled_irq = 0; | ||
307 | int i; | ||
308 | |||
309 | do { | ||
310 | for (i = 0; i < omap_nr_pending; i++) { | ||
311 | irqnr = intc_readl(INTC_PENDING_IRQ0 + (0x20 * i)); | ||
312 | if (irqnr) | ||
313 | goto out; | ||
314 | } | ||
315 | |||
316 | out: | ||
317 | if (!irqnr) | ||
318 | break; | ||
319 | |||
320 | irqnr = intc_readl(INTC_SIR); | ||
321 | irqnr &= ACTIVEIRQ_MASK; | ||
322 | |||
323 | if (irqnr) { | ||
324 | irqnr = irq_find_mapping(domain, irqnr); | ||
325 | handle_IRQ(irqnr, regs); | ||
326 | handled_irq = 1; | ||
327 | } | ||
328 | } while (irqnr); | ||
329 | |||
330 | /* If an irq is masked or deasserted while active, we will | ||
331 | * keep ending up here with no irq handled. So remove it from | ||
332 | * the INTC with an ack.*/ | ||
333 | if (!handled_irq) | ||
334 | omap_ack_irq(NULL); | ||
335 | } | ||
336 | |||
337 | void __init omap2_init_irq(void) | ||
338 | { | ||
339 | omap_nr_irqs = 96; | ||
340 | omap_nr_pending = 3; | ||
341 | omap_init_irq(OMAP24XX_IC_BASE, NULL); | ||
342 | set_handle_irq(omap_intc_handle_irq); | ||
343 | } | ||
344 | |||
345 | void __init omap3_init_irq(void) | ||
346 | { | ||
347 | omap_nr_irqs = 96; | ||
348 | omap_nr_pending = 3; | ||
349 | omap_init_irq(OMAP34XX_IC_BASE, NULL); | ||
350 | set_handle_irq(omap_intc_handle_irq); | ||
351 | } | ||
352 | |||
353 | void __init ti81xx_init_irq(void) | ||
354 | { | ||
355 | omap_nr_irqs = 96; | ||
356 | omap_nr_pending = 4; | ||
357 | omap_init_irq(OMAP34XX_IC_BASE, NULL); | ||
358 | set_handle_irq(omap_intc_handle_irq); | ||
359 | } | ||
360 | |||
361 | static int __init intc_of_init(struct device_node *node, | ||
362 | struct device_node *parent) | ||
363 | { | ||
364 | struct resource res; | ||
365 | int ret; | ||
366 | |||
367 | omap_nr_pending = 3; | ||
368 | omap_nr_irqs = 96; | ||
369 | |||
370 | if (WARN_ON(!node)) | ||
371 | return -ENODEV; | ||
372 | |||
373 | if (of_address_to_resource(node, 0, &res)) { | ||
374 | WARN(1, "unable to get intc registers\n"); | ||
375 | return -EINVAL; | ||
376 | } | ||
377 | |||
378 | if (of_device_is_compatible(node, "ti,am33xx-intc")) { | ||
379 | omap_nr_irqs = 128; | ||
380 | omap_nr_pending = 4; | ||
381 | } | ||
382 | |||
383 | ret = omap_init_irq(-1, of_node_get(node)); | ||
384 | if (ret < 0) | ||
385 | return ret; | ||
386 | |||
387 | set_handle_irq(omap_intc_handle_irq); | ||
388 | |||
389 | return 0; | ||
390 | } | ||
391 | |||
392 | IRQCHIP_DECLARE(omap2_intc, "ti,omap2-intc", intc_of_init); | ||
393 | IRQCHIP_DECLARE(omap3_intc, "ti,omap3-intc", intc_of_init); | ||
394 | IRQCHIP_DECLARE(am33xx_intc, "ti,am33xx-intc", intc_of_init); | ||