diff options
-rw-r--r-- | docs/porting-guide.rst | 33 | ||||
-rw-r--r-- | drivers/arm/gic/v3/arm_gicv3_common.c | 105 | ||||
-rw-r--r-- | drivers/arm/gic/v3/gic500.c | 22 | ||||
-rw-r--r-- | drivers/arm/gic/v3/gic600.c | 27 | ||||
-rw-r--r-- | drivers/arm/gic/v3/gicv3_main.c | 363 | ||||
-rw-r--r-- | drivers/arm/gic/v3/gicv3_private.h | 185 | ||||
-rw-r--r-- | include/drivers/arm/arm_gicv3_common.h | 20 | ||||
-rw-r--r-- | include/drivers/arm/gic_common.h | 6 | ||||
-rw-r--r-- | include/drivers/arm/gicv3.h | 110 | ||||
-rw-r--r-- | include/lib/utils_def.h | 11 | ||||
-rw-r--r-- | plat/arm/board/fvp/platform.mk | 3 |
11 files changed, 842 insertions, 43 deletions
diff --git a/docs/porting-guide.rst b/docs/porting-guide.rst index 97985666..6c07b2eb 100644 --- a/docs/porting-guide.rst +++ b/docs/porting-guide.rst @@ -2107,13 +2107,32 @@ power domain levels. The handler needs to perform power management operation corresponding to the local state at each power level. The generic code expects the handler to succeed. -The difference between turning a power domain off versus suspending it -is that in the former case, the power domain is expected to re-initialize -its state when it is next powered on (see ``pwr_domain_on_finish()``). In the -latter case, the power domain is expected to save enough state so that it can -resume execution by restoring this state when its powered on (see +The difference between turning a power domain off versus suspending it is that +in the former case, the power domain is expected to re-initialize its state +when it is next powered on (see ``pwr_domain_on_finish()``). In the latter +case, the power domain is expected to save enough state so that it can resume +execution by restoring this state when its powered on (see ``pwr_domain_suspend_finish()``). +When suspending a core, the platform can also choose to power off the GICv3 +Redistributor and ITS through an implementation-defined sequence. To achieve +this safely, the ITS context must be saved first. The architectural part is +implemented by the ``gicv3_its_save_disable()`` helper, but most of the needed +sequence is implementation defined and it is therefore the responsibility of +the platform code to implement the necessary sequence. Then the GIC +Redistributor context can be saved using the ``gicv3_rdistif_save()`` helper. +Powering off the Redistributor requires the implementation to support it and it +is the responsibility of the platform code to execute the right implementation +defined sequence. + +When a system suspend is requested, the platform can also make use of the +``gicv3_distif_save()`` helper to save the context of the GIC Distributor after +it has saved the context of the Redistributors and ITS of all the cores in the +system. The context of the Distributor can be large and may require it to be +allocated in a special area if it cannot fit in the platform's global static +data, for example in DRAM. The Distributor can then be powered down using an +implementation-defined sequence. + plat\_psci\_ops.pwr\_domain\_pwr\_down\_wfi() ............................................. @@ -2159,6 +2178,10 @@ The ``target_state`` (first argument) has a similar meaning as described in the ``pwr_domain_on_finish()`` operation. The generic code expects the platform to succeed. +If the Distributor, Redistributors or ITS have been powered off as part of a +suspend, their context must be restored in this function in the reverse order +to how they were saved during suspend sequence. + plat\_psci\_ops.system\_off() ............................. diff --git a/drivers/arm/gic/v3/arm_gicv3_common.c b/drivers/arm/gic/v3/arm_gicv3_common.c new file mode 100644 index 00000000..8d552ca5 --- /dev/null +++ b/drivers/arm/gic/v3/arm_gicv3_common.c @@ -0,0 +1,105 @@ +/* + * Copyright (c) 2017, ARM Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +/* + * Driver for implementation defined features that are identical in ARM GICv3 +* implementations (GIC-500 and GIC-600 for now). This driver only overrides +* APIs that are different to those generic ones in GICv3 driver. + */ + +#include <arch_helpers.h> +#include <assert.h> +#include <gicv3.h> + +#include "gicv3_private.h" +#include "arm_gicv3_common.h" + +/* + * Flush the internal GIC cache of the LPIs pending tables to memory before + * saving the state of the Redistributor. This is required before powering off + * the GIC when the pending status must be preserved. + * `rdist_proc_num` is the processor number corresponding to the Redistributor of the + * current CPU. + */ +void arm_gicv3_distif_pre_save(unsigned int rdist_proc_num) +{ + uintptr_t gicr_base = 0; + + assert(gicv3_driver_data); + assert(gicv3_driver_data->rdistif_base_addrs); + + /* + * The GICR_WAKER.Sleep bit should be set only when both + * GICR_WAKER.ChildrenAsleep and GICR_WAKER.ProcessorSleep are set on + * all the Redistributors. + */ + for (unsigned int i = 0; i < gicv3_driver_data->rdistif_num; i++) { + gicr_base = gicv3_driver_data->rdistif_base_addrs[i]; + assert(gicr_base); + assert(gicr_read_waker(gicr_base) & WAKER_CA_BIT); + assert(gicr_read_waker(gicr_base) & WAKER_PS_BIT); + } + + gicr_base = gicv3_driver_data->rdistif_base_addrs[rdist_proc_num]; + /* + * According to the TRM, there is only one instance of the + * GICR_WAKER.Sleep and GICR_WAKER.Quiescent bits that can be accessed + * through any of the Redistributor. + */ + + /* + * Set GICR_WAKER.Sleep + * After this point, the system must be configured so that the + * wake_request signals for the right cores are asserted when a wakeup + * interrupt is detected. The GIC will not be able to do that anymore + * when the GICR_WAKER.Sleep bit is set to 1. + */ + gicr_write_waker(gicr_base, gicr_read_waker(gicr_base) | WAKER_SL_BIT); + + /* Wait until the GICR_WAKER.Quiescent bit is set */ + while (!(gicr_read_waker(gicr_base) & WAKER_QSC_BIT)) + ; +} + +/* + * Allow the LPIs pending state to be read back from the tables in memory after + * having restored the state of the GIC Redistributor. + */ +void arm_gicv3_distif_post_restore(unsigned int rdist_proc_num) +{ + uintptr_t gicr_base; + + assert(gicv3_driver_data); + assert(gicv3_driver_data->rdistif_base_addrs); + + /* + * According to the TRM, there is only one instance of the + * GICR_WAKER.Sleep and GICR_WAKER.Quiescent bits that can be accessed + * through any of the Redistributor. + */ + gicr_base = gicv3_driver_data->rdistif_base_addrs[rdist_proc_num]; + assert(gicr_base); + + /* + * Writes to GICR_WAKER.Sleep bit are ignored if GICR_WAKER.Quiescent + * bit is not set. We should be alright on power on path, therefore + * coming out of sleep and Quiescent should be set, but we assert in + * case. + */ + assert(gicr_read_waker(gicr_base) & WAKER_QSC_BIT); + + /* Clear GICR_WAKER.Sleep */ + gicr_write_waker(gicr_base, gicr_read_waker(gicr_base) & ~WAKER_SL_BIT); + + /* + * We don't know if the effects of setting GICR_WAKER.Sleep bit is + * instantaneous, so we wait until the interface is not Quiescent + * anymore. + */ + while (gicr_read_waker(gicr_base) & WAKER_QSC_BIT) + ; +} + diff --git a/drivers/arm/gic/v3/gic500.c b/drivers/arm/gic/v3/gic500.c new file mode 100644 index 00000000..f03e33f8 --- /dev/null +++ b/drivers/arm/gic/v3/gic500.c @@ -0,0 +1,22 @@ +/* + * Copyright (c) 2017, ARM Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +/* + * Driver for GIC500-specific features. This driver only overrides APIs that are + * different to those generic ones in GICv3 driver. + */ +#include "gicv3_private.h" + +void gicv3_distif_pre_save(unsigned int proc_num) +{ + arm_gicv3_distif_pre_save(proc_num); +} + +void gicv3_distif_post_restore(unsigned int proc_num) +{ + arm_gicv3_distif_post_restore(proc_num); +} + diff --git a/drivers/arm/gic/v3/gic600.c b/drivers/arm/gic/v3/gic600.c index 4ea31ab1..eb4fc548 100644 --- a/drivers/arm/gic/v3/gic600.c +++ b/drivers/arm/gic/v3/gic600.c @@ -18,32 +18,29 @@ #include "gicv3_private.h" /* GIC600-specific register offsets */ -#define GICR_PWRR 0x24 +#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) +#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; +#define PWRR_ON (0 << PWRR_RDPD_SHIFT) +#define PWRR_OFF (1 << PWRR_RDPD_SHIFT) /* GIC600-specific accessor functions */ static void gicr_write_pwrr(uintptr_t base, unsigned int val) { - mmio_write_32(base + GICR_PWRR, val); + mmio_write_32(base + GICR_PWRR, val); } static uint32_t gicr_read_pwrr(uintptr_t base) { - return mmio_read_32(base + GICR_PWRR); + return mmio_read_32(base + GICR_PWRR); } static int gicr_group_powering_down(uint32_t pwrr) @@ -82,6 +79,16 @@ static void gic600_pwr_off(uintptr_t base) } } +void gicv3_distif_pre_save(unsigned int proc_num) +{ + arm_gicv3_distif_pre_save(proc_num); +} + +void gicv3_distif_post_restore(unsigned int proc_num) +{ + arm_gicv3_distif_post_restore(proc_num); +} + /* * Power off GIC600 redistributor */ diff --git a/drivers/arm/gic/v3/gicv3_main.c b/drivers/arm/gic/v3/gicv3_main.c index b68d9988..7282bdc4 100644 --- a/drivers/arm/gic/v3/gicv3_main.c +++ b/drivers/arm/gic/v3/gicv3_main.c @@ -21,6 +21,27 @@ static unsigned int gicv2_compat; #pragma weak gicv3_rdistif_off #pragma weak gicv3_rdistif_on + +/* Helper macros to save and restore GICD registers to and from the context */ +#define RESTORE_GICD_REGS(base, ctx, intr_num, reg, REG) \ + do { \ + for (unsigned int int_id = MIN_SPI_ID; int_id < intr_num; \ + int_id += (1 << REG##_SHIFT)) { \ + gicd_write_##reg(base, int_id, \ + ctx->gicd_##reg[(int_id - MIN_SPI_ID) >> REG##_SHIFT]); \ + } \ + } while (0) + +#define SAVE_GICD_REGS(base, ctx, intr_num, reg, REG) \ + do { \ + for (unsigned int int_id = MIN_SPI_ID; int_id < intr_num; \ + int_id += (1 << REG##_SHIFT)) { \ + ctx->gicd_##reg[(int_id - MIN_SPI_ID) >> REG##_SHIFT] =\ + gicd_read_##reg(base, int_id); \ + } \ + } while (0) + + /******************************************************************************* * This function initialises the ARM GICv3 driver in EL3 with provided platform * inputs. @@ -406,3 +427,345 @@ unsigned int gicv3_get_interrupt_type(unsigned int id, /* Else it is a Group 0 Secure interrupt */ return INTR_GROUP0; } + +/***************************************************************************** + * Function to save and disable the GIC ITS register context. The power + * management of GIC ITS is implementation-defined and this function doesn't + * save any memory structures required to support ITS. As the sequence to save + * this state is implementation defined, it should be executed in platform + * specific code. Calling this function alone and then powering down the GIC and + * ITS without implementing the aforementioned platform specific code will + * corrupt the ITS state. + * + * This function must be invoked after the GIC CPU interface is disabled. + *****************************************************************************/ +void gicv3_its_save_disable(uintptr_t gits_base, gicv3_its_ctx_t * const its_ctx) +{ + int i; + + assert(gicv3_driver_data); + assert(IS_IN_EL3()); + assert(its_ctx); + assert(gits_base); + + its_ctx->gits_ctlr = gits_read_ctlr(gits_base); + + /* Disable the ITS */ + gits_write_ctlr(gits_base, its_ctx->gits_ctlr & + (~GITS_CTLR_ENABLED_BIT)); + + /* Wait for quiescent state */ + gits_wait_for_quiescent_bit(gits_base); + + its_ctx->gits_cbaser = gits_read_cbaser(gits_base); + its_ctx->gits_cwriter = gits_read_cwriter(gits_base); + + for (i = 0; i < ARRAY_SIZE(its_ctx->gits_baser); i++) + its_ctx->gits_baser[i] = gits_read_baser(gits_base, i); +} + +/***************************************************************************** + * Function to restore the GIC ITS register context. The power + * management of GIC ITS is implementation defined and this function doesn't + * restore any memory structures required to support ITS. The assumption is + * that these structures are in memory and are retained during system suspend. + * + * This must be invoked before the GIC CPU interface is enabled. + *****************************************************************************/ +void gicv3_its_restore(uintptr_t gits_base, const gicv3_its_ctx_t * const its_ctx) +{ + int i; + + assert(gicv3_driver_data); + assert(IS_IN_EL3()); + assert(its_ctx); + assert(gits_base); + + /* Assert that the GITS is disabled and quiescent */ + assert((gits_read_ctlr(gits_base) & GITS_CTLR_ENABLED_BIT) == 0); + assert((gits_read_ctlr(gits_base) & GITS_CTLR_QUIESCENT_BIT) == 1); + + gits_write_cbaser(gits_base, its_ctx->gits_cbaser); + gits_write_cwriter(gits_base, its_ctx->gits_cwriter); + + for (i = 0; i < ARRAY_SIZE(its_ctx->gits_baser); i++) + gits_write_baser(gits_base, i, its_ctx->gits_baser[i]); + + /* Restore the ITS CTLR but leave the ITS disabled */ + gits_write_ctlr(gits_base, its_ctx->gits_ctlr & + (~GITS_CTLR_ENABLED_BIT)); +} + +/***************************************************************************** + * Function to save the GIC Redistributor register context. This function + * must be invoked after CPU interface disable and prior to Distributor save. + *****************************************************************************/ +void gicv3_rdistif_save(unsigned int proc_num, gicv3_redist_ctx_t * const rdist_ctx) +{ + uintptr_t gicr_base; + unsigned int int_id; + + assert(gicv3_driver_data); + assert(proc_num < gicv3_driver_data->rdistif_num); + assert(gicv3_driver_data->rdistif_base_addrs); + assert(IS_IN_EL3()); + assert(rdist_ctx); + + gicr_base = gicv3_driver_data->rdistif_base_addrs[proc_num]; + + /* + * Wait for any write to GICR_CTLR to complete before trying to save any + * state. + */ + gicr_wait_for_pending_write(gicr_base); + + rdist_ctx->gicr_ctlr = gicr_read_ctlr(gicr_base); + + rdist_ctx->gicr_propbaser = gicr_read_propbaser(gicr_base); + rdist_ctx->gicr_pendbaser = gicr_read_pendbaser(gicr_base); + + rdist_ctx->gicr_igroupr0 = gicr_read_igroupr0(gicr_base); + rdist_ctx->gicr_isenabler0 = gicr_read_isenabler0(gicr_base); + rdist_ctx->gicr_ispendr0 = gicr_read_ispendr0(gicr_base); + rdist_ctx->gicr_isactiver0 = gicr_read_isactiver0(gicr_base); + rdist_ctx->gicr_icfgr0 = gicr_read_icfgr0(gicr_base); + rdist_ctx->gicr_icfgr1 = gicr_read_icfgr1(gicr_base); + rdist_ctx->gicr_igrpmodr0 = gicr_read_igrpmodr0(gicr_base); + rdist_ctx->gicr_nsacr = gicr_read_nsacr(gicr_base); + for (int_id = MIN_SGI_ID; int_id < TOTAL_PCPU_INTR_NUM; + int_id += (1 << IPRIORITYR_SHIFT)) { + rdist_ctx->gicr_ipriorityr[(int_id - MIN_SGI_ID) >> IPRIORITYR_SHIFT] = + gicr_read_ipriorityr(gicr_base, int_id); + } + + + /* + * Call the pre-save hook that implements the IMP DEF sequence that may + * be required on some GIC implementations. As this may need to access + * the Redistributor registers, we pass it proc_num. + */ + gicv3_distif_pre_save(proc_num); +} + +/***************************************************************************** + * Function to restore the GIC Redistributor register context. We disable + * LPI and per-cpu interrupts before we start restore of the Redistributor. + * This function must be invoked after Distributor restore but prior to + * CPU interface enable. The pending and active interrupts are restored + * after the interrupts are fully configured and enabled. + *****************************************************************************/ +void gicv3_rdistif_init_restore(unsigned int proc_num, + const gicv3_redist_ctx_t * const rdist_ctx) +{ + uintptr_t gicr_base; + unsigned int int_id; + + assert(gicv3_driver_data); + assert(proc_num < gicv3_driver_data->rdistif_num); + assert(gicv3_driver_data->rdistif_base_addrs); + assert(IS_IN_EL3()); + assert(rdist_ctx); + + gicr_base = gicv3_driver_data->rdistif_base_addrs[proc_num]; + + /* Power on redistributor */ + gicv3_rdistif_on(proc_num); + + /* + * Call the post-restore hook that implements the IMP DEF sequence that + * may be required on some GIC implementations. As this may need to + * access the Redistributor registers, we pass it proc_num. + */ + gicv3_distif_post_restore(proc_num); + + /* + * Disable all SGIs (imp. def.)/PPIs before configuring them. This is a + * more scalable approach as it avoids clearing the enable bits in the + * GICD_CTLR + */ + gicr_write_icenabler0(gicr_base, ~0); + /* Wait for pending writes to GICR_ICENABLER */ + gicr_wait_for_pending_write(gicr_base); + + /* + * Disable the LPIs to avoid unpredictable behavior when writing to + * GICR_PROPBASER and GICR_PENDBASER. + */ + gicr_write_ctlr(gicr_base, + rdist_ctx->gicr_ctlr & ~(GICR_CTLR_EN_LPIS_BIT)); + + /* Restore registers' content */ + gicr_write_propbaser(gicr_base, rdist_ctx->gicr_propbaser); + gicr_write_pendbaser(gicr_base, rdist_ctx->gicr_pendbaser); + + gicr_write_igroupr0(gicr_base, rdist_ctx->gicr_igroupr0); + + for (int_id = MIN_SGI_ID; int_id < TOTAL_PCPU_INTR_NUM; + int_id += (1 << IPRIORITYR_SHIFT)) { + gicr_write_ipriorityr(gicr_base, int_id, + rdist_ctx->gicr_ipriorityr[ + (int_id - MIN_SGI_ID) >> IPRIORITYR_SHIFT]); + } + + gicr_write_icfgr0(gicr_base, rdist_ctx->gicr_icfgr0); + gicr_write_icfgr1(gicr_base, rdist_ctx->gicr_icfgr1); + gicr_write_igrpmodr0(gicr_base, rdist_ctx->gicr_igrpmodr0); + gicr_write_nsacr(gicr_base, rdist_ctx->gicr_nsacr); + + /* Restore after group and priorities are set */ + gicr_write_ispendr0(gicr_base, rdist_ctx->gicr_ispendr0); + gicr_write_isactiver0(gicr_base, rdist_ctx->gicr_isactiver0); + + /* + * Wait for all writes to the Distributor to complete before enabling + * the SGI and PPIs. + */ + gicr_wait_for_upstream_pending_write(gicr_base); + gicr_write_isenabler0(gicr_base, rdist_ctx->gicr_isenabler0); + + /* + * Restore GICR_CTLR.Enable_LPIs bit and wait for pending writes in case + * the first write to GICR_CTLR was still in flight (this write only + * restores GICR_CTLR.Enable_LPIs and no waiting is required for this + * bit). + */ + gicr_write_ctlr(gicr_base, rdist_ctx->gicr_ctlr); + gicr_wait_for_pending_write(gicr_base); +} + +/***************************************************************************** + * Function to save the GIC Distributor register context. This function + * must be invoked after CPU interface disable and Redistributor save. + *****************************************************************************/ +void gicv3_distif_save(gicv3_dist_ctx_t * const dist_ctx) +{ + unsigned int num_ints; + + assert(gicv3_driver_data); + assert(gicv3_driver_data->gicd_base); + assert(IS_IN_EL3()); + assert(dist_ctx); + + uintptr_t gicd_base = gicv3_driver_data->gicd_base; + + num_ints = gicd_read_typer(gicd_base); + num_ints &= TYPER_IT_LINES_NO_MASK; + num_ints = (num_ints + 1) << 5; + + assert(num_ints <= MAX_SPI_ID + 1); + + /* Wait for pending write to complete */ + gicd_wait_for_pending_write(gicd_base); + + /* Save the GICD_CTLR */ + dist_ctx->gicd_ctlr = gicd_read_ctlr(gicd_base); + + /* Save GICD_IGROUPR for INTIDs 32 - 1020 */ + SAVE_GICD_REGS(gicd_base, dist_ctx, num_ints, igroupr, IGROUPR); + + /* Save GICD_ISENABLER for INT_IDs 32 - 1020 */ + SAVE_GICD_REGS(gicd_base, dist_ctx, num_ints, isenabler, ISENABLER); + + /* Save GICD_ISPENDR for INTIDs 32 - 1020 */ + SAVE_GICD_REGS(gicd_base, dist_ctx, num_ints, ispendr, ISPENDR); + + /* Save GICD_ISACTIVER for INTIDs 32 - 1020 */ + SAVE_GICD_REGS(gicd_base, dist_ctx, num_ints, isactiver, ISACTIVER); + + /* Save GICD_IPRIORITYR for INTIDs 32 - 1020 */ + SAVE_GICD_REGS(gicd_base, dist_ctx, num_ints, ipriorityr, IPRIORITYR); + + /* Save GICD_ICFGR for INTIDs 32 - 1020 */ + SAVE_GICD_REGS(gicd_base, dist_ctx, num_ints, icfgr, ICFGR); + + /* Save GICD_IGRPMODR for INTIDs 32 - 1020 */ + SAVE_GICD_REGS(gicd_base, dist_ctx, num_ints, igrpmodr, IGRPMODR); + + /* Save GICD_NSACR for INTIDs 32 - 1020 */ + SAVE_GICD_REGS(gicd_base, dist_ctx, num_ints, nsacr, NSACR); + + /* Save GICD_IROUTER for INTIDs 32 - 1024 */ + SAVE_GICD_REGS(gicd_base, dist_ctx, num_ints, irouter, IROUTER); + + /* + * GICD_ITARGETSR<n> and GICD_SPENDSGIR<n> are RAZ/WI when + * GICD_CTLR.ARE_(S|NS) bits are set which is the case for our GICv3 + * driver. + */ +} + +/***************************************************************************** + * Function to restore the GIC Distributor register context. We disable G0, G1S + * and G1NS interrupt groups before we start restore of the Distributor. This + * function must be invoked prior to Redistributor restore and CPU interface + * enable. The pending and active interrupts are restored after the interrupts + * are fully configured and enabled. + *****************************************************************************/ +void gicv3_distif_init_restore(const gicv3_dist_ctx_t * const dist_ctx) +{ + unsigned int num_ints = 0; + + assert(gicv3_driver_data); + assert(gicv3_driver_data->gicd_base); + assert(IS_IN_EL3()); + assert(dist_ctx); + + uintptr_t gicd_base = gicv3_driver_data->gicd_base; + + /* + * Clear the "enable" bits for G0/G1S/G1NS interrupts before configuring + * the ARE_S bit. The Distributor might generate a system error + * otherwise. + */ + gicd_clr_ctlr(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(gicd_base, CTLR_ARE_S_BIT | CTLR_ARE_NS_BIT, RWP_TRUE); + + num_ints = gicd_read_typer(gicd_base); + num_ints &= TYPER_IT_LINES_NO_MASK; + num_ints = (num_ints + 1) << 5; + + assert(num_ints <= MAX_SPI_ID + 1); + + /* Restore GICD_IGROUPR for INTIDs 32 - 1020 */ + RESTORE_GICD_REGS(gicd_base, dist_ctx, num_ints, igroupr, IGROUPR); + + /* Restore GICD_IPRIORITYR for INTIDs 32 - 1020 */ + RESTORE_GICD_REGS(gicd_base, dist_ctx, num_ints, ipriorityr, IPRIORITYR); + + /* Restore GICD_ICFGR for INTIDs 32 - 1020 */ + RESTORE_GICD_REGS(gicd_base, dist_ctx, num_ints, icfgr, ICFGR); + + /* Restore GICD_IGRPMODR for INTIDs 32 - 1020 */ + RESTORE_GICD_REGS(gicd_base, dist_ctx, num_ints, igrpmodr, IGRPMODR); + + /* Restore GICD_NSACR for INTIDs 32 - 1020 */ + RESTORE_GICD_REGS(gicd_base, dist_ctx, num_ints, nsacr, NSACR); + + /* Restore GICD_IROUTER for INTIDs 32 - 1020 */ + RESTORE_GICD_REGS(gicd_base, dist_ctx, num_ints, irouter, IROUTER); + + /* + * Restore ISENABLER, ISPENDR and ISACTIVER after the interrupts are + * configured. + */ + + /* Restore GICD_ISENABLER for INT_IDs 32 - 1020 */ + RESTORE_GICD_REGS(gicd_base, dist_ctx, num_ints, isenabler, ISENABLER); + + /* Restore GICD_ISPENDR for INTIDs 32 - 1020 */ + RESTORE_GICD_REGS(gicd_base, dist_ctx, num_ints, ispendr, ISPENDR); + + /* Restore GICD_ISACTIVER for INTIDs 32 - 1020 */ + RESTORE_GICD_REGS(gicd_base, dist_ctx, num_ints, isactiver, ISACTIVER); + + /* Restore the GICD_CTLR */ + gicd_write_ctlr(gicd_base, dist_ctx->gicd_ctlr); + gicd_wait_for_pending_write(gicd_base); + +} diff --git a/drivers/arm/gic/v3/gicv3_private.h b/drivers/arm/gic/v3/gicv3_private.h index f95cfab6..59298edf 100644 --- a/drivers/arm/gic/v3/gicv3_private.h +++ b/drivers/arm/gic/v3/gicv3_private.h @@ -7,6 +7,7 @@ #ifndef __GICV3_PRIVATE_H__ #define __GICV3_PRIVATE_H__ +#include <assert.h> #include <gic_common.h> #include <gicv3.h> #include <mmio.h> @@ -22,17 +23,6 @@ #define RWP_FALSE 0 /* - * Macro to wait for updates to : - * GICD_CTLR[2:0] - the Group Enables - * GICD_CTLR[5:4] - the ARE bits - * GICD_ICENABLERn - the clearing of enable state for SPIs - */ -#define gicd_wait_for_pending_write(gicd_base) \ - do { \ - ; \ - } while (gicd_read_ctlr(gicd_base) & GICD_CTLR_RWP_BIT) - -/* * Macro to convert an mpidr to a value suitable for programming into a * GICD_IROUTER. Bits[31:24] in the MPIDR are cleared as they are not relevant * to GICv3. @@ -42,18 +32,6 @@ (irm & IROUTER_IRM_MASK) << IROUTER_IRM_SHIFT) /* - * Macro to wait for updates to : - * GICR_ICENABLER0 - * GICR_CTLR.DPG1S - * GICR_CTLR.DPG1NS - * GICR_CTLR.DPG0 - */ -#define gicr_wait_for_pending_write(gicr_base) \ - do { \ - ; \ - } while (gicr_read_ctlr(gicr_base) & GICR_CTLR_RWP_BIT) - -/* * Macro to convert a GICR_TYPER affinity value into a MPIDR value. Bits[31:24] * are zeroes. */ @@ -66,6 +44,11 @@ #endif /******************************************************************************* + * GICv3 private global variables declarations + ******************************************************************************/ +extern const gicv3_driver_data_t *gicv3_driver_data; + +/******************************************************************************* * Private GICv3 function prototypes for accessing entire registers. * Note: The raw register values correspond to multiple interrupt IDs and * the number of interrupt IDs involved depends on the register accessed. @@ -116,6 +99,18 @@ void gicv3_rdistif_mark_core_asleep(uintptr_t gicr_base); /******************************************************************************* * GIC Distributor interface accessors ******************************************************************************/ +/* + * Wait for updates to : + * GICD_CTLR[2:0] - the Group Enables + * GICD_CTLR[5:4] - the ARE bits + * GICD_ICENABLERn - the clearing of enable state for SPIs + */ +static inline void gicd_wait_for_pending_write(uintptr_t gicd_base) +{ + while (gicd_read_ctlr(gicd_base) & GICD_CTLR_RWP_BIT) + ; +} + static inline unsigned int gicd_read_pidr2(uintptr_t base) { return mmio_read_32(base + GICD_PIDR2_GICV3); @@ -161,6 +156,11 @@ static inline unsigned long long gicr_read_ctlr(uintptr_t base) return mmio_read_64(base + GICR_CTLR); } +static inline void gicr_write_ctlr(uintptr_t base, uint64_t val) +{ + mmio_write_64(base + GICR_CTLR, val); +} + static inline unsigned long long gicr_read_typer(uintptr_t base) { return mmio_read_64(base + GICR_TYPER); @@ -176,6 +176,29 @@ static inline void gicr_write_waker(uintptr_t base, unsigned int val) mmio_write_32(base + GICR_WAKER, val); } +/* + * Wait for updates to : + * GICR_ICENABLER0 + * GICR_CTLR.DPG1S + * GICR_CTLR.DPG1NS + * GICR_CTLR.DPG0 + */ +static inline void gicr_wait_for_pending_write(uintptr_t gicr_base) +{ + while (gicr_read_ctlr(gicr_base) & GICR_CTLR_RWP_BIT) + ; +} + +static inline void gicr_wait_for_upstream_pending_write(uintptr_t gicr_base) +{ + while (gicr_read_ctlr(gicr_base) & GICR_CTLR_UWP_BIT) + ; +} + +/* Private implementation of Distributor power control hooks */ +void arm_gicv3_distif_pre_save(unsigned int rdist_proc_num); +void arm_gicv3_distif_post_restore(unsigned int rdist_proc_num); + /******************************************************************************* * GIC Re-distributor functions for accessing entire registers. * Note: The raw register values correspond to multiple interrupt IDs and @@ -206,6 +229,16 @@ static inline unsigned int gicr_read_igroupr0(uintptr_t base) return mmio_read_32(base + GICR_IGROUPR0); } +static inline unsigned int gicr_read_ispendr0(uintptr_t base) +{ + return mmio_read_32(base + GICR_ISPENDR0); +} + +static inline void gicr_write_ispendr0(uintptr_t base, unsigned int val) +{ + mmio_write_32(base + GICR_ISPENDR0, val); +} + static inline void gicr_write_igroupr0(uintptr_t base, unsigned int val) { mmio_write_32(base + GICR_IGROUPR0, val); @@ -221,14 +254,120 @@ static inline void gicr_write_igrpmodr0(uintptr_t base, unsigned int val) mmio_write_32(base + GICR_IGRPMODR0, val); } +static inline unsigned int gicr_read_nsacr(uintptr_t base) +{ + return mmio_read_32(base + GICR_NSACR); +} + +static inline void gicr_write_nsacr(uintptr_t base, unsigned int val) +{ + mmio_write_32(base + GICR_NSACR, val); +} + +static inline unsigned int gicr_read_isactiver0(uintptr_t base) +{ + return mmio_read_32(base + GICR_ISACTIVER0); +} + +static inline void gicr_write_isactiver0(uintptr_t base, unsigned int val) +{ + mmio_write_32(base + GICR_ISACTIVER0, val); +} + +static inline unsigned int gicr_read_icfgr0(uintptr_t base) +{ + return mmio_read_32(base + GICR_ICFGR0); +} + static inline unsigned int gicr_read_icfgr1(uintptr_t base) { return mmio_read_32(base + GICR_ICFGR1); } +static inline void gicr_write_icfgr0(uintptr_t base, unsigned int val) +{ + mmio_write_32(base + GICR_ICFGR0, val); +} + static inline void gicr_write_icfgr1(uintptr_t base, unsigned int val) { mmio_write_32(base + GICR_ICFGR1, val); } +static inline unsigned int gicr_read_propbaser(uintptr_t base) +{ + return mmio_read_32(base + GICR_PROPBASER); +} + +static inline void gicr_write_propbaser(uintptr_t base, unsigned int val) +{ + mmio_write_32(base + GICR_PROPBASER, val); +} + +static inline unsigned int gicr_read_pendbaser(uintptr_t base) +{ + return mmio_read_32(base + GICR_PENDBASER); +} + +static inline void gicr_write_pendbaser(uintptr_t base, unsigned int val) +{ + mmio_write_32(base + GICR_PENDBASER, val); +} + +/******************************************************************************* + * GIC ITS functions to read and write entire ITS registers. + ******************************************************************************/ +static inline uint32_t gits_read_ctlr(uintptr_t base) +{ + return mmio_read_32(base + GITS_CTLR); +} + +static inline void gits_write_ctlr(uintptr_t base, unsigned int val) +{ + mmio_write_32(base + GITS_CTLR, val); +} + +static inline uint64_t gits_read_cbaser(uintptr_t base) +{ + return mmio_read_64(base + GITS_CBASER); +} + +static inline void gits_write_cbaser(uintptr_t base, uint64_t val) +{ + mmio_write_32(base + GITS_CBASER, val); +} + +static inline uint64_t gits_read_cwriter(uintptr_t base) +{ + return mmio_read_64(base + GITS_CWRITER); +} + +static inline void gits_write_cwriter(uintptr_t base, uint64_t val) +{ + mmio_write_32(base + GITS_CWRITER, val); +} + +static inline uint64_t gits_read_baser(uintptr_t base, unsigned int its_table_id) +{ + assert(its_table_id < 8); + return mmio_read_64(base + GITS_BASER + (8 * its_table_id)); +} + +static inline void gits_write_baser(uintptr_t base, unsigned int its_table_id, uint64_t val) +{ + assert(its_table_id < 8); + mmio_write_64(base + GITS_BASER + (8 * its_table_id), val); +} + +/* + * Wait for Quiescent bit when GIC ITS is disabled + */ +static inline void gits_wait_for_quiescent_bit(uintptr_t gits_base) +{ + assert(!(gits_read_ctlr(gits_base) & GITS_CTLR_ENABLED_BIT)); + while ((gits_read_ctlr(gits_base) & GITS_CTLR_QUIESCENT_BIT) == 0) + ; +} + + #endif /* __GICV3_PRIVATE_H__ */ diff --git a/include/drivers/arm/arm_gicv3_common.h b/include/drivers/arm/arm_gicv3_common.h new file mode 100644 index 00000000..8970e3f4 --- /dev/null +++ b/include/drivers/arm/arm_gicv3_common.h @@ -0,0 +1,20 @@ +/* + * Copyright (c) 2017, ARM Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ +#ifndef __ARM_GICV3_COMMON_H__ +#define __ARM_GICV3_COMMON_H__ + +/******************************************************************************* + * GIC500/GIC600 Re-distributor interface registers & constants + ******************************************************************************/ + +/* GICR_WAKER implementation-defined bit definitions */ +#define WAKER_SL_SHIFT 0 +#define WAKER_QSC_SHIFT 31 + +#define WAKER_SL_BIT (1U << WAKER_SL_SHIFT) +#define WAKER_QSC_BIT (1U << WAKER_QSC_SHIFT) + +#endif /* __ARM_GICV3_COMMON_H__ */ diff --git a/include/drivers/arm/gic_common.h b/include/drivers/arm/gic_common.h index 7e097adc..b9cae802 100644 --- a/include/drivers/arm/gic_common.h +++ b/include/drivers/arm/gic_common.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 */ @@ -14,6 +14,10 @@ #define MIN_SGI_ID 0 #define MIN_PPI_ID 16 #define MIN_SPI_ID 32 +#define MAX_SPI_ID 1019 + +#define TOTAL_SPI_INTR_NUM (MAX_SPI_ID - MIN_SPI_ID + 1) +#define TOTAL_PCPU_INTR_NUM (MIN_SPI_ID - MIN_SGI_ID) /* Mask for the priority field common to all GIC interfaces */ #define GIC_PRI_MASK 0xff diff --git a/include/drivers/arm/gicv3.h b/include/drivers/arm/gicv3.h index 8d9981ac..c52fe483 100644 --- a/include/drivers/arm/gicv3.h +++ b/include/drivers/arm/gicv3.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,6 +7,8 @@ #ifndef __GICV3_H__ #define __GICV3_H__ +#include "utils_def.h" + /******************************************************************************* * GICv3 miscellaneous definitions ******************************************************************************/ @@ -66,9 +68,12 @@ #define GICD_CTLR_RWP_BIT (1 << GICD_CTLR_RWP_SHIFT) /* GICD_IROUTER shifts and masks */ +#define IROUTER_SHIFT 0 #define IROUTER_IRM_SHIFT 31 #define IROUTER_IRM_MASK 0x1 +#define NUM_OF_DIST_REGS 30 + /******************************************************************************* * GICv3 Re-distributor interface registers & constants ******************************************************************************/ @@ -77,18 +82,29 @@ #define GICR_CTLR 0x0 #define GICR_TYPER 0x08 #define GICR_WAKER 0x14 +#define GICR_PROPBASER 0x70 +#define GICR_PENDBASER 0x78 #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_ISACTIVER0 (GICR_SGIBASE_OFFSET + 0x300) +#define GICR_ICACTIVER0 (GICR_SGIBASE_OFFSET + 0x380) #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) +#define GICR_NSACR (GICR_SGIBASE_OFFSET + 0xe00) /* GICR_CTLR bit definitions */ +#define GICR_CTLR_UWP_SHIFT 31 +#define GICR_CTLR_UWP_MASK 0x1 +#define GICR_CTLR_UWP_BIT (1U << GICR_CTLR_UWP_SHIFT) #define GICR_CTLR_RWP_SHIFT 3 #define GICR_CTLR_RWP_MASK 0x1 -#define GICR_CTLR_RWP_BIT (1 << GICR_CTLR_RWP_SHIFT) +#define GICR_CTLR_RWP_BIT (1U << GICR_CTLR_RWP_SHIFT) +#define GICR_CTLR_EN_LPIS_BIT (1U << 0) /* GICR_WAKER bit definitions */ #define WAKER_CA_SHIFT 2 @@ -111,6 +127,8 @@ #define TYPER_LAST_BIT (1 << TYPER_LAST_SHIFT) +#define NUM_OF_REDIST_REGS 30 + /******************************************************************************* * GICv3 CPU interface registers & constants ******************************************************************************/ @@ -147,10 +165,29 @@ #define IAR1_EL1_INTID_SHIFT 0 #define IAR1_EL1_INTID_MASK 0xffffff +/***************************************************************************** + * GICv3 ITS registers and constants + *****************************************************************************/ + +#define GITS_CTLR 0x0 +#define GITS_IIDR 0x4 +#define GITS_TYPER 0x8 +#define GITS_CBASER 0x80 +#define GITS_CWRITER 0x88 +#define GITS_CREADR 0x90 +#define GITS_BASER 0x100 + +/* GITS_CTLR bit definitions */ +#define GITS_CTLR_ENABLED_BIT 1 +#define GITS_CTLR_QUIESCENT_SHIFT 31 +#define GITS_CTLR_QUIESCENT_BIT (1U << GITS_CTLR_QUIESCENT_SHIFT) + #ifndef __ASSEMBLY__ +#include <gic_common.h> #include <stdint.h> #include <types.h> +#include <utils_def.h> #define gicv3_is_intr_id_special_identifier(id) \ (((id) >= PENDING_G1S_INTID) && ((id) <= GIC_SPURIOUS_INTERRUPT)) @@ -172,6 +209,16 @@ IAR0_EL1_INTID_MASK #define gicv3_end_of_interrupt(id) write_icc_eoir0_el1(id) +/* + * This macro returns the total number of GICD registers corresponding to + * the name. + */ +#define GICD_NUM_REGS(reg_name) \ + DIV_ROUND_UP_2EVAL(TOTAL_SPI_INTR_NUM, (1 << reg_name ## _SHIFT)) + +#define GICR_NUM_REGS(reg_name) \ + DIV_ROUND_UP_2EVAL(TOTAL_PCPU_INTR_NUM, (1 << reg_name ## _SHIFT)) + /******************************************************************************* * This structure describes some of the implementation defined attributes of the * GICv3 IP. It is used by the platform port to specify these attributes in order @@ -229,6 +276,50 @@ typedef struct gicv3_driver_data { mpidr_hash_fn mpidr_to_core_pos; } gicv3_driver_data_t; +typedef struct gicv3_redist_ctx { + /* 64 bits registers */ + uint64_t gicr_propbaser; + uint64_t gicr_pendbaser; + + /* 32 bits registers */ + uint32_t gicr_ctlr; + uint32_t gicr_igroupr0; + uint32_t gicr_isenabler0; + uint32_t gicr_ispendr0; + uint32_t gicr_isactiver0; + uint32_t gicr_ipriorityr[GICR_NUM_REGS(IPRIORITYR)]; + uint32_t gicr_icfgr0; + uint32_t gicr_icfgr1; + uint32_t gicr_igrpmodr0; + uint32_t gicr_nsacr; +} gicv3_redist_ctx_t; + +typedef struct gicv3_dist_ctx { + /* 64 bits registers */ + uint64_t gicd_irouter[TOTAL_SPI_INTR_NUM]; + + /* 32 bits registers */ + uint32_t gicd_ctlr; + uint32_t gicd_igroupr[GICD_NUM_REGS(IGROUPR)]; + uint32_t gicd_isenabler[GICD_NUM_REGS(ISENABLER)]; + uint32_t gicd_ispendr[GICD_NUM_REGS(ISPENDR)]; + uint32_t gicd_isactiver[GICD_NUM_REGS(ISACTIVER)]; + uint32_t gicd_ipriorityr[GICD_NUM_REGS(IPRIORITYR)]; + uint32_t gicd_icfgr[GICD_NUM_REGS(ICFGR)]; + uint32_t gicd_igrpmodr[GICD_NUM_REGS(IGRPMODR)]; + uint32_t gicd_nsacr[GICD_NUM_REGS(NSACR)]; +} gicv3_dist_ctx_t; + +typedef struct gicv3_its_ctx { + /* 64 bits registers */ + uint64_t gits_cbaser; + uint64_t gits_cwriter; + uint64_t gits_baser[8]; + + /* 32 bits registers */ + uint32_t gits_ctlr; +} gicv3_its_ctx_t; + /******************************************************************************* * GICv3 EL3 driver API ******************************************************************************/ @@ -243,7 +334,20 @@ unsigned int gicv3_get_pending_interrupt_type(void); unsigned int gicv3_get_pending_interrupt_id(void); unsigned int gicv3_get_interrupt_type(unsigned int id, unsigned int proc_num); - +void gicv3_distif_init_restore(const gicv3_dist_ctx_t * const dist_ctx); +void gicv3_distif_save(gicv3_dist_ctx_t * const dist_ctx); +/* + * gicv3_distif_post_restore and gicv3_distif_pre_save must be implemented if + * gicv3_distif_save and gicv3_rdistif_init_restore are used. If no + * implementation-defined sequence is needed at these steps, an empty function + * can be provided. + */ +void gicv3_distif_post_restore(unsigned int proc_num); +void gicv3_distif_pre_save(unsigned int proc_num); +void gicv3_rdistif_init_restore(unsigned int proc_num, const gicv3_redist_ctx_t * const rdist_ctx); +void gicv3_rdistif_save(unsigned int proc_num, gicv3_redist_ctx_t * const rdist_ctx); +void gicv3_its_save_disable(uintptr_t gits_base, gicv3_its_ctx_t * const its_ctx); +void gicv3_its_restore(uintptr_t gits_base, const gicv3_its_ctx_t * const its_ctx); #endif /* __ASSEMBLY__ */ #endif /* __GICV3_H__ */ diff --git a/include/lib/utils_def.h b/include/lib/utils_def.h index b397e302..185a1c12 100644 --- a/include/lib/utils_def.h +++ b/include/lib/utils_def.h @@ -18,6 +18,12 @@ #define BIT(nr) (1ULL << (nr)) +/* + * This variant of div_round_up can be used in macro definition but should not + * be used in C code as the `div` parameter is evaluated twice. + */ +#define DIV_ROUND_UP_2EVAL(n, d) (((n) + (d) - 1) / (d)) + #define MIN(x, y) __extension__ ({ \ __typeof__(x) _x = (x); \ __typeof__(y) _y = (y); \ @@ -49,6 +55,11 @@ #define round_down(value, boundary) \ ((value) & ~round_boundary(value, boundary)) +#define div_round_up(val, div) __extension__ ({ \ + __typeof__(div) _div = (div); \ + round_up((val), _div)/_div; \ +}) + /* * Evaluates to 1 if (ptr + inc) overflows, 0 otherwise. * Both arguments must be unsigned pointer values (i.e. uintptr_t). diff --git a/plat/arm/board/fvp/platform.mk b/plat/arm/board/fvp/platform.mk index 6d8aa5fb..29da12ee 100644 --- a/plat/arm/board/fvp/platform.mk +++ b/plat/arm/board/fvp/platform.mk @@ -48,7 +48,8 @@ FVP_GICV3_SOURCES := drivers/arm/gic/common/gic_common.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} +FVP_GIC_SOURCES := ${FVP_GICV3_SOURCES} \ + drivers/arm/gic/v3/gic500.c else ifeq (${FVP_USE_GIC_DRIVER},FVP_GIC600) FVP_GIC_SOURCES := ${FVP_GICV3_SOURCES} \ drivers/arm/gic/v3/gic600.c |