summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-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 \