[PATCH] irqchip/armada-370-xp: Correctly align bits in 'msi_used' bitmap (multi-MSI)

Stefan Roese sr at denx.de
Thu Mar 23 05:34:29 PDT 2017

Testing on an Armada XP platform has shown, that requesting 4 MSI vectors
may lead to a MSI message data value of 0x0f15 on a PCIe device
supporting 4 MSI vectors. This MSI message data register needs to be
aligned to power-of-2 of the supported vector count [1]. The result from
this setup was, that 0x0f14 has been written into this data register
and therefore not all interrupt vectors were available, or even worse,
an incorrect (off by one) IRQ was trigger by the PCIe device.

This patch now fixes this issue by making sure, that the bits are
correctly aligned in the 'msi_used' bitmap. Resulting in the example
from above in a MSI message data value of 0x0f18.

[1] PCI Local Bus Spec:

Signed-off-by: Stefan Roese <sr at denx.de>
Cc: Thomas Petazzoni <thomas.petazzoni at free-electrons.com>
Cc: Gregory CLEMENT <gregory.clement at free-electrons.com>
Cc: Jason Cooper <jason at lakedaemon.net>
Cc: Thomas Gleixner <tglx at linutronix.de>
 drivers/irqchip/irq-armada-370-xp.c | 15 ++++++++++++++-
 1 file changed, 14 insertions(+), 1 deletion(-)

diff --git a/drivers/irqchip/irq-armada-370-xp.c b/drivers/irqchip/irq-armada-370-xp.c
index eb0d4d41b156..793862978f19 100644
--- a/drivers/irqchip/irq-armada-370-xp.c
+++ b/drivers/irqchip/irq-armada-370-xp.c
@@ -151,11 +151,24 @@ static int armada_370_xp_msi_alloc(struct irq_domain *domain, unsigned int virq,
 				   unsigned int nr_irqs, void *args)
 	int hwirq, i;
+	int nr_irqs_pow2;
+	/*
+	 * Round number of requested irqs up to next power of 2. This is
+	 * needed since the MSI message data register in the configuration
+	 * space is only allowed to have the low order bits changed for
+	 * multi MSI vectors. This result will be used to allocated the
+	 * bits in the "msi_used" bitmap for the hwirq vectors, this time
+	 * aligned to "nr_irqs_pow2".
+	 *
+	 * Please look at PCI Local Bus Spec: for further details.
+	 */
+	nr_irqs_pow2 = 1 << fls(nr_irqs - 1);
 	hwirq = bitmap_find_next_zero_area(msi_used, PCI_MSI_DOORBELL_NR,
-					   0, nr_irqs, 0);
+					   0, nr_irqs, nr_irqs_pow2 - 1);
 	if (hwirq >= PCI_MSI_DOORBELL_NR) {
 		return -ENOSPC;

More information about the linux-arm-kernel mailing list