aboutsummaryrefslogtreecommitdiffstats
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
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>
-rw-r--r--arch/powerpc/include/asm/irq.h3
-rw-r--r--arch/powerpc/sysdev/i8259.c2
-rw-r--r--arch/powerpc/sysdev/tsi108_pci.c2
-rw-r--r--include/linux/irqdomain.h20
-rw-r--r--kernel/irq/irqdomain.c96
5 files changed, 85 insertions, 38 deletions
diff --git a/arch/powerpc/include/asm/irq.h b/arch/powerpc/include/asm/irq.h
index 728cc30d04ea..fe0b09dceb7d 100644
--- a/arch/powerpc/include/asm/irq.h
+++ b/arch/powerpc/include/asm/irq.h
@@ -36,9 +36,6 @@ extern atomic_t ppc_n_lost_interrupts;
36/* Total number of virq in the platform */ 36/* Total number of virq in the platform */
37#define NR_IRQS CONFIG_NR_IRQS 37#define NR_IRQS CONFIG_NR_IRQS
38 38
39/* Number of irqs reserved for the legacy controller */
40#define NUM_ISA_INTERRUPTS 16
41
42/* Same thing, used by the generic IRQ code */ 39/* Same thing, used by the generic IRQ code */
43#define NR_IRQS_LEGACY NUM_ISA_INTERRUPTS 40#define NR_IRQS_LEGACY NUM_ISA_INTERRUPTS
44 41
diff --git a/arch/powerpc/sysdev/i8259.c b/arch/powerpc/sysdev/i8259.c
index 573a73bd954a..997df6a7ab5d 100644
--- a/arch/powerpc/sysdev/i8259.c
+++ b/arch/powerpc/sysdev/i8259.c
@@ -263,7 +263,7 @@ void i8259_init(struct device_node *node, unsigned long intack_addr)
263 raw_spin_unlock_irqrestore(&i8259_lock, flags); 263 raw_spin_unlock_irqrestore(&i8259_lock, flags);
264 264
265 /* create a legacy host */ 265 /* create a legacy host */
266 i8259_host = irq_domain_add_legacy(node, &i8259_host_ops, NULL); 266 i8259_host = irq_domain_add_legacy_isa(node, &i8259_host_ops, NULL);
267 if (i8259_host == NULL) { 267 if (i8259_host == NULL) {
268 printk(KERN_ERR "i8259: failed to allocate irq host !\n"); 268 printk(KERN_ERR "i8259: failed to allocate irq host !\n");
269 return; 269 return;
diff --git a/arch/powerpc/sysdev/tsi108_pci.c b/arch/powerpc/sysdev/tsi108_pci.c
index 1be26f4b9c96..188012c58f7f 100644
--- a/arch/powerpc/sysdev/tsi108_pci.c
+++ b/arch/powerpc/sysdev/tsi108_pci.c
@@ -419,7 +419,7 @@ void __init tsi108_pci_int_init(struct device_node *node)
419{ 419{
420 DBG("Tsi108_pci_int_init: initializing PCI interrupts\n"); 420 DBG("Tsi108_pci_int_init: initializing PCI interrupts\n");
421 421
422 pci_irq_host = irq_domain_add_legacy(node, &pci_irq_domain_ops, NULL); 422 pci_irq_host = irq_domain_add_legacy_isa(node, &pci_irq_domain_ops, NULL);
423 if (pci_irq_host == NULL) { 423 if (pci_irq_host == NULL) {
424 printk(KERN_ERR "pci_irq_host: failed to allocate irq domain!\n"); 424 printk(KERN_ERR "pci_irq_host: failed to allocate irq domain!\n");
425 return; 425 return;
diff --git a/include/linux/irqdomain.h b/include/linux/irqdomain.h
index f95553fa6872..7fef39ed5523 100644
--- a/include/linux/irqdomain.h
+++ b/include/linux/irqdomain.h
@@ -39,6 +39,9 @@ struct device_node;
39struct irq_domain; 39struct irq_domain;
40struct of_device_id; 40struct of_device_id;
41 41
42/* Number of irqs reserved for a legacy isa controller */
43#define NUM_ISA_INTERRUPTS 16
44
42/* This type is the placeholder for a hardware interrupt number. It has to 45/* This type is the placeholder for a hardware interrupt number. It has to
43 * be big enough to enclose whatever representation is used by a given 46 * be big enough to enclose whatever representation is used by a given
44 * platform. 47 * platform.
@@ -98,6 +101,11 @@ struct irq_domain {
98 union { 101 union {
99 struct { 102 struct {
100 unsigned int size; 103 unsigned int size;
104 unsigned int first_irq;
105 irq_hw_number_t first_hwirq;
106 } legacy;
107 struct {
108 unsigned int size;
101 unsigned int *revmap; 109 unsigned int *revmap;
102 } linear; 110 } linear;
103 struct radix_tree_root tree; 111 struct radix_tree_root tree;
@@ -117,6 +125,9 @@ struct irq_domain {
117#ifdef CONFIG_IRQ_DOMAIN 125#ifdef CONFIG_IRQ_DOMAIN
118#ifdef CONFIG_PPC 126#ifdef CONFIG_PPC
119struct irq_domain *irq_domain_add_legacy(struct device_node *of_node, 127struct irq_domain *irq_domain_add_legacy(struct device_node *of_node,
128 unsigned int size,
129 unsigned int first_irq,
130 irq_hw_number_t first_hwirq,
120 struct irq_domain_ops *ops, 131 struct irq_domain_ops *ops,
121 void *host_data); 132 void *host_data);
122struct irq_domain *irq_domain_add_linear(struct device_node *of_node, 133struct irq_domain *irq_domain_add_linear(struct device_node *of_node,
@@ -130,11 +141,18 @@ struct irq_domain *irq_domain_add_tree(struct device_node *of_node,
130 struct irq_domain_ops *ops, 141 struct irq_domain_ops *ops,
131 void *host_data); 142 void *host_data);
132 143
133
134extern struct irq_domain *irq_find_host(struct device_node *node); 144extern struct irq_domain *irq_find_host(struct device_node *node);
135extern void irq_set_default_host(struct irq_domain *host); 145extern void irq_set_default_host(struct irq_domain *host);
136extern void irq_set_virq_count(unsigned int count); 146extern void irq_set_virq_count(unsigned int count);
137 147
148static inline struct irq_domain *irq_domain_add_legacy_isa(
149 struct device_node *of_node,
150 struct irq_domain_ops *ops,
151 void *host_data)
152{
153 return irq_domain_add_legacy(of_node, NUM_ISA_INTERRUPTS, 0, 0, ops,
154 host_data);
155}
138 156
139extern unsigned int irq_create_mapping(struct irq_domain *host, 157extern unsigned int irq_create_mapping(struct irq_domain *host,
140 irq_hw_number_t hwirq); 158 irq_hw_number_t hwirq);
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)