aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/of/irq.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/of/irq.c')
-rw-r--r--drivers/of/irq.c164
1 files changed, 91 insertions, 73 deletions
diff --git a/drivers/of/irq.c b/drivers/of/irq.c
index 1752988d6aa8..786b0b47fae4 100644
--- a/drivers/of/irq.c
+++ b/drivers/of/irq.c
@@ -31,18 +31,17 @@
31 * @dev: Device node of the device whose interrupt is to be mapped 31 * @dev: Device node of the device whose interrupt is to be mapped
32 * @index: Index of the interrupt to map 32 * @index: Index of the interrupt to map
33 * 33 *
34 * This function is a wrapper that chains of_irq_map_one() and 34 * This function is a wrapper that chains of_irq_parse_one() and
35 * irq_create_of_mapping() to make things easier to callers 35 * irq_create_of_mapping() to make things easier to callers
36 */ 36 */
37unsigned int irq_of_parse_and_map(struct device_node *dev, int index) 37unsigned int irq_of_parse_and_map(struct device_node *dev, int index)
38{ 38{
39 struct of_irq oirq; 39 struct of_phandle_args oirq;
40 40
41 if (of_irq_map_one(dev, index, &oirq)) 41 if (of_irq_parse_one(dev, index, &oirq))
42 return 0; 42 return 0;
43 43
44 return irq_create_of_mapping(oirq.controller, oirq.specifier, 44 return irq_create_of_mapping(&oirq);
45 oirq.size);
46} 45}
47EXPORT_SYMBOL_GPL(irq_of_parse_and_map); 46EXPORT_SYMBOL_GPL(irq_of_parse_and_map);
48 47
@@ -79,33 +78,34 @@ struct device_node *of_irq_find_parent(struct device_node *child)
79} 78}
80 79
81/** 80/**
82 * of_irq_map_raw - Low level interrupt tree parsing 81 * of_irq_parse_raw - Low level interrupt tree parsing
83 * @parent: the device interrupt parent 82 * @parent: the device interrupt parent
84 * @intspec: interrupt specifier ("interrupts" property of the device) 83 * @addr: address specifier (start of "reg" property of the device) in be32 format
85 * @ointsize: size of the passed in interrupt specifier 84 * @out_irq: structure of_irq updated by this function
86 * @addr: address specifier (start of "reg" property of the device)
87 * @out_irq: structure of_irq filled by this function
88 * 85 *
89 * Returns 0 on success and a negative number on error 86 * Returns 0 on success and a negative number on error
90 * 87 *
91 * This function is a low-level interrupt tree walking function. It 88 * This function is a low-level interrupt tree walking function. It
92 * can be used to do a partial walk with synthetized reg and interrupts 89 * can be used to do a partial walk with synthetized reg and interrupts
93 * properties, for example when resolving PCI interrupts when no device 90 * properties, for example when resolving PCI interrupts when no device
94 * node exist for the parent. 91 * node exist for the parent. It takes an interrupt specifier structure as
92 * input, walks the tree looking for any interrupt-map properties, translates
93 * the specifier for each map, and then returns the translated map.
95 */ 94 */
96int of_irq_map_raw(struct device_node *parent, const __be32 *intspec, 95int of_irq_parse_raw(const __be32 *addr, struct of_phandle_args *out_irq)
97 u32 ointsize, const __be32 *addr, struct of_irq *out_irq)
98{ 96{
99 struct device_node *ipar, *tnode, *old = NULL, *newpar = NULL; 97 struct device_node *ipar, *tnode, *old = NULL, *newpar = NULL;
100 const __be32 *tmp, *imap, *imask; 98 __be32 initial_match_array[MAX_PHANDLE_ARGS];
99 const __be32 *match_array = initial_match_array;
100 const __be32 *tmp, *imap, *imask, dummy_imask[] = { [0 ... MAX_PHANDLE_ARGS] = ~0 };
101 u32 intsize = 1, addrsize, newintsize = 0, newaddrsize = 0; 101 u32 intsize = 1, addrsize, newintsize = 0, newaddrsize = 0;
102 int imaplen, match, i; 102 int imaplen, match, i;
103 103
104 pr_debug("of_irq_map_raw: par=%s,intspec=[0x%08x 0x%08x...],ointsize=%d\n", 104#ifdef DEBUG
105 parent->full_name, be32_to_cpup(intspec), 105 of_print_phandle_args("of_irq_parse_raw: ", out_irq);
106 be32_to_cpup(intspec + 1), ointsize); 106#endif
107 107
108 ipar = of_node_get(parent); 108 ipar = of_node_get(out_irq->np);
109 109
110 /* First get the #interrupt-cells property of the current cursor 110 /* First get the #interrupt-cells property of the current cursor
111 * that tells us how to interpret the passed-in intspec. If there 111 * that tells us how to interpret the passed-in intspec. If there
@@ -126,9 +126,9 @@ int of_irq_map_raw(struct device_node *parent, const __be32 *intspec,
126 goto fail; 126 goto fail;
127 } 127 }
128 128
129 pr_debug("of_irq_map_raw: ipar=%s, size=%d\n", ipar->full_name, intsize); 129 pr_debug("of_irq_parse_raw: ipar=%s, size=%d\n", of_node_full_name(ipar), intsize);
130 130
131 if (ointsize != intsize) 131 if (out_irq->args_count != intsize)
132 return -EINVAL; 132 return -EINVAL;
133 133
134 /* Look for this #address-cells. We have to implement the old linux 134 /* Look for this #address-cells. We have to implement the old linux
@@ -147,6 +147,16 @@ int of_irq_map_raw(struct device_node *parent, const __be32 *intspec,
147 147
148 pr_debug(" -> addrsize=%d\n", addrsize); 148 pr_debug(" -> addrsize=%d\n", addrsize);
149 149
150 /* Range check so that the temporary buffer doesn't overflow */
151 if (WARN_ON(addrsize + intsize > MAX_PHANDLE_ARGS))
152 goto fail;
153
154 /* Precalculate the match array - this simplifies match loop */
155 for (i = 0; i < addrsize; i++)
156 initial_match_array[i] = addr ? addr[i] : 0;
157 for (i = 0; i < intsize; i++)
158 initial_match_array[addrsize + i] = cpu_to_be32(out_irq->args[i]);
159
150 /* Now start the actual "proper" walk of the interrupt tree */ 160 /* Now start the actual "proper" walk of the interrupt tree */
151 while (ipar != NULL) { 161 while (ipar != NULL) {
152 /* Now check if cursor is an interrupt-controller and if it is 162 /* Now check if cursor is an interrupt-controller and if it is
@@ -155,15 +165,19 @@ int of_irq_map_raw(struct device_node *parent, const __be32 *intspec,
155 if (of_get_property(ipar, "interrupt-controller", NULL) != 165 if (of_get_property(ipar, "interrupt-controller", NULL) !=
156 NULL) { 166 NULL) {
157 pr_debug(" -> got it !\n"); 167 pr_debug(" -> got it !\n");
158 for (i = 0; i < intsize; i++)
159 out_irq->specifier[i] =
160 of_read_number(intspec +i, 1);
161 out_irq->size = intsize;
162 out_irq->controller = ipar;
163 of_node_put(old); 168 of_node_put(old);
164 return 0; 169 return 0;
165 } 170 }
166 171
172 /*
173 * interrupt-map parsing does not work without a reg
174 * property when #address-cells != 0
175 */
176 if (addrsize && !addr) {
177 pr_debug(" -> no reg passed in when needed !\n");
178 goto fail;
179 }
180
167 /* Now look for an interrupt-map */ 181 /* Now look for an interrupt-map */
168 imap = of_get_property(ipar, "interrupt-map", &imaplen); 182 imap = of_get_property(ipar, "interrupt-map", &imaplen);
169 /* No interrupt map, check for an interrupt parent */ 183 /* No interrupt map, check for an interrupt parent */
@@ -176,34 +190,16 @@ int of_irq_map_raw(struct device_node *parent, const __be32 *intspec,
176 190
177 /* Look for a mask */ 191 /* Look for a mask */
178 imask = of_get_property(ipar, "interrupt-map-mask", NULL); 192 imask = of_get_property(ipar, "interrupt-map-mask", NULL);
179 193 if (!imask)
180 /* If we were passed no "reg" property and we attempt to parse 194 imask = dummy_imask;
181 * an interrupt-map, then #address-cells must be 0.
182 * Fail if it's not.
183 */
184 if (addr == NULL && addrsize != 0) {
185 pr_debug(" -> no reg passed in when needed !\n");
186 goto fail;
187 }
188 195
189 /* Parse interrupt-map */ 196 /* Parse interrupt-map */
190 match = 0; 197 match = 0;
191 while (imaplen > (addrsize + intsize + 1) && !match) { 198 while (imaplen > (addrsize + intsize + 1) && !match) {
192 /* Compare specifiers */ 199 /* Compare specifiers */
193 match = 1; 200 match = 1;
194 for (i = 0; i < addrsize && match; ++i) { 201 for (i = 0; i < (addrsize + intsize); i++, imaplen--)
195 __be32 mask = imask ? imask[i] 202 match &= !((match_array[i] ^ *imap++) & imask[i]);
196 : cpu_to_be32(0xffffffffu);
197 match = ((addr[i] ^ imap[i]) & mask) == 0;
198 }
199 for (; i < (addrsize + intsize) && match; ++i) {
200 __be32 mask = imask ? imask[i]
201 : cpu_to_be32(0xffffffffu);
202 match =
203 ((intspec[i-addrsize] ^ imap[i]) & mask) == 0;
204 }
205 imap += addrsize + intsize;
206 imaplen -= addrsize + intsize;
207 203
208 pr_debug(" -> match=%d (imaplen=%d)\n", match, imaplen); 204 pr_debug(" -> match=%d (imaplen=%d)\n", match, imaplen);
209 205
@@ -237,6 +233,8 @@ int of_irq_map_raw(struct device_node *parent, const __be32 *intspec,
237 newintsize, newaddrsize); 233 newintsize, newaddrsize);
238 234
239 /* Check for malformed properties */ 235 /* Check for malformed properties */
236 if (WARN_ON(newaddrsize + newintsize > MAX_PHANDLE_ARGS))
237 goto fail;
240 if (imaplen < (newaddrsize + newintsize)) 238 if (imaplen < (newaddrsize + newintsize))
241 goto fail; 239 goto fail;
242 240
@@ -248,12 +246,18 @@ int of_irq_map_raw(struct device_node *parent, const __be32 *intspec,
248 if (!match) 246 if (!match)
249 goto fail; 247 goto fail;
250 248
251 of_node_put(old); 249 /*
252 old = of_node_get(newpar); 250 * Successfully parsed an interrrupt-map translation; copy new
251 * interrupt specifier into the out_irq structure
252 */
253 of_node_put(out_irq->np);
254 out_irq->np = of_node_get(newpar);
255
256 match_array = imap - newaddrsize - newintsize;
257 for (i = 0; i < newintsize; i++)
258 out_irq->args[i] = be32_to_cpup(imap - newintsize + i);
259 out_irq->args_count = intsize = newintsize;
253 addrsize = newaddrsize; 260 addrsize = newaddrsize;
254 intsize = newintsize;
255 intspec = imap - intsize;
256 addr = intspec - addrsize;
257 261
258 skiplevel: 262 skiplevel:
259 /* Iterate again with new parent */ 263 /* Iterate again with new parent */
@@ -264,46 +268,53 @@ int of_irq_map_raw(struct device_node *parent, const __be32 *intspec,
264 } 268 }
265 fail: 269 fail:
266 of_node_put(ipar); 270 of_node_put(ipar);
267 of_node_put(old); 271 of_node_put(out_irq->np);
268 of_node_put(newpar); 272 of_node_put(newpar);
269 273
270 return -EINVAL; 274 return -EINVAL;
271} 275}
272EXPORT_SYMBOL_GPL(of_irq_map_raw); 276EXPORT_SYMBOL_GPL(of_irq_parse_raw);
273 277
274/** 278/**
275 * of_irq_map_one - Resolve an interrupt for a device 279 * of_irq_parse_one - Resolve an interrupt for a device
276 * @device: the device whose interrupt is to be resolved 280 * @device: the device whose interrupt is to be resolved
277 * @index: index of the interrupt to resolve 281 * @index: index of the interrupt to resolve
278 * @out_irq: structure of_irq filled by this function 282 * @out_irq: structure of_irq filled by this function
279 * 283 *
280 * This function resolves an interrupt, walking the tree, for a given 284 * This function resolves an interrupt for a node by walking the interrupt tree,
281 * device-tree node. It's the high level pendant to of_irq_map_raw(). 285 * finding which interrupt controller node it is attached to, and returning the
286 * interrupt specifier that can be used to retrieve a Linux IRQ number.
282 */ 287 */
283int of_irq_map_one(struct device_node *device, int index, struct of_irq *out_irq) 288int of_irq_parse_one(struct device_node *device, int index, struct of_phandle_args *out_irq)
284{ 289{
285 struct device_node *p; 290 struct device_node *p;
286 const __be32 *intspec, *tmp, *addr; 291 const __be32 *intspec, *tmp, *addr;
287 u32 intsize, intlen; 292 u32 intsize, intlen;
288 int res = -EINVAL; 293 int i, res = -EINVAL;
289 294
290 pr_debug("of_irq_map_one: dev=%s, index=%d\n", device->full_name, index); 295 pr_debug("of_irq_parse_one: dev=%s, index=%d\n", of_node_full_name(device), index);
291 296
292 /* OldWorld mac stuff is "special", handle out of line */ 297 /* OldWorld mac stuff is "special", handle out of line */
293 if (of_irq_workarounds & OF_IMAP_OLDWORLD_MAC) 298 if (of_irq_workarounds & OF_IMAP_OLDWORLD_MAC)
294 return of_irq_map_oldworld(device, index, out_irq); 299 return of_irq_parse_oldworld(device, index, out_irq);
300
301 /* Get the reg property (if any) */
302 addr = of_get_property(device, "reg", NULL);
295 303
296 /* Get the interrupts property */ 304 /* Get the interrupts property */
297 intspec = of_get_property(device, "interrupts", &intlen); 305 intspec = of_get_property(device, "interrupts", &intlen);
298 if (intspec == NULL) 306 if (intspec == NULL) {
299 return -EINVAL; 307 /* Try the new-style interrupts-extended */
308 res = of_parse_phandle_with_args(device, "interrupts-extended",
309 "#interrupt-cells", index, out_irq);
310 if (res)
311 return -EINVAL;
312 return of_irq_parse_raw(addr, out_irq);
313 }
300 intlen /= sizeof(*intspec); 314 intlen /= sizeof(*intspec);
301 315
302 pr_debug(" intspec=%d intlen=%d\n", be32_to_cpup(intspec), intlen); 316 pr_debug(" intspec=%d intlen=%d\n", be32_to_cpup(intspec), intlen);
303 317
304 /* Get the reg property (if any) */
305 addr = of_get_property(device, "reg", NULL);
306
307 /* Look for the interrupt parent. */ 318 /* Look for the interrupt parent. */
308 p = of_irq_find_parent(device); 319 p = of_irq_find_parent(device);
309 if (p == NULL) 320 if (p == NULL)
@@ -321,14 +332,20 @@ int of_irq_map_one(struct device_node *device, int index, struct of_irq *out_irq
321 if ((index + 1) * intsize > intlen) 332 if ((index + 1) * intsize > intlen)
322 goto out; 333 goto out;
323 334
324 /* Get new specifier and map it */ 335 /* Copy intspec into irq structure */
325 res = of_irq_map_raw(p, intspec + index * intsize, intsize, 336 intspec += index * intsize;
326 addr, out_irq); 337 out_irq->np = p;
338 out_irq->args_count = intsize;
339 for (i = 0; i < intsize; i++)
340 out_irq->args[i] = be32_to_cpup(intspec++);
341
342 /* Check if there are any interrupt-map translations to process */
343 res = of_irq_parse_raw(addr, out_irq);
327 out: 344 out:
328 of_node_put(p); 345 of_node_put(p);
329 return res; 346 return res;
330} 347}
331EXPORT_SYMBOL_GPL(of_irq_map_one); 348EXPORT_SYMBOL_GPL(of_irq_parse_one);
332 349
333/** 350/**
334 * of_irq_to_resource - Decode a node's IRQ and return it as a resource 351 * of_irq_to_resource - Decode a node's IRQ and return it as a resource
@@ -354,8 +371,8 @@ int of_irq_to_resource(struct device_node *dev, int index, struct resource *r)
354 &name); 371 &name);
355 372
356 r->start = r->end = irq; 373 r->start = r->end = irq;
357 r->flags = IORESOURCE_IRQ; 374 r->flags = IORESOURCE_IRQ | irqd_get_trigger_type(irq_get_irq_data(irq));
358 r->name = name ? name : dev->full_name; 375 r->name = name ? name : of_node_full_name(dev);
359 } 376 }
360 377
361 return irq; 378 return irq;
@@ -368,9 +385,10 @@ EXPORT_SYMBOL_GPL(of_irq_to_resource);
368 */ 385 */
369int of_irq_count(struct device_node *dev) 386int of_irq_count(struct device_node *dev)
370{ 387{
388 struct of_phandle_args irq;
371 int nr = 0; 389 int nr = 0;
372 390
373 while (of_irq_to_resource(dev, nr, NULL)) 391 while (of_irq_parse_one(dev, nr, &irq) == 0)
374 nr++; 392 nr++;
375 393
376 return nr; 394 return nr;