fix(gicv3): workaround for NVIDIA erratum T241-FABRIC-4
The purpose of this patch is to address the T241 erratum T241-FABRIC-4,
which causes unexpected behavior in the GIC when multiple transactions
are received simultaneously from different sources. This hardware issue
impacts NVIDIA server platforms that use more than two T241 chips
interconnected. Each chip has support for 320 {E}SPIs.
This issue occurs when multiple packets from different GICs are
incorrectly interleaved at the target chip. The erratum text below
specifies exactly what can cause multiple transfer packets susceptible
to interleaving and GIC state corruption. GIC state corruption can
lead to a range of problems, including kernel panics, and unexpected
behavior.
Erratum documentation:
https://developer.nvidia.com/docs/t241-fabric-4/nvidia-t241-fabric-4-errata.pdf
The workaround is to ensure that MMIO accesses target the GIC on the
socket that holds the data, for example SPI ranges owned by the socket’s
GIC. This ensures that the GIC will not utilize the inter-socket AXI
Stream interface for servicing these GIC MMIO accesses.
This patch updates the functions that use the GICD_In{E} registers to
ensure that the accesses are directed to the chip that owns the SPI,
instead of using the global alias.
Signed-off-by: Varun Wadekar <vwadekar@nvidia.com>
Change-Id: I04e33ba64eb306bd5fdabb56e63cbe273d8cd632
diff --git a/drivers/arm/gic/v3/gicv3_helpers.c b/drivers/arm/gic/v3/gicv3_helpers.c
index 940c939..00bd7a1 100644
--- a/drivers/arm/gic/v3/gicv3_helpers.c
+++ b/drivers/arm/gic/v3/gicv3_helpers.c
@@ -1,5 +1,6 @@
/*
* Copyright (c) 2015-2022, Arm Limited and Contributors. All rights reserved.
+ * Copyright (c) 2023, NVIDIA Corporation. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
@@ -10,6 +11,7 @@
#include <arch_helpers.h>
#include <common/debug.h>
#include <common/interrupt_props.h>
+#include <drivers/arm/gic600_multichip.h>
#include <drivers/arm/gic_common.h>
#include <platform_def.h>
@@ -17,6 +19,16 @@
#include "../common/gic_common_private.h"
#include "gicv3_private.h"
+uintptr_t gicv3_get_multichip_base(uint32_t spi_id, uintptr_t gicd_base)
+{
+#if GICV3_IMPL_GIC600_MULTICHIP
+ if (gic600_multichip_is_initialized()) {
+ return gic600_multichip_gicd_base_for_spi(spi_id);
+ }
+#endif
+ return gicd_base;
+}
+
/******************************************************************************
* This function marks the core as awake in the re-distributor and
* ensures that the interface is active.
@@ -148,7 +160,7 @@
/* Treat all (E)SPIs as G1NS by default. We do 32 at a time. */
for (i = MIN_SPI_ID; i < num_ints; i += (1U << IGROUPR_SHIFT)) {
- gicd_write_igroupr(gicd_base, i, ~0U);
+ gicd_write_igroupr(gicv3_get_multichip_base(i, gicd_base), i, ~0U);
}
#if GIC_EXT_INTID
@@ -158,7 +170,7 @@
for (i = MIN_ESPI_ID; i < num_eints;
i += (1U << IGROUPR_SHIFT)) {
- gicd_write_igroupr(gicd_base, i, ~0U);
+ gicd_write_igroupr(gicv3_get_multichip_base(i, gicd_base), i, ~0U);
}
} else {
INFO("ESPI range is not implemented.\n");
@@ -167,25 +179,25 @@
/* Setup the default (E)SPI priorities doing four at a time */
for (i = MIN_SPI_ID; i < num_ints; i += (1U << IPRIORITYR_SHIFT)) {
- gicd_write_ipriorityr(gicd_base, i, GICD_IPRIORITYR_DEF_VAL);
+ gicd_write_ipriorityr(gicv3_get_multichip_base(i, gicd_base), i, GICD_IPRIORITYR_DEF_VAL);
}
#if GIC_EXT_INTID
for (i = MIN_ESPI_ID; i < num_eints;
i += (1U << IPRIORITYR_SHIFT)) {
- gicd_write_ipriorityr(gicd_base, i, GICD_IPRIORITYR_DEF_VAL);
+ gicd_write_ipriorityr(gicv3_get_multichip_base(i, gicd_base), i, GICD_IPRIORITYR_DEF_VAL);
}
#endif
/*
* Treat all (E)SPIs as level triggered by default, write 16 at a time
*/
for (i = MIN_SPI_ID; i < num_ints; i += (1U << ICFGR_SHIFT)) {
- gicd_write_icfgr(gicd_base, i, 0U);
+ gicd_write_icfgr(gicv3_get_multichip_base(i, gicd_base), i, 0U);
}
#if GIC_EXT_INTID
for (i = MIN_ESPI_ID; i < num_eints; i += (1U << ICFGR_SHIFT)) {
- gicd_write_icfgr(gicd_base, i, 0U);
+ gicd_write_icfgr(gicv3_get_multichip_base(i, gicd_base), i, 0U);
}
#endif
}
@@ -211,6 +223,7 @@
current_prop = &interrupt_props[i];
unsigned int intr_num = current_prop->intr_num;
+ uintptr_t multichip_gicd_base = gicv3_get_multichip_base(intr_num, gicd_base);
/* Skip SGI, (E)PPI and LPI interrupts */
if (!IS_SPI(intr_num)) {
@@ -218,35 +231,36 @@
}
/* Configure this interrupt as a secure interrupt */
- gicd_clr_igroupr(gicd_base, intr_num);
+ gicd_clr_igroupr(multichip_gicd_base, intr_num);
/* Configure this interrupt as G0 or a G1S interrupt */
assert((current_prop->intr_grp == INTR_GROUP0) ||
(current_prop->intr_grp == INTR_GROUP1S));
if (current_prop->intr_grp == INTR_GROUP1S) {
- gicd_set_igrpmodr(gicd_base, intr_num);
+ gicd_set_igrpmodr(multichip_gicd_base, intr_num);
ctlr_enable |= CTLR_ENABLE_G1S_BIT;
} else {
- gicd_clr_igrpmodr(gicd_base, intr_num);
+ gicd_clr_igrpmodr(multichip_gicd_base, intr_num);
ctlr_enable |= CTLR_ENABLE_G0_BIT;
}
/* Set interrupt configuration */
- gicd_set_icfgr(gicd_base, intr_num, current_prop->intr_cfg);
+ gicd_set_icfgr(multichip_gicd_base, intr_num,
+ current_prop->intr_cfg);
/* Set the priority of this interrupt */
- gicd_set_ipriorityr(gicd_base, intr_num,
- current_prop->intr_pri);
+ gicd_set_ipriorityr(multichip_gicd_base, intr_num,
+ current_prop->intr_pri);
/* Target (E)SPIs to the primary CPU */
gic_affinity_val =
gicd_irouter_val_from_mpidr(read_mpidr(), 0U);
- gicd_write_irouter(gicd_base, intr_num,
- gic_affinity_val);
+ gicd_write_irouter(multichip_gicd_base, intr_num,
+ gic_affinity_val);
/* Enable this interrupt */
- gicd_set_isenabler(gicd_base, intr_num);
+ gicd_set_isenabler(multichip_gicd_base, intr_num);
}
return ctlr_enable;