aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/irqchip
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2014-10-08 17:37:16 -0400
committerLinus Torvalds <torvalds@linux-foundation.org>2014-10-08 17:37:16 -0400
commit8b45bc892e6842115fc87c2b2a3b86a20617606a (patch)
tree636c804eb29bb97070e7d2bc4d5ab9d1dad27cac /drivers/irqchip
parenteb785bef684f2b7d03b530efc8e6f199e9777e2f (diff)
parentfa637bf0595ee1796d728a0d33b6b7fff12e1f3d (diff)
Merge tag 'drivers-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/arm/arm-soc
Pull ARM SoC driver updates from Arnd Bergmann: "These are changes for drivers that are intimately tied to some SoC and for some reason could not get merged through the respective subsystem maintainer tree. Most of the new code is for the Keystone Navigator driver, which is new base support that is going to be needed for their hardware accelerated network driver and other units. Most of the commits are for moving old code around from at91 and omap for things that are done in device drivers nowadays. - at91: move reset, poweroff, memory and clocksource code into drivers directories - socfpga: add edac driver (through arm-soc, as requested by Boris) - omap: move omap-intc code to drivers/irqchip - sunxi: added an RTC driver for sun6i - omap: mailbox driver related changes - keystone: support for the "Navigator" component - versatile: new reboot, led and soc drivers" * tag 'drivers-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/arm/arm-soc: (92 commits) bus: arm-ccn: Fix spurious warning message leds: add device tree bindings for register bit LEDs soc: add driver for the ARM RealView power: reset: driver for the Versatile syscon reboot leds: add a driver for syscon-based LEDs drivers/soc: ti: fix build break with modules MAINTAINERS: Add Keystone Multicore Navigator drivers entry soc: ti: add Keystone Navigator DMA support Documentation: dt: soc: add Keystone Navigator DMA bindings soc: ti: add Keystone Navigator QMSS driver Documentation: dt: soc: add Keystone Navigator QMSS bindings rtc: sunxi: Depend on platforms sun4i/sun7i that actually have the rtc rtc: sun6i: Add sun6i RTC driver irqchip: omap-intc: remove unnecessary comments irqchip: omap-intc: correct maximum number or MIR registers irqchip: omap-intc: enable TURBO idle mode irqchip: omap-intc: enable IP protection irqchip: omap-intc: remove unnecesary of_address_to_resource() call irqchip: omap-intc: comment style cleanup irqchip: omap-intc: minor improvement to omap_irq_pending() ...
Diffstat (limited to 'drivers/irqchip')
-rw-r--r--drivers/irqchip/Kconfig5
-rw-r--r--drivers/irqchip/Makefile1
-rw-r--r--drivers/irqchip/irq-omap-intc.c403
3 files changed, 409 insertions, 0 deletions
diff --git a/drivers/irqchip/Kconfig b/drivers/irqchip/Kconfig
index a31a9e40eed9..78d4ff551590 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
78config OMAP_IRQCHIP
79 bool
80 select GENERIC_IRQ_CHIP
81 select IRQ_DOMAIN
82
78config ORION_IRQCHIP 83config 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
13obj-$(CONFIG_CLPS711X_IRQCHIP) += irq-clps711x.o 13obj-$(CONFIG_CLPS711X_IRQCHIP) += irq-clps711x.o
14obj-$(CONFIG_OR1K_PIC) += irq-or1k-pic.o 14obj-$(CONFIG_OR1K_PIC) += irq-or1k-pic.o
15obj-$(CONFIG_ORION_IRQCHIP) += irq-orion.o 15obj-$(CONFIG_ORION_IRQCHIP) += irq-orion.o
16obj-$(CONFIG_OMAP_IRQCHIP) += irq-omap-intc.o
16obj-$(CONFIG_ARCH_SUNXI) += irq-sun4i.o 17obj-$(CONFIG_ARCH_SUNXI) += irq-sun4i.o
17obj-$(CONFIG_ARCH_SUNXI) += irq-sunxi-nmi.o 18obj-$(CONFIG_ARCH_SUNXI) += irq-sunxi-nmi.o
18obj-$(CONFIG_ARCH_SPEAR3XX) += spear-shirq.o 19obj-$(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..f3814e79192d
--- /dev/null
+++ b/drivers/irqchip/irq-omap-intc.c
@@ -0,0 +1,403 @@
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 4
53
54#define INTC_IDLE_FUNCIDLE (1 << 0)
55#define INTC_IDLE_TURBO (1 << 1)
56
57#define INTC_PROTECTION_ENABLE (1 << 0)
58
59struct omap_intc_regs {
60 u32 sysconfig;
61 u32 protection;
62 u32 idle;
63 u32 threshold;
64 u32 ilr[INTCPS_NR_ILR_REGS];
65 u32 mir[INTCPS_NR_MIR_REGS];
66};
67static struct omap_intc_regs intc_context;
68
69static struct irq_domain *domain;
70static void __iomem *omap_irq_base;
71static int omap_nr_pending = 3;
72static int omap_nr_irqs = 96;
73
74static void intc_writel(u32 reg, u32 val)
75{
76 writel_relaxed(val, omap_irq_base + reg);
77}
78
79static u32 intc_readl(u32 reg)
80{
81 return readl_relaxed(omap_irq_base + reg);
82}
83
84void omap_intc_save_context(void)
85{
86 int i;
87
88 intc_context.sysconfig =
89 intc_readl(INTC_SYSCONFIG);
90 intc_context.protection =
91 intc_readl(INTC_PROTECTION);
92 intc_context.idle =
93 intc_readl(INTC_IDLE);
94 intc_context.threshold =
95 intc_readl(INTC_THRESHOLD);
96
97 for (i = 0; i < omap_nr_irqs; i++)
98 intc_context.ilr[i] =
99 intc_readl((INTC_ILR0 + 0x4 * i));
100 for (i = 0; i < INTCPS_NR_MIR_REGS; i++)
101 intc_context.mir[i] =
102 intc_readl(INTC_MIR0 + (0x20 * i));
103}
104
105void omap_intc_restore_context(void)
106{
107 int i;
108
109 intc_writel(INTC_SYSCONFIG, intc_context.sysconfig);
110 intc_writel(INTC_PROTECTION, intc_context.protection);
111 intc_writel(INTC_IDLE, intc_context.idle);
112 intc_writel(INTC_THRESHOLD, intc_context.threshold);
113
114 for (i = 0; i < omap_nr_irqs; i++)
115 intc_writel(INTC_ILR0 + 0x4 * i,
116 intc_context.ilr[i]);
117
118 for (i = 0; i < INTCPS_NR_MIR_REGS; i++)
119 intc_writel(INTC_MIR0 + 0x20 * i,
120 intc_context.mir[i]);
121 /* MIRs are saved and restore with other PRCM registers */
122}
123
124void omap3_intc_prepare_idle(void)
125{
126 /*
127 * Disable autoidle as it can stall interrupt controller,
128 * cf. errata ID i540 for 3430 (all revisions up to 3.1.x)
129 */
130 intc_writel(INTC_SYSCONFIG, 0);
131 intc_writel(INTC_IDLE, INTC_IDLE_TURBO);
132}
133
134void omap3_intc_resume_idle(void)
135{
136 /* Re-enable autoidle */
137 intc_writel(INTC_SYSCONFIG, 1);
138 intc_writel(INTC_IDLE, 0);
139}
140
141/* XXX: FIQ and additional INTC support (only MPU at the moment) */
142static void omap_ack_irq(struct irq_data *d)
143{
144 intc_writel(INTC_CONTROL, 0x1);
145}
146
147static void omap_mask_ack_irq(struct irq_data *d)
148{
149 irq_gc_mask_disable_reg(d);
150 omap_ack_irq(d);
151}
152
153static void __init omap_irq_soft_reset(void)
154{
155 unsigned long tmp;
156
157 tmp = intc_readl(INTC_REVISION) & 0xff;
158
159 pr_info("IRQ: Found an INTC at 0x%p (revision %ld.%ld) with %d interrupts\n",
160 omap_irq_base, tmp >> 4, tmp & 0xf, omap_nr_irqs);
161
162 tmp = intc_readl(INTC_SYSCONFIG);
163 tmp |= 1 << 1; /* soft reset */
164 intc_writel(INTC_SYSCONFIG, tmp);
165
166 while (!(intc_readl(INTC_SYSSTATUS) & 0x1))
167 /* Wait for reset to complete */;
168
169 /* Enable autoidle */
170 intc_writel(INTC_SYSCONFIG, 1 << 0);
171}
172
173int omap_irq_pending(void)
174{
175 int i;
176
177 for (i = 0; i < omap_nr_pending; i++)
178 if (intc_readl(INTC_PENDING_IRQ0 + (0x20 * i)))
179 return 1;
180 return 0;
181}
182
183void omap3_intc_suspend(void)
184{
185 /* A pending interrupt would prevent OMAP from entering suspend */
186 omap_ack_irq(NULL);
187}
188
189static int __init omap_alloc_gc_of(struct irq_domain *d, void __iomem *base)
190{
191 int ret;
192 int i;
193
194 ret = irq_alloc_domain_generic_chips(d, 32, 1, "INTC",
195 handle_level_irq, IRQ_NOREQUEST | IRQ_NOPROBE,
196 IRQ_LEVEL, 0);
197 if (ret) {
198 pr_warn("Failed to allocate irq chips\n");
199 return ret;
200 }
201
202 for (i = 0; i < omap_nr_pending; i++) {
203 struct irq_chip_generic *gc;
204 struct irq_chip_type *ct;
205
206 gc = irq_get_domain_generic_chip(d, 32 * i);
207 gc->reg_base = base;
208 ct = gc->chip_types;
209
210 ct->type = IRQ_TYPE_LEVEL_MASK;
211 ct->handler = handle_level_irq;
212
213 ct->chip.irq_ack = omap_mask_ack_irq;
214 ct->chip.irq_mask = irq_gc_mask_disable_reg;
215 ct->chip.irq_unmask = irq_gc_unmask_enable_reg;
216
217 ct->chip.flags |= IRQCHIP_SKIP_SET_WAKE;
218
219 ct->regs.enable = INTC_MIR_CLEAR0 + 32 * i;
220 ct->regs.disable = INTC_MIR_SET0 + 32 * i;
221 }
222
223 return 0;
224}
225
226static void __init omap_alloc_gc_legacy(void __iomem *base,
227 unsigned int irq_start, unsigned int num)
228{
229 struct irq_chip_generic *gc;
230 struct irq_chip_type *ct;
231
232 gc = irq_alloc_generic_chip("INTC", 1, irq_start, base,
233 handle_level_irq);
234 ct = gc->chip_types;
235 ct->chip.irq_ack = omap_mask_ack_irq;
236 ct->chip.irq_mask = irq_gc_mask_disable_reg;
237 ct->chip.irq_unmask = irq_gc_unmask_enable_reg;
238 ct->chip.flags |= IRQCHIP_SKIP_SET_WAKE;
239
240 ct->regs.enable = INTC_MIR_CLEAR0;
241 ct->regs.disable = INTC_MIR_SET0;
242 irq_setup_generic_chip(gc, IRQ_MSK(num), IRQ_GC_INIT_MASK_CACHE,
243 IRQ_NOREQUEST | IRQ_NOPROBE, 0);
244}
245
246static int __init omap_init_irq_of(struct device_node *node)
247{
248 int ret;
249
250 omap_irq_base = of_iomap(node, 0);
251 if (WARN_ON(!omap_irq_base))
252 return -ENOMEM;
253
254 domain = irq_domain_add_linear(node, omap_nr_irqs,
255 &irq_generic_chip_ops, NULL);
256
257 omap_irq_soft_reset();
258
259 ret = omap_alloc_gc_of(domain, omap_irq_base);
260 if (ret < 0)
261 irq_domain_remove(domain);
262
263 return ret;
264}
265
266static int __init omap_init_irq_legacy(u32 base)
267{
268 int j, irq_base;
269
270 omap_irq_base = ioremap(base, SZ_4K);
271 if (WARN_ON(!omap_irq_base))
272 return -ENOMEM;
273
274 irq_base = irq_alloc_descs(-1, 0, omap_nr_irqs, 0);
275 if (irq_base < 0) {
276 pr_warn("Couldn't allocate IRQ numbers\n");
277 irq_base = 0;
278 }
279
280 domain = irq_domain_add_legacy(NULL, omap_nr_irqs, irq_base, 0,
281 &irq_domain_simple_ops, NULL);
282
283 omap_irq_soft_reset();
284
285 for (j = 0; j < omap_nr_irqs; j += 32)
286 omap_alloc_gc_legacy(omap_irq_base + j, j + irq_base, 32);
287
288 return 0;
289}
290
291static void __init omap_irq_enable_protection(void)
292{
293 u32 reg;
294
295 reg = intc_readl(INTC_PROTECTION);
296 reg |= INTC_PROTECTION_ENABLE;
297 intc_writel(INTC_PROTECTION, reg);
298}
299
300static int __init omap_init_irq(u32 base, struct device_node *node)
301{
302 int ret;
303
304 if (node)
305 ret = omap_init_irq_of(node);
306 else
307 ret = omap_init_irq_legacy(base);
308
309 if (ret == 0)
310 omap_irq_enable_protection();
311
312 return ret;
313}
314
315static asmlinkage void __exception_irq_entry
316omap_intc_handle_irq(struct pt_regs *regs)
317{
318 u32 irqnr = 0;
319 int handled_irq = 0;
320 int i;
321
322 do {
323 for (i = 0; i < omap_nr_pending; i++) {
324 irqnr = intc_readl(INTC_PENDING_IRQ0 + (0x20 * i));
325 if (irqnr)
326 goto out;
327 }
328
329out:
330 if (!irqnr)
331 break;
332
333 irqnr = intc_readl(INTC_SIR);
334 irqnr &= ACTIVEIRQ_MASK;
335
336 if (irqnr) {
337 irqnr = irq_find_mapping(domain, irqnr);
338 handle_IRQ(irqnr, regs);
339 handled_irq = 1;
340 }
341 } while (irqnr);
342
343 /*
344 * If an irq is masked or deasserted while active, we will
345 * keep ending up here with no irq handled. So remove it from
346 * the INTC with an ack.
347 */
348 if (!handled_irq)
349 omap_ack_irq(NULL);
350}
351
352void __init omap2_init_irq(void)
353{
354 omap_nr_irqs = 96;
355 omap_nr_pending = 3;
356 omap_init_irq(OMAP24XX_IC_BASE, NULL);
357 set_handle_irq(omap_intc_handle_irq);
358}
359
360void __init omap3_init_irq(void)
361{
362 omap_nr_irqs = 96;
363 omap_nr_pending = 3;
364 omap_init_irq(OMAP34XX_IC_BASE, NULL);
365 set_handle_irq(omap_intc_handle_irq);
366}
367
368void __init ti81xx_init_irq(void)
369{
370 omap_nr_irqs = 96;
371 omap_nr_pending = 4;
372 omap_init_irq(OMAP34XX_IC_BASE, NULL);
373 set_handle_irq(omap_intc_handle_irq);
374}
375
376static int __init intc_of_init(struct device_node *node,
377 struct device_node *parent)
378{
379 int ret;
380
381 omap_nr_pending = 3;
382 omap_nr_irqs = 96;
383
384 if (WARN_ON(!node))
385 return -ENODEV;
386
387 if (of_device_is_compatible(node, "ti,am33xx-intc")) {
388 omap_nr_irqs = 128;
389 omap_nr_pending = 4;
390 }
391
392 ret = omap_init_irq(-1, of_node_get(node));
393 if (ret < 0)
394 return ret;
395
396 set_handle_irq(omap_intc_handle_irq);
397
398 return 0;
399}
400
401IRQCHIP_DECLARE(omap2_intc, "ti,omap2-intc", intc_of_init);
402IRQCHIP_DECLARE(omap3_intc, "ti,omap3-intc", intc_of_init);
403IRQCHIP_DECLARE(am33xx_intc, "ti,am33xx-intc", intc_of_init);