ARM: GICv3: poke_irq: make RWP optional
authorAndre Przywara <andre.przywara@linaro.org>
Fri, 9 Mar 2018 15:11:32 +0000 (15:11 +0000)
committerJulien Grall <julien.grall@arm.com>
Mon, 12 Mar 2018 11:45:36 +0000 (11:45 +0000)
A GICv3 hardware implementation can be implemented in several parts that
communicate with each other (think multi-socket systems).
To make sure that critical settings have arrived at all endpoints, some
bits are tracked using the RWP bit in the GICD_CTLR register, which
signals whether a register write is still in progress.
However this only applies to *some* registers, namely the bits in the
GICD_ICENABLER (disabling interrupts) and some bits in the GICD_CTLR
register (cf. Arm IHI 0069D, 8.9.4: RWP, bit[31]).
But our gicv3_poke_irq() was always polling this bit before returning,
resulting in pointless MMIO reads for many registers.
Add an option to gicv3_poke_irq() to state whether we want to wait for
this bit and use it accordingly to match the spec.
Replace a "1 << " with a "1U << " on the way to fix a potentially
undefined behaviour when the argument evaluates to 31.

Signed-off-by: Andre Przywara <andre.przywara@linaro.org>
Reviewed-by: Julien Grall <julien.grall@arm.com>
xen/arch/arm/gic-v3.c

index 8b41704cf18dd9bf3f9677fe0f64dc5198d2c52a..09b49a07d541697bfe7f4e88745444de63b083c9 100644 (file)
@@ -428,9 +428,9 @@ static void gicv3_dump_state(const struct vcpu *v)
     }
 }
 
-static void gicv3_poke_irq(struct irq_desc *irqd, u32 offset)
+static void gicv3_poke_irq(struct irq_desc *irqd, u32 offset, bool wait_for_rwp)
 {
-    u32 mask = 1 << (irqd->irq % 32);
+    u32 mask = 1U << (irqd->irq % 32);
     void __iomem *base;
 
     if ( irqd->irq < NR_GIC_LOCAL_IRQS )
@@ -439,17 +439,19 @@ static void gicv3_poke_irq(struct irq_desc *irqd, u32 offset)
         base = GICD;
 
     writel_relaxed(mask, base + offset + (irqd->irq / 32) * 4);
-    gicv3_wait_for_rwp(irqd->irq);
+
+    if ( wait_for_rwp )
+        gicv3_wait_for_rwp(irqd->irq);
 }
 
 static void gicv3_unmask_irq(struct irq_desc *irqd)
 {
-    gicv3_poke_irq(irqd, GICD_ISENABLER);
+    gicv3_poke_irq(irqd, GICD_ISENABLER, false);
 }
 
 static void gicv3_mask_irq(struct irq_desc *irqd)
 {
-    gicv3_poke_irq(irqd, GICD_ICENABLER);
+    gicv3_poke_irq(irqd, GICD_ICENABLER, true);
 }
 
 static void gicv3_eoi_irq(struct irq_desc *irqd)