diff options
Diffstat (limited to 'kernel')
-rw-r--r-- | kernel/irq/irqdomain.c | 96 |
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 | ||
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) |