aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-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)