aboutsummaryrefslogtreecommitdiffstats
path: root/kernel/irq
diff options
context:
space:
mode:
authorGrant Likely <grant.likely@secretlab.ca>2012-02-14 16:06:55 -0500
committerGrant Likely <grant.likely@secretlab.ca>2012-02-16 08:11:23 -0500
commit1bc04f2cf8c2a1feadbd994f50c40bb145bf2989 (patch)
tree26508a452ca52542ac437f55bb632b48fa607048 /kernel/irq
parenta8db8cf0d894df5f1dcfd4bce9894e0dbcc01c96 (diff)
irq_domain: Add support for base irq and hwirq in legacy mappings
Add support for a legacy mapping where irq = (hwirq - first_hwirq + first_irq) so that a controller driver can allocate a fixed range of irq_descs and use a simple calculation to translate back and forth between linux and hw irq numbers. This is needed to use an irq_domain with many of the ARM interrupt controller drivers that manage their own irq_desc allocations. Ultimately the goal is to migrate those drivers to use the linear revmap, but doing it this way allows each driver to be converted separately which makes the migration path easier. This patch generalizes the IRQ_DOMAIN_MAP_LEGACY method to use (first_irq-first_hwirq) as the offset between hwirq and linux irq number, and adds checks to make sure that the hwirq number does not exceed range assigned to the controller. Signed-off-by: Grant Likely <grant.likely@secretlab.ca> Cc: Rob Herring <rob.herring@calxeda.com> Cc: Benjamin Herrenschmidt <benh@kernel.crashing.org> Cc: Thomas Gleixner <tglx@linutronix.de> Cc: Milton Miller <miltonm@bga.com> Tested-by: Olof Johansson <olof@lixom.net>
Diffstat (limited to 'kernel/irq')
-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)