aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/irqchip
diff options
context:
space:
mode:
authorMarc Zyngier <marc.zyngier@arm.com>2015-03-11 11:43:44 -0400
committerJason Cooper <jason@lakedaemon.net>2015-03-14 20:55:24 -0400
commit783d31863fb826f1a3754a2d5959a022a1b12d54 (patch)
tree4610918642e934e9b3d0260a9d33bd569b89d55b /drivers/irqchip
parent08b55e2a9208e4841a17c9d9c2c454986392977d (diff)
irqchip: crossbar: Convert dra7 crossbar to stacked domains
Support for the TI crossbar used on the DRA7 family of chips is implemented as an ugly hack on the side of the GIC. Converting it to stacked domains makes it slightly more palatable, as it results in a cleanup. Unfortunately, as the DT bindings failed to acknowledge the fact that this is actually yet another interrupt controller (the third, actually), we have yet another breakage. Oh well. Acked-by: Tony Lindgren <tony@atomide.com> Signed-off-by: Marc Zyngier <marc.zyngier@arm.com> Link: https://lkml.kernel.org/r/1426088629-15377-3-git-send-email-marc.zyngier@arm.com Signed-off-by: Jason Cooper <jason@lakedaemon.net>
Diffstat (limited to 'drivers/irqchip')
-rw-r--r--drivers/irqchip/irq-crossbar.c210
1 files changed, 123 insertions, 87 deletions
diff --git a/drivers/irqchip/irq-crossbar.c b/drivers/irqchip/irq-crossbar.c
index bbbaf5de65d2..692fe2bc8197 100644
--- a/drivers/irqchip/irq-crossbar.c
+++ b/drivers/irqchip/irq-crossbar.c
@@ -11,11 +11,12 @@
11 */ 11 */
12#include <linux/err.h> 12#include <linux/err.h>
13#include <linux/io.h> 13#include <linux/io.h>
14#include <linux/irqdomain.h>
14#include <linux/of_address.h> 15#include <linux/of_address.h>
15#include <linux/of_irq.h> 16#include <linux/of_irq.h>
16#include <linux/slab.h> 17#include <linux/slab.h>
17#include <linux/irqchip/arm-gic.h> 18
18#include <linux/irqchip/irq-crossbar.h> 19#include "irqchip.h"
19 20
20#define IRQ_FREE -1 21#define IRQ_FREE -1
21#define IRQ_RESERVED -2 22#define IRQ_RESERVED -2
@@ -24,6 +25,7 @@
24 25
25/** 26/**
26 * struct crossbar_device - crossbar device description 27 * struct crossbar_device - crossbar device description
28 * @lock: spinlock serializing access to @irq_map
27 * @int_max: maximum number of supported interrupts 29 * @int_max: maximum number of supported interrupts
28 * @safe_map: safe default value to initialize the crossbar 30 * @safe_map: safe default value to initialize the crossbar
29 * @max_crossbar_sources: Maximum number of crossbar sources 31 * @max_crossbar_sources: Maximum number of crossbar sources
@@ -33,6 +35,7 @@
33 * @write: register write function pointer 35 * @write: register write function pointer
34 */ 36 */
35struct crossbar_device { 37struct crossbar_device {
38 raw_spinlock_t lock;
36 uint int_max; 39 uint int_max;
37 uint safe_map; 40 uint safe_map;
38 uint max_crossbar_sources; 41 uint max_crossbar_sources;
@@ -44,72 +47,101 @@ struct crossbar_device {
44 47
45static struct crossbar_device *cb; 48static struct crossbar_device *cb;
46 49
47static inline void crossbar_writel(int irq_no, int cb_no) 50static void crossbar_writel(int irq_no, int cb_no)
48{ 51{
49 writel(cb_no, cb->crossbar_base + cb->register_offsets[irq_no]); 52 writel(cb_no, cb->crossbar_base + cb->register_offsets[irq_no]);
50} 53}
51 54
52static inline void crossbar_writew(int irq_no, int cb_no) 55static void crossbar_writew(int irq_no, int cb_no)
53{ 56{
54 writew(cb_no, cb->crossbar_base + cb->register_offsets[irq_no]); 57 writew(cb_no, cb->crossbar_base + cb->register_offsets[irq_no]);
55} 58}
56 59
57static inline void crossbar_writeb(int irq_no, int cb_no) 60static void crossbar_writeb(int irq_no, int cb_no)
58{ 61{
59 writeb(cb_no, cb->crossbar_base + cb->register_offsets[irq_no]); 62 writeb(cb_no, cb->crossbar_base + cb->register_offsets[irq_no]);
60} 63}
61 64
62static inline int get_prev_map_irq(int cb_no) 65static struct irq_chip crossbar_chip = {
63{ 66 .name = "CBAR",
64 int i; 67 .irq_eoi = irq_chip_eoi_parent,
65 68 .irq_mask = irq_chip_mask_parent,
66 for (i = cb->int_max - 1; i >= 0; i--) 69 .irq_unmask = irq_chip_unmask_parent,
67 if (cb->irq_map[i] == cb_no) 70 .irq_retrigger = irq_chip_retrigger_hierarchy,
68 return i; 71 .irq_set_wake = irq_chip_set_wake_parent,
69 72#ifdef CONFIG_SMP
70 return -ENODEV; 73 .irq_set_affinity = irq_chip_set_affinity_parent,
71} 74#endif
75};
72 76
73static inline int allocate_free_irq(int cb_no) 77static int allocate_gic_irq(struct irq_domain *domain, unsigned virq,
78 irq_hw_number_t hwirq)
74{ 79{
80 struct of_phandle_args args;
75 int i; 81 int i;
82 int err;
76 83
84 raw_spin_lock(&cb->lock);
77 for (i = cb->int_max - 1; i >= 0; i--) { 85 for (i = cb->int_max - 1; i >= 0; i--) {
78 if (cb->irq_map[i] == IRQ_FREE) { 86 if (cb->irq_map[i] == IRQ_FREE) {
79 cb->irq_map[i] = cb_no; 87 cb->irq_map[i] = hwirq;
80 return i; 88 break;
81 } 89 }
82 } 90 }
91 raw_spin_unlock(&cb->lock);
83 92
84 return -ENODEV; 93 if (i < 0)
85} 94 return -ENODEV;
86 95
87static inline bool needs_crossbar_write(irq_hw_number_t hw) 96 args.np = domain->parent->of_node;
88{ 97 args.args_count = 3;
89 int cb_no; 98 args.args[0] = 0; /* SPI */
99 args.args[1] = i;
100 args.args[2] = IRQ_TYPE_LEVEL_HIGH;
90 101
91 if (hw > GIC_IRQ_START) { 102 err = irq_domain_alloc_irqs_parent(domain, virq, 1, &args);
92 cb_no = cb->irq_map[hw - GIC_IRQ_START]; 103 if (err)
93 if (cb_no != IRQ_RESERVED && cb_no != IRQ_SKIP) 104 cb->irq_map[i] = IRQ_FREE;
94 return true; 105 else
95 } 106 cb->write(i, hwirq);
96 107
97 return false; 108 return err;
98} 109}
99 110
100static int crossbar_domain_map(struct irq_domain *d, unsigned int irq, 111static int crossbar_domain_alloc(struct irq_domain *d, unsigned int virq,
101 irq_hw_number_t hw) 112 unsigned int nr_irqs, void *data)
102{ 113{
103 if (needs_crossbar_write(hw)) 114 struct of_phandle_args *args = data;
104 cb->write(hw - GIC_IRQ_START, cb->irq_map[hw - GIC_IRQ_START]); 115 irq_hw_number_t hwirq;
116 int i;
117
118 if (args->args_count != 3)
119 return -EINVAL; /* Not GIC compliant */
120 if (args->args[0] != 0)
121 return -EINVAL; /* No PPI should point to this domain */
122
123 hwirq = args->args[1];
124 if ((hwirq + nr_irqs) > cb->max_crossbar_sources)
125 return -EINVAL; /* Can't deal with this */
126
127 for (i = 0; i < nr_irqs; i++) {
128 int err = allocate_gic_irq(d, virq + i, hwirq + i);
129
130 if (err)
131 return err;
132
133 irq_domain_set_hwirq_and_chip(d, virq + i, hwirq + i,
134 &crossbar_chip, NULL);
135 }
105 136
106 return 0; 137 return 0;
107} 138}
108 139
109/** 140/**
110 * crossbar_domain_unmap - unmap a crossbar<->irq connection 141 * crossbar_domain_free - unmap/free a crossbar<->irq connection
111 * @d: domain of irq to unmap 142 * @domain: domain of irq to unmap
112 * @irq: virq number 143 * @virq: virq number
144 * @nr_irqs: number of irqs to free
113 * 145 *
114 * We do not maintain a use count of total number of map/unmap 146 * 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 147 * calls for a particular irq to find out if a irq can be really
@@ -117,14 +149,20 @@ static int crossbar_domain_map(struct irq_domain *d, unsigned int irq,
117 * after which irq is anyways unusable. So an explicit map has to be called 149 * after which irq is anyways unusable. So an explicit map has to be called
118 * after that. 150 * after that.
119 */ 151 */
120static void crossbar_domain_unmap(struct irq_domain *d, unsigned int irq) 152static void crossbar_domain_free(struct irq_domain *domain, unsigned int virq,
153 unsigned int nr_irqs)
121{ 154{
122 irq_hw_number_t hw = irq_get_irq_data(irq)->hwirq; 155 int i;
123 156
124 if (needs_crossbar_write(hw)) { 157 raw_spin_lock(&cb->lock);
125 cb->irq_map[hw - GIC_IRQ_START] = IRQ_FREE; 158 for (i = 0; i < nr_irqs; i++) {
126 cb->write(hw - GIC_IRQ_START, cb->safe_map); 159 struct irq_data *d = irq_domain_get_irq_data(domain, virq + i);
160
161 irq_domain_reset_irq_data(d);
162 cb->irq_map[d->hwirq] = IRQ_FREE;
163 cb->write(d->hwirq, cb->safe_map);
127 } 164 }
165 raw_spin_unlock(&cb->lock);
128} 166}
129 167
130static int crossbar_domain_xlate(struct irq_domain *d, 168static int crossbar_domain_xlate(struct irq_domain *d,
@@ -133,44 +171,22 @@ static int crossbar_domain_xlate(struct irq_domain *d,
133 unsigned long *out_hwirq, 171 unsigned long *out_hwirq,
134 unsigned int *out_type) 172 unsigned int *out_type)
135{ 173{
136 int ret; 174 if (d->of_node != controller)
137 int req_num = intspec[1]; 175 return -EINVAL; /* Shouldn't happen, really... */
138 int direct_map_num; 176 if (intsize != 3)
139 177 return -EINVAL; /* Not GIC compliant */
140 if (req_num >= cb->max_crossbar_sources) { 178 if (intspec[0] != 0)
141 direct_map_num = req_num - cb->max_crossbar_sources; 179 return -EINVAL; /* No PPI should point to this domain */
142 if (direct_map_num < cb->int_max) { 180
143 ret = cb->irq_map[direct_map_num]; 181 *out_hwirq = intspec[1];
144 if (ret == IRQ_RESERVED || ret == IRQ_SKIP) { 182 *out_type = intspec[2];
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 }
155
156 ret = get_prev_map_irq(req_num);
157 if (ret >= 0)
158 goto found;
159
160 ret = allocate_free_irq(req_num);
161
162 if (ret < 0)
163 return ret;
164
165found:
166 *out_hwirq = ret + GIC_IRQ_START;
167 return 0; 183 return 0;
168} 184}
169 185
170static const struct irq_domain_ops routable_irq_domain_ops = { 186static const struct irq_domain_ops crossbar_domain_ops = {
171 .map = crossbar_domain_map, 187 .alloc = crossbar_domain_alloc,
172 .unmap = crossbar_domain_unmap, 188 .free = crossbar_domain_free,
173 .xlate = crossbar_domain_xlate 189 .xlate = crossbar_domain_xlate,
174}; 190};
175 191
176static int __init crossbar_of_init(struct device_node *node) 192static int __init crossbar_of_init(struct device_node *node)
@@ -293,7 +309,8 @@ static int __init crossbar_of_init(struct device_node *node)
293 cb->write(i, cb->safe_map); 309 cb->write(i, cb->safe_map);
294 } 310 }
295 311
296 register_routable_domain_ops(&routable_irq_domain_ops); 312 raw_spin_lock_init(&cb->lock);
313
297 return 0; 314 return 0;
298 315
299err_reg_offset: 316err_reg_offset:
@@ -309,18 +326,37 @@ err_cb:
309 return ret; 326 return ret;
310} 327}
311 328
312static const struct of_device_id crossbar_match[] __initconst = { 329static int __init irqcrossbar_init(struct device_node *node,
313 { .compatible = "ti,irq-crossbar" }, 330 struct device_node *parent)
314 {}
315};
316
317int __init irqcrossbar_init(void)
318{ 331{
319 struct device_node *np; 332 struct irq_domain *parent_domain, *domain;
320 np = of_find_matching_node(NULL, crossbar_match); 333 int err;
321 if (!np) 334
335 if (!parent) {
336 pr_err("%s: no parent, giving up\n", node->full_name);
322 return -ENODEV; 337 return -ENODEV;
338 }
339
340 parent_domain = irq_find_host(parent);
341 if (!parent_domain) {
342 pr_err("%s: unable to obtain parent domain\n", node->full_name);
343 return -ENXIO;
344 }
345
346 err = crossbar_of_init(node);
347 if (err)
348 return err;
349
350 domain = irq_domain_add_hierarchy(parent_domain, 0,
351 cb->max_crossbar_sources,
352 node, &crossbar_domain_ops,
353 NULL);
354 if (!domain) {
355 pr_err("%s: failed to allocated domain\n", node->full_name);
356 return -ENOMEM;
357 }
323 358
324 crossbar_of_init(np);
325 return 0; 359 return 0;
326} 360}
361
362IRQCHIP_DECLARE(ti_irqcrossbar, "ti,irq-crossbar", irqcrossbar_init);