diff options
-rw-r--r-- | arch/powerpc/include/asm/irq.h | 3 | ||||
-rw-r--r-- | arch/powerpc/sysdev/i8259.c | 2 | ||||
-rw-r--r-- | arch/powerpc/sysdev/tsi108_pci.c | 2 | ||||
-rw-r--r-- | include/linux/irqdomain.h | 20 | ||||
-rw-r--r-- | kernel/irq/irqdomain.c | 96 |
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; | |||
39 | struct irq_domain; | 39 | struct irq_domain; |
40 | struct of_device_id; | 40 | struct 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 |
119 | struct irq_domain *irq_domain_add_legacy(struct device_node *of_node, | 127 | struct 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); |
122 | struct irq_domain *irq_domain_add_linear(struct device_node *of_node, | 133 | struct 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 | |||
134 | extern struct irq_domain *irq_find_host(struct device_node *node); | 144 | extern struct irq_domain *irq_find_host(struct device_node *node); |
135 | extern void irq_set_default_host(struct irq_domain *host); | 145 | extern void irq_set_default_host(struct irq_domain *host); |
136 | extern void irq_set_virq_count(unsigned int count); | 146 | extern void irq_set_virq_count(unsigned int count); |
137 | 147 | ||
148 | static 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 | ||
139 | extern unsigned int irq_create_mapping(struct irq_domain *host, | 157 | extern 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 | ||
78 | static 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 | */ |
87 | struct irq_domain *irq_domain_add_legacy(struct device_node *of_node, | 104 | struct 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) |