diff options
Diffstat (limited to 'drivers')
-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 |
4 files changed, 289 insertions, 10 deletions
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 | ||