diff options
Diffstat (limited to 'kernel/irq/irqdomain.c')
| -rw-r--r-- | kernel/irq/irqdomain.c | 106 |
1 files changed, 88 insertions, 18 deletions
diff --git a/kernel/irq/irqdomain.c b/kernel/irq/irqdomain.c index 0e0ba5f840b2..41c1564103f1 100644 --- a/kernel/irq/irqdomain.c +++ b/kernel/irq/irqdomain.c | |||
| @@ -1,3 +1,5 @@ | |||
| 1 | #define pr_fmt(fmt) "irq: " fmt | ||
| 2 | |||
| 1 | #include <linux/debugfs.h> | 3 | #include <linux/debugfs.h> |
| 2 | #include <linux/hardirq.h> | 4 | #include <linux/hardirq.h> |
| 3 | #include <linux/interrupt.h> | 5 | #include <linux/interrupt.h> |
| @@ -56,14 +58,73 @@ static struct irq_domain *irq_domain_alloc(struct device_node *of_node, | |||
| 56 | return domain; | 58 | return domain; |
| 57 | } | 59 | } |
| 58 | 60 | ||
| 61 | static void irq_domain_free(struct irq_domain *domain) | ||
| 62 | { | ||
| 63 | of_node_put(domain->of_node); | ||
| 64 | kfree(domain); | ||
| 65 | } | ||
| 66 | |||
| 59 | static void irq_domain_add(struct irq_domain *domain) | 67 | static void irq_domain_add(struct irq_domain *domain) |
| 60 | { | 68 | { |
| 61 | mutex_lock(&irq_domain_mutex); | 69 | mutex_lock(&irq_domain_mutex); |
| 62 | list_add(&domain->link, &irq_domain_list); | 70 | list_add(&domain->link, &irq_domain_list); |
| 63 | mutex_unlock(&irq_domain_mutex); | 71 | mutex_unlock(&irq_domain_mutex); |
| 64 | pr_debug("irq: Allocated domain of type %d @0x%p\n", | 72 | pr_debug("Allocated domain of type %d @0x%p\n", |
| 73 | domain->revmap_type, domain); | ||
| 74 | } | ||
| 75 | |||
| 76 | /** | ||
| 77 | * irq_domain_remove() - Remove an irq domain. | ||
| 78 | * @domain: domain to remove | ||
| 79 | * | ||
| 80 | * This routine is used to remove an irq domain. The caller must ensure | ||
| 81 | * that all mappings within the domain have been disposed of prior to | ||
| 82 | * use, depending on the revmap type. | ||
| 83 | */ | ||
| 84 | void irq_domain_remove(struct irq_domain *domain) | ||
| 85 | { | ||
| 86 | mutex_lock(&irq_domain_mutex); | ||
| 87 | |||
| 88 | switch (domain->revmap_type) { | ||
| 89 | case IRQ_DOMAIN_MAP_LEGACY: | ||
| 90 | /* | ||
| 91 | * Legacy domains don't manage their own irq_desc | ||
| 92 | * allocations, we expect the caller to handle irq_desc | ||
| 93 | * freeing on their own. | ||
| 94 | */ | ||
| 95 | break; | ||
| 96 | case IRQ_DOMAIN_MAP_TREE: | ||
| 97 | /* | ||
| 98 | * radix_tree_delete() takes care of destroying the root | ||
| 99 | * node when all entries are removed. Shout if there are | ||
| 100 | * any mappings left. | ||
| 101 | */ | ||
| 102 | WARN_ON(domain->revmap_data.tree.height); | ||
| 103 | break; | ||
| 104 | case IRQ_DOMAIN_MAP_LINEAR: | ||
| 105 | kfree(domain->revmap_data.linear.revmap); | ||
| 106 | domain->revmap_data.linear.size = 0; | ||
| 107 | break; | ||
| 108 | case IRQ_DOMAIN_MAP_NOMAP: | ||
| 109 | break; | ||
| 110 | } | ||
| 111 | |||
| 112 | list_del(&domain->link); | ||
| 113 | |||
| 114 | /* | ||
| 115 | * If the going away domain is the default one, reset it. | ||
| 116 | */ | ||
| 117 | if (unlikely(irq_default_domain == domain)) | ||
| 118 | irq_set_default_host(NULL); | ||
| 119 | |||
| 120 | mutex_unlock(&irq_domain_mutex); | ||
| 121 | |||
| 122 | pr_debug("Removed domain of type %d @0x%p\n", | ||
| 65 | domain->revmap_type, domain); | 123 | domain->revmap_type, domain); |
| 124 | |||
| 125 | irq_domain_free(domain); | ||
| 66 | } | 126 | } |
| 127 | EXPORT_SYMBOL_GPL(irq_domain_remove); | ||
| 67 | 128 | ||
| 68 | static unsigned int irq_domain_legacy_revmap(struct irq_domain *domain, | 129 | static unsigned int irq_domain_legacy_revmap(struct irq_domain *domain, |
| 69 | irq_hw_number_t hwirq) | 130 | irq_hw_number_t hwirq) |
| @@ -117,8 +178,7 @@ struct irq_domain *irq_domain_add_legacy(struct device_node *of_node, | |||
| 117 | 178 | ||
| 118 | if (WARN_ON(!irq_data || irq_data->domain)) { | 179 | if (WARN_ON(!irq_data || irq_data->domain)) { |
| 119 | mutex_unlock(&irq_domain_mutex); | 180 | mutex_unlock(&irq_domain_mutex); |
| 120 | of_node_put(domain->of_node); | 181 | irq_domain_free(domain); |
| 121 | kfree(domain); | ||
| 122 | return NULL; | 182 | return NULL; |
| 123 | } | 183 | } |
| 124 | } | 184 | } |
| @@ -152,10 +212,12 @@ struct irq_domain *irq_domain_add_legacy(struct device_node *of_node, | |||
| 152 | irq_domain_add(domain); | 212 | irq_domain_add(domain); |
| 153 | return domain; | 213 | return domain; |
| 154 | } | 214 | } |
| 215 | EXPORT_SYMBOL_GPL(irq_domain_add_legacy); | ||
| 155 | 216 | ||
| 156 | /** | 217 | /** |
| 157 | * irq_domain_add_linear() - Allocate and register a legacy revmap irq_domain. | 218 | * irq_domain_add_linear() - Allocate and register a legacy revmap irq_domain. |
| 158 | * @of_node: pointer to interrupt controller's device tree node. | 219 | * @of_node: pointer to interrupt controller's device tree node. |
| 220 | * @size: Number of interrupts in the domain. | ||
| 159 | * @ops: map/unmap domain callbacks | 221 | * @ops: map/unmap domain callbacks |
| 160 | * @host_data: Controller private data pointer | 222 | * @host_data: Controller private data pointer |
| 161 | */ | 223 | */ |
| @@ -181,6 +243,7 @@ struct irq_domain *irq_domain_add_linear(struct device_node *of_node, | |||
| 181 | irq_domain_add(domain); | 243 | irq_domain_add(domain); |
| 182 | return domain; | 244 | return domain; |
| 183 | } | 245 | } |
| 246 | EXPORT_SYMBOL_GPL(irq_domain_add_linear); | ||
| 184 | 247 | ||
| 185 | struct irq_domain *irq_domain_add_nomap(struct device_node *of_node, | 248 | struct irq_domain *irq_domain_add_nomap(struct device_node *of_node, |
| 186 | unsigned int max_irq, | 249 | unsigned int max_irq, |
| @@ -195,6 +258,7 @@ struct irq_domain *irq_domain_add_nomap(struct device_node *of_node, | |||
| 195 | } | 258 | } |
| 196 | return domain; | 259 | return domain; |
| 197 | } | 260 | } |
| 261 | EXPORT_SYMBOL_GPL(irq_domain_add_nomap); | ||
| 198 | 262 | ||
| 199 | /** | 263 | /** |
| 200 | * irq_domain_add_tree() | 264 | * irq_domain_add_tree() |
| @@ -216,6 +280,7 @@ struct irq_domain *irq_domain_add_tree(struct device_node *of_node, | |||
| 216 | } | 280 | } |
| 217 | return domain; | 281 | return domain; |
| 218 | } | 282 | } |
| 283 | EXPORT_SYMBOL_GPL(irq_domain_add_tree); | ||
| 219 | 284 | ||
| 220 | /** | 285 | /** |
| 221 | * irq_find_host() - Locates a domain for a given device node | 286 | * irq_find_host() - Locates a domain for a given device node |
| @@ -259,10 +324,11 @@ EXPORT_SYMBOL_GPL(irq_find_host); | |||
| 259 | */ | 324 | */ |
| 260 | void irq_set_default_host(struct irq_domain *domain) | 325 | void irq_set_default_host(struct irq_domain *domain) |
| 261 | { | 326 | { |
| 262 | pr_debug("irq: Default domain set to @0x%p\n", domain); | 327 | pr_debug("Default domain set to @0x%p\n", domain); |
| 263 | 328 | ||
| 264 | irq_default_domain = domain; | 329 | irq_default_domain = domain; |
| 265 | } | 330 | } |
| 331 | EXPORT_SYMBOL_GPL(irq_set_default_host); | ||
| 266 | 332 | ||
| 267 | static int irq_setup_virq(struct irq_domain *domain, unsigned int virq, | 333 | static int irq_setup_virq(struct irq_domain *domain, unsigned int virq, |
| 268 | irq_hw_number_t hwirq) | 334 | irq_hw_number_t hwirq) |
| @@ -272,7 +338,7 @@ static int irq_setup_virq(struct irq_domain *domain, unsigned int virq, | |||
| 272 | irq_data->hwirq = hwirq; | 338 | irq_data->hwirq = hwirq; |
| 273 | irq_data->domain = domain; | 339 | irq_data->domain = domain; |
| 274 | if (domain->ops->map(domain, virq, hwirq)) { | 340 | if (domain->ops->map(domain, virq, hwirq)) { |
| 275 | pr_debug("irq: -> mapping failed, freeing\n"); | 341 | pr_debug("irq-%i==>hwirq-0x%lx mapping failed\n", virq, hwirq); |
| 276 | irq_data->domain = NULL; | 342 | irq_data->domain = NULL; |
| 277 | irq_data->hwirq = 0; | 343 | irq_data->hwirq = 0; |
| 278 | return -1; | 344 | return -1; |
| @@ -303,7 +369,7 @@ unsigned int irq_create_direct_mapping(struct irq_domain *domain) | |||
| 303 | 369 | ||
| 304 | virq = irq_alloc_desc_from(1, 0); | 370 | virq = irq_alloc_desc_from(1, 0); |
| 305 | if (!virq) { | 371 | if (!virq) { |
| 306 | pr_debug("irq: create_direct virq allocation failed\n"); | 372 | pr_debug("create_direct virq allocation failed\n"); |
| 307 | return 0; | 373 | return 0; |
| 308 | } | 374 | } |
| 309 | if (virq >= domain->revmap_data.nomap.max_irq) { | 375 | if (virq >= domain->revmap_data.nomap.max_irq) { |
| @@ -312,7 +378,7 @@ unsigned int irq_create_direct_mapping(struct irq_domain *domain) | |||
| 312 | irq_free_desc(virq); | 378 | irq_free_desc(virq); |
| 313 | return 0; | 379 | return 0; |
| 314 | } | 380 | } |
| 315 | pr_debug("irq: create_direct obtained virq %d\n", virq); | 381 | pr_debug("create_direct obtained virq %d\n", virq); |
| 316 | 382 | ||
| 317 | if (irq_setup_virq(domain, virq, virq)) { | 383 | if (irq_setup_virq(domain, virq, virq)) { |
| 318 | irq_free_desc(virq); | 384 | irq_free_desc(virq); |
| @@ -321,6 +387,7 @@ unsigned int irq_create_direct_mapping(struct irq_domain *domain) | |||
| 321 | 387 | ||
| 322 | return virq; | 388 | return virq; |
| 323 | } | 389 | } |
| 390 | EXPORT_SYMBOL_GPL(irq_create_direct_mapping); | ||
| 324 | 391 | ||
| 325 | /** | 392 | /** |
| 326 | * irq_create_mapping() - Map a hardware interrupt into linux irq space | 393 | * irq_create_mapping() - Map a hardware interrupt into linux irq space |
| @@ -338,23 +405,23 @@ unsigned int irq_create_mapping(struct irq_domain *domain, | |||
| 338 | unsigned int hint; | 405 | unsigned int hint; |
| 339 | int virq; | 406 | int virq; |
| 340 | 407 | ||
| 341 | pr_debug("irq: irq_create_mapping(0x%p, 0x%lx)\n", domain, hwirq); | 408 | pr_debug("irq_create_mapping(0x%p, 0x%lx)\n", domain, hwirq); |
| 342 | 409 | ||
| 343 | /* Look for default domain if nececssary */ | 410 | /* Look for default domain if nececssary */ |
| 344 | if (domain == NULL) | 411 | if (domain == NULL) |
| 345 | domain = irq_default_domain; | 412 | domain = irq_default_domain; |
| 346 | if (domain == NULL) { | 413 | if (domain == NULL) { |
| 347 | printk(KERN_WARNING "irq_create_mapping called for" | 414 | pr_warning("irq_create_mapping called for" |
| 348 | " NULL domain, hwirq=%lx\n", hwirq); | 415 | " NULL domain, hwirq=%lx\n", hwirq); |
| 349 | WARN_ON(1); | 416 | WARN_ON(1); |
| 350 | return 0; | 417 | return 0; |
| 351 | } | 418 | } |
| 352 | pr_debug("irq: -> using domain @%p\n", domain); | 419 | pr_debug("-> using domain @%p\n", domain); |
| 353 | 420 | ||
| 354 | /* Check if mapping already exists */ | 421 | /* Check if mapping already exists */ |
| 355 | virq = irq_find_mapping(domain, hwirq); | 422 | virq = irq_find_mapping(domain, hwirq); |
| 356 | if (virq) { | 423 | if (virq) { |
| 357 | pr_debug("irq: -> existing mapping on virq %d\n", virq); | 424 | pr_debug("-> existing mapping on virq %d\n", virq); |
| 358 | return virq; | 425 | return virq; |
| 359 | } | 426 | } |
| 360 | 427 | ||
| @@ -370,7 +437,7 @@ unsigned int irq_create_mapping(struct irq_domain *domain, | |||
| 370 | if (virq <= 0) | 437 | if (virq <= 0) |
| 371 | virq = irq_alloc_desc_from(1, 0); | 438 | virq = irq_alloc_desc_from(1, 0); |
| 372 | if (virq <= 0) { | 439 | if (virq <= 0) { |
| 373 | pr_debug("irq: -> virq allocation failed\n"); | 440 | pr_debug("-> virq allocation failed\n"); |
| 374 | return 0; | 441 | return 0; |
| 375 | } | 442 | } |
| 376 | 443 | ||
| @@ -380,7 +447,7 @@ unsigned int irq_create_mapping(struct irq_domain *domain, | |||
| 380 | return 0; | 447 | return 0; |
| 381 | } | 448 | } |
| 382 | 449 | ||
| 383 | pr_debug("irq: irq %lu on domain %s mapped to virtual irq %u\n", | 450 | pr_debug("irq %lu on domain %s mapped to virtual irq %u\n", |
| 384 | hwirq, domain->of_node ? domain->of_node->full_name : "null", virq); | 451 | hwirq, domain->of_node ? domain->of_node->full_name : "null", virq); |
| 385 | 452 | ||
| 386 | return virq; | 453 | return virq; |
| @@ -409,8 +476,8 @@ unsigned int irq_create_of_mapping(struct device_node *controller, | |||
| 409 | if (intsize > 0) | 476 | if (intsize > 0) |
| 410 | return intspec[0]; | 477 | return intspec[0]; |
| 411 | #endif | 478 | #endif |
| 412 | printk(KERN_WARNING "irq: no irq domain found for %s !\n", | 479 | pr_warning("no irq domain found for %s !\n", |
| 413 | controller->full_name); | 480 | controller->full_name); |
| 414 | return 0; | 481 | return 0; |
| 415 | } | 482 | } |
| 416 | 483 | ||
| @@ -560,6 +627,7 @@ unsigned int irq_radix_revmap_lookup(struct irq_domain *domain, | |||
| 560 | */ | 627 | */ |
| 561 | return irq_data ? irq_data->irq : irq_find_mapping(domain, hwirq); | 628 | return irq_data ? irq_data->irq : irq_find_mapping(domain, hwirq); |
| 562 | } | 629 | } |
| 630 | EXPORT_SYMBOL_GPL(irq_radix_revmap_lookup); | ||
| 563 | 631 | ||
| 564 | /** | 632 | /** |
| 565 | * irq_radix_revmap_insert() - Insert a hw irq to linux irq number mapping. | 633 | * irq_radix_revmap_insert() - Insert a hw irq to linux irq number mapping. |
| @@ -584,6 +652,7 @@ void irq_radix_revmap_insert(struct irq_domain *domain, unsigned int virq, | |||
| 584 | mutex_unlock(&revmap_trees_mutex); | 652 | mutex_unlock(&revmap_trees_mutex); |
| 585 | } | 653 | } |
| 586 | } | 654 | } |
| 655 | EXPORT_SYMBOL_GPL(irq_radix_revmap_insert); | ||
| 587 | 656 | ||
| 588 | /** | 657 | /** |
| 589 | * irq_linear_revmap() - Find a linux irq from a hw irq number. | 658 | * irq_linear_revmap() - Find a linux irq from a hw irq number. |
| @@ -617,6 +686,7 @@ unsigned int irq_linear_revmap(struct irq_domain *domain, | |||
| 617 | 686 | ||
| 618 | return revmap[hwirq]; | 687 | return revmap[hwirq]; |
| 619 | } | 688 | } |
| 689 | EXPORT_SYMBOL_GPL(irq_linear_revmap); | ||
| 620 | 690 | ||
| 621 | #ifdef CONFIG_IRQ_DOMAIN_DEBUG | 691 | #ifdef CONFIG_IRQ_DOMAIN_DEBUG |
| 622 | static int virq_debug_show(struct seq_file *m, void *private) | 692 | static int virq_debug_show(struct seq_file *m, void *private) |
| @@ -691,8 +761,8 @@ static int __init irq_debugfs_init(void) | |||
| 691 | __initcall(irq_debugfs_init); | 761 | __initcall(irq_debugfs_init); |
| 692 | #endif /* CONFIG_IRQ_DOMAIN_DEBUG */ | 762 | #endif /* CONFIG_IRQ_DOMAIN_DEBUG */ |
| 693 | 763 | ||
| 694 | int irq_domain_simple_map(struct irq_domain *d, unsigned int irq, | 764 | static int irq_domain_simple_map(struct irq_domain *d, unsigned int irq, |
| 695 | irq_hw_number_t hwirq) | 765 | irq_hw_number_t hwirq) |
| 696 | { | 766 | { |
| 697 | return 0; | 767 | return 0; |
| 698 | } | 768 | } |
