diff options
| -rw-r--r-- | include/linux/irqdomain.h | 3 | ||||
| -rw-r--r-- | kernel/irq/irqdomain.c | 106 |
2 files changed, 89 insertions, 20 deletions
diff --git a/include/linux/irqdomain.h b/include/linux/irqdomain.h index c65740d76e66..5abb533eb8eb 100644 --- a/include/linux/irqdomain.h +++ b/include/linux/irqdomain.h | |||
| @@ -141,9 +141,8 @@ static inline struct irq_domain *irq_domain_add_legacy_isa( | |||
| 141 | return irq_domain_add_legacy(of_node, NUM_ISA_INTERRUPTS, 0, 0, ops, | 141 | return irq_domain_add_legacy(of_node, NUM_ISA_INTERRUPTS, 0, 0, ops, |
| 142 | host_data); | 142 | host_data); |
| 143 | } | 143 | } |
| 144 | extern struct irq_domain *irq_find_host(struct device_node *node); | ||
| 145 | extern void irq_set_default_host(struct irq_domain *host); | ||
| 146 | 144 | ||
| 145 | extern void irq_domain_remove(struct irq_domain *host); | ||
| 147 | 146 | ||
| 148 | extern unsigned int irq_create_mapping(struct irq_domain *host, | 147 | extern unsigned int irq_create_mapping(struct irq_domain *host, |
| 149 | irq_hw_number_t hwirq); | 148 | irq_hw_number_t hwirq); |
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); | ||
