aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSricharan R <r.sricharan@ti.com>2013-12-03 05:27:23 -0500
committerSricharan R <r.sricharan@ti.com>2014-02-05 09:38:34 -0500
commit96ca848ef7ea1be7e92d1cceb34ef3aa86053828 (patch)
treee57b452cb1a6f383f5c9dcc346bf53e062ced0da
parent006e983bbc805431c44e2135e13841f66059a045 (diff)
DRIVERS: IRQCHIP: CROSSBAR: Add support for Crossbar IP
Some socs have a large number of interrupts requests to service the needs of its many peripherals and subsystems. All of the interrupt lines from the subsystems are not needed at the same time, so they have to be muxed to the irq-controller appropriately. In such places a interrupt controllers are preceded by an CROSSBAR that provides flexibility in muxing the device requests to the controller inputs. This driver takes care a allocating a free irq and then configuring the crossbar IP as a part of the mpu's irqchip callbacks. crossbar_init should be called right before the irqchip_init, so that it is setup to handle the irqchip callbacks. Cc: Thomas Gleixner <tglx@linutronix.de> Cc: Linus Walleij <linus.walleij@linaro.org> Cc: Santosh Shilimkar <santosh.shilimkar@ti.com> Cc: Russell King <linux@arm.linux.org.uk> Cc: Tony Lindgren <tony@atomide.com> Cc: Rajendra Nayak <rnayak@ti.com> Cc: Marc Zyngier <marc.zyngier@arm.com> Cc: Grant Likely <grant.likely@linaro.org> Cc: Rob Herring <rob.herring@calxeda.com> Signed-off-by: Sricharan R <r.sricharan@ti.com> Acked-by: Kumar Gala <galak@codeaurora.org> (for DT binding portion) Acked-by: Santosh Shilimkar <santosh.shilimkar@ti.com> Acked-by: Linus Walleij <linus.walleij@linaro.org> Acked-by: Thomas Gleixner <tglx@linutronix.de>
-rw-r--r--Documentation/devicetree/bindings/arm/omap/crossbar.txt27
-rw-r--r--drivers/irqchip/Kconfig8
-rw-r--r--drivers/irqchip/Makefile1
-rw-r--r--drivers/irqchip/irq-crossbar.c208
-rw-r--r--include/linux/irqchip/irq-crossbar.h11
5 files changed, 255 insertions, 0 deletions
diff --git a/Documentation/devicetree/bindings/arm/omap/crossbar.txt b/Documentation/devicetree/bindings/arm/omap/crossbar.txt
new file mode 100644
index 000000000000..fb88585cfb93
--- /dev/null
+++ b/Documentation/devicetree/bindings/arm/omap/crossbar.txt
@@ -0,0 +1,27 @@
1Some socs have a large number of interrupts requests to service
2the needs of its many peripherals and subsystems. All of the
3interrupt lines from the subsystems are not needed at the same
4time, so they have to be muxed to the irq-controller appropriately.
5In such places a interrupt controllers are preceded by an CROSSBAR
6that provides flexibility in muxing the device requests to the controller
7inputs.
8
9Required properties:
10- compatible : Should be "ti,irq-crossbar"
11- reg: Base address and the size of the crossbar registers.
12- ti,max-irqs: Total number of irqs available at the interrupt controller.
13- ti,reg-size: Size of a individual register in bytes. Every individual
14 register is assumed to be of same size. Valid sizes are 1, 2, 4.
15- ti,irqs-reserved: List of the reserved irq lines that are not muxed using
16 crossbar. These interrupt lines are reserved in the soc,
17 so crossbar bar driver should not consider them as free
18 lines.
19
20Examples:
21 crossbar_mpu: @4a020000 {
22 compatible = "ti,irq-crossbar";
23 reg = <0x4a002a48 0x130>;
24 ti,max-irqs = <160>;
25 ti,reg-size = <2>;
26 ti,irqs-reserved = <0 1 2 3 5 6 131 132 139 140>;
27 };
diff --git a/drivers/irqchip/Kconfig b/drivers/irqchip/Kconfig
index 61ffdca96e25..111068782da4 100644
--- a/drivers/irqchip/Kconfig
+++ b/drivers/irqchip/Kconfig
@@ -69,3 +69,11 @@ config VERSATILE_FPGA_IRQ_NR
69config XTENSA_MX 69config XTENSA_MX
70 bool 70 bool
71 select IRQ_DOMAIN 71 select IRQ_DOMAIN
72
73config IRQ_CROSSBAR
74 bool
75 help
76 Support for a CROSSBAR ip that preceeds the main interrupt controller.
77 The primary irqchip invokes the crossbar's callback which inturn allocates
78 a free irq and configures the IP. Thus the peripheral interrupts are
79 routed to one of the free irqchip interrupt lines.
diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile
index 86b484cb3ec2..3e776cb8dd46 100644
--- a/drivers/irqchip/Makefile
+++ b/drivers/irqchip/Makefile
@@ -25,3 +25,4 @@ obj-$(CONFIG_ARCH_VT8500) += irq-vt8500.o
25obj-$(CONFIG_TB10X_IRQC) += irq-tb10x.o 25obj-$(CONFIG_TB10X_IRQC) += irq-tb10x.o
26obj-$(CONFIG_XTENSA) += irq-xtensa-pic.o 26obj-$(CONFIG_XTENSA) += irq-xtensa-pic.o
27obj-$(CONFIG_XTENSA_MX) += irq-xtensa-mx.o 27obj-$(CONFIG_XTENSA_MX) += irq-xtensa-mx.o
28obj-$(CONFIG_IRQ_CROSSBAR) += irq-crossbar.o
diff --git a/drivers/irqchip/irq-crossbar.c b/drivers/irqchip/irq-crossbar.c
new file mode 100644
index 000000000000..fc817d28d1fe
--- /dev/null
+++ b/drivers/irqchip/irq-crossbar.c
@@ -0,0 +1,208 @@
1/*
2 * drivers/irqchip/irq-crossbar.c
3 *
4 * Copyright (C) 2013 Texas Instruments Incorporated - http://www.ti.com
5 * Author: Sricharan R <r.sricharan@ti.com>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License version 2 as
9 * published by the Free Software Foundation.
10 *
11 */
12#include <linux/err.h>
13#include <linux/io.h>
14#include <linux/of_address.h>
15#include <linux/of_irq.h>
16#include <linux/slab.h>
17#include <linux/irqchip/arm-gic.h>
18
19#define IRQ_FREE -1
20#define GIC_IRQ_START 32
21
22/*
23 * @int_max: maximum number of supported interrupts
24 * @irq_map: array of interrupts to crossbar number mapping
25 * @crossbar_base: crossbar base address
26 * @register_offsets: offsets for each irq number
27 */
28struct crossbar_device {
29 uint int_max;
30 uint *irq_map;
31 void __iomem *crossbar_base;
32 int *register_offsets;
33 void (*write) (int, int);
34};
35
36static struct crossbar_device *cb;
37
38static inline void crossbar_writel(int irq_no, int cb_no)
39{
40 writel(cb_no, cb->crossbar_base + cb->register_offsets[irq_no]);
41}
42
43static inline void crossbar_writew(int irq_no, int cb_no)
44{
45 writew(cb_no, cb->crossbar_base + cb->register_offsets[irq_no]);
46}
47
48static inline void crossbar_writeb(int irq_no, int cb_no)
49{
50 writeb(cb_no, cb->crossbar_base + cb->register_offsets[irq_no]);
51}
52
53static inline int allocate_free_irq(int cb_no)
54{
55 int i;
56
57 for (i = 0; i < cb->int_max; i++) {
58 if (cb->irq_map[i] == IRQ_FREE) {
59 cb->irq_map[i] = cb_no;
60 return i;
61 }
62 }
63
64 return -ENODEV;
65}
66
67static int crossbar_domain_map(struct irq_domain *d, unsigned int irq,
68 irq_hw_number_t hw)
69{
70 cb->write(hw - GIC_IRQ_START, cb->irq_map[hw - GIC_IRQ_START]);
71 return 0;
72}
73
74static void crossbar_domain_unmap(struct irq_domain *d, unsigned int irq)
75{
76 irq_hw_number_t hw = irq_get_irq_data(irq)->hwirq;
77
78 if (hw > GIC_IRQ_START)
79 cb->irq_map[hw - GIC_IRQ_START] = IRQ_FREE;
80}
81
82static int crossbar_domain_xlate(struct irq_domain *d,
83 struct device_node *controller,
84 const u32 *intspec, unsigned int intsize,
85 unsigned long *out_hwirq,
86 unsigned int *out_type)
87{
88 unsigned long ret;
89
90 ret = allocate_free_irq(intspec[1]);
91
92 if (IS_ERR_VALUE(ret))
93 return ret;
94
95 *out_hwirq = ret + GIC_IRQ_START;
96 return 0;
97}
98
99const struct irq_domain_ops routable_irq_domain_ops = {
100 .map = crossbar_domain_map,
101 .unmap = crossbar_domain_unmap,
102 .xlate = crossbar_domain_xlate
103};
104
105static int __init crossbar_of_init(struct device_node *node)
106{
107 int i, size, max, reserved = 0, entry;
108 const __be32 *irqsr;
109
110 cb = kzalloc(sizeof(struct cb_device *), GFP_KERNEL);
111
112 if (!cb)
113 return -ENOMEM;
114
115 cb->crossbar_base = of_iomap(node, 0);
116 if (!cb->crossbar_base)
117 goto err1;
118
119 of_property_read_u32(node, "ti,max-irqs", &max);
120 cb->irq_map = kzalloc(max * sizeof(int), GFP_KERNEL);
121 if (!cb->irq_map)
122 goto err2;
123
124 cb->int_max = max;
125
126 for (i = 0; i < max; i++)
127 cb->irq_map[i] = IRQ_FREE;
128
129 /* Get and mark reserved irqs */
130 irqsr = of_get_property(node, "ti,irqs-reserved", &size);
131 if (irqsr) {
132 size /= sizeof(__be32);
133
134 for (i = 0; i < size; i++) {
135 of_property_read_u32_index(node,
136 "ti,irqs-reserved",
137 i, &entry);
138 if (entry > max) {
139 pr_err("Invalid reserved entry\n");
140 goto err3;
141 }
142 cb->irq_map[entry] = 0;
143 }
144 }
145
146 cb->register_offsets = kzalloc(max * sizeof(int), GFP_KERNEL);
147 if (!cb->register_offsets)
148 goto err3;
149
150 of_property_read_u32(node, "ti,reg-size", &size);
151
152 switch (size) {
153 case 1:
154 cb->write = crossbar_writeb;
155 break;
156 case 2:
157 cb->write = crossbar_writew;
158 break;
159 case 4:
160 cb->write = crossbar_writel;
161 break;
162 default:
163 pr_err("Invalid reg-size property\n");
164 goto err4;
165 break;
166 }
167
168 /*
169 * Register offsets are not linear because of the
170 * reserved irqs. so find and store the offsets once.
171 */
172 for (i = 0; i < max; i++) {
173 if (!cb->irq_map[i])
174 continue;
175
176 cb->register_offsets[i] = reserved;
177 reserved += size;
178 }
179
180 register_routable_domain_ops(&routable_irq_domain_ops);
181 return 0;
182
183err4:
184 kfree(cb->register_offsets);
185err3:
186 kfree(cb->irq_map);
187err2:
188 iounmap(cb->crossbar_base);
189err1:
190 kfree(cb);
191 return -ENOMEM;
192}
193
194static const struct of_device_id crossbar_match[] __initconst = {
195 { .compatible = "ti,irq-crossbar" },
196 {}
197};
198
199int __init irqcrossbar_init(void)
200{
201 struct device_node *np;
202 np = of_find_matching_node(NULL, crossbar_match);
203 if (!np)
204 return -ENODEV;
205
206 crossbar_of_init(np);
207 return 0;
208}
diff --git a/include/linux/irqchip/irq-crossbar.h b/include/linux/irqchip/irq-crossbar.h
new file mode 100644
index 000000000000..e5537b81df8d
--- /dev/null
+++ b/include/linux/irqchip/irq-crossbar.h
@@ -0,0 +1,11 @@
1/*
2 * drivers/irqchip/irq-crossbar.h
3 *
4 * Copyright (C) 2013 Texas Instruments Incorporated - http://www.ti.com
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License version 2 as
8 * published by the Free Software Foundation.
9 *
10 */
11int irqcrossbar_init(void);