fix(gic600): implement workaround to forward highest priority interrupt

If the interrupt being targeted is released from the CPU before the
CLEAR command is sent to the CPU then a subsequent SET command may not
be delivered in a finite time. To workaround this, issue an unblocking
event by toggling GICR_CTLR.DPG* bits after clearing the cpu group
enable (EnableGrp* bits of GIC CPU interface register)
This fix is implemented as per the errata 2384374-part 2 workaround
mentioned here:
https://developer.arm.com/documentation/sden892601/latest/

Change-Id: I13926ceeb7740fa4c05cc5b43170e7ce49598f70
Signed-off-by: Manish V Badarkhe <Manish.Badarkhe@arm.com>
diff --git a/drivers/arm/gic/v3/gic-x00.c b/drivers/arm/gic/v3/gic-x00.c
index aaef485..75eb69a 100644
--- a/drivers/arm/gic/v3/gic-x00.c
+++ b/drivers/arm/gic/v3/gic-x00.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2017-2020, ARM Limited and Contributors. All rights reserved.
+ * Copyright (c) 2017-2022, Arm Limited and Contributors. All rights reserved.
  * Copyright (c) 2020, NVIDIA Corporation. All rights reserved.
  *
  * SPDX-License-Identifier: BSD-3-Clause
@@ -42,6 +42,8 @@
 #define PWRR_ON				(0U << PWRR_RDPD_SHIFT)
 #define PWRR_OFF			(1U << PWRR_RDPD_SHIFT)
 
+static bool gic600_errata_wa_2384374 __unused;
+
 #if GICV3_SUPPORT_GIC600
 
 /* GIC-600/700 specific accessor functions */
@@ -170,3 +172,60 @@
 	}
 #endif
 }
+
+#if GIC600_ERRATA_WA_2384374
+/*******************************************************************************
+ * Apply part 2 of workaround for errata-2384374 as per SDEN:
+ * https://developer.arm.com/documentation/sden892601/latest/
+ ******************************************************************************/
+void gicv3_apply_errata_wa_2384374(uintptr_t gicr_base)
+{
+	if (gic600_errata_wa_2384374) {
+		uint32_t gicr_ctlr_val = gicr_read_ctlr(gicr_base);
+
+		gicr_write_ctlr(gicr_base, gicr_ctlr_val |
+				(GICR_CTLR_DPG0_BIT | GICR_CTLR_DPG1NS_BIT |
+				GICR_CTLR_DPG1S_BIT));
+		gicr_write_ctlr(gicr_base, gicr_ctlr_val &
+				~(GICR_CTLR_DPG0_BIT | GICR_CTLR_DPG1NS_BIT |
+				  GICR_CTLR_DPG1S_BIT));
+	}
+}
+#endif /* GIC600_ERRATA_WA_2384374 */
+
+void gicv3_check_erratas_applies(uintptr_t gicd_base)
+{
+	unsigned int gic_prod_id;
+	uint8_t gic_rev;
+
+	assert(gicd_base != 0UL);
+
+	gicv3_get_component_prodid_rev(gicd_base, &gic_prod_id, &gic_rev);
+
+	/*
+	 * This workaround applicable only to GIC600 and GIC600AE products with
+	 * revision less than r1p6 and r0p2 respectively.
+	 * As per GIC600/GIC600AE specification -
+	 * r1p6 = 0x17 => GICD_IIDR[19:12]
+	 * r0p2 = 0x04 => GICD_IIDR[19:12]
+	 */
+	if ((gic_prod_id == GIC_PRODUCT_ID_GIC600) ||
+		    (gic_prod_id == GIC_PRODUCT_ID_GIC600AE)) {
+		if (((gic_prod_id == GIC_PRODUCT_ID_GIC600) &&
+		     (gic_rev <= GIC_REV(GIC_VARIANT_R1, GIC_REV_P6))) ||
+		     ((gic_prod_id == GIC_PRODUCT_ID_GIC600AE) &&
+		     (gic_rev <= GIC_REV(GIC_VARIANT_R0, GIC_REV_P2)))) {
+#if GIC600_ERRATA_WA_2384374
+			gic600_errata_wa_2384374 = true;
+			VERBOSE("%s applies\n",
+				"GIC600/GIC600AE errata workaround 2384374");
+#else
+			WARN("%s missing\n",
+			     "GIC600/GIC600AE errata workaround 2384374");
+#endif /* GIC600_ERRATA_WA_2384374 */
+		} else {
+			VERBOSE("%s not applies\n",
+				"GIC600/GIC600AE errata workaround 2384374");
+		}
+	}
+}
diff --git a/drivers/arm/gic/v3/gicv3.mk b/drivers/arm/gic/v3/gicv3.mk
index d7e3536..1d20ff3 100644
--- a/drivers/arm/gic/v3/gicv3.mk
+++ b/drivers/arm/gic/v3/gicv3.mk
@@ -1,5 +1,5 @@
 #
