GICv3: add functions for save and restore

During system suspend, the GICv3 Distributor and Redistributor context
can be lost due to power gating of the system power domain. This means
that the GICv3 context needs to be saved prior to system suspend and
restored on wakeup. Currently the consensus is that the Firmware should
be in charge of this. See tf-issues#464 for more details.

This patch introduces helper APIs in the GICv3 driver to save and
restore the Distributor and Redistributor contexts. The GICv3 ITS
context is not considered in this patch because the specification says
that the details of ITS power management is implementation-defined.
These APIs are expected to be appropriately invoked by the platform
layer during system suspend.

Fixes ARM-software/tf-issues#464

Change-Id: Iebb9c6770ab8c4d522546f161fa402d2fe02ec00
Signed-off-by: Soby Mathew <soby.mathew@arm.com>
Signed-off-by: Douglas Raillard <douglas.raillard@arm.com>
This commit is contained in:
Soby Mathew 2017-07-13 15:19:51 +01:00
parent a64b4e626e
commit ebf1ca10e4
10 changed files with 629 additions and 8 deletions

View File

@ -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)
;
}

View File

@ -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);
}

View File

@ -32,9 +32,6 @@
#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)
{
@ -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
*/

View File

@ -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,277 @@ unsigned int gicv3_get_interrupt_type(unsigned int id,
/* Else it is a Group 0 Secure interrupt */
return INTR_GROUP0;
}
/*****************************************************************************
* 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);
}

View File

@ -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>
@ -42,6 +43,11 @@
(((typer_val) >> 32) & 0xffffff))
#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
@ -150,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);
@ -178,6 +189,16 @@ static inline void gicr_wait_for_pending_write(uintptr_t gicr_base)
;
}
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
@ -208,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);
@ -223,14 +254,64 @@ 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);
}
#endif /* __GICV3_PRIVATE_H__ */

View File

@ -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__ */

View File

@ -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

View File

@ -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
******************************************************************************/
@ -149,8 +167,10 @@
#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 +192,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 +259,40 @@ 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;
/*******************************************************************************
* GICv3 EL3 driver API
******************************************************************************/
@ -243,7 +307,18 @@ 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);
#endif /* __ASSEMBLY__ */
#endif /* __GICV3_H__ */

View File

@ -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).

View File

@ -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