summaryrefslogtreecommitdiff
path: root/drivers/arm/gic/v3/arm_gicv3_common.c
diff options
context:
space:
mode:
authorSoby Mathew <soby.mathew@arm.com>2017-07-13 15:19:51 +0100
committerSoby Mathew <soby.mathew@arm.com>2017-10-05 16:47:53 +0100
commitebf1ca10e466f39c45fa4ae4043fd53487d32362 (patch)
treed005dc71d6d0a9d5f64eb58d60fb355163942412 /drivers/arm/gic/v3/arm_gicv3_common.c
parenta64b4e626ecad5d5a77dfd26c94a32c425748f4c (diff)
GICv3: add functions for save and restore
During system suspend, the GICv3 Distributor and Redistributor context can be lost due to power gating of the system power domain. This means that the GICv3 context needs to be saved prior to system suspend and restored on wakeup. Currently the consensus is that the Firmware should be in charge of this. See tf-issues#464 for more details. This patch introduces helper APIs in the GICv3 driver to save and restore the Distributor and Redistributor contexts. The GICv3 ITS context is not considered in this patch because the specification says that the details of ITS power management is implementation-defined. These APIs are expected to be appropriately invoked by the platform layer during system suspend. Fixes ARM-software/tf-issues#464 Change-Id: Iebb9c6770ab8c4d522546f161fa402d2fe02ec00 Signed-off-by: Soby Mathew <soby.mathew@arm.com> Signed-off-by: Douglas Raillard <douglas.raillard@arm.com>
Diffstat (limited to 'drivers/arm/gic/v3/arm_gicv3_common.c')
-rw-r--r--drivers/arm/gic/v3/arm_gicv3_common.c105
1 files changed, 105 insertions, 0 deletions
diff --git a/drivers/arm/gic/v3/arm_gicv3_common.c b/drivers/arm/gic/v3/arm_gicv3_common.c
new file mode 100644
index 00000000..8d552ca5
--- /dev/null
+++ b/drivers/arm/gic/v3/arm_gicv3_common.c
@@ -0,0 +1,105 @@
+/*
+ * Copyright (c) 2017, ARM Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+/*
+ * Driver for implementation defined features that are identical in ARM GICv3
+* implementations (GIC-500 and GIC-600 for now). This driver only overrides
+* APIs that are different to those generic ones in GICv3 driver.
+ */
+
+#include <arch_helpers.h>
+#include <assert.h>
+#include <gicv3.h>
+
+#include "gicv3_private.h"
+#include "arm_gicv3_common.h"
+
+/*
+ * Flush the internal GIC cache of the LPIs pending tables to memory before
+ * saving the state of the Redistributor. This is required before powering off
+ * the GIC when the pending status must be preserved.
+ * `rdist_proc_num` is the processor number corresponding to the Redistributor of the
+ * current CPU.
+ */
+void arm_gicv3_distif_pre_save(unsigned int rdist_proc_num)
+{
+ uintptr_t gicr_base = 0;
+
+ assert(gicv3_driver_data);
+ assert(gicv3_driver_data->rdistif_base_addrs);
+
+ /*
+ * The GICR_WAKER.Sleep bit should be set only when both
+ * GICR_WAKER.ChildrenAsleep and GICR_WAKER.ProcessorSleep are set on
+ * all the Redistributors.
+ */
+ for (unsigned int i = 0; i < gicv3_driver_data->rdistif_num; i++) {
+ gicr_base = gicv3_driver_data->rdistif_base_addrs[i];
+ assert(gicr_base);
+ assert(gicr_read_waker(gicr_base) & WAKER_CA_BIT);
+ assert(gicr_read_waker(gicr_base) & WAKER_PS_BIT);
+ }
+
+ gicr_base = gicv3_driver_data->rdistif_base_addrs[rdist_proc_num];
+ /*
+ * According to the TRM, there is only one instance of the
+ * GICR_WAKER.Sleep and GICR_WAKER.Quiescent bits that can be accessed
+ * through any of the Redistributor.
+ */
+
+ /*
+ * Set GICR_WAKER.Sleep
+ * After this point, the system must be configured so that the
+ * wake_request signals for the right cores are asserted when a wakeup
+ * interrupt is detected. The GIC will not be able to do that anymore
+ * when the GICR_WAKER.Sleep bit is set to 1.
+ */
+ gicr_write_waker(gicr_base, gicr_read_waker(gicr_base) | WAKER_SL_BIT);
+
+ /* Wait until the GICR_WAKER.Quiescent bit is set */
+ while (!(gicr_read_waker(gicr_base) & WAKER_QSC_BIT))
+ ;
+}
+
+/*
+ * Allow the LPIs pending state to be read back from the tables in memory after
+ * having restored the state of the GIC Redistributor.
+ */
+void arm_gicv3_distif_post_restore(unsigned int rdist_proc_num)
+{
+ uintptr_t gicr_base;
+
+ assert(gicv3_driver_data);
+ assert(gicv3_driver_data->rdistif_base_addrs);
+
+ /*
+ * According to the TRM, there is only one instance of the
+ * GICR_WAKER.Sleep and GICR_WAKER.Quiescent bits that can be accessed
+ * through any of the Redistributor.
+ */
+ gicr_base = gicv3_driver_data->rdistif_base_addrs[rdist_proc_num];
+ assert(gicr_base);
+
+ /*
+ * Writes to GICR_WAKER.Sleep bit are ignored if GICR_WAKER.Quiescent
+ * bit is not set. We should be alright on power on path, therefore
+ * coming out of sleep and Quiescent should be set, but we assert in
+ * case.
+ */
+ assert(gicr_read_waker(gicr_base) & WAKER_QSC_BIT);
+
+ /* Clear GICR_WAKER.Sleep */
+ gicr_write_waker(gicr_base, gicr_read_waker(gicr_base) & ~WAKER_SL_BIT);
+
+ /*
+ * We don't know if the effects of setting GICR_WAKER.Sleep bit is
+ * instantaneous, so we wait until the interface is not Quiescent
+ * anymore.
+ */
+ while (gicr_read_waker(gicr_base) & WAKER_QSC_BIT)
+ ;
+}
+