aboutsummaryrefslogtreecommitdiffstats
path: root/kernel
diff options
context:
space:
mode:
Diffstat (limited to 'kernel')
-rw-r--r--kernel/irq/irqdomain.c96
1 files changed, 64 insertions, 32 deletions
diff --git a/kernel/irq/irqdomain.c b/kernel/irq/irqdomain.c
index acedba1a2651..c6740d72073e 100644
--- a/kernel/irq/irqdomain.c
+++ b/kernel/irq/irqdomain.c
@@ -13,7 +13,8 @@
13#include <linux/smp.h> 13#include <linux/smp.h>
14#include <linux/fs.h> 14#include <linux/fs.h>
15 15
16#define IRQ_DOMAIN_MAP_LEGACY 0 /* legacy 8259, gets irqs 1..15 */ 16#define IRQ_DOMAIN_MAP_LEGACY 0 /* driver allocated fixed range of irqs.
17 * ie. legacy 8259, gets irqs 1..15 */
17#define IRQ_DOMAIN_MAP_NOMAP 1 /* no fast reverse mapping */ 18#define IRQ_DOMAIN_MAP_NOMAP 1 /* no fast reverse mapping */
18#define IRQ_DOMAIN_MAP_LINEAR 2 /* linear map of interrupts */ 19#define IRQ_DOMAIN_MAP_LINEAR 2 /* linear map of interrupts */
19#define IRQ_DOMAIN_MAP_TREE 3 /* radix tree */ 20#define IRQ_DOMAIN_MAP_TREE 3 /* radix tree */
@@ -74,9 +75,25 @@ static void irq_domain_add(struct irq_domain *domain)
74 domain->revmap_type, domain); 75 domain->revmap_type, domain);
75} 76}
76 77
78static unsigned int irq_domain_legacy_revmap(struct irq_domain *domain,
79 irq_hw_number_t hwirq)
80{
81 irq_hw_number_t first_hwirq = domain->revmap_data.legacy.first_hwirq;
82 int size = domain->revmap_data.legacy.size;
83
84 if (WARN_ON(hwirq < first_hwirq || hwirq >= first_hwirq + size))
85 return 0;
86 return hwirq - first_hwirq + domain->revmap_data.legacy.first_irq;
87}
88
77/** 89/**
78 * irq_domain_add_legacy() - Allocate and register a legacy revmap irq_domain. 90 * irq_domain_add_legacy() - Allocate and register a legacy revmap irq_domain.
79 * @of_node: pointer to interrupt controller's device tree node. 91 * @of_node: pointer to interrupt controller's device tree node.
92 * @size: total number of irqs in legacy mapping
93 * @first_irq: first number of irq block assigned to the domain
94 * @first_hwirq: first hwirq number to use for the translation. Should normally
95 * be '0', but a positive integer can be used if the effective
96 * hwirqs numbering does not begin at zero.
80 * @ops: map/unmap domain callbacks 97 * @ops: map/unmap domain callbacks
81 * @host_data: Controller private data pointer 98 * @host_data: Controller private data pointer
82 * 99 *
@@ -85,44 +102,64 @@ static void irq_domain_add(struct irq_domain *domain)
85 * a legacy controller). 102 * a legacy controller).
86 */ 103 */
87struct irq_domain *irq_domain_add_legacy(struct device_node *of_node, 104struct irq_domain *irq_domain_add_legacy(struct device_node *of_node,
105 unsigned int size,
106 unsigned int first_irq,
107 irq_hw_number_t first_hwirq,
88 struct irq_domain_ops *ops, 108 struct irq_domain_ops *ops,
89 void *host_data) 109 void *host_data)
90{ 110{
91 struct irq_domain *domain, *h; 111 struct irq_domain *domain;
92 unsigned int i; 112 unsigned int i;
93 113
94 domain = irq_domain_alloc(of_node, IRQ_DOMAIN_MAP_LEGACY, ops, host_data); 114 domain = irq_domain_alloc(of_node, IRQ_DOMAIN_MAP_LEGACY, ops, host_data);
95 if (!domain) 115 if (!domain)
96 return NULL; 116 return NULL;
97 117
118 domain->revmap_data.legacy.first_irq = first_irq;
119 domain->revmap_data.legacy.first_hwirq = first_hwirq;
120 domain->revmap_data.legacy.size = size;
121
98 mutex_lock(&irq_domain_mutex); 122 mutex_lock(&irq_domain_mutex);
99 /* Make sure only one legacy controller can be created */ 123 /* Verify that all the irqs are available */
100 list_for_each_entry(h, &irq_domain_list, link) { 124 for (i = 0; i < size; i++) {
101 if (WARN_ON(h->revmap_type == IRQ_DOMAIN_MAP_LEGACY)) { 125 int irq = first_irq + i;
126 struct irq_data *irq_data = irq_get_irq_data(irq);
127
128 if (WARN_ON(!irq_data || irq_data->domain)) {
102 mutex_unlock(&irq_domain_mutex); 129 mutex_unlock(&irq_domain_mutex);
103 of_node_put(domain->of_node); 130 of_node_put(domain->of_node);
104 kfree(domain); 131 kfree(domain);
105 return NULL; 132 return NULL;
106 } 133 }
107 } 134 }
108 list_add(&domain->link, &irq_domain_list);
109 mutex_unlock(&irq_domain_mutex);
110 135
111 /* setup us as the domain for all legacy interrupts */ 136 /* Claim all of the irqs before registering a legacy domain */
112 for (i = 1; i < NUM_ISA_INTERRUPTS; i++) { 137 for (i = 0; i < size; i++) {
113 struct irq_data *irq_data = irq_get_irq_data(i); 138 struct irq_data *irq_data = irq_get_irq_data(first_irq + i);
114 irq_data->hwirq = i; 139 irq_data->hwirq = first_hwirq + i;
115 irq_data->domain = domain; 140 irq_data->domain = domain;
141 }
142 mutex_unlock(&irq_domain_mutex);
143
144 for (i = 0; i < size; i++) {
145 int irq = first_irq + i;
146 int hwirq = first_hwirq + i;
147
148 /* IRQ0 gets ignored */
149 if (!irq)
150 continue;
116 151
117 /* Legacy flags are left to default at this point, 152 /* Legacy flags are left to default at this point,
118 * one can then use irq_create_mapping() to 153 * one can then use irq_create_mapping() to
119 * explicitly change them 154 * explicitly change them
120 */ 155 */
121 ops->map(domain, i, i); 156 ops->map(domain, irq, hwirq);
122 157
123 /* Clear norequest flags */ 158 /* Clear norequest flags */
124 irq_clear_status_flags(i, IRQ_NOREQUEST); 159 irq_clear_status_flags(irq, IRQ_NOREQUEST);
125 } 160 }
161
162 irq_domain_add(domain);
126 return domain; 163 return domain;
127} 164}
128 165
@@ -338,24 +375,19 @@ unsigned int irq_create_mapping(struct irq_domain *domain,
338 } 375 }
339 376
340 /* Get a virtual interrupt number */ 377 /* Get a virtual interrupt number */
341 if (domain->revmap_type == IRQ_DOMAIN_MAP_LEGACY) { 378 if (domain->revmap_type == IRQ_DOMAIN_MAP_LEGACY)
342 /* Handle legacy */ 379 return irq_domain_legacy_revmap(domain, hwirq);
343 virq = (unsigned int)hwirq; 380
344 if (virq == 0 || virq >= NUM_ISA_INTERRUPTS) 381 /* Allocate a virtual interrupt number */
345 return 0; 382 hint = hwirq % irq_virq_count;
346 return virq; 383 if (hint == 0)
347 } else { 384 hint++;
348 /* Allocate a virtual interrupt number */ 385 virq = irq_alloc_desc_from(hint, 0);
349 hint = hwirq % irq_virq_count; 386 if (!virq)
350 if (hint == 0) 387 virq = irq_alloc_desc_from(1, 0);
351 hint++; 388 if (!virq) {
352 virq = irq_alloc_desc_from(hint, 0); 389 pr_debug("irq: -> virq allocation failed\n");
353 if (!virq) 390 return 0;
354 virq = irq_alloc_desc_from(1, 0);
355 if (!virq) {
356 pr_debug("irq: -> virq allocation failed\n");
357 return 0;
358 }
359 } 391 }
360 392
361 if (irq_setup_virq(domain, virq, hwirq)) { 393 if (irq_setup_virq(domain, virq, hwirq)) {
@@ -483,7 +515,7 @@ unsigned int irq_find_mapping(struct irq_domain *domain,
483 515
484 /* legacy -> bail early */ 516 /* legacy -> bail early */
485 if (domain->revmap_type == IRQ_DOMAIN_MAP_LEGACY) 517 if (domain->revmap_type == IRQ_DOMAIN_MAP_LEGACY)
486 return hwirq; 518 return irq_domain_legacy_revmap(domain, hwirq);
487 519
488 /* Slow path does a linear search of the map */ 520 /* Slow path does a linear search of the map */
489 if (hint == 0) 521 if (hint == 0)