From e1c59ab3f18a49301f649251b4d8a7d011cc9fb9 Mon Sep 17 00:00:00 2001 From: Jeenu Viswambharan Date: Tue, 6 Dec 2016 16:15:22 +0000 Subject: [PATCH] Introduce ARM GIC-600 driver ARM GIC-600 IP complies with ARM GICv3 architecture, but among others, implements a power control register in the Redistributor frame. This register must be programmed to mark the frame as powered on, before accessing other registers in the frame. Rest of initialization sequence remains the same. The driver provides APIs for Redistributor power management, and overrides those in the generic GICv3 driver. The driver data is shared between generic GICv3 driver and that of GIC-600. For FVP platform, the GIC-600 driver is chosen when FVP_USE_GIC_DRIVER is set to FVP_GIC600. Also update user guide. Change-Id: I321b2360728d69f6d4b0a747b2cfcc3fe5a20d67 Signed-off-by: Jeenu Viswambharan --- docs/user-guide.md | 1 + drivers/arm/gic/v3/gic600.c | 119 +++++++++++++++++++++++++++++ drivers/arm/gic/v3/gicv3_main.c | 104 ++++++++++++------------- drivers/arm/gic/v3/gicv3_private.h | 4 +- plat/arm/board/fvp/platform.mk | 11 ++- 5 files changed, 183 insertions(+), 56 deletions(-) create mode 100644 drivers/arm/gic/v3/gic600.c diff --git a/docs/user-guide.md b/docs/user-guide.md index 5165000d3..21e1f9991 100644 --- a/docs/user-guide.md +++ b/docs/user-guide.md @@ -641,6 +641,7 @@ map is explained in the [Firmware Design]. 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 000000000..4ea31ab16 --- /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 566c446d6..b68d99882 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 @@ void gicv3_driver_init(const gicv3_driver_data_t *plat_driver_data) 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 @@ void gicv3_distif_init(void) { 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 @@ void gicv3_distif_init(void) * 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 @@ void gicv3_rdistif_init(unsigned int proc_num) { 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 @@ void gicv3_cpuif_enable(unsigned int proc_num) 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 @@ void gicv3_cpuif_disable(unsigned int proc_num) { 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 @@ void gicv3_cpuif_disable(unsigned int proc_num) 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 @@ unsigned int gicv3_get_interrupt_type(unsigned int id, 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 473fdb15b..f95cfab65 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 6a759c5a7..0baa8267e 100644 --- a/plat/arm/board/fvp/platform.mk +++ b/plat/arm/board/fvp/platform.mk @@ -31,13 +31,18 @@ endif $(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 \