diff options
| -rw-r--r-- | drivers/irqchip/irq-gic-v2m.c | 46 |
1 files changed, 22 insertions, 24 deletions
diff --git a/drivers/irqchip/irq-gic-v2m.c b/drivers/irqchip/irq-gic-v2m.c index 993a8426a453..1ff38aff9f29 100644 --- a/drivers/irqchip/irq-gic-v2m.c +++ b/drivers/irqchip/irq-gic-v2m.c | |||
| @@ -94,7 +94,7 @@ static struct irq_chip gicv2m_msi_irq_chip = { | |||
| 94 | 94 | ||
| 95 | static struct msi_domain_info gicv2m_msi_domain_info = { | 95 | static struct msi_domain_info gicv2m_msi_domain_info = { |
| 96 | .flags = (MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS | | 96 | .flags = (MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS | |
| 97 | MSI_FLAG_PCI_MSIX), | 97 | MSI_FLAG_PCI_MSIX | MSI_FLAG_MULTI_PCI_MSI), |
| 98 | .chip = &gicv2m_msi_irq_chip, | 98 | .chip = &gicv2m_msi_irq_chip, |
| 99 | }; | 99 | }; |
| 100 | 100 | ||
| @@ -155,18 +155,12 @@ static int gicv2m_irq_gic_domain_alloc(struct irq_domain *domain, | |||
| 155 | return 0; | 155 | return 0; |
| 156 | } | 156 | } |
| 157 | 157 | ||
| 158 | static void gicv2m_unalloc_msi(struct v2m_data *v2m, unsigned int hwirq) | 158 | static void gicv2m_unalloc_msi(struct v2m_data *v2m, unsigned int hwirq, |
| 159 | int nr_irqs) | ||
| 159 | { | 160 | { |
| 160 | int pos; | ||
| 161 | |||
| 162 | pos = hwirq - v2m->spi_start; | ||
| 163 | if (pos < 0 || pos >= v2m->nr_spis) { | ||
| 164 | pr_err("Failed to teardown msi. Invalid hwirq %d\n", hwirq); | ||
| 165 | return; | ||
| 166 | } | ||
| 167 | |||
| 168 | spin_lock(&v2m_lock); | 161 | spin_lock(&v2m_lock); |
| 169 | __clear_bit(pos, v2m->bm); | 162 | bitmap_release_region(v2m->bm, hwirq - v2m->spi_start, |
| 163 | get_count_order(nr_irqs)); | ||
| 170 | spin_unlock(&v2m_lock); | 164 | spin_unlock(&v2m_lock); |
| 171 | } | 165 | } |
| 172 | 166 | ||
| @@ -174,13 +168,13 @@ static int gicv2m_irq_domain_alloc(struct irq_domain *domain, unsigned int virq, | |||
| 174 | unsigned int nr_irqs, void *args) | 168 | unsigned int nr_irqs, void *args) |
| 175 | { | 169 | { |
| 176 | struct v2m_data *v2m = NULL, *tmp; | 170 | struct v2m_data *v2m = NULL, *tmp; |
| 177 | int hwirq, offset, err = 0; | 171 | int hwirq, offset, i, err = 0; |
| 178 | 172 | ||
| 179 | spin_lock(&v2m_lock); | 173 | spin_lock(&v2m_lock); |
| 180 | list_for_each_entry(tmp, &v2m_nodes, entry) { | 174 | list_for_each_entry(tmp, &v2m_nodes, entry) { |
| 181 | offset = find_first_zero_bit(tmp->bm, tmp->nr_spis); | 175 | offset = bitmap_find_free_region(tmp->bm, tmp->nr_spis, |
| 182 | if (offset < tmp->nr_spis) { | 176 | get_count_order(nr_irqs)); |
| 183 | __set_bit(offset, tmp->bm); | 177 | if (offset >= 0) { |
| 184 | v2m = tmp; | 178 | v2m = tmp; |
| 185 | break; | 179 | break; |
| 186 | } | 180 | } |
| @@ -192,16 +186,21 @@ static int gicv2m_irq_domain_alloc(struct irq_domain *domain, unsigned int virq, | |||
| 192 | 186 | ||
| 193 | hwirq = v2m->spi_start + offset; | 187 | hwirq = v2m->spi_start + offset; |
| 194 | 188 | ||
| 195 | err = gicv2m_irq_gic_domain_alloc(domain, virq, hwirq); | 189 | for (i = 0; i < nr_irqs; i++) { |
| 196 | if (err) { | 190 | err = gicv2m_irq_gic_domain_alloc(domain, virq + i, hwirq + i); |
| 197 | gicv2m_unalloc_msi(v2m, hwirq); | 191 | if (err) |
| 198 | return err; | 192 | goto fail; |
| 199 | } | ||
| 200 | 193 | ||
| 201 | irq_domain_set_hwirq_and_chip(domain, virq, hwirq, | 194 | irq_domain_set_hwirq_and_chip(domain, virq + i, hwirq + i, |
| 202 | &gicv2m_irq_chip, v2m); | 195 | &gicv2m_irq_chip, v2m); |
| 196 | } | ||
| 203 | 197 | ||
| 204 | return 0; | 198 | return 0; |
| 199 | |||
| 200 | fail: | ||
| 201 | irq_domain_free_irqs_parent(domain, virq, nr_irqs); | ||
| 202 | gicv2m_unalloc_msi(v2m, hwirq, get_count_order(nr_irqs)); | ||
| 203 | return err; | ||
| 205 | } | 204 | } |
| 206 | 205 | ||
| 207 | static void gicv2m_irq_domain_free(struct irq_domain *domain, | 206 | static void gicv2m_irq_domain_free(struct irq_domain *domain, |
| @@ -210,8 +209,7 @@ static void gicv2m_irq_domain_free(struct irq_domain *domain, | |||
| 210 | struct irq_data *d = irq_domain_get_irq_data(domain, virq); | 209 | struct irq_data *d = irq_domain_get_irq_data(domain, virq); |
| 211 | struct v2m_data *v2m = irq_data_get_irq_chip_data(d); | 210 | struct v2m_data *v2m = irq_data_get_irq_chip_data(d); |
| 212 | 211 | ||
| 213 | BUG_ON(nr_irqs != 1); | 212 | gicv2m_unalloc_msi(v2m, d->hwirq, nr_irqs); |
| 214 | gicv2m_unalloc_msi(v2m, d->hwirq); | ||
| 215 | irq_domain_free_irqs_parent(domain, virq, nr_irqs); | 213 | irq_domain_free_irqs_parent(domain, virq, nr_irqs); |
| 216 | } | 214 | } |
| 217 | 215 | ||
