diff options
Diffstat (limited to 'drivers/of/irq.c')
-rw-r--r-- | drivers/of/irq.c | 164 |
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 | */ |
37 | unsigned int irq_of_parse_and_map(struct device_node *dev, int index) | 37 | unsigned 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 | } |
47 | EXPORT_SYMBOL_GPL(irq_of_parse_and_map); | 46 | EXPORT_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 | */ |
96 | int of_irq_map_raw(struct device_node *parent, const __be32 *intspec, | 95 | int 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 | } |
272 | EXPORT_SYMBOL_GPL(of_irq_map_raw); | 276 | EXPORT_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 | */ |
283 | int of_irq_map_one(struct device_node *device, int index, struct of_irq *out_irq) | 288 | int 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 | } |
331 | EXPORT_SYMBOL_GPL(of_irq_map_one); | 348 | EXPORT_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 | */ |
369 | int of_irq_count(struct device_node *dev) | 386 | int 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; |