diff --git a/docs/user-guide.md b/docs/user-guide.md index 5165000..21e1f99 100644 --- a/docs/user-guide.md +++ b/docs/user-guide.md @@ -641,6 +641,7 @@ if `FVP_CLUSTER_COUNT` > 2. * `FVP_USE_GIC_DRIVER` : Selects the GIC driver to be built. Options: + - `FVP_GIC600` : The GIC600 implementation of GICv3 is selected - `FVP_GICV2` : The GICv2 only driver is selected - `FVP_GICV3` : The GICv3 only driver is selected (default option) - `FVP_GICV3_LEGACY`: The Legacy GICv3 driver is selected (deprecated) diff --git a/drivers/arm/gic/v3/gic600.c b/drivers/arm/gic/v3/gic600.c new file mode 100644 index 0000000..4ea31ab --- /dev/null +++ b/drivers/arm/gic/v3/gic600.c @@ -0,0 +1,119 @@ +/* + * Copyright (c) 2017, ARM Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +/* + * Driver for GIC600-specific features. This driver only overrides APIs that are + * different to those generic ones in GICv3 driver. + * + * GIC600 supports independently power-gating redistributor interface. + */ + +#include +#include +#include + +#include "gicv3_private.h" + +/* GIC600-specific register offsets */ +#define GICR_PWRR 0x24 + +/* GICR_PWRR fields */ +#define PWRR_RDPD_SHIFT 0 +#define PWRR_RDGPD_SHIFT 2 +#define PWRR_RDGPO_SHIFT 3 + +#define PWRR_RDGPD (1 << PWRR_RDGPD_SHIFT) +#define PWRR_RDGPO (1 << PWRR_RDGPO_SHIFT) + +/* Values to write to GICR_PWRR register to power redistributor */ +#define PWRR_ON (0 << PWRR_RDPD_SHIFT) +#define PWRR_OFF (1 << PWRR_RDPD_SHIFT) + +/* Generic GICv3 resources */ +extern const gicv3_driver_data_t *gicv3_driver_data; + +/* GIC600-specific accessor functions */ +static void gicr_write_pwrr(uintptr_t base, unsigned int val) +{ + mmio_write_32(base + GICR_PWRR, val); +} + +static uint32_t gicr_read_pwrr(uintptr_t base) +{ + return mmio_read_32(base + GICR_PWRR); +} + +static int gicr_group_powering_down(uint32_t pwrr) +{ + /* + * Whether the redistributor group power down operation is in transit: + * i.e. it's intending to, but not finished yet. + */ + return ((pwrr & PWRR_RDGPD) && !(pwrr & PWRR_RDGPO)); +} + +static void gic600_pwr_on(uintptr_t base) +{ + /* Power on redistributor */ + gicr_write_pwrr(base, PWRR_ON); + + /* Wait until the power on state is reflected */ + while (gicr_read_pwrr(base) & PWRR_RDGPO) + ; +} + +static void gic600_pwr_off(uintptr_t base) +{ + /* Power off redistributor */ + gicr_write_pwrr(base, PWRR_OFF); + + /* + * If this is the last man, turning this redistributor frame off will + * result in the group itself being powered off. In that case, wait as + * long as it's in transition, or has aborted the transition altogether + * for any reason. + */ + if (gicr_read_pwrr(base) & PWRR_RDGPD) { + while (gicr_group_powering_down(gicr_read_pwrr(base))) + ; + } +} + +/* + * Power off GIC600 redistributor + */ +void gicv3_rdistif_off(unsigned int proc_num) +{ + uintptr_t gicr_base; + + assert(gicv3_driver_data); + assert(proc_num < gicv3_driver_data->rdistif_num); + assert(gicv3_driver_data->rdistif_base_addrs); + + gicr_base = gicv3_driver_data->rdistif_base_addrs[proc_num]; + assert(gicr_base); + + /* Attempt to power redistributor off */ + gic600_pwr_off(gicr_base); +} + +/* + * Power on GIC600 redistributor + */ +void gicv3_rdistif_on(unsigned int proc_num) +{ + uintptr_t gicr_base; + + assert(gicv3_driver_data); + assert(proc_num < gicv3_driver_data->rdistif_num); + assert(gicv3_driver_data->rdistif_base_addrs); + + gicr_base = gicv3_driver_data->rdistif_base_addrs[proc_num]; + assert(gicr_base); + + /* Power redistributor on */ + gic600_pwr_on(gicr_base); +} diff --git a/drivers/arm/gic/v3/gicv3_main.c b/drivers/arm/gic/v3/gicv3_main.c index 566c446..b68d998 100644 --- a/drivers/arm/gic/v3/gicv3_main.c +++ b/drivers/arm/gic/v3/gicv3_main.c @@ -8,12 +8,10 @@ #include #include #include -#include #include -#include "../common/gic_common_private.h" #include "gicv3_private.h" -static const gicv3_driver_data_t *driver_data; +const gicv3_driver_data_t *gicv3_driver_data; static unsigned int gicv2_compat; /* @@ -90,18 +88,20 @@ plat_driver_data->gicr_base, plat_driver_data->mpidr_to_core_pos); - driver_data = plat_driver_data; + gicv3_driver_data = plat_driver_data; /* * The GIC driver data is initialized by the primary CPU with caches * enabled. When the secondary CPU boots up, it initializes the * GICC/GICR interface with the caches disabled. Hence flush the - * driver_data to ensure coherency. This is not required if the + * driver data to ensure coherency. This is not required if the * platform has HW_ASSISTED_COHERENCY enabled. */ #if !HW_ASSISTED_COHERENCY - flush_dcache_range((uintptr_t) &driver_data, sizeof(driver_data)); - flush_dcache_range((uintptr_t) driver_data, sizeof(*driver_data)); + flush_dcache_range((uintptr_t) &gicv3_driver_data, + sizeof(gicv3_driver_data)); + flush_dcache_range((uintptr_t) gicv3_driver_data, + sizeof(*gicv3_driver_data)); #endif INFO("GICv3 %s legacy support detected." @@ -117,10 +117,10 @@ { unsigned int bitmap = 0; - assert(driver_data); - assert(driver_data->gicd_base); - assert(driver_data->g1s_interrupt_array || - driver_data->g0_interrupt_array); + assert(gicv3_driver_data); + assert(gicv3_driver_data->gicd_base); + assert(gicv3_driver_data->g1s_interrupt_array || + gicv3_driver_data->g0_interrupt_array); assert(IS_IN_EL3()); @@ -129,39 +129,39 @@ * the ARE_S bit. The Distributor might generate a system error * otherwise. */ - gicd_clr_ctlr(driver_data->gicd_base, + gicd_clr_ctlr(gicv3_driver_data->gicd_base, CTLR_ENABLE_G0_BIT | CTLR_ENABLE_G1S_BIT | CTLR_ENABLE_G1NS_BIT, RWP_TRUE); /* Set the ARE_S and ARE_NS bit now that interrupts have been disabled */ - gicd_set_ctlr(driver_data->gicd_base, + gicd_set_ctlr(gicv3_driver_data->gicd_base, CTLR_ARE_S_BIT | CTLR_ARE_NS_BIT, RWP_TRUE); /* Set the default attribute of all SPIs */ - gicv3_spis_configure_defaults(driver_data->gicd_base); + gicv3_spis_configure_defaults(gicv3_driver_data->gicd_base); /* Configure the G1S SPIs */ - if (driver_data->g1s_interrupt_array) { - gicv3_secure_spis_configure(driver_data->gicd_base, - driver_data->g1s_interrupt_num, - driver_data->g1s_interrupt_array, + if (gicv3_driver_data->g1s_interrupt_array) { + gicv3_secure_spis_configure(gicv3_driver_data->gicd_base, + gicv3_driver_data->g1s_interrupt_num, + gicv3_driver_data->g1s_interrupt_array, INTR_GROUP1S); bitmap |= CTLR_ENABLE_G1S_BIT; } /* Configure the G0 SPIs */ - if (driver_data->g0_interrupt_array) { - gicv3_secure_spis_configure(driver_data->gicd_base, - driver_data->g0_interrupt_num, - driver_data->g0_interrupt_array, + if (gicv3_driver_data->g0_interrupt_array) { + gicv3_secure_spis_configure(gicv3_driver_data->gicd_base, + gicv3_driver_data->g0_interrupt_num, + gicv3_driver_data->g0_interrupt_array, INTR_GROUP0); bitmap |= CTLR_ENABLE_G0_BIT; } /* Enable the secure SPIs now that they have been configured */ - gicd_set_ctlr(driver_data->gicd_base, bitmap, RWP_TRUE); + gicd_set_ctlr(gicv3_driver_data->gicd_base, bitmap, RWP_TRUE); } /******************************************************************************* @@ -173,37 +173,37 @@ { uintptr_t gicr_base; - assert(driver_data); - assert(proc_num < driver_data->rdistif_num); - assert(driver_data->rdistif_base_addrs); - assert(driver_data->gicd_base); - assert(gicd_read_ctlr(driver_data->gicd_base) & CTLR_ARE_S_BIT); - assert(driver_data->g1s_interrupt_array || - driver_data->g0_interrupt_array); + assert(gicv3_driver_data); + assert(proc_num < gicv3_driver_data->rdistif_num); + assert(gicv3_driver_data->rdistif_base_addrs); + assert(gicv3_driver_data->gicd_base); + assert(gicd_read_ctlr(gicv3_driver_data->gicd_base) & CTLR_ARE_S_BIT); + assert(gicv3_driver_data->g1s_interrupt_array || + gicv3_driver_data->g0_interrupt_array); assert(IS_IN_EL3()); /* Power on redistributor */ gicv3_rdistif_on(proc_num); - gicr_base = driver_data->rdistif_base_addrs[proc_num]; + gicr_base = gicv3_driver_data->rdistif_base_addrs[proc_num]; /* Set the default attribute of all SGIs and PPIs */ gicv3_ppi_sgi_configure_defaults(gicr_base); /* Configure the G1S SGIs/PPIs */ - if (driver_data->g1s_interrupt_array) { + if (gicv3_driver_data->g1s_interrupt_array) { gicv3_secure_ppi_sgi_configure(gicr_base, - driver_data->g1s_interrupt_num, - driver_data->g1s_interrupt_array, + gicv3_driver_data->g1s_interrupt_num, + gicv3_driver_data->g1s_interrupt_array, INTR_GROUP1S); } /* Configure the G0 SGIs/PPIs */ - if (driver_data->g0_interrupt_array) { + if (gicv3_driver_data->g0_interrupt_array) { gicv3_secure_ppi_sgi_configure(gicr_base, - driver_data->g0_interrupt_num, - driver_data->g0_interrupt_array, + gicv3_driver_data->g0_interrupt_num, + gicv3_driver_data->g0_interrupt_array, INTR_GROUP0); } } @@ -231,13 +231,13 @@ unsigned int scr_el3; unsigned int icc_sre_el3; - assert(driver_data); - assert(proc_num < driver_data->rdistif_num); - assert(driver_data->rdistif_base_addrs); + assert(gicv3_driver_data); + assert(proc_num < gicv3_driver_data->rdistif_num); + assert(gicv3_driver_data->rdistif_base_addrs); assert(IS_IN_EL3()); /* Mark the connected core as awake */ - gicr_base = driver_data->rdistif_base_addrs[proc_num]; + gicr_base = gicv3_driver_data->rdistif_base_addrs[proc_num]; gicv3_rdistif_mark_core_awake(gicr_base); /* Disable the legacy interrupt bypass */ @@ -291,9 +291,9 @@ { uintptr_t gicr_base; - assert(driver_data); - assert(proc_num < driver_data->rdistif_num); - assert(driver_data->rdistif_base_addrs); + assert(gicv3_driver_data); + assert(proc_num < gicv3_driver_data->rdistif_num); + assert(gicv3_driver_data->rdistif_base_addrs); assert(IS_IN_EL3()); @@ -314,7 +314,7 @@ isb(); /* Mark the connected core as asleep */ - gicr_base = driver_data->rdistif_base_addrs[proc_num]; + gicr_base = gicv3_driver_data->rdistif_base_addrs[proc_num]; gicv3_rdistif_mark_core_asleep(gicr_base); } @@ -371,25 +371,25 @@ uintptr_t gicr_base; assert(IS_IN_EL3()); - assert(driver_data); + assert(gicv3_driver_data); /* Ensure the parameters are valid */ assert(id < PENDING_G1S_INTID || id >= MIN_LPI_ID); - assert(proc_num < driver_data->rdistif_num); + assert(proc_num < gicv3_driver_data->rdistif_num); /* All LPI interrupts are Group 1 non secure */ if (id >= MIN_LPI_ID) return INTR_GROUP1NS; if (id < MIN_SPI_ID) { - assert(driver_data->rdistif_base_addrs); - gicr_base = driver_data->rdistif_base_addrs[proc_num]; + assert(gicv3_driver_data->rdistif_base_addrs); + gicr_base = gicv3_driver_data->rdistif_base_addrs[proc_num]; igroup = gicr_get_igroupr0(gicr_base, id); grpmodr = gicr_get_igrpmodr0(gicr_base, id); } else { - assert(driver_data->gicd_base); - igroup = gicd_get_igroupr(driver_data->gicd_base, id); - grpmodr = gicd_get_igrpmodr(driver_data->gicd_base, id); + assert(gicv3_driver_data->gicd_base); + igroup = gicd_get_igroupr(gicv3_driver_data->gicd_base, id); + grpmodr = gicd_get_igrpmodr(gicv3_driver_data->gicd_base, id); } /* diff --git a/drivers/arm/gic/v3/gicv3_private.h b/drivers/arm/gic/v3/gicv3_private.h index 473fdb1..f95cfab 100644 --- a/drivers/arm/gic/v3/gicv3_private.h +++ b/drivers/arm/gic/v3/gicv3_private.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015-2016, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2015-2017, ARM Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -7,9 +7,11 @@ #ifndef __GICV3_PRIVATE_H__ #define __GICV3_PRIVATE_H__ +#include #include #include #include +#include "../common/gic_common_private.h" /******************************************************************************* * GICv3 private macro definitions diff --git a/plat/arm/board/fvp/platform.mk b/plat/arm/board/fvp/platform.mk index 6a759c5..0baa826 100644 --- a/plat/arm/board/fvp/platform.mk +++ b/plat/arm/board/fvp/platform.mk @@ -31,13 +31,18 @@ $(eval $(call add_define,FVP_INTERCONNECT_DRIVER)) -# Choose the GIC sources depending upon the how the FVP will be invoked -ifeq (${FVP_USE_GIC_DRIVER}, FVP_GICV3) -FVP_GIC_SOURCES := drivers/arm/gic/common/gic_common.c \ +FVP_GICV3_SOURCES := drivers/arm/gic/common/gic_common.c \ drivers/arm/gic/v3/gicv3_main.c \ drivers/arm/gic/v3/gicv3_helpers.c \ plat/common/plat_gicv3.c \ plat/arm/common/arm_gicv3.c + +# Choose the GIC sources depending upon the how the FVP will be invoked +ifeq (${FVP_USE_GIC_DRIVER}, FVP_GICV3) +FVP_GIC_SOURCES := ${FVP_GICV3_SOURCES} +else ifeq (${FVP_USE_GIC_DRIVER},FVP_GIC600) +FVP_GIC_SOURCES := ${FVP_GICV3_SOURCES} \ + drivers/arm/gic/v3/gic600.c else ifeq (${FVP_USE_GIC_DRIVER}, FVP_GICV2) FVP_GIC_SOURCES := drivers/arm/gic/common/gic_common.c \ drivers/arm/gic/v2/gicv2_main.c \