diff options
-rw-r--r-- | Documentation/devicetree/bindings/arm/omap/crossbar.txt | 27 | ||||
-rw-r--r-- | drivers/irqchip/Kconfig | 8 | ||||
-rw-r--r-- | drivers/irqchip/Makefile | 1 | ||||
-rw-r--r-- | drivers/irqchip/irq-crossbar.c | 208 | ||||
-rw-r--r-- | include/linux/irqchip/irq-crossbar.h | 11 |
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 @@ | |||
1 | Some socs have a large number of interrupts requests to service | ||
2 | the needs of its many peripherals and subsystems. All of the | ||
3 | interrupt lines from the subsystems are not needed at the same | ||
4 | time, so they have to be muxed to the irq-controller appropriately. | ||
5 | In such places a interrupt controllers are preceded by an CROSSBAR | ||
6 | that provides flexibility in muxing the device requests to the controller | ||
7 | inputs. | ||
8 | |||
9 | Required 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 | |||
20 | Examples: | ||
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 | |||
69 | config XTENSA_MX | 69 | config XTENSA_MX |
70 | bool | 70 | bool |
71 | select IRQ_DOMAIN | 71 | select IRQ_DOMAIN |
72 | |||
73 | config 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 | |||
25 | obj-$(CONFIG_TB10X_IRQC) += irq-tb10x.o | 25 | obj-$(CONFIG_TB10X_IRQC) += irq-tb10x.o |
26 | obj-$(CONFIG_XTENSA) += irq-xtensa-pic.o | 26 | obj-$(CONFIG_XTENSA) += irq-xtensa-pic.o |
27 | obj-$(CONFIG_XTENSA_MX) += irq-xtensa-mx.o | 27 | obj-$(CONFIG_XTENSA_MX) += irq-xtensa-mx.o |
28 | obj-$(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 | */ | ||
28 | struct 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 | |||
36 | static struct crossbar_device *cb; | ||
37 | |||
38 | static 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 | |||
43 | static 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 | |||
48 | static 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 | |||
53 | static 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 | |||
67 | static 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 | |||
74 | static 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 | |||
82 | static 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 | |||
99 | const 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 | |||
105 | static 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 | |||
183 | err4: | ||
184 | kfree(cb->register_offsets); | ||
185 | err3: | ||
186 | kfree(cb->irq_map); | ||
187 | err2: | ||
188 | iounmap(cb->crossbar_base); | ||
189 | err1: | ||
190 | kfree(cb); | ||
191 | return -ENOMEM; | ||
192 | } | ||
193 | |||
194 | static const struct of_device_id crossbar_match[] __initconst = { | ||
195 | { .compatible = "ti,irq-crossbar" }, | ||
196 | {} | ||
197 | }; | ||
198 | |||
199 | int __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 | */ | ||
11 | int irqcrossbar_init(void); | ||