From 57f2d009fb198181c53f768233f76087ab918ce1 Mon Sep 17 00:00:00 2001
From: Andre Przywara <andre.przywara@arm.com>
Date: Mon, 16 Sep 2024 16:11:50 +0100
Subject: [PATCH] fix(gicv3): do not assume redistributors are powered down

When initialising a GICv3 compatible interrupt controller, we currently
assume that the GIC is still in its reset state, which means
the GICR_WAKER.ProcessorSleep bit is set. There is an "assert" in the
GIC setup function to check this.
However when using RESET_TO_BL31, there might be prior firmware running,
and it might have used the GIC already. This is for instance the case on
the Allwinner A523 SoC, where the BootROM initialises the GIC to use it
when handling the built-in USB debug protocol.

Drop the assert, which is not the right thing to do here anyway: it's
not checking an internal state. Instead return early when the
redistributor is already marked as active.
Also keep waiting if ChildrenAsleep is unexpectedly set, but warn about
this.

This fixes booting TF-A on an Allwinner A523 SoC when using the USB
debug mode.

Change-Id: I5be9e1b0489d33b8371fff484e526483d5f3d937
Signed-off-by: Andre Przywara <andre.przywara@arm.com>
---
 drivers/arm/gic/v3/gicv3_helpers.c | 17 ++++++++++++++---
 1 file changed, 14 insertions(+), 3 deletions(-)

diff --git a/drivers/arm/gic/v3/gicv3_helpers.c b/drivers/arm/gic/v3/gicv3_helpers.c
index b27debfa7..62ae0d276 100644
--- a/drivers/arm/gic/v3/gicv3_helpers.c
+++ b/drivers/arm/gic/v3/gicv3_helpers.c
@@ -35,11 +35,22 @@ uintptr_t gicv3_get_multichip_base(uint32_t spi_id, uintptr_t gicd_base)
  *****************************************************************************/
 void gicv3_rdistif_mark_core_awake(uintptr_t gicr_base)
 {
+	uint32_t waker = gicr_read_waker(gicr_base);
+
+	/* Only try to mark it as awake when it is asleep. */
+	if ((waker & WAKER_PS_BIT) == 0U) {
+		return;
+	}
+
 	/*
-	 * The WAKER_PS_BIT should be changed to 0
-	 * only when WAKER_CA_BIT is 1.
+	 * ProcessorSleep must only be changed when ChildrenAsleep is 1.
+	 * If PS is 1 and CA isn't, wait for that to happen, but warn.
 	 */
-	assert((gicr_read_waker(gicr_base) & WAKER_CA_BIT) != 0U);
+	if ((waker & WAKER_CA_BIT) == 0U) {
+		WARN("GICR_WAKER.ChildrenAsleep unexpectedly set, waiting...\n");
+		while ((gicr_read_waker(gicr_base) & WAKER_CA_BIT) == 0U) {
+		}
+	}
 
 	/* Mark the connected core as awake */
 	gicr_write_waker(gicr_base, gicr_read_waker(gicr_base) & ~WAKER_PS_BIT);