-# Copyright (c) 2013-2020, Arm Limited and Contributors. All rights reserved.
+# Copyright (c) 2013-2022, Arm Limited and Contributors. All rights reserved.
 # Copyright (c) 2021, NVIDIA Corporation. All rights reserved.
 #
 # SPDX-License-Identifier: BSD-3-Clause
@@ -12,6 +12,7 @@
 GICV3_OVERRIDE_DISTIF_PWR_OPS	?=	0
 GIC_ENABLE_V4_EXTN		?=	0
 GIC_EXT_INTID			?=	0
+GIC600_ERRATA_WA_2384374	?=	${GICV3_SUPPORT_GIC600}
 
 GICV3_SOURCES	+=	drivers/arm/gic/v3/gicv3_main.c		\
 			drivers/arm/gic/v3/gicv3_helpers.c	\
@@ -47,3 +48,7 @@
 # Set support for extended PPI and SPI range
 $(eval $(call assert_boolean,GIC_EXT_INTID))
 $(eval $(call add_define,GIC_EXT_INTID))
+
+# Set errata workaround for GIC600/GIC600AE
+$(eval $(call assert_boolean,GIC600_ERRATA_WA_2384374))
+$(eval $(call add_define,GIC600_ERRATA_WA_2384374))
diff --git a/drivers/arm/gic/v3/gicv3_helpers.c b/drivers/arm/gic/v3/gicv3_helpers.c
index 753d995..f3852d2 100644
--- a/drivers/arm/gic/v3/gicv3_helpers.c
+++ b/drivers/arm/gic/v3/gicv3_helpers.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015-2021, ARM Limited and Contributors. All rights reserved.
+ * Copyright (c) 2015-2022, Arm Limited and Contributors. All rights reserved.
  *
  * SPDX-License-Identifier: BSD-3-Clause
  */
@@ -408,3 +408,34 @@
 
 	return part_id;
 }
+
+/*******************************************************************************
+ * Helper function to return product ID and revision of GIC
+ * @gicd_base:   base address of the GIC distributor
+ * @gic_prod_id: retrieved product id of GIC
+ * @gic_rev:     retrieved revision of GIC
+ ******************************************************************************/
+void gicv3_get_component_prodid_rev(const uintptr_t gicd_base,
+				    unsigned int *gic_prod_id,
+				    uint8_t *gic_rev)
+{
+	unsigned int gicd_iidr;
+	uint8_t gic_variant;
+
+	gicd_iidr = gicd_read_iidr(gicd_base);
+	*gic_prod_id = gicd_iidr >> IIDR_PRODUCT_ID_SHIFT;
+	*gic_prod_id &= IIDR_PRODUCT_ID_MASK;
+
+	gic_variant = gicd_iidr >> IIDR_VARIANT_SHIFT;
+	gic_variant &= IIDR_VARIANT_MASK;
+
+	*gic_rev = gicd_iidr >> IIDR_REV_SHIFT;
+	*gic_rev &= IIDR_REV_MASK;
+
+	/*
+	 * pack gic variant and gic_rev in 1 byte
+	 * gic_rev = gic_variant[7:4] and gic_rev[0:3]
+	 */
+	*gic_rev = *gic_rev | gic_variant << 0x4;
+
+}
diff --git a/drivers/arm/gic/v3/gicv3_main.c b/drivers/arm/gic/v3/gicv3_main.c
index 53a8fae..8ead43b 100644
--- a/drivers/arm/gic/v3/gicv3_main.c
+++ b/drivers/arm/gic/v3/gicv3_main.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015-2021, ARM Limited and Contributors. All rights reserved.
+ * Copyright (c) 2015-2022, Arm Limited and Contributors. All rights reserved.
  *
  * SPDX-License-Identifier: BSD-3-Clause
  */
@@ -169,6 +169,8 @@
 	flush_dcache_range((uintptr_t)gicv3_driver_data,
 		sizeof(*gicv3_driver_data));
 #endif
+	gicv3_check_erratas_applies(plat_driver_data->gicd_base);
+
 	INFO("GICv%u with%s legacy support detected.\n", gic_version,
 				(gicv2_compat == 0U) ? "" : "out");
 	INFO("ARM GICv%u driver initialized in EL3\n", gic_version);
@@ -362,9 +364,17 @@
 	/* Add DSB to ensure visibility of System register writes */
 	dsb();
 
-	/* Mark the connected core as asleep */
 	gicr_base = gicv3_driver_data->rdistif_base_addrs[proc_num];
-	assert(gicr_base != 0U);
+	assert(gicr_base != 0UL);
+
+	/*
+	 * dsb() already issued previously after clearing the CPU group
+	 * enabled, apply below workaround to toggle the "DPG*"
+	 * bits of GICR_CTLR register for unblocking event.
+	 */
+	gicv3_apply_errata_wa_2384374(gicr_base);
+
+	/* Mark the connected core as asleep */
 	gicv3_rdistif_mark_core_asleep(gicr_base);
 }