diff options
author | Olof Johansson <olof@lixom.net> | 2014-03-09 03:32:26 -0400 |
---|---|---|
committer | Olof Johansson <olof@lixom.net> | 2014-03-09 03:32:26 -0400 |
commit | 63261d76c81ff89638d15a181d5ef5adb03c0e30 (patch) | |
tree | 07461c0864dcf3f2e1b8f5a376af6bd23a42a647 | |
parent | c37abe5ad2607b163244ac4686debd6687d5b5aa (diff) | |
parent | 9594274201ac3c67d5c569aae63792e58bcd33e6 (diff) |
Merge tag 'omap-for-v3.15/crossbar-signed' of git://git.kernel.org/pub/scm/linux/kernel/git/tmlind/linux-omap into next/drivers
Merge OMAP crossbar support from Tony Lindgren:
Add support for GIC crossbar that routes interrupts on newer omaps.
Looks like people wanted these merged via the omap tree as it's
the only user for the GIC crossbar.
* tag 'omap-for-v3.15/crossbar-signed' of git://git.kernel.org/pub/scm/linux/kernel/git/tmlind/linux-omap:
ARM: DRA: Enable Crossbar IP support for DRA7XX
ARM: OMAP4+: Correct Wakeup-gen code to use physical irq number
DRIVERS: IRQCHIP: CROSSBAR: Add support for Crossbar IP
DRIVERS: IRQCHIP: IRQ-GIC: Add support for routable irqs
Signed-off-by: Olof Johansson <olof@lixom.net>
-rw-r--r-- | Documentation/devicetree/bindings/arm/gic.txt | 6 | ||||
-rw-r--r-- | Documentation/devicetree/bindings/arm/omap/crossbar.txt | 27 | ||||
-rw-r--r-- | arch/arm/mach-omap2/Kconfig | 1 | ||||
-rw-r--r-- | arch/arm/mach-omap2/omap-wakeupgen.c | 4 | ||||
-rw-r--r-- | arch/arm/mach-omap2/omap4-common.c | 4 | ||||
-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-- | drivers/irqchip/irq-gic.c | 82 | ||||
-rw-r--r-- | include/linux/irqchip/arm-gic.h | 7 | ||||
-rw-r--r-- | include/linux/irqchip/irq-crossbar.h | 11 |
11 files changed, 346 insertions, 13 deletions
diff --git a/Documentation/devicetree/bindings/arm/gic.txt b/Documentation/devicetree/bindings/arm/gic.txt index bae0d87a38b2..5573c08d3180 100644 --- a/Documentation/devicetree/bindings/arm/gic.txt +++ b/Documentation/devicetree/bindings/arm/gic.txt | |||
@@ -50,6 +50,11 @@ Optional | |||
50 | regions, used when the GIC doesn't have banked registers. The offset is | 50 | regions, used when the GIC doesn't have banked registers. The offset is |
51 | cpu-offset * cpu-nr. | 51 | cpu-offset * cpu-nr. |
52 | 52 | ||
53 | - arm,routable-irqs : Total number of gic irq inputs which are not directly | ||
54 | connected from the peripherals, but are routed dynamically | ||
55 | by a crossbar/multiplexer preceding the GIC. The GIC irq | ||
56 | input line is assigned dynamically when the corresponding | ||
57 | peripheral's crossbar line is mapped. | ||
53 | Example: | 58 | Example: |
54 | 59 | ||
55 | intc: interrupt-controller@fff11000 { | 60 | intc: interrupt-controller@fff11000 { |
@@ -57,6 +62,7 @@ Example: | |||
57 | #interrupt-cells = <3>; | 62 | #interrupt-cells = <3>; |
58 | #address-cells = <1>; | 63 | #address-cells = <1>; |
59 | interrupt-controller; | 64 | interrupt-controller; |
65 | arm,routable-irqs = <160>; | ||
60 | reg = <0xfff11000 0x1000>, | 66 | reg = <0xfff11000 0x1000>, |
61 | <0xfff10100 0x100>; | 67 | <0xfff10100 0x100>; |
62 | }; | 68 | }; |
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/arch/arm/mach-omap2/Kconfig b/arch/arm/mach-omap2/Kconfig index e2ce4f8366a7..46f8c538d2bc 100644 --- a/arch/arm/mach-omap2/Kconfig +++ b/arch/arm/mach-omap2/Kconfig | |||
@@ -85,6 +85,7 @@ config SOC_DRA7XX | |||
85 | select CPU_V7 | 85 | select CPU_V7 |
86 | select HAVE_SMP | 86 | select HAVE_SMP |
87 | select HAVE_ARM_ARCH_TIMER | 87 | select HAVE_ARM_ARCH_TIMER |
88 | select IRQ_CROSSBAR | ||
88 | 89 | ||
89 | config ARCH_OMAP2PLUS | 90 | config ARCH_OMAP2PLUS |
90 | bool | 91 | bool |
diff --git a/arch/arm/mach-omap2/omap-wakeupgen.c b/arch/arm/mach-omap2/omap-wakeupgen.c index 3664562f9148..693fe486e917 100644 --- a/arch/arm/mach-omap2/omap-wakeupgen.c +++ b/arch/arm/mach-omap2/omap-wakeupgen.c | |||
@@ -138,7 +138,7 @@ static void wakeupgen_mask(struct irq_data *d) | |||
138 | unsigned long flags; | 138 | unsigned long flags; |
139 | 139 | ||
140 | raw_spin_lock_irqsave(&wakeupgen_lock, flags); | 140 | raw_spin_lock_irqsave(&wakeupgen_lock, flags); |
141 | _wakeupgen_clear(d->irq, irq_target_cpu[d->irq]); | 141 | _wakeupgen_clear(d->hwirq, irq_target_cpu[d->hwirq]); |
142 | raw_spin_unlock_irqrestore(&wakeupgen_lock, flags); | 142 | raw_spin_unlock_irqrestore(&wakeupgen_lock, flags); |
143 | } | 143 | } |
144 | 144 | ||
@@ -150,7 +150,7 @@ static void wakeupgen_unmask(struct irq_data *d) | |||
150 | unsigned long flags; | 150 | unsigned long flags; |
151 | 151 | ||
152 | raw_spin_lock_irqsave(&wakeupgen_lock, flags); | 152 | raw_spin_lock_irqsave(&wakeupgen_lock, flags); |
153 | _wakeupgen_set(d->irq, irq_target_cpu[d->irq]); | 153 | _wakeupgen_set(d->hwirq, irq_target_cpu[d->hwirq]); |
154 | raw_spin_unlock_irqrestore(&wakeupgen_lock, flags); | 154 | raw_spin_unlock_irqrestore(&wakeupgen_lock, flags); |
155 | } | 155 | } |
156 | 156 | ||
diff --git a/arch/arm/mach-omap2/omap4-common.c b/arch/arm/mach-omap2/omap4-common.c index 6cd3f3772ecf..95e171a055f3 100644 --- a/arch/arm/mach-omap2/omap4-common.c +++ b/arch/arm/mach-omap2/omap4-common.c | |||
@@ -22,6 +22,7 @@ | |||
22 | #include <linux/of_platform.h> | 22 | #include <linux/of_platform.h> |
23 | #include <linux/export.h> | 23 | #include <linux/export.h> |
24 | #include <linux/irqchip/arm-gic.h> | 24 | #include <linux/irqchip/arm-gic.h> |
25 | #include <linux/irqchip/irq-crossbar.h> | ||
25 | #include <linux/of_address.h> | 26 | #include <linux/of_address.h> |
26 | #include <linux/reboot.h> | 27 | #include <linux/reboot.h> |
27 | 28 | ||
@@ -288,5 +289,8 @@ void __init omap_gic_of_init(void) | |||
288 | 289 | ||
289 | skip_errata_init: | 290 | skip_errata_init: |
290 | omap_wakeupgen_init(); | 291 | omap_wakeupgen_init(); |
292 | #ifdef CONFIG_IRQ_CROSSBAR | ||
293 | irqcrossbar_init(); | ||
294 | #endif | ||
291 | irqchip_init(); | 295 | irqchip_init(); |
292 | } | 296 | } |
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 5194afb39e78..cb37e5777f18 100644 --- a/drivers/irqchip/Makefile +++ b/drivers/irqchip/Makefile | |||
@@ -26,3 +26,4 @@ obj-$(CONFIG_ARCH_VT8500) += irq-vt8500.o | |||
26 | obj-$(CONFIG_TB10X_IRQC) += irq-tb10x.o | 26 | obj-$(CONFIG_TB10X_IRQC) += irq-tb10x.o |
27 | obj-$(CONFIG_XTENSA) += irq-xtensa-pic.o | 27 | obj-$(CONFIG_XTENSA) += irq-xtensa-pic.o |
28 | obj-$(CONFIG_XTENSA_MX) += irq-xtensa-mx.o | 28 | obj-$(CONFIG_XTENSA_MX) += irq-xtensa-mx.o |
29 | 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/drivers/irqchip/irq-gic.c b/drivers/irqchip/irq-gic.c index 341c6016812d..07a7050841ec 100644 --- a/drivers/irqchip/irq-gic.c +++ b/drivers/irqchip/irq-gic.c | |||
@@ -824,16 +824,25 @@ static int gic_irq_domain_map(struct irq_domain *d, unsigned int irq, | |||
824 | irq_set_chip_and_handler(irq, &gic_chip, | 824 | irq_set_chip_and_handler(irq, &gic_chip, |
825 | handle_fasteoi_irq); | 825 | handle_fasteoi_irq); |
826 | set_irq_flags(irq, IRQF_VALID | IRQF_PROBE); | 826 | set_irq_flags(irq, IRQF_VALID | IRQF_PROBE); |
827 | |||
828 | gic_routable_irq_domain_ops->map(d, irq, hw); | ||
827 | } | 829 | } |
828 | irq_set_chip_data(irq, d->host_data); | 830 | irq_set_chip_data(irq, d->host_data); |
829 | return 0; | 831 | return 0; |
830 | } | 832 | } |
831 | 833 | ||
834 | static void gic_irq_domain_unmap(struct irq_domain *d, unsigned int irq) | ||
835 | { | ||
836 | gic_routable_irq_domain_ops->unmap(d, irq); | ||
837 | } | ||
838 | |||
832 | static int gic_irq_domain_xlate(struct irq_domain *d, | 839 | static int gic_irq_domain_xlate(struct irq_domain *d, |
833 | struct device_node *controller, | 840 | struct device_node *controller, |
834 | const u32 *intspec, unsigned int intsize, | 841 | const u32 *intspec, unsigned int intsize, |
835 | unsigned long *out_hwirq, unsigned int *out_type) | 842 | unsigned long *out_hwirq, unsigned int *out_type) |
836 | { | 843 | { |
844 | unsigned long ret = 0; | ||
845 | |||
837 | if (d->of_node != controller) | 846 | if (d->of_node != controller) |
838 | return -EINVAL; | 847 | return -EINVAL; |
839 | if (intsize < 3) | 848 | if (intsize < 3) |
@@ -843,11 +852,20 @@ static int gic_irq_domain_xlate(struct irq_domain *d, | |||
843 | *out_hwirq = intspec[1] + 16; | 852 | *out_hwirq = intspec[1] + 16; |
844 | 853 | ||
845 | /* For SPIs, we need to add 16 more to get the GIC irq ID number */ | 854 | /* For SPIs, we need to add 16 more to get the GIC irq ID number */ |
846 | if (!intspec[0]) | 855 | if (!intspec[0]) { |
847 | *out_hwirq += 16; | 856 | ret = gic_routable_irq_domain_ops->xlate(d, controller, |
857 | intspec, | ||
858 | intsize, | ||
859 | out_hwirq, | ||
860 | out_type); | ||
861 | |||
862 | if (IS_ERR_VALUE(ret)) | ||
863 | return ret; | ||
864 | } | ||
848 | 865 | ||
849 | *out_type = intspec[2] & IRQ_TYPE_SENSE_MASK; | 866 | *out_type = intspec[2] & IRQ_TYPE_SENSE_MASK; |
850 | return 0; | 867 | |
868 | return ret; | ||
851 | } | 869 | } |
852 | 870 | ||
853 | #ifdef CONFIG_SMP | 871 | #ifdef CONFIG_SMP |
@@ -871,9 +889,41 @@ static struct notifier_block gic_cpu_notifier = { | |||
871 | 889 | ||
872 | const struct irq_domain_ops gic_irq_domain_ops = { | 890 | const struct irq_domain_ops gic_irq_domain_ops = { |
873 | .map = gic_irq_domain_map, | 891 | .map = gic_irq_domain_map, |
892 | .unmap = gic_irq_domain_unmap, | ||
874 | .xlate = gic_irq_domain_xlate, | 893 | .xlate = gic_irq_domain_xlate, |
875 | }; | 894 | }; |
876 | 895 | ||
896 | /* Default functions for routable irq domain */ | ||
897 | static int gic_routable_irq_domain_map(struct irq_domain *d, unsigned int irq, | ||
898 | irq_hw_number_t hw) | ||
899 | { | ||
900 | return 0; | ||
901 | } | ||
902 | |||
903 | static void gic_routable_irq_domain_unmap(struct irq_domain *d, | ||
904 | unsigned int irq) | ||
905 | { | ||
906 | } | ||
907 | |||
908 | static int gic_routable_irq_domain_xlate(struct irq_domain *d, | ||
909 | struct device_node *controller, | ||
910 | const u32 *intspec, unsigned int intsize, | ||
911 | unsigned long *out_hwirq, | ||
912 | unsigned int *out_type) | ||
913 | { | ||
914 | *out_hwirq += 16; | ||
915 | return 0; | ||
916 | } | ||
917 | |||
918 | const struct irq_domain_ops gic_default_routable_irq_domain_ops = { | ||
919 | .map = gic_routable_irq_domain_map, | ||
920 | .unmap = gic_routable_irq_domain_unmap, | ||
921 | .xlate = gic_routable_irq_domain_xlate, | ||
922 | }; | ||
923 | |||
924 | const struct irq_domain_ops *gic_routable_irq_domain_ops = | ||
925 | &gic_default_routable_irq_domain_ops; | ||
926 | |||
877 | void __init gic_init_bases(unsigned int gic_nr, int irq_start, | 927 | void __init gic_init_bases(unsigned int gic_nr, int irq_start, |
878 | void __iomem *dist_base, void __iomem *cpu_base, | 928 | void __iomem *dist_base, void __iomem *cpu_base, |
879 | u32 percpu_offset, struct device_node *node) | 929 | u32 percpu_offset, struct device_node *node) |
@@ -881,6 +931,7 @@ void __init gic_init_bases(unsigned int gic_nr, int irq_start, | |||
881 | irq_hw_number_t hwirq_base; | 931 | irq_hw_number_t hwirq_base; |
882 | struct gic_chip_data *gic; | 932 | struct gic_chip_data *gic; |
883 | int gic_irqs, irq_base, i; | 933 | int gic_irqs, irq_base, i; |
934 | int nr_routable_irqs; | ||
884 | 935 | ||
885 | BUG_ON(gic_nr >= MAX_GIC_NR); | 936 | BUG_ON(gic_nr >= MAX_GIC_NR); |
886 | 937 | ||
@@ -946,14 +997,25 @@ void __init gic_init_bases(unsigned int gic_nr, int irq_start, | |||
946 | gic->gic_irqs = gic_irqs; | 997 | gic->gic_irqs = gic_irqs; |
947 | 998 | ||
948 | gic_irqs -= hwirq_base; /* calculate # of irqs to allocate */ | 999 | gic_irqs -= hwirq_base; /* calculate # of irqs to allocate */ |
949 | irq_base = irq_alloc_descs(irq_start, 16, gic_irqs, numa_node_id()); | 1000 | |
950 | if (IS_ERR_VALUE(irq_base)) { | 1001 | if (of_property_read_u32(node, "arm,routable-irqs", |
951 | WARN(1, "Cannot allocate irq_descs @ IRQ%d, assuming pre-allocated\n", | 1002 | &nr_routable_irqs)) { |
952 | irq_start); | 1003 | irq_base = irq_alloc_descs(irq_start, 16, gic_irqs, |
953 | irq_base = irq_start; | 1004 | numa_node_id()); |
1005 | if (IS_ERR_VALUE(irq_base)) { | ||
1006 | WARN(1, "Cannot allocate irq_descs @ IRQ%d, assuming pre-allocated\n", | ||
1007 | irq_start); | ||
1008 | irq_base = irq_start; | ||
1009 | } | ||
1010 | |||
1011 | gic->domain = irq_domain_add_legacy(node, gic_irqs, irq_base, | ||
1012 | hwirq_base, &gic_irq_domain_ops, gic); | ||
1013 | } else { | ||
1014 | gic->domain = irq_domain_add_linear(node, nr_routable_irqs, | ||
1015 | &gic_irq_domain_ops, | ||
1016 | gic); | ||
954 | } | 1017 | } |
955 | gic->domain = irq_domain_add_legacy(node, gic_irqs, irq_base, | 1018 | |
956 | hwirq_base, &gic_irq_domain_ops, gic); | ||
957 | if (WARN_ON(!gic->domain)) | 1019 | if (WARN_ON(!gic->domain)) |
958 | return; | 1020 | return; |
959 | 1021 | ||
diff --git a/include/linux/irqchip/arm-gic.h b/include/linux/irqchip/arm-gic.h index 0ceb389dba6c..7ed92d0560d5 100644 --- a/include/linux/irqchip/arm-gic.h +++ b/include/linux/irqchip/arm-gic.h | |||
@@ -93,6 +93,11 @@ int gic_get_cpu_id(unsigned int cpu); | |||
93 | void gic_migrate_target(unsigned int new_cpu_id); | 93 | void gic_migrate_target(unsigned int new_cpu_id); |
94 | unsigned long gic_get_sgir_physaddr(void); | 94 | unsigned long gic_get_sgir_physaddr(void); |
95 | 95 | ||
96 | extern const struct irq_domain_ops *gic_routable_irq_domain_ops; | ||
97 | static inline void __init register_routable_domain_ops | ||
98 | (const struct irq_domain_ops *ops) | ||
99 | { | ||
100 | gic_routable_irq_domain_ops = ops; | ||
101 | } | ||
96 | #endif /* __ASSEMBLY */ | 102 | #endif /* __ASSEMBLY */ |
97 | |||
98 | #endif | 103 | #endif |
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); | ||