Branch data Line data Source code
1 : : #define pr_fmt(fmt) "irq: " fmt
2 : :
3 : : #include <linux/debugfs.h>
4 : : #include <linux/hardirq.h>
5 : : #include <linux/interrupt.h>
6 : : #include <linux/irq.h>
7 : : #include <linux/irqdesc.h>
8 : : #include <linux/irqdomain.h>
9 : : #include <linux/module.h>
10 : : #include <linux/mutex.h>
11 : : #include <linux/of.h>
12 : : #include <linux/of_address.h>
13 : : #include <linux/topology.h>
14 : : #include <linux/seq_file.h>
15 : : #include <linux/slab.h>
16 : : #include <linux/smp.h>
17 : : #include <linux/fs.h>
18 : :
19 : : static LIST_HEAD(irq_domain_list);
20 : : static DEFINE_MUTEX(irq_domain_mutex);
21 : :
22 : : static DEFINE_MUTEX(revmap_trees_mutex);
23 : : static struct irq_domain *irq_default_domain;
24 : :
25 : : /**
26 : : * __irq_domain_add() - Allocate a new irq_domain data structure
27 : : * @of_node: optional device-tree node of the interrupt controller
28 : : * @size: Size of linear map; 0 for radix mapping only
29 : : * @direct_max: Maximum value of direct maps; Use ~0 for no limit; 0 for no
30 : : * direct mapping
31 : : * @ops: map/unmap domain callbacks
32 : : * @host_data: Controller private data pointer
33 : : *
34 : : * Allocates and initialize and irq_domain structure. Caller is expected to
35 : : * register allocated irq_domain with irq_domain_register(). Returns pointer
36 : : * to IRQ domain, or NULL on failure.
37 : : */
38 : 0 : struct irq_domain *__irq_domain_add(struct device_node *of_node, int size,
39 : : irq_hw_number_t hwirq_max, int direct_max,
40 : : const struct irq_domain_ops *ops,
41 : : void *host_data)
42 : : {
43 : : struct irq_domain *domain;
44 : :
45 : 0 : domain = kzalloc_node(sizeof(*domain) + (sizeof(unsigned int) * size),
46 : : GFP_KERNEL, of_node_to_nid(of_node));
47 [ # # ][ # # ]: 0 : if (WARN_ON(!domain))
48 : : return NULL;
49 : :
50 : : /* Fill structure */
51 : 0 : INIT_RADIX_TREE(&domain->revmap_tree, GFP_KERNEL);
52 : 0 : domain->ops = ops;
53 : 0 : domain->host_data = host_data;
54 : 0 : domain->of_node = of_node_get(of_node);
55 : 0 : domain->hwirq_max = hwirq_max;
56 : 0 : domain->revmap_size = size;
57 : 0 : domain->revmap_direct_max_irq = direct_max;
58 : :
59 : 0 : mutex_lock(&irq_domain_mutex);
60 : 0 : list_add(&domain->link, &irq_domain_list);
61 : 0 : mutex_unlock(&irq_domain_mutex);
62 : :
63 : : pr_debug("Added domain %s\n", domain->name);
64 : 0 : return domain;
65 : : }
66 : : EXPORT_SYMBOL_GPL(__irq_domain_add);
67 : :
68 : : /**
69 : : * irq_domain_remove() - Remove an irq domain.
70 : : * @domain: domain to remove
71 : : *
72 : : * This routine is used to remove an irq domain. The caller must ensure
73 : : * that all mappings within the domain have been disposed of prior to
74 : : * use, depending on the revmap type.
75 : : */
76 : 0 : void irq_domain_remove(struct irq_domain *domain)
77 : : {
78 : 0 : mutex_lock(&irq_domain_mutex);
79 : :
80 : : /*
81 : : * radix_tree_delete() takes care of destroying the root
82 : : * node when all entries are removed. Shout if there are
83 : : * any mappings left.
84 : : */
85 [ # # ]: 0 : WARN_ON(domain->revmap_tree.height);
86 : :
87 : : list_del(&domain->link);
88 : :
89 : : /*
90 : : * If the going away domain is the default one, reset it.
91 : : */
92 [ # # ]: 0 : if (unlikely(irq_default_domain == domain))
93 : : irq_set_default_host(NULL);
94 : :
95 : 0 : mutex_unlock(&irq_domain_mutex);
96 : :
97 : : pr_debug("Removed domain %s\n", domain->name);
98 : :
99 : : of_node_put(domain->of_node);
100 : 0 : kfree(domain);
101 : 0 : }
102 : : EXPORT_SYMBOL_GPL(irq_domain_remove);
103 : :
104 : : /**
105 : : * irq_domain_add_simple() - Register an irq_domain and optionally map a range of irqs
106 : : * @of_node: pointer to interrupt controller's device tree node.
107 : : * @size: total number of irqs in mapping
108 : : * @first_irq: first number of irq block assigned to the domain,
109 : : * pass zero to assign irqs on-the-fly. If first_irq is non-zero, then
110 : : * pre-map all of the irqs in the domain to virqs starting at first_irq.
111 : : * @ops: map/unmap domain callbacks
112 : : * @host_data: Controller private data pointer
113 : : *
114 : : * Allocates an irq_domain, and optionally if first_irq is positive then also
115 : : * allocate irq_descs and map all of the hwirqs to virqs starting at first_irq.
116 : : *
117 : : * This is intended to implement the expected behaviour for most
118 : : * interrupt controllers. If device tree is used, then first_irq will be 0 and
119 : : * irqs get mapped dynamically on the fly. However, if the controller requires
120 : : * static virq assignments (non-DT boot) then it will set that up correctly.
121 : : */
122 : 0 : struct irq_domain *irq_domain_add_simple(struct device_node *of_node,
123 : : unsigned int size,
124 : : unsigned int first_irq,
125 : : const struct irq_domain_ops *ops,
126 : : void *host_data)
127 : : {
128 : : struct irq_domain *domain;
129 : :
130 : 0 : domain = __irq_domain_add(of_node, size, size, 0, ops, host_data);
131 [ # # ]: 0 : if (!domain)
132 : : return NULL;
133 : :
134 [ # # ]: 0 : if (first_irq > 0) {
135 : : if (IS_ENABLED(CONFIG_SPARSE_IRQ)) {
136 : : /* attempt to allocated irq_descs */
137 : 0 : int rc = irq_alloc_descs(first_irq, first_irq, size,
138 : : of_node_to_nid(of_node));
139 [ # # ]: 0 : if (rc < 0)
140 : 0 : pr_info("Cannot allocate irq_descs @ IRQ%d, assuming pre-allocated\n",
141 : : first_irq);
142 : : }
143 : : irq_domain_associate_many(domain, first_irq, 0, size);
144 : : }
145 : :
146 : 0 : return domain;
147 : : }
148 : : EXPORT_SYMBOL_GPL(irq_domain_add_simple);
149 : :
150 : : /**
151 : : * irq_domain_add_legacy() - Allocate and register a legacy revmap irq_domain.
152 : : * @of_node: pointer to interrupt controller's device tree node.
153 : : * @size: total number of irqs in legacy mapping
154 : : * @first_irq: first number of irq block assigned to the domain
155 : : * @first_hwirq: first hwirq number to use for the translation. Should normally
156 : : * be '0', but a positive integer can be used if the effective
157 : : * hwirqs numbering does not begin at zero.
158 : : * @ops: map/unmap domain callbacks
159 : : * @host_data: Controller private data pointer
160 : : *
161 : : * Note: the map() callback will be called before this function returns
162 : : * for all legacy interrupts except 0 (which is always the invalid irq for
163 : : * a legacy controller).
164 : : */
165 : 0 : struct irq_domain *irq_domain_add_legacy(struct device_node *of_node,
166 : : unsigned int size,
167 : : unsigned int first_irq,
168 : : irq_hw_number_t first_hwirq,
169 : : const struct irq_domain_ops *ops,
170 : : void *host_data)
171 : : {
172 : : struct irq_domain *domain;
173 : :
174 : 0 : domain = __irq_domain_add(of_node, first_hwirq + size,
175 : : first_hwirq + size, 0, ops, host_data);
176 [ # # ]: 0 : if (!domain)
177 : : return NULL;
178 : :
179 : 0 : irq_domain_associate_many(domain, first_irq, first_hwirq, size);
180 : :
181 : : return domain;
182 : : }
183 : : EXPORT_SYMBOL_GPL(irq_domain_add_legacy);
184 : :
185 : : /**
186 : : * irq_find_host() - Locates a domain for a given device node
187 : : * @node: device-tree node of the interrupt controller
188 : : */
189 : 0 : struct irq_domain *irq_find_host(struct device_node *node)
190 : : {
191 : : struct irq_domain *h, *found = NULL;
192 : : int rc;
193 : :
194 : : /* We might want to match the legacy controller last since
195 : : * it might potentially be set to match all interrupts in
196 : : * the absence of a device node. This isn't a problem so far
197 : : * yet though...
198 : : */
199 : 0 : mutex_lock(&irq_domain_mutex);
200 [ # # ]: 0 : list_for_each_entry(h, &irq_domain_list, link) {
201 [ # # ]: 0 : if (h->ops->match)
202 : 0 : rc = h->ops->match(h, node);
203 : : else
204 [ # # ][ # # ]: 0 : rc = (h->of_node != NULL) && (h->of_node == node);
205 : :
206 [ # # ]: 0 : if (rc) {
207 : : found = h;
208 : : break;
209 : : }
210 : : }
211 : 0 : mutex_unlock(&irq_domain_mutex);
212 : 0 : return found;
213 : : }
214 : : EXPORT_SYMBOL_GPL(irq_find_host);
215 : :
216 : : /**
217 : : * irq_set_default_host() - Set a "default" irq domain
218 : : * @domain: default domain pointer
219 : : *
220 : : * For convenience, it's possible to set a "default" domain that will be used
221 : : * whenever NULL is passed to irq_create_mapping(). It makes life easier for
222 : : * platforms that want to manipulate a few hard coded interrupt numbers that
223 : : * aren't properly represented in the device-tree.
224 : : */
225 : 0 : void irq_set_default_host(struct irq_domain *domain)
226 : : {
227 : : pr_debug("Default domain set to @0x%p\n", domain);
228 : :
229 : 0 : irq_default_domain = domain;
230 : 0 : }
231 : : EXPORT_SYMBOL_GPL(irq_set_default_host);
232 : :
233 : 0 : static void irq_domain_disassociate(struct irq_domain *domain, unsigned int irq)
234 : : {
235 : 0 : struct irq_data *irq_data = irq_get_irq_data(irq);
236 : : irq_hw_number_t hwirq;
237 : :
238 [ # # ][ # # ]: 0 : if (WARN(!irq_data || irq_data->domain != domain,
[ # # ][ # # ]
239 : : "virq%i doesn't exist; cannot disassociate\n", irq))
240 : 0 : return;
241 : :
242 : 0 : hwirq = irq_data->hwirq;
243 : : irq_set_status_flags(irq, IRQ_NOREQUEST);
244 : :
245 : : /* remove chip and handler */
246 : : irq_set_chip_and_handler(irq, NULL, NULL);
247 : :
248 : : /* Make sure it's completed */
249 : 0 : synchronize_irq(irq);
250 : :
251 : : /* Tell the PIC about it */
252 [ # # ]: 0 : if (domain->ops->unmap)
253 : 0 : domain->ops->unmap(domain, irq);
254 : 0 : smp_mb();
255 : :
256 : 0 : irq_data->domain = NULL;
257 : 0 : irq_data->hwirq = 0;
258 : :
259 : : /* Clear reverse map for this hwirq */
260 [ # # ]: 0 : if (hwirq < domain->revmap_size) {
261 : 0 : domain->linear_revmap[hwirq] = 0;
262 : : } else {
263 : 0 : mutex_lock(&revmap_trees_mutex);
264 : 0 : radix_tree_delete(&domain->revmap_tree, hwirq);
265 : 0 : mutex_unlock(&revmap_trees_mutex);
266 : : }
267 : : }
268 : :
269 : 0 : int irq_domain_associate(struct irq_domain *domain, unsigned int virq,
270 : : irq_hw_number_t hwirq)
271 : : {
272 : 0 : struct irq_data *irq_data = irq_get_irq_data(virq);
273 : : int ret;
274 : :
275 [ # # ][ # # ]: 0 : if (WARN(hwirq >= domain->hwirq_max,
276 : : "error: hwirq 0x%x is too large for %s\n", (int)hwirq, domain->name))
277 : : return -EINVAL;
278 [ # # ][ # # ]: 0 : if (WARN(!irq_data, "error: virq%i is not allocated", virq))
279 : : return -EINVAL;
280 [ # # ][ # # ]: 0 : if (WARN(irq_data->domain, "error: virq%i is already associated", virq))
281 : : return -EINVAL;
282 : :
283 : 0 : mutex_lock(&irq_domain_mutex);
284 : 0 : irq_data->hwirq = hwirq;
285 : 0 : irq_data->domain = domain;
286 [ # # ]: 0 : if (domain->ops->map) {
287 : 0 : ret = domain->ops->map(domain, virq, hwirq);
288 [ # # ]: 0 : if (ret != 0) {
289 : : /*
290 : : * If map() returns -EPERM, this interrupt is protected
291 : : * by the firmware or some other service and shall not
292 : : * be mapped. Don't bother telling the user about it.
293 : : */
294 [ # # ]: 0 : if (ret != -EPERM) {
295 : 0 : pr_info("%s didn't like hwirq-0x%lx to VIRQ%i mapping (rc=%d)\n",
296 : : domain->name, hwirq, virq, ret);
297 : : }
298 : 0 : irq_data->domain = NULL;
299 : 0 : irq_data->hwirq = 0;
300 : 0 : mutex_unlock(&irq_domain_mutex);
301 : 0 : return ret;
302 : : }
303 : :
304 : : /* If not already assigned, give the domain the chip's name */
305 [ # # ][ # # ]: 0 : if (!domain->name && irq_data->chip)
306 : 0 : domain->name = irq_data->chip->name;
307 : : }
308 : :
309 [ # # ]: 0 : if (hwirq < domain->revmap_size) {
310 : 0 : domain->linear_revmap[hwirq] = virq;
311 : : } else {
312 : 0 : mutex_lock(&revmap_trees_mutex);
313 : 0 : radix_tree_insert(&domain->revmap_tree, hwirq, irq_data);
314 : 0 : mutex_unlock(&revmap_trees_mutex);
315 : : }
316 : 0 : mutex_unlock(&irq_domain_mutex);
317 : :
318 : : irq_clear_status_flags(virq, IRQ_NOREQUEST);
319 : :
320 : 0 : return 0;
321 : : }
322 : : EXPORT_SYMBOL_GPL(irq_domain_associate);
323 : :
324 : 0 : void irq_domain_associate_many(struct irq_domain *domain, unsigned int irq_base,
325 : : irq_hw_number_t hwirq_base, int count)
326 : : {
327 : : int i;
328 : :
329 : : pr_debug("%s(%s, irqbase=%i, hwbase=%i, count=%i)\n", __func__,
330 : : of_node_full_name(domain->of_node), irq_base, (int)hwirq_base, count);
331 : :
332 [ # # ][ # # ]: 0 : for (i = 0; i < count; i++) {
[ # # ][ # # ]
333 : 0 : irq_domain_associate(domain, irq_base + i, hwirq_base + i);
334 : : }
335 : 0 : }
336 : : EXPORT_SYMBOL_GPL(irq_domain_associate_many);
337 : :
338 : : /**
339 : : * irq_create_direct_mapping() - Allocate an irq for direct mapping
340 : : * @domain: domain to allocate the irq for or NULL for default domain
341 : : *
342 : : * This routine is used for irq controllers which can choose the hardware
343 : : * interrupt numbers they generate. In such a case it's simplest to use
344 : : * the linux irq as the hardware interrupt number. It still uses the linear
345 : : * or radix tree to store the mapping, but the irq controller can optimize
346 : : * the revmap path by using the hwirq directly.
347 : : */
348 : 0 : unsigned int irq_create_direct_mapping(struct irq_domain *domain)
349 : : {
350 : : unsigned int virq;
351 : :
352 [ # # ]: 0 : if (domain == NULL)
353 : 0 : domain = irq_default_domain;
354 : :
355 : 0 : virq = irq_alloc_desc_from(1, of_node_to_nid(domain->of_node));
356 [ # # ]: 0 : if (!virq) {
357 : : pr_debug("create_direct virq allocation failed\n");
358 : : return 0;
359 : : }
360 [ # # ]: 0 : if (virq >= domain->revmap_direct_max_irq) {
361 : 0 : pr_err("ERROR: no free irqs available below %i maximum\n",
362 : : domain->revmap_direct_max_irq);
363 : : irq_free_desc(virq);
364 : 0 : return 0;
365 : : }
366 : : pr_debug("create_direct obtained virq %d\n", virq);
367 : :
368 [ # # ]: 0 : if (irq_domain_associate(domain, virq, virq)) {
369 : : irq_free_desc(virq);
370 : 0 : return 0;
371 : : }
372 : :
373 : : return virq;
374 : : }
375 : : EXPORT_SYMBOL_GPL(irq_create_direct_mapping);
376 : :
377 : : /**
378 : : * irq_create_mapping() - Map a hardware interrupt into linux irq space
379 : : * @domain: domain owning this hardware interrupt or NULL for default domain
380 : : * @hwirq: hardware irq number in that domain space
381 : : *
382 : : * Only one mapping per hardware interrupt is permitted. Returns a linux
383 : : * irq number.
384 : : * If the sense/trigger is to be specified, set_irq_type() should be called
385 : : * on the number returned from that call.
386 : : */
387 : 0 : unsigned int irq_create_mapping(struct irq_domain *domain,
388 : : irq_hw_number_t hwirq)
389 : : {
390 : : unsigned int hint;
391 : : int virq;
392 : :
393 : : pr_debug("irq_create_mapping(0x%p, 0x%lx)\n", domain, hwirq);
394 : :
395 : : /* Look for default domain if nececssary */
396 [ # # ]: 0 : if (domain == NULL)
397 : 0 : domain = irq_default_domain;
398 [ # # ]: 0 : if (domain == NULL) {
399 : 0 : WARN(1, "%s(, %lx) called with NULL domain\n", __func__, hwirq);
400 : 0 : return 0;
401 : : }
402 : : pr_debug("-> using domain @%p\n", domain);
403 : :
404 : : /* Check if mapping already exists */
405 : 0 : virq = irq_find_mapping(domain, hwirq);
406 [ # # ]: 0 : if (virq) {
407 : : pr_debug("-> existing mapping on virq %d\n", virq);
408 : : return virq;
409 : : }
410 : :
411 : : /* Allocate a virtual interrupt number */
412 : 0 : hint = hwirq % nr_irqs;
413 [ # # ]: 0 : if (hint == 0)
414 : 0 : hint++;
415 : 0 : virq = irq_alloc_desc_from(hint, of_node_to_nid(domain->of_node));
416 [ # # ]: 0 : if (virq <= 0)
417 : 0 : virq = irq_alloc_desc_from(1, of_node_to_nid(domain->of_node));
418 [ # # ]: 0 : if (virq <= 0) {
419 : : pr_debug("-> virq allocation failed\n");
420 : : return 0;
421 : : }
422 : :
423 [ # # ]: 0 : if (irq_domain_associate(domain, virq, hwirq)) {
424 : : irq_free_desc(virq);
425 : 0 : return 0;
426 : : }
427 : :
428 : : pr_debug("irq %lu on domain %s mapped to virtual irq %u\n",
429 : : hwirq, of_node_full_name(domain->of_node), virq);
430 : :
431 : : return virq;
432 : : }
433 : : EXPORT_SYMBOL_GPL(irq_create_mapping);
434 : :
435 : : /**
436 : : * irq_create_strict_mappings() - Map a range of hw irqs to fixed linux irqs
437 : : * @domain: domain owning the interrupt range
438 : : * @irq_base: beginning of linux IRQ range
439 : : * @hwirq_base: beginning of hardware IRQ range
440 : : * @count: Number of interrupts to map
441 : : *
442 : : * This routine is used for allocating and mapping a range of hardware
443 : : * irqs to linux irqs where the linux irq numbers are at pre-defined
444 : : * locations. For use by controllers that already have static mappings
445 : : * to insert in to the domain.
446 : : *
447 : : * Non-linear users can use irq_create_identity_mapping() for IRQ-at-a-time
448 : : * domain insertion.
449 : : *
450 : : * 0 is returned upon success, while any failure to establish a static
451 : : * mapping is treated as an error.
452 : : */
453 : 0 : int irq_create_strict_mappings(struct irq_domain *domain, unsigned int irq_base,
454 : : irq_hw_number_t hwirq_base, int count)
455 : : {
456 : : int ret;
457 : :
458 : 0 : ret = irq_alloc_descs(irq_base, irq_base, count,
459 : : of_node_to_nid(domain->of_node));
460 [ # # ]: 0 : if (unlikely(ret < 0))
461 : : return ret;
462 : :
463 : : irq_domain_associate_many(domain, irq_base, hwirq_base, count);
464 : : return 0;
465 : : }
466 : : EXPORT_SYMBOL_GPL(irq_create_strict_mappings);
467 : :
468 : 0 : unsigned int irq_create_of_mapping(struct of_phandle_args *irq_data)
469 : : {
470 : : struct irq_domain *domain;
471 : : irq_hw_number_t hwirq;
472 : 0 : unsigned int type = IRQ_TYPE_NONE;
473 : : unsigned int virq;
474 : :
475 [ # # ]: 0 : domain = irq_data->np ? irq_find_host(irq_data->np) : irq_default_domain;
476 [ # # ]: 0 : if (!domain) {
477 : 0 : pr_warn("no irq domain found for %s !\n",
478 : : of_node_full_name(irq_data->np));
479 : 0 : return 0;
480 : : }
481 : :
482 : : /* If domain has no translation, then we assume interrupt line */
483 [ # # ]: 0 : if (domain->ops->xlate == NULL)
484 : 0 : hwirq = irq_data->args[0];
485 : : else {
486 [ # # ]: 0 : if (domain->ops->xlate(domain, irq_data->np, irq_data->args,
487 : 0 : irq_data->args_count, &hwirq, &type))
488 : : return 0;
489 : : }
490 : :
491 : : /* Create mapping */
492 : 0 : virq = irq_create_mapping(domain, hwirq);
493 [ # # ]: 0 : if (!virq)
494 : : return virq;
495 : :
496 : : /* Set type if specified and different than the current one */
497 [ # # ][ # # ]: 0 : if (type != IRQ_TYPE_NONE &&
498 : 0 : type != irq_get_trigger_type(virq))
499 : 0 : irq_set_irq_type(virq, type);
500 : 0 : return virq;
501 : : }
502 : : EXPORT_SYMBOL_GPL(irq_create_of_mapping);
503 : :
504 : : /**
505 : : * irq_dispose_mapping() - Unmap an interrupt
506 : : * @virq: linux irq number of the interrupt to unmap
507 : : */
508 : 0 : void irq_dispose_mapping(unsigned int virq)
509 : : {
510 : 0 : struct irq_data *irq_data = irq_get_irq_data(virq);
511 : : struct irq_domain *domain;
512 : :
513 [ # # ]: 0 : if (!virq || !irq_data)
514 : : return;
515 : :
516 : 0 : domain = irq_data->domain;
517 [ # # ][ # # ]: 0 : if (WARN_ON(domain == NULL))
518 : : return;
519 : :
520 : 0 : irq_domain_disassociate(domain, virq);
521 : : irq_free_desc(virq);
522 : : }
523 : : EXPORT_SYMBOL_GPL(irq_dispose_mapping);
524 : :
525 : : /**
526 : : * irq_find_mapping() - Find a linux irq from an hw irq number.
527 : : * @domain: domain owning this hardware interrupt
528 : : * @hwirq: hardware irq number in that domain space
529 : : */
530 : 0 : unsigned int irq_find_mapping(struct irq_domain *domain,
531 : : irq_hw_number_t hwirq)
532 : : {
533 : : struct irq_data *data;
534 : :
535 : : /* Look for default domain if nececssary */
536 [ - + ]: 10549373 : if (domain == NULL)
537 : 0 : domain = irq_default_domain;
538 [ + + ]: 10549373 : if (domain == NULL)
539 : : return 0;
540 : :
541 [ - + ]: 10547656 : if (hwirq < domain->revmap_direct_max_irq) {
542 : 0 : data = irq_get_irq_data(hwirq);
543 [ # # ][ # # ]: 0 : if (data && (data->domain == domain) && (data->hwirq == hwirq))
[ # # ]
544 : : return hwirq;
545 : : }
546 : :
547 : : /* Check if the hwirq is in the linear revmap. */
548 [ + - ]: 10547946 : if (hwirq < domain->revmap_size)
549 : 10547946 : return domain->linear_revmap[hwirq];
550 : :
551 : : rcu_read_lock();
552 : 0 : data = radix_tree_lookup(&domain->revmap_tree, hwirq);
553 : : rcu_read_unlock();
554 [ # # ]: 0 : return data ? data->irq : 0;
555 : : }
556 : : EXPORT_SYMBOL_GPL(irq_find_mapping);
557 : :
558 : : #ifdef CONFIG_IRQ_DOMAIN_DEBUG
559 : : static int virq_debug_show(struct seq_file *m, void *private)
560 : : {
561 : : unsigned long flags;
562 : : struct irq_desc *desc;
563 : : struct irq_domain *domain;
564 : : struct radix_tree_iter iter;
565 : : void *data, **slot;
566 : : int i;
567 : :
568 : : seq_printf(m, " %-16s %-6s %-10s %-10s %s\n",
569 : : "name", "mapped", "linear-max", "direct-max", "devtree-node");
570 : : mutex_lock(&irq_domain_mutex);
571 : : list_for_each_entry(domain, &irq_domain_list, link) {
572 : : int count = 0;
573 : : radix_tree_for_each_slot(slot, &domain->revmap_tree, &iter, 0)
574 : : count++;
575 : : seq_printf(m, "%c%-16s %6u %10u %10u %s\n",
576 : : domain == irq_default_domain ? '*' : ' ', domain->name,
577 : : domain->revmap_size + count, domain->revmap_size,
578 : : domain->revmap_direct_max_irq,
579 : : domain->of_node ? of_node_full_name(domain->of_node) : "");
580 : : }
581 : : mutex_unlock(&irq_domain_mutex);
582 : :
583 : : seq_printf(m, "%-5s %-7s %-15s %-*s %6s %-14s %s\n", "irq", "hwirq",
584 : : "chip name", (int)(2 * sizeof(void *) + 2), "chip data",
585 : : "active", "type", "domain");
586 : :
587 : : for (i = 1; i < nr_irqs; i++) {
588 : : desc = irq_to_desc(i);
589 : : if (!desc)
590 : : continue;
591 : :
592 : : raw_spin_lock_irqsave(&desc->lock, flags);
593 : : domain = desc->irq_data.domain;
594 : :
595 : : if (domain) {
596 : : struct irq_chip *chip;
597 : : int hwirq = desc->irq_data.hwirq;
598 : : bool direct;
599 : :
600 : : seq_printf(m, "%5d ", i);
601 : : seq_printf(m, "0x%05x ", hwirq);
602 : :
603 : : chip = irq_desc_get_chip(desc);
604 : : seq_printf(m, "%-15s ", (chip && chip->name) ? chip->name : "none");
605 : :
606 : : data = irq_desc_get_chip_data(desc);
607 : : seq_printf(m, data ? "0x%p " : " %p ", data);
608 : :
609 : : seq_printf(m, " %c ", (desc->action && desc->action->handler) ? '*' : ' ');
610 : : direct = (i == hwirq) && (i < domain->revmap_direct_max_irq);
611 : : seq_printf(m, "%6s%-8s ",
612 : : (hwirq < domain->revmap_size) ? "LINEAR" : "RADIX",
613 : : direct ? "(DIRECT)" : "");
614 : : seq_printf(m, "%s\n", desc->irq_data.domain->name);
615 : : }
616 : :
617 : : raw_spin_unlock_irqrestore(&desc->lock, flags);
618 : : }
619 : :
620 : : return 0;
621 : : }
622 : :
623 : : static int virq_debug_open(struct inode *inode, struct file *file)
624 : : {
625 : : return single_open(file, virq_debug_show, inode->i_private);
626 : : }
627 : :
628 : : static const struct file_operations virq_debug_fops = {
629 : : .open = virq_debug_open,
630 : : .read = seq_read,
631 : : .llseek = seq_lseek,
632 : : .release = single_release,
633 : : };
634 : :
635 : : static int __init irq_debugfs_init(void)
636 : : {
637 : : if (debugfs_create_file("irq_domain_mapping", S_IRUGO, NULL,
638 : : NULL, &virq_debug_fops) == NULL)
639 : : return -ENOMEM;
640 : :
641 : : return 0;
642 : : }
643 : : __initcall(irq_debugfs_init);
644 : : #endif /* CONFIG_IRQ_DOMAIN_DEBUG */
645 : :
646 : : /**
647 : : * irq_domain_xlate_onecell() - Generic xlate for direct one cell bindings
648 : : *
649 : : * Device Tree IRQ specifier translation function which works with one cell
650 : : * bindings where the cell value maps directly to the hwirq number.
651 : : */
652 : 0 : int irq_domain_xlate_onecell(struct irq_domain *d, struct device_node *ctrlr,
653 : : const u32 *intspec, unsigned int intsize,
654 : : unsigned long *out_hwirq, unsigned int *out_type)
655 : : {
656 [ # # ][ # # ]: 0 : if (WARN_ON(intsize < 1))
657 : : return -EINVAL;
658 : 0 : *out_hwirq = intspec[0];
659 : 0 : *out_type = IRQ_TYPE_NONE;
660 : 0 : return 0;
661 : : }
662 : : EXPORT_SYMBOL_GPL(irq_domain_xlate_onecell);
663 : :
664 : : /**
665 : : * irq_domain_xlate_twocell() - Generic xlate for direct two cell bindings
666 : : *
667 : : * Device Tree IRQ specifier translation function which works with two cell
668 : : * bindings where the cell values map directly to the hwirq number
669 : : * and linux irq flags.
670 : : */
671 : 0 : int irq_domain_xlate_twocell(struct irq_domain *d, struct device_node *ctrlr,
672 : : const u32 *intspec, unsigned int intsize,
673 : : irq_hw_number_t *out_hwirq, unsigned int *out_type)
674 : : {
675 [ # # ][ # # ]: 0 : if (WARN_ON(intsize < 2))
676 : : return -EINVAL;
677 : 0 : *out_hwirq = intspec[0];
678 : 0 : *out_type = intspec[1] & IRQ_TYPE_SENSE_MASK;
679 : 0 : return 0;
680 : : }
681 : : EXPORT_SYMBOL_GPL(irq_domain_xlate_twocell);
682 : :
683 : : /**
684 : : * irq_domain_xlate_onetwocell() - Generic xlate for one or two cell bindings
685 : : *
686 : : * Device Tree IRQ specifier translation function which works with either one
687 : : * or two cell bindings where the cell values map directly to the hwirq number
688 : : * and linux irq flags.
689 : : *
690 : : * Note: don't use this function unless your interrupt controller explicitly
691 : : * supports both one and two cell bindings. For the majority of controllers
692 : : * the _onecell() or _twocell() variants above should be used.
693 : : */
694 : 0 : int irq_domain_xlate_onetwocell(struct irq_domain *d,
695 : : struct device_node *ctrlr,
696 : : const u32 *intspec, unsigned int intsize,
697 : : unsigned long *out_hwirq, unsigned int *out_type)
698 : : {
699 [ # # ][ # # ]: 0 : if (WARN_ON(intsize < 1))
700 : : return -EINVAL;
701 : 0 : *out_hwirq = intspec[0];
702 [ # # ]: 0 : *out_type = (intsize > 1) ? intspec[1] : IRQ_TYPE_NONE;
703 : 0 : return 0;
704 : : }
705 : : EXPORT_SYMBOL_GPL(irq_domain_xlate_onetwocell);
706 : :
707 : : const struct irq_domain_ops irq_domain_simple_ops = {
708 : : .xlate = irq_domain_xlate_onetwocell,
709 : : };
710 : : EXPORT_SYMBOL_GPL(irq_domain_simple_ops);
|