diff options
-rw-r--r-- | docs/implementing-tests.md | 3 | ||||
-rw-r--r-- | drivers/arm/gic/arm_gic_v2v3.c | 204 | ||||
-rw-r--r-- | drivers/arm/gic/gic_common.c | 11 | ||||
-rw-r--r-- | drivers/arm/gic/gic_v2.c | 2 | ||||
-rw-r--r-- | drivers/arm/gic/gic_v3.c | 468 | ||||
-rw-r--r-- | include/drivers/arm/gic_common.h | 3 | ||||
-rw-r--r-- | include/drivers/arm/gic_v3.h | 226 | ||||
-rw-r--r-- | include/lib/aarch64/arch.h | 19 | ||||
-rw-r--r-- | include/lib/aarch64/arch_helpers.h | 15 | ||||
-rw-r--r-- | plat/armstrong/platform.mk | 3 | ||||
-rw-r--r-- | plat/fvp/platform.mk | 3 |
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 \ |