documentation: dt: omap: crossbar: Add description for interrupt consumer
[deliverable/linux.git] / drivers / irqchip / irq-crossbar.c
CommitLineData
96ca848e
S
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>
4dbf45e3 18#include <linux/irqchip/irq-crossbar.h>
96ca848e
S
19
20#define IRQ_FREE -1
1d50d2ce 21#define IRQ_RESERVED -2
64e0f8ba 22#define IRQ_SKIP -3
96ca848e
S
23#define GIC_IRQ_START 32
24
e30ef8ab
NM
25/**
26 * struct crossbar_device - crossbar device description
96ca848e 27 * @int_max: maximum number of supported interrupts
a35057d1 28 * @safe_map: safe default value to initialize the crossbar
2f7d2fb7 29 * @max_crossbar_sources: Maximum number of crossbar sources
96ca848e
S
30 * @irq_map: array of interrupts to crossbar number mapping
31 * @crossbar_base: crossbar base address
32 * @register_offsets: offsets for each irq number
e30ef8ab 33 * @write: register write function pointer
96ca848e
S
34 */
35struct crossbar_device {
36 uint int_max;
a35057d1 37 uint safe_map;
2f7d2fb7 38 uint max_crossbar_sources;
96ca848e
S
39 uint *irq_map;
40 void __iomem *crossbar_base;
41 int *register_offsets;
a35057d1 42 void (*write)(int, int);
96ca848e
S
43};
44
45static struct crossbar_device *cb;
46
47static inline void crossbar_writel(int irq_no, int cb_no)
48{
49 writel(cb_no, cb->crossbar_base + cb->register_offsets[irq_no]);
50}
51
52static inline void crossbar_writew(int irq_no, int cb_no)
53{
54 writew(cb_no, cb->crossbar_base + cb->register_offsets[irq_no]);
55}
56
57static inline void crossbar_writeb(int irq_no, int cb_no)
58{
59 writeb(cb_no, cb->crossbar_base + cb->register_offsets[irq_no]);
60}
61
6f16fc87
NM
62static inline int get_prev_map_irq(int cb_no)
63{
64 int i;
65
ddee0fb4 66 for (i = cb->int_max - 1; i >= 0; i--)
6f16fc87
NM
67 if (cb->irq_map[i] == cb_no)
68 return i;
69
70 return -ENODEV;
71}
72
96ca848e
S
73static inline int allocate_free_irq(int cb_no)
74{
75 int i;
76
ddee0fb4 77 for (i = cb->int_max - 1; i >= 0; i--) {
96ca848e
S
78 if (cb->irq_map[i] == IRQ_FREE) {
79 cb->irq_map[i] = cb_no;
80 return i;
81 }
82 }
83
84 return -ENODEV;
85}
86
29918b67
NM
87static inline bool needs_crossbar_write(irq_hw_number_t hw)
88{
89 if (hw > GIC_IRQ_START)
90 return true;
91
92 return false;
93}
94
96ca848e
S
95static int crossbar_domain_map(struct irq_domain *d, unsigned int irq,
96 irq_hw_number_t hw)
97{
29918b67
NM
98 if (needs_crossbar_write(hw))
99 cb->write(hw - GIC_IRQ_START, cb->irq_map[hw - GIC_IRQ_START]);
100
96ca848e
S
101 return 0;
102}
103
8b09a45d
S
104/**
105 * crossbar_domain_unmap - unmap a crossbar<->irq connection
106 * @d: domain of irq to unmap
107 * @irq: virq number
108 *
109 * We do not maintain a use count of total number of map/unmap
110 * calls for a particular irq to find out if a irq can be really
111 * unmapped. This is because unmap is called during irq_dispose_mapping(irq),
112 * after which irq is anyways unusable. So an explicit map has to be called
113 * after that.
114 */
96ca848e
S
115static void crossbar_domain_unmap(struct irq_domain *d, unsigned int irq)
116{
117 irq_hw_number_t hw = irq_get_irq_data(irq)->hwirq;
118
29918b67 119 if (needs_crossbar_write(hw)) {
96ca848e 120 cb->irq_map[hw - GIC_IRQ_START] = IRQ_FREE;
a35057d1
NM
121 cb->write(hw - GIC_IRQ_START, cb->safe_map);
122 }
96ca848e
S
123}
124
125static int crossbar_domain_xlate(struct irq_domain *d,
126 struct device_node *controller,
127 const u32 *intspec, unsigned int intsize,
128 unsigned long *out_hwirq,
129 unsigned int *out_type)
130{
d4922a95 131 int ret;
2f7d2fb7 132 int req_num = intspec[1];
96ca848e 133
2f7d2fb7
NM
134 if (req_num >= cb->max_crossbar_sources) {
135 pr_err("%s: requested crossbar number %d > max %d\n",
136 __func__, req_num, cb->max_crossbar_sources);
137 return -EINVAL;
138 }
139
140 ret = get_prev_map_irq(req_num);
d4922a95 141 if (ret >= 0)
6f16fc87
NM
142 goto found;
143
2f7d2fb7 144 ret = allocate_free_irq(req_num);
96ca848e 145
d4922a95 146 if (ret < 0)
96ca848e
S
147 return ret;
148
6f16fc87 149found:
96ca848e
S
150 *out_hwirq = ret + GIC_IRQ_START;
151 return 0;
152}
153
4dbf45e3 154static const struct irq_domain_ops routable_irq_domain_ops = {
96ca848e
S
155 .map = crossbar_domain_map,
156 .unmap = crossbar_domain_unmap,
157 .xlate = crossbar_domain_xlate
158};
159
160static int __init crossbar_of_init(struct device_node *node)
161{
edb442de 162 int i, size, max = 0, reserved = 0, entry;
96ca848e 163 const __be32 *irqsr;
edb442de 164 int ret = -ENOMEM;
96ca848e 165
3894e9e8 166 cb = kzalloc(sizeof(*cb), GFP_KERNEL);
96ca848e
S
167
168 if (!cb)
edb442de 169 return ret;
96ca848e
S
170
171 cb->crossbar_base = of_iomap(node, 0);
172 if (!cb->crossbar_base)
3c44d515 173 goto err_cb;
96ca848e 174
2f7d2fb7
NM
175 of_property_read_u32(node, "ti,max-crossbar-sources",
176 &cb->max_crossbar_sources);
177 if (!cb->max_crossbar_sources) {
178 pr_err("missing 'ti,max-crossbar-sources' property\n");
179 ret = -EINVAL;
180 goto err_base;
181 }
182
96ca848e 183 of_property_read_u32(node, "ti,max-irqs", &max);
edb442de
NM
184 if (!max) {
185 pr_err("missing 'ti,max-irqs' property\n");
186 ret = -EINVAL;
3c44d515 187 goto err_base;
edb442de 188 }
4dbf45e3 189 cb->irq_map = kcalloc(max, sizeof(int), GFP_KERNEL);
96ca848e 190 if (!cb->irq_map)
3c44d515 191 goto err_base;
96ca848e
S
192
193 cb->int_max = max;
194
195 for (i = 0; i < max; i++)
196 cb->irq_map[i] = IRQ_FREE;
197
198 /* Get and mark reserved irqs */
199 irqsr = of_get_property(node, "ti,irqs-reserved", &size);
200 if (irqsr) {
201 size /= sizeof(__be32);
202
203 for (i = 0; i < size; i++) {
204 of_property_read_u32_index(node,
205 "ti,irqs-reserved",
206 i, &entry);
207 if (entry > max) {
208 pr_err("Invalid reserved entry\n");
edb442de 209 ret = -EINVAL;
3c44d515 210 goto err_irq_map;
96ca848e 211 }
1d50d2ce 212 cb->irq_map[entry] = IRQ_RESERVED;
96ca848e
S
213 }
214 }
215
64e0f8ba
NM
216 /* Skip irqs hardwired to bypass the crossbar */
217 irqsr = of_get_property(node, "ti,irqs-skip", &size);
218 if (irqsr) {
219 size /= sizeof(__be32);
220
221 for (i = 0; i < size; i++) {
222 of_property_read_u32_index(node,
223 "ti,irqs-skip",
224 i, &entry);
225 if (entry > max) {
226 pr_err("Invalid skip entry\n");
227 ret = -EINVAL;
3c44d515 228 goto err_irq_map;
64e0f8ba
NM
229 }
230 cb->irq_map[entry] = IRQ_SKIP;
231 }
232 }
233
234
4dbf45e3 235 cb->register_offsets = kcalloc(max, sizeof(int), GFP_KERNEL);
96ca848e 236 if (!cb->register_offsets)
3c44d515 237 goto err_irq_map;
96ca848e
S
238
239 of_property_read_u32(node, "ti,reg-size", &size);
240
241 switch (size) {
242 case 1:
243 cb->write = crossbar_writeb;
244 break;
245 case 2:
246 cb->write = crossbar_writew;
247 break;
248 case 4:
249 cb->write = crossbar_writel;
250 break;
251 default:
252 pr_err("Invalid reg-size property\n");
edb442de 253 ret = -EINVAL;
3c44d515 254 goto err_reg_offset;
96ca848e
S
255 break;
256 }
257
258 /*
259 * Register offsets are not linear because of the
260 * reserved irqs. so find and store the offsets once.
261 */
262 for (i = 0; i < max; i++) {
1d50d2ce 263 if (cb->irq_map[i] == IRQ_RESERVED)
96ca848e
S
264 continue;
265
266 cb->register_offsets[i] = reserved;
267 reserved += size;
268 }
269
a35057d1 270 of_property_read_u32(node, "ti,irqs-safe-map", &cb->safe_map);
a35057d1
NM
271 /* Initialize the crossbar with safe map to start with */
272 for (i = 0; i < max; i++) {
273 if (cb->irq_map[i] == IRQ_RESERVED ||
274 cb->irq_map[i] == IRQ_SKIP)
275 continue;
276
277 cb->write(i, cb->safe_map);
278 }
279
96ca848e
S
280 register_routable_domain_ops(&routable_irq_domain_ops);
281 return 0;
282
3c44d515 283err_reg_offset:
96ca848e 284 kfree(cb->register_offsets);
3c44d515 285err_irq_map:
96ca848e 286 kfree(cb->irq_map);
3c44d515 287err_base:
96ca848e 288 iounmap(cb->crossbar_base);
3c44d515 289err_cb:
96ca848e 290 kfree(cb);
99e37d0e
S
291
292 cb = NULL;
edb442de 293 return ret;
96ca848e
S
294}
295
296static const struct of_device_id crossbar_match[] __initconst = {
297 { .compatible = "ti,irq-crossbar" },
298 {}
299};
300
301int __init irqcrossbar_init(void)
302{
303 struct device_node *np;
304 np = of_find_matching_node(NULL, crossbar_match);
305 if (!np)
306 return -ENODEV;
307
308 crossbar_of_init(np);
309 return 0;
310}
This page took 0.053991 seconds and 5 git commands to generate.