aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMarc Zyngier <marc.zyngier@arm.com>2018-02-06 13:55:33 -0500
committerMarc Zyngier <marc.zyngier@arm.com>2018-02-16 08:47:58 -0500
commitde337ee301422756dff43d6c60fbb0400c1235e9 (patch)
treee9b697ef630c31e9ce5aafc336ed650678408cae
parent95a2562590c2f64a0398183f978d5cf3db6d0284 (diff)
irqchip/gic-v2m: Add PCI Multi-MSI support
We'd never implemented Multi-MSI support with GICv2m, because it is weird and clunky, and you'd think people would rather use MSI-X. Turns out there is still plenty of devices out there that rely on Multi-MSI. Oh well, let's teach that trick to the v2m widget, it is not a big deal anyway. Signed-off-by: Marc Zyngier <marc.zyngier@arm.com>
-rw-r--r--drivers/irqchip/irq-gic-v2m.c46
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
95static struct msi_domain_info gicv2m_msi_domain_info = { 95static 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
158static void gicv2m_unalloc_msi(struct v2m_data *v2m, unsigned int hwirq) 158static 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
200fail:
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
207static void gicv2m_irq_domain_free(struct irq_domain *domain, 206static 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