summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSoby Mathew <soby.mathew@arm.com>2015-11-10 18:03:20 +0000
committerSoby Mathew <soby.mathew@arm.com>2016-02-04 16:59:19 +0000
commit5dc673aef643cd59eca1267289de68710650864b (patch)
tree8b59a2ad2a1b4713183567985862e27e881f99ba
parentaf059017bbba5cf5626c9dbfe8391e086158ca8e (diff)
Introduce the TFTF GICv3 support
This patch adds GICv3 driver support to TFTF and also enables dynamic selection of the GIC driver within ARM GIC layer according to the GIC mode detected. The GICv3 driver is defined in `gic_v3.c` and prototypes are present in `gic_v3.h`. Appropriate architecture helpers are also added for accessing GICv3 system registers. A ARM GIC layer for dynamically selecting the appropriate GIC driver is added in `arm_gic_v2v3.c`. The fvp, armstrong, buzz and collins platform ports now use this dynamic ARM GIC layer which selects the GIC driver according to the way GIC has been configured. Change-Id: I78ef780a85b2edeb99851e44720e9c445e86dc37
-rw-r--r--docs/implementing-tests.md3
-rw-r--r--drivers/arm/gic/arm_gic_v2v3.c204
-rw-r--r--drivers/arm/gic/gic_common.c11
-rw-r--r--drivers/arm/gic/gic_v2.c2
-rw-r--r--drivers/arm/gic/gic_v3.c468
-rw-r--r--include/drivers/arm/gic_common.h3
-rw-r--r--include/drivers/arm/gic_v3.h226
-rw-r--r--include/lib/aarch64/arch.h19
-rw-r--r--include/lib/aarch64/arch_helpers.h15
-rw-r--r--plat/armstrong/platform.mk3
-rw-r--r--plat/fvp/platform.mk3
11 files changed, 948 insertions, 9 deletions
diff --git a/docs/implementing-tests.md b/docs/implementing-tests.md
index 1cc8660..312d38d 100644
--- a/docs/implementing-tests.md
+++ b/docs/implementing-tests.md
@@ -125,7 +125,8 @@ overview of the panel of features provided and pointers to the right header
files to find the documentation.
`include/drivers/`
- * GICv2 driver
+ * Generic GIC driver. The `arm_gic.h` contains the public API which can be
+ used by the tests. TFTF supports both GIC architecture version 2 and 3.
* PL011 UART driver
diff --git a/drivers/arm/gic/arm_gic_v2v3.c b/drivers/arm/gic/arm_gic_v2v3.c
new file mode 100644
index 0000000..239a823
--- /dev/null
+++ b/drivers/arm/gic/arm_gic_v2v3.c
@@ -0,0 +1,204 @@
+/*
+ * Copyright (c) 2016, ARM Limited and Contributors. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * Neither the name of ARM nor the names of its contributors may be used
+ * to endorse or promote products derived from this software without specific
+ * prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <arch.h>
+#include <arch_helpers.h>
+#include <assert.h>
+#include <debug.h>
+#include <gic_common.h>
+#include <gic_v2.h>
+#include <gic_v3.h>
+
+/* Record whether a GICv3 was detected on the system */
+static unsigned int gicv3_detected;
+
+void arm_gic_enable_interrupts_local(void)
+{
+ if (gicv3_detected)
+ gicv3_enable_cpuif();
+ else
+ gicv2_enable_cpuif();
+}
+
+void arm_gic_setup_local(void)
+{
+ if (gicv3_detected) {
+ gicv3_probe_redistif_addr();
+ gicv3_setup_cpuif();
+ } else {
+ gicv2_probe_gic_cpu_id();
+ gicv2_setup_cpuif();
+ }
+}
+
+void arm_gic_disable_interrupts_local(void)
+{
+ if (gicv3_detected)
+ gicv3_disable_cpuif();
+ else
+ gicv2_disable_cpuif();
+}
+
+void arm_gic_save_context_local(void)
+{
+ if (gicv3_detected)
+ gicv3_save_cpuif_context();
+ else
+ gicv2_save_cpuif_context();
+}
+
+void arm_gic_restore_context_local(void)
+{
+ if (gicv3_detected)
+ gicv3_restore_cpuif_context();
+ else
+ gicv2_restore_cpuif_context();
+}
+
+void arm_gic_save_context_global(void)
+{
+ if (gicv3_detected)
+ gicv3_save_sgi_ppi_context();
+ else
+ gicv2_save_sgi_ppi_context();
+}
+
+void arm_gic_restore_context_global(void)
+{
+ if (gicv3_detected) {
+ gicv3_setup_distif();
+ gicv3_restore_sgi_ppi_context();
+ } else {
+ gicv2_setup_distif();
+ gicv2_restore_sgi_ppi_context();
+ }
+}
+
+void arm_gic_setup_global(void)
+{
+ if (gicv3_detected)
+ gicv3_setup_distif();
+ else
+ gicv2_setup_distif();
+}
+
+void arm_gic_set_intr_priority(unsigned int num,
+ unsigned int priority)
+{
+ if (gicv3_detected)
+ gicv3_set_ipriorityr(num, priority);
+ else
+ gicv2_gicd_set_ipriorityr(num, priority);
+}
+
+void arm_gic_send_sgi(unsigned int sgi_id, unsigned int core_pos)
+{
+ if (gicv3_detected)
+ gicv3_send_sgi(sgi_id, core_pos);
+ else
+ gicv2_send_sgi(sgi_id, core_pos);
+}
+
+void arm_gic_set_intr_target(unsigned int num, unsigned int core_pos)
+{
+ if (gicv3_detected)
+ gicv3_set_intr_route(num, core_pos);
+ else
+ gicv2_set_itargetsr(num, core_pos);
+}
+
+void arm_gic_intr_enable(unsigned int num)
+{
+ if (gicv3_detected)
+ gicv3_set_isenabler(num);
+ else
+ gicv2_gicd_set_isenabler(num);
+}
+
+void arm_gic_intr_disable(unsigned int num)
+{
+ if (gicv3_detected)
+ gicv3_set_icenabler(num);
+ else
+ gicv2_gicd_set_icenabler(num);
+}
+
+unsigned int arm_gic_intr_ack(unsigned int *raw_iar)
+{
+ assert(raw_iar);
+
+ if (gicv3_detected) {
+ *raw_iar = gicv3_acknowledge_interrupt();
+ return *raw_iar;
+ } else {
+ *raw_iar = gicv2_gicc_read_iar();
+ return get_gicc_iar_intid(*raw_iar);
+ }
+}
+
+unsigned int arm_gic_is_intr_pending(unsigned int num)
+{
+ if (gicv3_detected)
+ return gicv3_get_ispendr(num);
+ else
+ return gicv2_gicd_get_ispendr(num);
+}
+
+void arm_gic_intr_clear(unsigned int num)
+{
+ if (gicv3_detected)
+ gicv3_set_icpendr(num);
+ else
+ gicv2_gicd_set_icpendr(num);
+}
+
+void arm_gic_end_of_intr(unsigned int raw_iar)
+{
+ if (gicv3_detected)
+ gicv3_end_of_interrupt(raw_iar);
+ else
+ gicv2_gicc_write_eoir(raw_iar);
+}
+
+void arm_gic_init(uintptr_t gicc_base,
+ uintptr_t gicd_base,
+ uintptr_t gicr_base)
+{
+
+ if (is_gicv3_mode()) {
+ gicv3_detected = 1;
+ gicv3_init(gicr_base, gicd_base);
+ } else {
+ gicv2_init(gicc_base, gicd_base);
+ }
+
+ INFO("%s mode detected\n", (gicv3_detected) ?
+ "GICv3" : "GICv2");
+}
diff --git a/drivers/arm/gic/gic_common.c b/drivers/arm/gic/gic_common.c
index 4dcd6c9..6077d3e 100644
--- a/drivers/arm/gic/gic_common.c
+++ b/drivers/arm/gic/gic_common.c
@@ -32,6 +32,7 @@
#include <arch_helpers.h>
#include <assert.h>
#include <gic_common.h>
+#include <gic_v3.h>
#include <mmio.h>
/*******************************************************************************
@@ -197,3 +198,13 @@ void gicd_set_ipriorityr(unsigned int base, unsigned int interrupt_id,
mmio_write_8(base + GICD_IPRIORITYR + interrupt_id,
priority & GIC_PRI_MASK);
}
+
+unsigned int is_gicv3_mode(void)
+{
+ /* Check if GICv3 system register available */
+ if (!(read_id_aa64pfr0_el1() & (ID_AA64PFR0_GIC_MASK << ID_AA64PFR0_GIC_SHIFT)))
+ return 0;
+
+ /* Check whether the system register interface is enabled */
+ return !!is_sre_enabled();
+}
diff --git a/drivers/arm/gic/gic_v2.c b/drivers/arm/gic/gic_v2.c
index b5c27fc..6612f62 100644
--- a/drivers/arm/gic/gic_v2.c
+++ b/drivers/arm/gic/gic_v2.c
@@ -333,6 +333,8 @@ void gicv2_init(uintptr_t gicc_base,
assert(gicc_base);
assert(gicd_base);
+ /* Assert that this is a GICv2 system */
+ assert(!is_gicv3_mode());
gicc_base_addr = gicc_base;
gicd_base_addr = gicd_base;
}
diff --git a/drivers/arm/gic/gic_v3.c b/drivers/arm/gic/gic_v3.c
new file mode 100644
index 0000000..5ea43a3
--- /dev/null
+++ b/drivers/arm/gic/gic_v3.c
@@ -0,0 +1,468 @@
+/*
+ * Copyright (c) 2016, ARM Limited and Contributors. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * Neither the name of ARM nor the names of its contributors may be used
+ * to endorse or promote products derived from this software without specific
+ * prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <arch.h>
+#include <arch_helpers.h>
+#include <arm_gic.h>
+#include <assert.h>
+#include <debug.h>
+#include <gic_common.h>
+#include <gic_v3.h>
+#include <mmio.h>
+#include <platform.h>
+
+/* Global variables to store the GIC base addresses */
+static uintptr_t gicr_base_addr;
+static uintptr_t gicd_base_addr;
+
+#define MPIDR_AFFLVL3_MASK ((unsigned long long)MPIDR_AFFLVL_MASK << MPIDR_AFF3_SHIFT)
+#define gic_typer_affinity_from_mpidr(mpidr) \
+ (((mpidr) & (~MPIDR_AFFLVL3_MASK)) | (((mpidr) & MPIDR_AFFLVL3_MASK) >> 8))
+
+/*
+ * Data structure to store the GIC per CPU context before entering
+ * system suspend. Only the GIC context of first 32 interrupts (SGIs and PPIs)
+ * will be saved. The GIC SPI context needs to be restored by the respective
+ * drivers.
+ */
+struct gicv3_pcpu_ctx {
+ /* Flag to indicate whether the CPU is suspended */
+ unsigned int is_suspended;
+ unsigned int icc_igrpen1;
+ unsigned int gicr_isenabler;
+ unsigned int gicr_ipriorityr[NUM_PCPU_INTR >> IPRIORITYR_SHIFT];
+ unsigned int gicr_icfgr;
+};
+
+/* Array to store the per-cpu GICv3 context when being suspended.*/
+static struct gicv3_pcpu_ctx pcpu_ctx[PLATFORM_CORE_COUNT];
+
+/* Array to store the per-cpu redistributor frame addresses */
+static uintptr_t rdist_pcpu_base[PLATFORM_CORE_COUNT];
+
+/*
+ * Array to store the mpidr corresponding to each initialized per-CPU
+ * redistributor interface.
+ */
+static unsigned long long mpidr_list[PLATFORM_CORE_COUNT] = {UINT64_MAX};
+
+/******************************************************************************
+ * GIC Distributor interface accessors for writing entire registers
+ *****************************************************************************/
+static void gicd_write_irouter(unsigned int base,
+ unsigned int interrupt_id,
+ unsigned long long route)
+{
+ assert(interrupt_id >= MIN_SPI_ID);
+ mmio_write_64(base + GICD_IROUTER + (interrupt_id << 3), route);
+}
+
+/******************************************************************************
+ * GIC Re-distributor interface accessors for writing entire registers
+ *****************************************************************************/
+static void gicr_write_isenabler0(unsigned int base, unsigned int val)
+{
+ mmio_write_32(base + GICR_ISENABLER0, val);
+}
+
+static void gicr_write_icenabler0(unsigned int base, unsigned int val)
+{
+ mmio_write_32(base + GICR_ICENABLER0, val);
+}
+
+static void gicr_write_icpendr0(unsigned int base, unsigned int val)
+{
+ mmio_write_32(base + GICR_ICPENDR0, val);
+}
+
+static void gicr_write_ipriorityr(uintptr_t base, unsigned int id, unsigned int val)
+{
+ unsigned n = id >> IPRIORITYR_SHIFT;
+ mmio_write_32(base + GICR_IPRIORITYR + (n << 2), val);
+}
+
+static void gicr_write_icfgr1(uintptr_t base, unsigned int val)
+{
+ mmio_write_32(base + GICR_ICFGR1, val);
+}
+
+/******************************************************************************
+ * GIC Re-distributor interface accessors for reading entire registers
+ *****************************************************************************/
+static unsigned long long gicr_read_typer(uintptr_t base)
+{
+ return mmio_read_64(base + GICR_TYPER);
+}
+
+static unsigned int gicr_read_icfgr1(uintptr_t base)
+{
+ return mmio_read_32(base + GICR_ICFGR1);
+}
+
+static unsigned int gicr_read_isenabler0(unsigned int base)
+{
+ return mmio_read_32(base + GICR_ISENABLER0);
+}
+
+static unsigned int gicr_read_ipriorityr(uintptr_t base, unsigned int id)
+{
+ unsigned n = id >> IPRIORITYR_SHIFT;
+ return mmio_read_32(base + GICR_IPRIORITYR + (n << 2));
+}
+
+static unsigned int gicr_read_ispendr0(unsigned int base)
+{
+ return mmio_read_32(base + GICR_ISPENDR0);
+}
+
+/******************************************************************************
+ * GIC Re-distributor interface accessors for individual interrupt
+ * manipulation
+ *****************************************************************************/
+static void gicr_set_isenabler0(unsigned int base, unsigned int interrupt_id)
+{
+ unsigned bit_num = interrupt_id & ((1 << ISENABLER_SHIFT) - 1);
+ gicr_write_isenabler0(base, (1 << bit_num));
+}
+
+static void gicr_set_icenabler0(unsigned int base, unsigned int interrupt_id)
+{
+ unsigned bit_num = interrupt_id & ((1 << ISENABLER_SHIFT) - 1);
+ gicr_write_icenabler0(base, (1 << bit_num));
+}
+
+static void gicr_set_icpendr0(unsigned int base, unsigned int interrupt_id)
+{
+ unsigned bit_num = interrupt_id & ((1 << ICPENDR_SHIFT) - 1);
+ gicr_write_icpendr0(base, (1 << bit_num));
+}
+
+/******************************************************************************
+ * GICv3 public driver API
+ *****************************************************************************/
+void gicv3_enable_cpuif(void)
+{
+ /* Assert that system register access is enabled */
+ assert(IS_IN_EL2() ? (read_icc_sre_el2() & ICC_SRE_SRE_BIT) :
+ (read_icc_sre_el1() & ICC_SRE_SRE_BIT));
+
+ /* Enable Group1 non secure interrupts */
+ write_icc_igrpen1_el1(read_icc_igrpen1_el1() | IGRPEN1_EL1_ENABLE_BIT);
+}
+
+void gicv3_setup_cpuif(void)
+{
+ /* Set the priority mask register to allow all interrupts to trickle in */
+ write_icc_pmr_el1(GIC_PRI_MASK);
+ gicv3_enable_cpuif();
+}
+
+void gicv3_disable_cpuif(void)
+{
+ /* Disable Group1 non secure interrupts */
+ write_icc_igrpen1_el1(read_icc_igrpen1_el1() &
+ ~IGRPEN1_EL1_ENABLE_BIT);
+}
+
+void gicv3_save_cpuif_context(void)
+{
+ unsigned int core_pos = platform_get_core_pos(read_mpidr_el1());
+
+ /* Set the `is_suspended` flag as this core is being suspended. */
+ pcpu_ctx[core_pos].is_suspended = 1;
+ pcpu_ctx[core_pos].icc_igrpen1 = read_icc_igrpen1_el1();
+}
+
+void gicv3_restore_cpuif_context(void)
+{
+ unsigned int core_pos = platform_get_core_pos(read_mpidr_el1());
+
+ /* Reset the `is_suspended` flag as this core has resumed from suspend. */
+ pcpu_ctx[core_pos].is_suspended = 0;
+ write_icc_pmr_el1(GIC_PRI_MASK);
+ write_icc_igrpen1_el1(pcpu_ctx[core_pos].icc_igrpen1);
+}
+
+void gicv3_save_sgi_ppi_context(void)
+{
+ unsigned int i, core_pos;
+ unsigned int my_core_pos = platform_get_core_pos(read_mpidr_el1());
+
+ /* Save the context for all the suspended cores */
+ for (core_pos = 0; core_pos < PLATFORM_CORE_COUNT; core_pos++) {
+ /*
+ * Continue if the core pos is not the current core
+ * and has not suspended
+ */
+ if ((core_pos != my_core_pos) &&
+ (!pcpu_ctx[core_pos].is_suspended))
+ continue;
+
+ assert(rdist_pcpu_base[core_pos]);
+
+ pcpu_ctx[core_pos].gicr_isenabler =
+ gicr_read_isenabler0(rdist_pcpu_base[core_pos]);
+
+ /* Read the ipriority registers, 4 at a time */
+ for (i = 0; i < (NUM_PCPU_INTR >> IPRIORITYR_SHIFT); i++)
+ pcpu_ctx[core_pos].gicr_ipriorityr[i] =
+ gicr_read_ipriorityr(rdist_pcpu_base[core_pos],
+ i << IPRIORITYR_SHIFT);
+
+ pcpu_ctx[core_pos].gicr_icfgr =
+ gicr_read_icfgr1(rdist_pcpu_base[core_pos]);
+ }
+}
+
+void gicv3_restore_sgi_ppi_context(void)
+{
+ unsigned int i, core_pos;
+ unsigned int my_core_pos = platform_get_core_pos(read_mpidr_el1());
+
+ /* Restore the context for all the suspended cores */
+ for (core_pos = 0; core_pos < PLATFORM_CORE_COUNT; core_pos++) {
+ /*
+ * Continue if the core pos is not the current core
+ * and has not suspended
+ */
+ if ((core_pos != my_core_pos) &&
+ (!pcpu_ctx[core_pos].is_suspended))
+ continue;
+
+ assert(rdist_pcpu_base[core_pos]);
+
+ /* Read the ipriority registers, 4 at a time */
+ for (i = 0; i < (NUM_PCPU_INTR >> IPRIORITYR_SHIFT); i++)
+ gicr_write_ipriorityr(rdist_pcpu_base[core_pos],
+ i << IPRIORITYR_SHIFT,
+ pcpu_ctx[core_pos].gicr_ipriorityr[i]);
+
+ gicr_write_icfgr1(rdist_pcpu_base[core_pos],
+ pcpu_ctx[core_pos].gicr_icfgr);
+ gicr_write_isenabler0(rdist_pcpu_base[core_pos],
+ pcpu_ctx[core_pos].gicr_isenabler);
+ }
+}
+
+void gicv3_set_ipriorityr(unsigned int interrupt_id,
+ unsigned int priority)
+{
+ unsigned int core_pos;
+ assert(gicd_base_addr);
+ assert(IS_VALID_INTR_ID(interrupt_id));
+
+ if (interrupt_id < MIN_SPI_ID) {
+ core_pos = platform_get_core_pos(read_mpidr_el1());
+ assert(rdist_pcpu_base[core_pos]);
+ mmio_write_8(rdist_pcpu_base[core_pos] + GICR_IPRIORITYR
+ + interrupt_id, priority & GIC_PRI_MASK);
+ } else {
+ mmio_write_8(gicd_base_addr + GICD_IPRIORITYR + interrupt_id,
+ priority & GIC_PRI_MASK);
+ }
+}
+
+void gicv3_send_sgi(unsigned int sgi_id, unsigned int core_pos)
+{
+ unsigned long long aff0, aff1, aff2, aff3;
+ unsigned long long sgir, target_list;
+
+ assert(IS_SGI(sgi_id));
+ assert(core_pos < PLATFORM_CORE_COUNT);
+
+ assert(mpidr_list[core_pos] != UINT64_MAX);
+
+ /* Extract the affinity information */
+ aff0 = MPIDR_AFF_ID(mpidr_list[core_pos], 0);
+ aff1 = MPIDR_AFF_ID(mpidr_list[core_pos], 1);
+ aff2 = MPIDR_AFF_ID(mpidr_list[core_pos], 2);
+ aff3 = MPIDR_AFF_ID(mpidr_list[core_pos], 3);
+
+ /* Construct the SGI target list using Affinity 0 */
+ assert(aff0 < SGI_TARGET_MAX_AFF0);
+ target_list = 1 << aff0;
+
+ /* Construct the SGI target affinity */
+ sgir = ((aff3 & SGI1R_AFF_MASK) << SGI1R_AFF3_SHIFT) |
+ ((aff2 & SGI1R_AFF_MASK) << SGI1R_AFF2_SHIFT) |
+ ((aff1 & SGI1R_AFF_MASK) << SGI1R_AFF1_SHIFT) |
+ ((target_list & SGI1R_TARGET_LIST_MASK)
+ << SGI1R_TARGET_LIST_SHIFT);
+
+ /* Combine SGI target affinity with the SGI ID */
+ sgir |= ((sgi_id & SGI1R_INTID_MASK) << SGI1R_INTID_SHIFT);
+ write_icc_sgi1r(sgir);
+}
+
+void gicv3_set_intr_route(unsigned int interrupt_id,
+ unsigned int core_pos)
+{
+ unsigned long long route_affinity;
+
+ assert(gicd_base_addr);
+ assert(core_pos < PLATFORM_CORE_COUNT);
+ assert(mpidr_list[core_pos] != UINT64_MAX);
+
+ /* Routing information can be set only for SPIs */
+ assert(IS_SPI(interrupt_id));
+ route_affinity = mpidr_list[core_pos];
+
+ gicd_write_irouter(gicd_base_addr, interrupt_id, route_affinity);
+}
+
+void gicv3_set_isenabler(unsigned int interrupt_id)
+{
+ unsigned int core_pos;
+
+ assert(gicd_base_addr);
+ assert(IS_VALID_INTR_ID(interrupt_id));
+
+ if (interrupt_id < MIN_SPI_ID) {
+ core_pos = platform_get_core_pos(read_mpidr_el1());
+ assert(rdist_pcpu_base[core_pos]);
+ gicr_set_isenabler0(rdist_pcpu_base[core_pos], interrupt_id);
+ } else
+ gicd_set_isenabler(gicd_base_addr, interrupt_id);
+}
+
+void gicv3_set_icenabler(unsigned int interrupt_id)
+{
+ unsigned int core_pos;
+
+ assert(gicd_base_addr);
+ assert(IS_VALID_INTR_ID(interrupt_id));
+
+ if (interrupt_id < MIN_SPI_ID) {
+ core_pos = platform_get_core_pos(read_mpidr_el1());
+ assert(rdist_pcpu_base[core_pos]);
+ gicr_set_icenabler0(rdist_pcpu_base[core_pos], interrupt_id);
+ } else
+ gicd_set_icenabler(gicd_base_addr, interrupt_id);
+}
+
+unsigned int gicv3_get_ispendr(unsigned int interrupt_id)
+{
+ unsigned int ispendr;
+ unsigned int bit_pos, core_pos;
+
+ assert(gicd_base_addr);
+ assert(IS_VALID_INTR_ID(interrupt_id));
+
+ if (interrupt_id < MIN_SPI_ID) {
+ core_pos = platform_get_core_pos(read_mpidr_el1());
+ assert(rdist_pcpu_base[core_pos]);
+ ispendr = gicr_read_ispendr0(rdist_pcpu_base[core_pos]);
+ } else
+ ispendr = gicd_read_ispendr(gicd_base_addr, interrupt_id);
+
+ bit_pos = interrupt_id % (1 << ISPENDR_SHIFT);
+ return !!(ispendr & (1 << bit_pos));
+}
+
+void gicv3_set_icpendr(unsigned int interrupt_id)
+{
+ unsigned int core_pos;
+
+ assert(gicd_base_addr);
+ assert(IS_SPI(interrupt_id) || IS_PPI(interrupt_id));
+
+ if (interrupt_id < MIN_SPI_ID) {
+ core_pos = platform_get_core_pos(read_mpidr_el1());
+ assert(rdist_pcpu_base[core_pos]);
+ gicr_set_icpendr0(rdist_pcpu_base[core_pos], interrupt_id);
+
+ } else
+ gicd_set_icpendr(gicd_base_addr, interrupt_id);
+}
+
+void gicv3_probe_redistif_addr(void)
+{
+ unsigned long long typer_val;
+ uintptr_t rdistif_base;
+ unsigned long long affinity;
+ unsigned int core_pos = platform_get_core_pos(read_mpidr_el1());
+
+ assert(gicr_base_addr);
+
+ /*
+ * Return if the re-distributor base address is already populated
+ * for this core.
+ */
+ if (rdist_pcpu_base[core_pos])
+ return;
+
+ /* Iterate over the GICR frames and find the matching frame*/
+ rdistif_base = gicr_base_addr;
+ affinity = gic_typer_affinity_from_mpidr(read_mpidr_el1() & MPIDR_AFFINITY_MASK);
+ do {
+ typer_val = gicr_read_typer(rdistif_base);
+ if (affinity == ((typer_val >> TYPER_AFF_VAL_SHIFT) & TYPER_AFF_VAL_MASK)) {
+ rdist_pcpu_base[core_pos] = rdistif_base;
+ mpidr_list[core_pos] = read_mpidr_el1() & MPIDR_AFFINITY_MASK;
+ return;
+ }
+ rdistif_base += (1 << GICR_PCPUBASE_SHIFT);
+ } while (!(typer_val & TYPER_LAST_BIT));
+
+ ERROR("Re-distributor address not found for core %d\n", core_pos);
+ panic();
+}
+
+void gicv3_setup_distif(void)
+{
+ unsigned int gicd_ctlr;
+
+ assert(gicd_base_addr);
+
+ /* Check for system register support */
+ assert(read_id_aa64pfr0_el1() &
+ (ID_AA64PFR0_GIC_MASK << ID_AA64PFR0_GIC_SHIFT));
+
+ /* Assert that system register access is enabled */
+ assert(is_sre_enabled());
+
+ /* Enable the forwarding of interrupts to CPU interface */
+ gicd_ctlr = gicd_read_ctlr(gicd_base_addr);
+
+ /* Assert ARE_NS bit in GICD */
+ assert(gicd_ctlr & (GICD_CTLR_ARE_NS_MASK << GICD_CTLR_ARE_NS_SHIFT));
+
+ gicd_ctlr |= GICD_CTLR_ENABLE_GRP1A;
+ gicd_write_ctlr(gicd_base_addr, gicd_ctlr);
+}
+
+void gicv3_init(uintptr_t gicr_base, uintptr_t gicd_base)
+{
+ assert(gicr_base);
+ assert(gicd_base);
+
+ gicr_base_addr = gicr_base;
+ gicd_base_addr = gicd_base;
+}
diff --git a/include/drivers/arm/gic_common.h b/include/drivers/arm/gic_common.h
index d2947b2..337d613 100644
--- a/include/drivers/arm/gic_common.h
+++ b/include/drivers/arm/gic_common.h
@@ -72,6 +72,9 @@
#include <mmio.h>
+/* Helper to detect the GIC mode (GICv2 or GICv3) configured in the system */
+unsigned int is_gicv3_mode(void);
+
/*******************************************************************************
* Private GIC Distributor function prototypes for use by GIC drivers
******************************************************************************/
diff --git a/include/drivers/arm/gic_v3.h b/include/drivers/arm/gic_v3.h
new file mode 100644
index 0000000..8f8a92b
--- /dev/null
+++ b/include/drivers/arm/gic_v3.h
@@ -0,0 +1,226 @@
+/*
+ * Copyright (c) 2016, ARM Limited and Contributors. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * Neither the name of ARM nor the names of its contributors may be used
+ * to endorse or promote products derived from this software without specific
+ * prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __GIC_V3_H__
+#define __GIC_V3_H__
+
+/***************************************************************************
+ * Defines and prototypes specific to GIC v3.
+ *************************************************************************/
+
+/* GICD register offsets */
+#define GICD_IROUTER 0x6000
+
+/* GICD_CTLR bit definitions */
+#define GICD_CTLR_ENABLE_GRP1A (1 << 1)
+#define GICD_CTLR_ARE_NS_SHIFT 4
+#define GICD_CTLR_ARE_NS_MASK 0x1
+
+/* GICR_TYPER bit definitions */
+#define TYPER_AFF_VAL_SHIFT 32
+#define TYPER_PROC_NUM_SHIFT 8
+#define TYPER_LAST_SHIFT 4
+
+#define TYPER_AFF_VAL_MASK 0xffffffff
+#define TYPER_PROC_NUM_MASK 0xffff
+#define TYPER_LAST_MASK 0x1
+
+#define TYPER_LAST_BIT (1 << TYPER_LAST_SHIFT)
+
+/* GICD_IROUTER shifts and masks */
+#define IROUTER_IRM_SHIFT 31
+#define IROUTER_IRM_MASK 0x1
+
+/*******************************************************************************
+ * GICv3 Re-distributor interface registers & constants
+ ******************************************************************************/
+#define GICR_PCPUBASE_SHIFT 0x11
+#define GICR_SGIBASE_OFFSET (1 << 0x10) /* 64 KB */
+#define GICR_CTLR 0x0
+#define GICR_TYPER 0x08
+#define GICR_WAKER 0x14
+#define GICR_IGROUPR0 (GICR_SGIBASE_OFFSET + 0x80)
+#define GICR_ISENABLER0 (GICR_SGIBASE_OFFSET + 0x100)
+#define GICR_ICENABLER0 (GICR_SGIBASE_OFFSET + 0x180)
+#define GICR_ISPENDR0 (GICR_SGIBASE_OFFSET + 0x200)
+#define GICR_ICPENDR0 (GICR_SGIBASE_OFFSET + 0x280)
+#define GICR_IPRIORITYR (GICR_SGIBASE_OFFSET + 0x400)
+#define GICR_ICFGR0 (GICR_SGIBASE_OFFSET + 0xc00)
+#define GICR_ICFGR1 (GICR_SGIBASE_OFFSET + 0xc04)
+#define GICR_IGRPMODR0 (GICR_SGIBASE_OFFSET + 0xd00)
+
+/*******************************************************************************
+ * GICv3 CPU interface registers & constants
+ ******************************************************************************/
+/* ICC_SRE bit definitions*/
+#define ICC_SRE_EN_BIT (1 << 3)
+#define ICC_SRE_DIB_BIT (1 << 2)
+#define ICC_SRE_DFB_BIT (1 << 1)
+#define ICC_SRE_SRE_BIT (1 << 0)
+
+/* ICC_IAR1_EL1 bit definitions */
+#define IAR1_EL1_INTID_SHIFT 0
+#define IAR1_EL1_INTID_MASK 0xffffff
+
+/* ICC_SGI1R bit definitions */
+#define SGI1R_TARGET_LIST_MASK 0xffff
+#define SGI1R_TARGET_LIST_SHIFT 0x0
+#define SGI1R_AFF_MASK 0xff
+#define SGI1R_AFF1_SHIFT 16ULL
+#define SGI1R_AFF2_SHIFT 32ULL
+#define SGI1R_AFF3_SHIFT 48ULL
+#define SGI1R_INTID_MASK 0xf
+#define SGI1R_INTID_SHIFT 24
+#define SGI1R_IRM_MASK 0x1
+#define SGI1R_IRM_SHIFT 0x40
+
+/* ICC_IGRPEN1_EL1 bit definitions */
+#define IGRPEN1_EL1_ENABLE_SHIFT 0
+#define IGRPEN1_EL1_ENABLE_BIT (1 << IGRPEN1_EL1_ENABLE_SHIFT)
+
+/* The highest affinity 0 that can be a SGI target*/
+#define SGI_TARGET_MAX_AFF0 16
+
+#ifndef ASSEMBLY
+
+/*******************************************************************************
+ * Helper GICv3 macros
+ ******************************************************************************/
+#define gicv3_acknowledge_interrupt() read_icc_iar1_el1() &\
+ IAR1_EL1_INTID_MASK
+#define gicv3_end_of_interrupt(id) write_icc_eoir1_el1(id)
+
+#define is_sre_enabled() \
+ (IS_IN_EL2() ? (read_icc_sre_el2() & ICC_SRE_SRE_BIT) :\
+ (read_icc_sre_el1() & ICC_SRE_SRE_BIT))
+
+/******************************************************************************
+ * GICv3 public driver API
+ *****************************************************************************/
+ /*
+ * Initialize the GICv3 driver. The base addresses of GIC Re-distributor
+ * interface `gicr_base` and the Distributor interface `gicd_base` must
+ * be provided as arguments.
+ */
+void gicv3_init(uintptr_t gicr_base, uintptr_t gicd_base);
+
+/*
+ * Setup the GIC Distributor interface.
+ */
+void gicv3_setup_distif(void);
+
+/*
+ * Probe the Re-distributor base corresponding to this core.
+ * This function is required to be invoked on successful boot of a core.
+ * The base address will be stored internally by the driver and will be
+ * used when accessing the Re-distributor interface.
+ */
+void gicv3_probe_redistif_addr(void);
+
+/*
+ * Set the bit corresponding to `interrupt_id` in the ICPENDR register
+ * at either Distributor or Re-distributor depending on the interrupt.
+ */
+void gicv3_set_icpendr(unsigned int interrupt_id);
+
+/*
+ * Get the bit corresponding to `interrupt_id` in the ISPENDR register
+ * at either Distributor or Re-distributor depending on the interrupt.
+ */
+unsigned int gicv3_get_ispendr(unsigned int interrupt_id);
+
+/*
+ * Set the bit corresponding to `interrupt_id` in the ICENABLER register
+ * at either Distributor or Re-distributor depending on the interrupt.
+ */
+void gicv3_set_icenabler(unsigned int interrupt_id);
+
+/*
+ * Set the bit corresponding to `interrupt_id` in the ISENABLER register
+ * at either Distributor or Re-distributor depending on the interrupt.
+ */
+void gicv3_set_isenabler(unsigned int interrupt_id);
+
+/*
+ * Set the `route` corresponding to `interrupt_id` in the IROUTER register
+ * at Distributor.
+ */
+void gicv3_set_intr_route(unsigned int interrupt_id, unsigned int core_pos);
+
+/*
+ * Send SGI with ID `sgi_id` to core with index `core_pos`.
+ */
+void gicv3_send_sgi(unsigned int sgi_id, unsigned int core_pos);
+
+/*
+ * Set the priority of the interrupt `interrupt_id` to `priority`.
+ */
+void gicv3_set_ipriorityr(unsigned int interrupt_id, unsigned int priority);
+
+/*
+ * Restore the GICv3 SGI and PPI context after powering up the
+ * GIC Re-distributor.
+ */
+void gicv3_restore_sgi_ppi_context(void);
+
+/*
+ * Save the GICv3 SGI and PPI context prior to powering down the
+ * GIC Re-distributor.
+ */
+void gicv3_save_sgi_ppi_context(void);
+
+/*
+ * Restore the GICv3 CPU interface after powering up the CPU interface.
+ */
+void gicv3_restore_cpuif_context(void);
+
+/*
+ * Save the GICv3 CPU interface prior to powering down the CPU interface.
+ */
+void gicv3_save_cpuif_context(void);
+
+/*
+ * Disable the GIC CPU interface.
+ */
+void gicv3_disable_cpuif(void);
+
+/*
+ * Setup the GIC CPU interface.
+ */
+void gicv3_setup_cpuif(void);
+
+/*
+ * Enable the GIC CPU interface.
+ */
+void gicv3_enable_cpuif(void);
+
+
+#endif /*__ASSEMBLY__*/
+#endif /* __GIC_V3_H__ */
diff --git a/include/lib/aarch64/arch.h b/include/lib/aarch64/arch.h
index 93f68c1..a68bb77 100644
--- a/include/lib/aarch64/arch.h
+++ b/include/lib/aarch64/arch.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2013-2014, ARM Limited and Contributors. All rights reserved.
+ * Copyright (c) 2013-2016, ARM Limited and Contributors. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
@@ -86,10 +86,15 @@
/*******************************************************************************
* Definitions for CPU system register interface to GICv3
******************************************************************************/
-#define ICC_SRE_EL1 S3_0_C12_C12_5
-#define ICC_SRE_EL2 S3_4_C12_C9_5
-#define ICC_CTLR_EL1 S3_0_C12_C12_4
-#define ICC_PMR_EL1 S3_0_C4_C6_0
+#define ICC_SRE_EL1 S3_0_C12_C12_5
+#define ICC_SRE_EL2 S3_4_C12_C9_5
+#define ICC_CTLR_EL1 S3_0_C12_C12_4
+#define ICC_PMR_EL1 S3_0_C4_C6_0
+#define ICC_IGRPEN1_EL1 S3_0_C12_C12_7
+#define ICC_HPPIR1_EL1 S3_0_C12_C12_2
+#define ICC_IAR1_EL1 S3_0_C12_C12_0
+#define ICC_EOIR1_EL1 S3_0_C12_C12_1
+#define ICC_SGI1R S3_0_C12_C11_5
/*******************************************************************************
* Generic timer memory mapped registers & offsets
@@ -123,6 +128,10 @@
#define ID_AA64PFR0_EL2_SHIFT 8
#define ID_AA64PFR0_ELX_MASK 0xf
+#define ID_AA64PFR0_GIC_SHIFT 24
+#define ID_AA64PFR0_GIC_WIDTH 4
+#define ID_AA64PFR0_GIC_MASK ((1 << ID_AA64PFR0_GIC_WIDTH) - 1)
+
/* ID_PFR1_EL1 definitions */
#define ID_PFR1_VIRTEXT_SHIFT 12
#define ID_PFR1_VIRTEXT_MASK 0xf
diff --git a/include/lib/aarch64/arch_helpers.h b/include/lib/aarch64/arch_helpers.h
index aa0d7f4..e96feea 100644
--- a/include/lib/aarch64/arch_helpers.h
+++ b/include/lib/aarch64/arch_helpers.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2013-2014, ARM Limited and Contributors. All rights reserved.
+ * Copyright (c) 2013-2016, ARM Limited and Contributors. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
@@ -74,6 +74,14 @@ static inline void write_ ## _name(const uint64_t v) \
_DEFINE_SYSREG_READ_FUNC(_name, _reg_name) \
_DEFINE_SYSREG_WRITE_FUNC(_name, _reg_name)
+/* Define read function for renamed system register */
+#define DEFINE_RENAME_SYSREG_READ_FUNC(_name, _reg_name) \
+ _DEFINE_SYSREG_READ_FUNC(_name, _reg_name)
+
+/* Define write function for renamed system register */
+#define DEFINE_RENAME_SYSREG_WRITE_FUNC(_name, _reg_name) \
+ _DEFINE_SYSREG_WRITE_FUNC(_name, _reg_name)
+
/* Define write function for special system registers */
#define DEFINE_SYSREG_WRITE_CONST_FUNC(_name) \
_DEFINE_SYSREG_WRITE_CONST_FUNC(_name, _name)
@@ -261,6 +269,11 @@ DEFINE_SYSREG_RW_FUNCS(vmpidr_el2)
DEFINE_RENAME_SYSREG_RW_FUNCS(icc_sre_el1, ICC_SRE_EL1)
DEFINE_RENAME_SYSREG_RW_FUNCS(icc_sre_el2, ICC_SRE_EL2)
DEFINE_RENAME_SYSREG_RW_FUNCS(icc_pmr_el1, ICC_PMR_EL1)
+DEFINE_RENAME_SYSREG_RW_FUNCS(icc_igrpen1_el1, ICC_IGRPEN1_EL1)
+DEFINE_RENAME_SYSREG_RW_FUNCS(icc_sgi1r, ICC_SGI1R)
+DEFINE_RENAME_SYSREG_READ_FUNC(icc_hppir1_el1, ICC_HPPIR1_EL1)
+DEFINE_RENAME_SYSREG_READ_FUNC(icc_iar1_el1, ICC_IAR1_EL1)
+DEFINE_RENAME_SYSREG_WRITE_FUNC(icc_eoir1_el1, ICC_EOIR1_EL1)
#define IS_IN_EL(x) \
diff --git a/plat/armstrong/platform.mk b/plat/armstrong/platform.mk
index 0e92af1..4c7bde1 100644
--- a/plat/armstrong/platform.mk
+++ b/plat/armstrong/platform.mk
@@ -30,9 +30,10 @@
PLAT_INCLUDES := -Iplat/armstrong/include/
-PLAT_SOURCES := drivers/arm/gic/arm_gic_v2.c \
+PLAT_SOURCES := drivers/arm/gic/arm_gic_v2v3.c \
drivers/arm/gic/gic_common.c \
drivers/arm/gic/gic_v2.c \
+ drivers/arm/gic/gic_v3.c \
drivers/arm/pl011/pl011_console.S \
drivers/arm/sp805/sp805.c \
drivers/arm/timer/system_timer.c \
diff --git a/plat/fvp/platform.mk b/plat/fvp/platform.mk
index 9931ae0..a7d86f0 100644
--- a/plat/fvp/platform.mk
+++ b/plat/fvp/platform.mk
@@ -30,9 +30,10 @@
PLAT_INCLUDES := -Iplat/fvp/include/
-PLAT_SOURCES := drivers/arm/gic/arm_gic_v2.c \
+PLAT_SOURCES := drivers/arm/gic/arm_gic_v2v3.c \
drivers/arm/gic/gic_common.c \
drivers/arm/gic/gic_v2.c \
+ drivers/arm/gic/gic_v3.c \
drivers/arm/pl011/pl011_console.S \
drivers/arm/sp805/sp805.c \
drivers/arm/timer/system_timer.c \