aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/irqchip
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/irqchip')
-rw-r--r--drivers/irqchip/irq-crossbar.c168
1 files changed, 143 insertions, 25 deletions
diff --git a/drivers/irqchip/irq-crossbar.c b/drivers/irqchip/irq-crossbar.c
index 3d15d16a7088..85c2985d8bcb 100644
--- a/drivers/irqchip/irq-crossbar.c
+++ b/drivers/irqchip/irq-crossbar.c
@@ -15,22 +15,31 @@
15#include <linux/of_irq.h> 15#include <linux/of_irq.h>
16#include <linux/slab.h> 16#include <linux/slab.h>
17#include <linux/irqchip/arm-gic.h> 17#include <linux/irqchip/arm-gic.h>
18#include <linux/irqchip/irq-crossbar.h>
18 19
19#define IRQ_FREE -1 20#define IRQ_FREE -1
21#define IRQ_RESERVED -2
22#define IRQ_SKIP -3
20#define GIC_IRQ_START 32 23#define GIC_IRQ_START 32
21 24
22/* 25/**
26 * struct crossbar_device - crossbar device description
23 * @int_max: maximum number of supported interrupts 27 * @int_max: maximum number of supported interrupts
28 * @safe_map: safe default value to initialize the crossbar
29 * @max_crossbar_sources: Maximum number of crossbar sources
24 * @irq_map: array of interrupts to crossbar number mapping 30 * @irq_map: array of interrupts to crossbar number mapping
25 * @crossbar_base: crossbar base address 31 * @crossbar_base: crossbar base address
26 * @register_offsets: offsets for each irq number 32 * @register_offsets: offsets for each irq number
33 * @write: register write function pointer
27 */ 34 */
28struct crossbar_device { 35struct crossbar_device {
29 uint int_max; 36 uint int_max;
37 uint safe_map;
38 uint max_crossbar_sources;
30 uint *irq_map; 39 uint *irq_map;
31 void __iomem *crossbar_base; 40 void __iomem *crossbar_base;
32 int *register_offsets; 41 int *register_offsets;
33 void (*write) (int, int); 42 void (*write)(int, int);
34}; 43};
35 44
36static struct crossbar_device *cb; 45static struct crossbar_device *cb;
@@ -50,11 +59,22 @@ static inline void crossbar_writeb(int irq_no, int cb_no)
50 writeb(cb_no, cb->crossbar_base + cb->register_offsets[irq_no]); 59 writeb(cb_no, cb->crossbar_base + cb->register_offsets[irq_no]);
51} 60}
52 61
62static inline int get_prev_map_irq(int cb_no)
63{
64 int i;
65
66 for (i = cb->int_max - 1; i >= 0; i--)
67 if (cb->irq_map[i] == cb_no)
68 return i;
69
70 return -ENODEV;
71}
72
53static inline int allocate_free_irq(int cb_no) 73static inline int allocate_free_irq(int cb_no)
54{ 74{
55 int i; 75 int i;
56 76
57 for (i = 0; i < cb->int_max; i++) { 77 for (i = cb->int_max - 1; i >= 0; i--) {
58 if (cb->irq_map[i] == IRQ_FREE) { 78 if (cb->irq_map[i] == IRQ_FREE) {
59 cb->irq_map[i] = cb_no; 79 cb->irq_map[i] = cb_no;
60 return i; 80 return i;
@@ -64,19 +84,47 @@ static inline int allocate_free_irq(int cb_no)
64 return -ENODEV; 84 return -ENODEV;
65} 85}
66 86
87static inline bool needs_crossbar_write(irq_hw_number_t hw)
88{
89 int cb_no;
90
91 if (hw > GIC_IRQ_START) {
92 cb_no = cb->irq_map[hw - GIC_IRQ_START];
93 if (cb_no != IRQ_RESERVED && cb_no != IRQ_SKIP)
94 return true;
95 }
96
97 return false;
98}
99
67static int crossbar_domain_map(struct irq_domain *d, unsigned int irq, 100static int crossbar_domain_map(struct irq_domain *d, unsigned int irq,
68 irq_hw_number_t hw) 101 irq_hw_number_t hw)
69{ 102{
70 cb->write(hw - GIC_IRQ_START, cb->irq_map[hw - GIC_IRQ_START]); 103 if (needs_crossbar_write(hw))
104 cb->write(hw - GIC_IRQ_START, cb->irq_map[hw - GIC_IRQ_START]);
105
71 return 0; 106 return 0;
72} 107}
73 108
109/**
110 * crossbar_domain_unmap - unmap a crossbar<->irq connection
111 * @d: domain of irq to unmap
112 * @irq: virq number
113 *
114 * We do not maintain a use count of total number of map/unmap
115 * calls for a particular irq to find out if a irq can be really
116 * unmapped. This is because unmap is called during irq_dispose_mapping(irq),
117 * after which irq is anyways unusable. So an explicit map has to be called
118 * after that.
119 */
74static void crossbar_domain_unmap(struct irq_domain *d, unsigned int irq) 120static void crossbar_domain_unmap(struct irq_domain *d, unsigned int irq)
75{ 121{
76 irq_hw_number_t hw = irq_get_irq_data(irq)->hwirq; 122 irq_hw_number_t hw = irq_get_irq_data(irq)->hwirq;
77 123
78 if (hw > GIC_IRQ_START) 124 if (needs_crossbar_write(hw)) {
79 cb->irq_map[hw - GIC_IRQ_START] = IRQ_FREE; 125 cb->irq_map[hw - GIC_IRQ_START] = IRQ_FREE;
126 cb->write(hw - GIC_IRQ_START, cb->safe_map);
127 }
80} 128}
81 129
82static int crossbar_domain_xlate(struct irq_domain *d, 130static int crossbar_domain_xlate(struct irq_domain *d,
@@ -85,18 +133,41 @@ static int crossbar_domain_xlate(struct irq_domain *d,
85 unsigned long *out_hwirq, 133 unsigned long *out_hwirq,
86 unsigned int *out_type) 134 unsigned int *out_type)
87{ 135{
88 unsigned long ret; 136 int ret;
137 int req_num = intspec[1];
138 int direct_map_num;
139
140 if (req_num >= cb->max_crossbar_sources) {
141 direct_map_num = req_num - cb->max_crossbar_sources;
142 if (direct_map_num < cb->int_max) {
143 ret = cb->irq_map[direct_map_num];
144 if (ret == IRQ_RESERVED || ret == IRQ_SKIP) {
145 /* We use the interrupt num as h/w irq num */
146 ret = direct_map_num;
147 goto found;
148 }
149 }
150
151 pr_err("%s: requested crossbar number %d > max %d\n",
152 __func__, req_num, cb->max_crossbar_sources);
153 return -EINVAL;
154 }
89 155
90 ret = allocate_free_irq(intspec[1]); 156 ret = get_prev_map_irq(req_num);
157 if (ret >= 0)
158 goto found;
91 159
92 if (IS_ERR_VALUE(ret)) 160 ret = allocate_free_irq(req_num);
161
162 if (ret < 0)
93 return ret; 163 return ret;
94 164
165found:
95 *out_hwirq = ret + GIC_IRQ_START; 166 *out_hwirq = ret + GIC_IRQ_START;
96 return 0; 167 return 0;
97} 168}
98 169
99const struct irq_domain_ops routable_irq_domain_ops = { 170static const struct irq_domain_ops routable_irq_domain_ops = {
100 .map = crossbar_domain_map, 171 .map = crossbar_domain_map,
101 .unmap = crossbar_domain_unmap, 172 .unmap = crossbar_domain_unmap,
102 .xlate = crossbar_domain_xlate 173 .xlate = crossbar_domain_xlate
@@ -104,22 +175,36 @@ const struct irq_domain_ops routable_irq_domain_ops = {
104 175
105static int __init crossbar_of_init(struct device_node *node) 176static int __init crossbar_of_init(struct device_node *node)
106{ 177{
107 int i, size, max, reserved = 0, entry; 178 int i, size, max = 0, reserved = 0, entry;
108 const __be32 *irqsr; 179 const __be32 *irqsr;
180 int ret = -ENOMEM;
109 181
110 cb = kzalloc(sizeof(*cb), GFP_KERNEL); 182 cb = kzalloc(sizeof(*cb), GFP_KERNEL);
111 183
112 if (!cb) 184 if (!cb)
113 return -ENOMEM; 185 return ret;
114 186
115 cb->crossbar_base = of_iomap(node, 0); 187 cb->crossbar_base = of_iomap(node, 0);
116 if (!cb->crossbar_base) 188 if (!cb->crossbar_base)
117 goto err1; 189 goto err_cb;
190
191 of_property_read_u32(node, "ti,max-crossbar-sources",
192 &cb->max_crossbar_sources);
193 if (!cb->max_crossbar_sources) {
194 pr_err("missing 'ti,max-crossbar-sources' property\n");
195 ret = -EINVAL;
196 goto err_base;
197 }
118 198
119 of_property_read_u32(node, "ti,max-irqs", &max); 199 of_property_read_u32(node, "ti,max-irqs", &max);
120 cb->irq_map = kzalloc(max * sizeof(int), GFP_KERNEL); 200 if (!max) {
201 pr_err("missing 'ti,max-irqs' property\n");
202 ret = -EINVAL;
203 goto err_base;
204 }
205 cb->irq_map = kcalloc(max, sizeof(int), GFP_KERNEL);
121 if (!cb->irq_map) 206 if (!cb->irq_map)
122 goto err2; 207 goto err_base;
123 208
124 cb->int_max = max; 209 cb->int_max = max;
125 210
@@ -137,15 +222,35 @@ static int __init crossbar_of_init(struct device_node *node)
137 i, &entry); 222 i, &entry);
138 if (entry > max) { 223 if (entry > max) {
139 pr_err("Invalid reserved entry\n"); 224 pr_err("Invalid reserved entry\n");
140 goto err3; 225 ret = -EINVAL;
226 goto err_irq_map;
227 }
228 cb->irq_map[entry] = IRQ_RESERVED;
229 }
230 }
231
232 /* Skip irqs hardwired to bypass the crossbar */
233 irqsr = of_get_property(node, "ti,irqs-skip", &size);
234 if (irqsr) {
235 size /= sizeof(__be32);
236
237 for (i = 0; i < size; i++) {
238 of_property_read_u32_index(node,
239 "ti,irqs-skip",
240 i, &entry);
241 if (entry > max) {
242 pr_err("Invalid skip entry\n");
243 ret = -EINVAL;
244 goto err_irq_map;
141 } 245 }
142 cb->irq_map[entry] = 0; 246 cb->irq_map[entry] = IRQ_SKIP;
143 } 247 }
144 } 248 }
145 249
146 cb->register_offsets = kzalloc(max * sizeof(int), GFP_KERNEL); 250
251 cb->register_offsets = kcalloc(max, sizeof(int), GFP_KERNEL);
147 if (!cb->register_offsets) 252 if (!cb->register_offsets)
148 goto err3; 253 goto err_irq_map;
149 254
150 of_property_read_u32(node, "ti,reg-size", &size); 255 of_property_read_u32(node, "ti,reg-size", &size);
151 256
@@ -161,7 +266,8 @@ static int __init crossbar_of_init(struct device_node *node)
161 break; 266 break;
162 default: 267 default:
163 pr_err("Invalid reg-size property\n"); 268 pr_err("Invalid reg-size property\n");
164 goto err4; 269 ret = -EINVAL;
270 goto err_reg_offset;
165 break; 271 break;
166 } 272 }
167 273
@@ -170,25 +276,37 @@ static int __init crossbar_of_init(struct device_node *node)
170 * reserved irqs. so find and store the offsets once. 276 * reserved irqs. so find and store the offsets once.
171 */ 277 */
172 for (i = 0; i < max; i++) { 278 for (i = 0; i < max; i++) {
173 if (!cb->irq_map[i]) 279 if (cb->irq_map[i] == IRQ_RESERVED)
174 continue; 280 continue;
175 281
176 cb->register_offsets[i] = reserved; 282 cb->register_offsets[i] = reserved;
177 reserved += size; 283 reserved += size;
178 } 284 }
179 285
286 of_property_read_u32(node, "ti,irqs-safe-map", &cb->safe_map);
287 /* Initialize the crossbar with safe map to start with */
288 for (i = 0; i < max; i++) {
289 if (cb->irq_map[i] == IRQ_RESERVED ||
290 cb->irq_map[i] == IRQ_SKIP)
291 continue;
292
293 cb->write(i, cb->safe_map);
294 }
295
180 register_routable_domain_ops(&routable_irq_domain_ops); 296 register_routable_domain_ops(&routable_irq_domain_ops);
181 return 0; 297 return 0;
182 298
183err4: 299err_reg_offset:
184 kfree(cb->register_offsets); 300 kfree(cb->register_offsets);
185err3: 301err_irq_map:
186 kfree(cb->irq_map); 302 kfree(cb->irq_map);
187err2: 303err_base:
188 iounmap(cb->crossbar_base); 304 iounmap(cb->crossbar_base);
189err1: 305err_cb:
190 kfree(cb); 306 kfree(cb);
191 return -ENOMEM; 307
308 cb = NULL;
309 return ret;
192} 310}
193 311
194static const struct of_device_id crossbar_match[] __initconst = { 312static const struct of_device_id crossbar_match[] __initconst = {