aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSebastian Hesselbarth <sebastian.hesselbarth@gmail.com>2013-06-06 12:27:09 -0400
committerThomas Gleixner <tglx@linutronix.de>2013-06-11 10:18:50 -0400
commit9dbd90f17e4f380593ec5194c2a4d5e52c5f72d1 (patch)
tree56b781fce3882772962e0e4dbcbe517b37713ec2
parentd671a605580d2caafc77f1a25bcf8435795df6fe (diff)
irqchip: Add support for Marvell Orion SoCs
This patch adds an irqchip driver for the main interrupt controller found on Marvell Orion SoCs (Kirkwood, Dove, Orion5x, Discovery Innovation). Corresponding device tree documentation is also added. Signed-off-by: Sebastian Hesselbarth <sebastian.hesselbarth@gmail.com> Acked-by: Grant Likely <grant.likely@linaro.org> Cc: Rob Herring <rob.herring@calxeda.com> Cc: Rob Landley <rob@landley.net> Cc: John Stultz <john.stultz@linaro.org> Cc: Russell King <linux@arm.linux.org.uk> Cc: Jason Cooper <jason@lakedaemon.net> Cc: Andrew Lunn <andrew@lunn.ch> Cc: Thomas Petazzoni <thomas.petazzoni@free-electrons.com> Cc: Gregory Clement <gregory.clement@free-electrons.com> Cc: devicetree-discuss@lists.ozlabs.org Cc: linux-arm-kernel@lists.infradead.org Link: http://lkml.kernel.org/r/1370536034-23956-2-git-send-email-sebastian.hesselbarth@gmail.com Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
-rw-r--r--Documentation/devicetree/bindings/interrupt-controller/marvell,orion-intc.txt48
-rw-r--r--drivers/irqchip/Kconfig5
-rw-r--r--drivers/irqchip/Makefile1
-rw-r--r--drivers/irqchip/irq-orion.c192
4 files changed, 246 insertions, 0 deletions
diff --git a/Documentation/devicetree/bindings/interrupt-controller/marvell,orion-intc.txt b/Documentation/devicetree/bindings/interrupt-controller/marvell,orion-intc.txt
new file mode 100644
index 000000000000..2c11ac76fac9
--- /dev/null
+++ b/Documentation/devicetree/bindings/interrupt-controller/marvell,orion-intc.txt
@@ -0,0 +1,48 @@
1Marvell Orion SoC interrupt controllers
2
3* Main interrupt controller
4
5Required properties:
6- compatible: shall be "marvell,orion-intc"
7- reg: base address(es) of interrupt registers starting with CAUSE register
8- interrupt-controller: identifies the node as an interrupt controller
9- #interrupt-cells: number of cells to encode an interrupt source, shall be 1
10
11The interrupt sources map to the corresponding bits in the interrupt
12registers, i.e.
13- 0 maps to bit 0 of first base address,
14- 1 maps to bit 1 of first base address,
15- 32 maps to bit 0 of second base address, and so on.
16
17Example:
18 intc: interrupt-controller {
19 compatible = "marvell,orion-intc";
20 interrupt-controller;
21 #interrupt-cells = <1>;
22 /* Dove has 64 first level interrupts */
23 reg = <0x20200 0x10>, <0x20210 0x10>;
24 };
25
26* Bridge interrupt controller
27
28Required properties:
29- compatible: shall be "marvell,orion-bridge-intc"
30- reg: base address of bridge interrupt registers starting with CAUSE register
31- interrupts: bridge interrupt of the main interrupt controller
32- interrupt-controller: identifies the node as an interrupt controller
33- #interrupt-cells: number of cells to encode an interrupt source, shall be 1
34
35Optional properties:
36- marvell,#interrupts: number of interrupts provided by bridge interrupt
37 controller, defaults to 32 if not set
38
39Example:
40 bridge_intc: interrupt-controller {
41 compatible = "marvell,orion-bridge-intc";
42 interrupt-controller;
43 #interrupt-cells = <1>;
44 reg = <0x20110 0x8>;
45 interrupts = <0>;
46 /* Dove bridge provides 5 interrupts */
47 marvell,#interrupts = <5>;
48 };
diff --git a/drivers/irqchip/Kconfig b/drivers/irqchip/Kconfig
index 4a33351c25dc..68c31071968d 100644
--- a/drivers/irqchip/Kconfig
+++ b/drivers/irqchip/Kconfig
@@ -25,6 +25,11 @@ config ARM_VIC_NR
25 The maximum number of VICs available in the system, for 25 The maximum number of VICs available in the system, for
26 power management. 26 power management.
27 27
28config ORION_IRQCHIP
29 bool
30 select IRQ_DOMAIN
31 select MULTI_IRQ_HANDLER
32
28config RENESAS_INTC_IRQPIN 33config RENESAS_INTC_IRQPIN
29 bool 34 bool
30 select IRQ_DOMAIN 35 select IRQ_DOMAIN
diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile
index cda4cb5f7327..55df3bdf804d 100644
--- a/drivers/irqchip/Makefile
+++ b/drivers/irqchip/Makefile
@@ -7,6 +7,7 @@ obj-$(CONFIG_ARCH_MXS) += irq-mxs.o
7obj-$(CONFIG_ARCH_S3C24XX) += irq-s3c24xx.o 7obj-$(CONFIG_ARCH_S3C24XX) += irq-s3c24xx.o
8obj-$(CONFIG_METAG) += irq-metag-ext.o 8obj-$(CONFIG_METAG) += irq-metag-ext.o
9obj-$(CONFIG_METAG_PERFCOUNTER_IRQS) += irq-metag.o 9obj-$(CONFIG_METAG_PERFCOUNTER_IRQS) += irq-metag.o
10obj-$(CONFIG_ORION_IRQCHIP) += irq-orion.o
10obj-$(CONFIG_ARCH_SUNXI) += irq-sun4i.o 11obj-$(CONFIG_ARCH_SUNXI) += irq-sun4i.o
11obj-$(CONFIG_ARCH_SPEAR3XX) += spear-shirq.o 12obj-$(CONFIG_ARCH_SPEAR3XX) += spear-shirq.o
12obj-$(CONFIG_ARM_GIC) += irq-gic.o 13obj-$(CONFIG_ARM_GIC) += irq-gic.o
diff --git a/drivers/irqchip/irq-orion.c b/drivers/irqchip/irq-orion.c
new file mode 100644
index 000000000000..e51d40031884
--- /dev/null
+++ b/drivers/irqchip/irq-orion.c
@@ -0,0 +1,192 @@
1/*
2 * Marvell Orion SoCs IRQ chip driver.
3 *
4 * Sebastian Hesselbarth <sebastian.hesselbarth@gmail.com>
5 *
6 * This file is licensed under the terms of the GNU General Public
7 * License version 2. This program is licensed "as is" without any
8 * warranty of any kind, whether express or implied.
9 */
10
11#include <linux/io.h>
12#include <linux/irq.h>
13#include <linux/of.h>
14#include <linux/of_address.h>
15#include <linux/of_irq.h>
16#include <asm/exception.h>
17#include <asm/mach/irq.h>
18
19#include "irqchip.h"
20
21/*
22 * Orion SoC main interrupt controller
23 */
24#define ORION_IRQS_PER_CHIP 32
25
26#define ORION_IRQ_CAUSE 0x00
27#define ORION_IRQ_MASK 0x04
28#define ORION_IRQ_FIQ_MASK 0x08
29#define ORION_IRQ_ENDP_MASK 0x0c
30
31static struct irq_domain *orion_irq_domain;
32
33static asmlinkage void
34__exception_irq_entry orion_handle_irq(struct pt_regs *regs)
35{
36 struct irq_domain_chip_generic *dgc = orion_irq_domain->gc;
37 int n, base = 0;
38
39 for (n = 0; n < dgc->num_chips; n++, base += ORION_IRQS_PER_CHIP) {
40 struct irq_chip_generic *gc =
41 irq_get_domain_generic_chip(orion_irq_domain, base);
42 u32 stat = readl_relaxed(gc->reg_base + ORION_IRQ_CAUSE) &
43 gc->mask_cache;
44 while (stat) {
45 u32 hwirq = ffs(stat) - 1;
46 u32 irq = irq_find_mapping(orion_irq_domain,
47 gc->irq_base + hwirq);
48 handle_IRQ(irq, regs);
49 stat &= ~(1 << hwirq);
50 }
51 }
52}
53
54static int __init orion_irq_init(struct device_node *np,
55 struct device_node *parent)
56{
57 unsigned int clr = IRQ_NOREQUEST | IRQ_NOPROBE | IRQ_NOAUTOEN;
58 int n, ret, base, num_chips = 0;
59 struct resource r;
60
61 /* count number of irq chips by valid reg addresses */
62 while (of_address_to_resource(np, num_chips, &r) == 0)
63 num_chips++;
64
65 orion_irq_domain = irq_domain_add_linear(np,
66 num_chips * ORION_IRQS_PER_CHIP,
67 &irq_generic_chip_ops, NULL);
68 if (!orion_irq_domain)
69 panic("%s: unable to add irq domain\n", np->name);
70
71 ret = irq_alloc_domain_generic_chips(orion_irq_domain,
72 ORION_IRQS_PER_CHIP, 1, np->name,
73 handle_level_irq, clr, 0,
74 IRQ_GC_INIT_MASK_CACHE);
75 if (ret)
76 panic("%s: unable to alloc irq domain gc\n", np->name);
77
78 for (n = 0, base = 0; n < num_chips; n++, base += ORION_IRQS_PER_CHIP) {
79 struct irq_chip_generic *gc =
80 irq_get_domain_generic_chip(orion_irq_domain, base);
81
82 of_address_to_resource(np, n, &r);
83
84 if (!request_mem_region(r.start, resource_size(&r), np->name))
85 panic("%s: unable to request mem region %d",
86 np->name, n);
87
88 gc->reg_base = ioremap(r.start, resource_size(&r));
89 if (!gc->reg_base)
90 panic("%s: unable to map resource %d", np->name, n);
91
92 gc->chip_types[0].regs.mask = ORION_IRQ_MASK;
93 gc->chip_types[0].chip.irq_mask = irq_gc_mask_clr_bit;
94 gc->chip_types[0].chip.irq_unmask = irq_gc_mask_set_bit;
95
96 /* mask all interrupts */
97 writel(0, gc->reg_base + ORION_IRQ_MASK);
98 }
99
100 set_handle_irq(orion_handle_irq);
101 return 0;
102}
103IRQCHIP_DECLARE(orion_intc, "marvell,orion-intc", orion_irq_init);
104
105/*
106 * Orion SoC bridge interrupt controller
107 */
108#define ORION_BRIDGE_IRQ_CAUSE 0x00
109#define ORION_BRIDGE_IRQ_MASK 0x04
110
111static void orion_bridge_irq_handler(unsigned int irq, struct irq_desc *desc)
112{
113 struct irq_domain *d = irq_get_handler_data(irq);
114 struct irq_chip_generic *gc = irq_get_domain_generic_chip(d, irq);
115 u32 stat = readl_relaxed(gc->reg_base + ORION_BRIDGE_IRQ_CAUSE) &
116 gc->mask_cache;
117
118 while (stat) {
119 u32 hwirq = ffs(stat) - 1;
120
121 generic_handle_irq(irq_find_mapping(d, gc->irq_base + hwirq));
122 stat &= ~(1 << hwirq);
123 }
124}
125
126static int __init orion_bridge_irq_init(struct device_node *np,
127 struct device_node *parent)
128{
129 unsigned int clr = IRQ_NOREQUEST | IRQ_NOPROBE | IRQ_NOAUTOEN;
130 struct resource r;
131 struct irq_domain *domain;
132 struct irq_chip_generic *gc;
133 int ret, irq, nrirqs = 32;
134
135 /* get optional number of interrupts provided */
136 of_property_read_u32(np, "marvell,#interrupts", &nrirqs);
137
138 domain = irq_domain_add_linear(np, nrirqs,
139 &irq_generic_chip_ops, NULL);
140 if (!domain) {
141 pr_err("%s: unable to add irq domain\n", np->name);
142 return -ENOMEM;
143 }
144
145 ret = irq_alloc_domain_generic_chips(domain, nrirqs, 1, np->name,
146 handle_level_irq, clr, 0, IRQ_GC_INIT_MASK_CACHE);
147 if (ret) {
148 pr_err("%s: unable to alloc irq domain gc\n", np->name);
149 return ret;
150 }
151
152 ret = of_address_to_resource(np, 0, &r);
153 if (ret) {
154 pr_err("%s: unable to get resource\n", np->name);
155 return ret;
156 }
157
158 if (!request_mem_region(r.start, resource_size(&r), np->name)) {
159 pr_err("%s: unable to request mem region\n", np->name);
160 return -ENOMEM;
161 }
162
163 /* Map the parent interrupt for the chained handler */
164 irq = irq_of_parse_and_map(np, 0);
165 if (irq <= 0) {
166 pr_err("%s: unable to parse irq\n", np->name);
167 return -EINVAL;
168 }
169
170 gc = irq_get_domain_generic_chip(domain, 0);
171 gc->reg_base = ioremap(r.start, resource_size(&r));
172 if (!gc->reg_base) {
173 pr_err("%s: unable to map resource\n", np->name);
174 return -ENOMEM;
175 }
176
177 gc->chip_types[0].regs.ack = ORION_BRIDGE_IRQ_CAUSE;
178 gc->chip_types[0].regs.mask = ORION_BRIDGE_IRQ_MASK;
179 gc->chip_types[0].chip.irq_ack = irq_gc_ack_clr_bit;
180 gc->chip_types[0].chip.irq_mask = irq_gc_mask_clr_bit;
181 gc->chip_types[0].chip.irq_unmask = irq_gc_mask_set_bit;
182
183 /* mask all interrupts */
184 writel(0, gc->reg_base + ORION_BRIDGE_IRQ_MASK);
185
186 irq_set_handler_data(irq, domain);
187 irq_set_chained_handler(irq, orion_bridge_irq_handler);
188
189 return 0;
190}
191IRQCHIP_DECLARE(orion_bridge_intc,
192 "marvell,orion-bridge-intc", orion_bridge_irq_init);