fix(gic600): workaround for Part 1 of GIC600 erratum 2384374
GIC600 erratum 2384374 is a Category B erratum. Part 1 is fixed
in this patch, and the Part 1 failure mode is described as
'If the packet to be sent is a SET packet, then a higher priority SET
may not be sent when it should be until an unblocking event occurs.'
This is handled by calling gicv3_apply_errata_wa_2384374() in the
ehf_deactivate_priority() path, so that when EHF restores the priority
to the original priority, the interrupt packet buffered
in the GIC can be sent.
gicv3_apply_errata_wa_2384374() is the workaround for
the Part 2 of erratum 2384374 which flush packets from the GIC buffer
and is being used in this patch.
SDEN can be found here:
https://developer.arm.com/documentation/sden892601/latest/
Signed-off-by: Arvind Ram Prakash <arvind.ramprakash@arm.com>
Change-Id: I4bb6dcf86c94125cbc574e0dc5119abe43e84731
diff --git a/bl31/ehf.c b/bl31/ehf.c
index 5b78ebb..3a14635 100644
--- a/bl31/ehf.c
+++ b/bl31/ehf.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2017-2023, Arm Limited and Contributors. All rights reserved.
+ * Copyright (c) 2017-2024, Arm Limited and Contributors. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
@@ -203,10 +203,20 @@
* one stashed earlier if there are no more to deactivate.
*/
cur_pri_idx = get_pe_highest_active_idx(pe_data);
- if (cur_pri_idx == EHF_INVALID_IDX)
+
+#if GIC600_ERRATA_WA_2384374
+ if (cur_pri_idx == EHF_INVALID_IDX) {
+ old_mask = plat_ic_deactivate_priority(pe_data->init_pri_mask);
+ } else {
+ old_mask = plat_ic_deactivate_priority(priority);
+ }
+#else
+ if (cur_pri_idx == EHF_INVALID_IDX) {
old_mask = plat_ic_set_priority_mask(pe_data->init_pri_mask);
- else
+ } else {
old_mask = plat_ic_set_priority_mask(priority);
+ }
+#endif
if (old_mask > priority) {
ERROR("Deactivation priority (0x%x) lower than Priority Mask (0x%x)\n",
diff --git a/docs/components/platform-interrupt-controller-API.rst b/docs/components/platform-interrupt-controller-API.rst
index 4de39d1..8cd4bae 100644
--- a/docs/components/platform-interrupt-controller-API.rst
+++ b/docs/components/platform-interrupt-controller-API.rst
@@ -282,9 +282,28 @@
that it's overwriting.
In case of Arm standard platforms using GIC, the implementation of the API
-inserts to order memory updates before updating mask, then writes to the GIC
-*Priority Mask Register*, and make sure memory updates are visible before
-potential trigger due to mask update.
+inserts barriers to order memory updates before updating mask,
+then writes to the GIC *Priority Mask Register*, and make sure memory updates
+are visible before potential trigger due to mask update.
+
+Function: unsigned int plat_ic_deactivate_priority(unsigned int id); [optional]
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+::
+
+ Argument : unsigned int
+ Return : int
+
+This API performs the operations of plat_ic_set_priority_mask along with
+calling the errata workaround gicv3_apply_errata_wa_2384374(). This is
+performed when priority mask is restored to it's older value. This API returns
+the current priority value that it's overwriting.
+
+In case of Arm standard platforms using GIC, the implementation of the API
+inserts barriers to order memory updates before updating mask, then writes
+to the GIC *Priority Mask Register*, and make sure memory updates
+are visible before potential trigger due to mask update, and
+applies 2384374 GIC errata workaround to process pending interrupt packets.
.. _plat_ic_get_interrupt_id:
diff --git a/drivers/arm/gic/v3/gicv3_main.c b/drivers/arm/gic/v3/gicv3_main.c
index 3190f66..8ea164c 100644
--- a/drivers/arm/gic/v3/gicv3_main.c
+++ b/drivers/arm/gic/v3/gicv3_main.c
@@ -1321,6 +1321,31 @@
}
/*******************************************************************************
+ * This function restores the PMR register to old value and also triggers
+ * gicv3_apply_errata_wa_2384374() that flushes the GIC buffer allowing any
+ * pending interrupts to processed. Returns the original PMR.
+ ******************************************************************************/
+unsigned int gicv3_deactivate_priority(unsigned int mask)
+{
+
+ unsigned int old_mask, proc_num;
+ uintptr_t gicr_base;
+
+ old_mask = gicv3_set_pmr(mask);
+
+ proc_num = plat_my_core_pos();
+ gicr_base = gicv3_driver_data->rdistif_base_addrs[proc_num];
+ assert(gicr_base != 0UL);
+
+ /* Add DSB to ensure visibility of System register writes */
+ dsb();
+
+ gicv3_apply_errata_wa_2384374(gicr_base);
+
+ return old_mask;
+}
+
+/*******************************************************************************
* This function delegates the responsibility of discovering the corresponding
* Redistributor frames to each CPU itself. It is a modified version of
* gicv3_rdistif_base_addrs_probe() and is executed by each CPU in the platform
diff --git a/include/drivers/arm/gicv3.h b/include/drivers/arm/gicv3.h
index cf6a746..bfda31b 100644
--- a/include/drivers/arm/gicv3.h
+++ b/include/drivers/arm/gicv3.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2015-2023, Arm Limited and Contributors. All rights reserved.
+ * Copyright (c) 2015-2024, Arm Limited and Contributors. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
@@ -588,6 +588,7 @@
void gicv3_set_interrupt_pending(unsigned int id, unsigned int proc_num);
void gicv3_clear_interrupt_pending(unsigned int id, unsigned int proc_num);
unsigned int gicv3_set_pmr(unsigned int mask);
+unsigned int gicv3_deactivate_priority(unsigned int mask);
void gicv3_get_component_prodid_rev(const uintptr_t gicd_base,
unsigned int *gic_prod_id,
diff --git a/include/plat/common/platform.h b/include/plat/common/platform.h
index 4d1b1c1..2bb23c4 100644
--- a/include/plat/common/platform.h
+++ b/include/plat/common/platform.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2013-2023, Arm Limited and Contributors. All rights reserved.
+ * Copyright (c) 2013-2024, Arm Limited and Contributors. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
@@ -136,6 +136,7 @@
void plat_ic_set_interrupt_pending(unsigned int id);
void plat_ic_clear_interrupt_pending(unsigned int id);
unsigned int plat_ic_set_priority_mask(unsigned int mask);
+unsigned int plat_ic_deactivate_priority(unsigned int mask);
unsigned int plat_ic_get_interrupt_id(unsigned int raw);
/*******************************************************************************
diff --git a/plat/common/plat_gicv3.c b/plat/common/plat_gicv3.c
index baa70e0..d0c7a31 100644
--- a/plat/common/plat_gicv3.c
+++ b/plat/common/plat_gicv3.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2015-2023, Arm Limited and Contributors. All rights reserved.
+ * Copyright (c) 2015-2024, Arm Limited and Contributors. All rights reserved.
* Portions copyright (c) 2021-2022, ProvenRun S.A.S. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
@@ -344,6 +344,11 @@
return gicv3_set_pmr(mask);
}
+unsigned int plat_ic_deactivate_priority(unsigned int mask)
+{
+ return gicv3_deactivate_priority(mask);
+}
+
unsigned int plat_ic_get_interrupt_id(unsigned int raw)
{
unsigned int id = raw & INT_ID_MASK;